mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
Merge branch 'main' into unicode-passwords-smtplib
This commit is contained in:
commit
5d870db7ee
272 changed files with 15699 additions and 9070 deletions
|
@ -9,8 +9,8 @@ ENV WASMTIME_HOME=/opt/wasmtime
|
|||
ENV WASMTIME_VERSION=7.0.0
|
||||
ENV WASMTIME_CPU_ARCH=x86_64
|
||||
|
||||
RUN dnf -y --nodocs install git clang xz python3-blurb dnf-plugins-core && \
|
||||
dnf -y --nodocs builddep python3 && \
|
||||
RUN dnf -y --nodocs --setopt=install_weak_deps=False install /usr/bin/{blurb,clang,curl,git,ln,tar,xz} 'dnf-command(builddep)' && \
|
||||
dnf -y --nodocs --setopt=install_weak_deps=False builddep python3 && \
|
||||
dnf -y clean all
|
||||
|
||||
RUN mkdir ${WASI_SDK_PATH} && \
|
||||
|
|
4
.github/workflows/require-pr-label.yml
vendored
4
.github/workflows/require-pr-label.yml
vendored
|
@ -6,7 +6,7 @@ on:
|
|||
|
||||
jobs:
|
||||
label:
|
||||
name: DO-NOT-MERGE
|
||||
name: DO-NOT-MERGE / unresolved review
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
|
@ -15,4 +15,4 @@ jobs:
|
|||
with:
|
||||
mode: exactly
|
||||
count: 0
|
||||
labels: "DO-NOT-MERGE"
|
||||
labels: "DO-NOT-MERGE, awaiting changes, awaiting change review"
|
||||
|
|
|
@ -232,6 +232,15 @@ Type Objects
|
|||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
|
||||
|
||||
Attempt to assign a version tag to the given type.
|
||||
|
||||
Returns 1 if the type already had a valid version tag or a new one was
|
||||
assigned, or 0 if a new tag could not be assigned.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
||||
Creating Heap-Allocated Types
|
||||
.............................
|
||||
|
|
25
Doc/conf.py
25
Doc/conf.py
|
@ -76,6 +76,13 @@ venvdir = os.getenv('VENVDIR')
|
|||
if venvdir is not None:
|
||||
exclude_patterns.append(venvdir + '/*')
|
||||
|
||||
nitpick_ignore = [
|
||||
# Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot
|
||||
# be resolved, as the method is currently undocumented. For context, see
|
||||
# https://github.com/python/cpython/pull/103289.
|
||||
('py:meth', '_SubParsersAction.add_parser'),
|
||||
]
|
||||
|
||||
# Disable Docutils smartquotes for several translations
|
||||
smartquotes_excludes = {
|
||||
'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'],
|
||||
|
@ -263,6 +270,24 @@ linkcheck_allowed_redirects = {
|
|||
r'https://github.com/python/cpython/tree/.*': 'https://github.com/python/cpython/blob/.*'
|
||||
}
|
||||
|
||||
linkcheck_anchors_ignore = [
|
||||
# ignore anchors that start with a '/', e.g. Wikipedia media files:
|
||||
# https://en.wikipedia.org/wiki/Walrus#/media/File:Pacific_Walrus_-_Bull_(8247646168).jpg
|
||||
r'\/.*',
|
||||
]
|
||||
|
||||
linkcheck_ignore = [
|
||||
# The crawler gets "Anchor not found"
|
||||
r'https://developer.apple.com/documentation/.+?#.*',
|
||||
r'https://devguide.python.org.+?/#.*',
|
||||
r'https://github.com.+?#.*',
|
||||
# Robot crawlers not allowed: "403 Client Error: Forbidden"
|
||||
r'https://support.enthought.com/hc/.*',
|
||||
# SSLError CertificateError, even though it is valid
|
||||
r'https://unix.org/version2/whatsnew/lp64_wp.html',
|
||||
]
|
||||
|
||||
|
||||
# Options for extensions
|
||||
# ----------------------
|
||||
|
||||
|
|
|
@ -129,14 +129,10 @@ involved in creating and publishing a project:
|
|||
* `Uploading the project to the Python Package Index`_
|
||||
* `The .pypirc file`_
|
||||
|
||||
.. _Project structure: \
|
||||
https://packaging.python.org/tutorials/packaging-projects/#packaging-python-projects
|
||||
.. _Building and packaging the project: \
|
||||
https://packaging.python.org/tutorials/packaging-projects/#creating-the-package-files
|
||||
.. _Uploading the project to the Python Package Index: \
|
||||
https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives
|
||||
.. _The .pypirc file: \
|
||||
https://packaging.python.org/specifications/pypirc/
|
||||
.. _Project structure: https://packaging.python.org/tutorials/packaging-projects/#packaging-python-projects
|
||||
.. _Building and packaging the project: https://packaging.python.org/tutorials/packaging-projects/#creating-the-package-files
|
||||
.. _Uploading the project to the Python Package Index: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives
|
||||
.. _The .pypirc file: https://packaging.python.org/specifications/pypirc/
|
||||
|
||||
|
||||
How do I...?
|
||||
|
|
|
@ -337,7 +337,7 @@ Here is an example::
|
|||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.50s' object has no attribute '%.400s'",
|
||||
"'%.100s' object has no attribute '%.400s'",
|
||||
tp->tp_name, name);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -780,7 +780,7 @@ socket to :meth:`select.select` to check if it's writable.
|
|||
The :mod:`asyncio` module provides a general purpose single-threaded and
|
||||
concurrent asynchronous library, which can be used for writing non-blocking
|
||||
network code.
|
||||
The third-party `Twisted <https://twistedmatrix.com/trac/>`_ library is
|
||||
The third-party `Twisted <https://twisted.org/>`_ library is
|
||||
a popular and feature-rich alternative.
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
.. _argparse-tutorial:
|
||||
|
||||
*****************
|
||||
Argparse Tutorial
|
||||
*****************
|
||||
|
||||
:author: Tshepang Mbambo
|
||||
|
||||
.. _argparse-tutorial:
|
||||
.. currentmodule:: argparse
|
||||
|
||||
This tutorial is intended to be a gentle introduction to :mod:`argparse`, the
|
||||
recommended command-line parsing module in the Python standard library.
|
||||
|
@ -12,7 +14,7 @@ recommended command-line parsing module in the Python standard library.
|
|||
.. note::
|
||||
|
||||
There are two other modules that fulfill the same task, namely
|
||||
:mod:`getopt` (an equivalent for :c:func:`getopt` from the C
|
||||
:mod:`getopt` (an equivalent for ``getopt()`` from the C
|
||||
language) and the deprecated :mod:`optparse`.
|
||||
Note also that :mod:`argparse` is based on :mod:`optparse`,
|
||||
and therefore very similar in terms of usage.
|
||||
|
@ -137,13 +139,13 @@ And running the code:
|
|||
|
||||
Here is what's happening:
|
||||
|
||||
* We've added the :meth:`add_argument` method, which is what we use to specify
|
||||
* We've added the :meth:`~ArgumentParser.add_argument` method, which is what we use to specify
|
||||
which command-line options the program is willing to accept. In this case,
|
||||
I've named it ``echo`` so that it's in line with its function.
|
||||
|
||||
* Calling our program now requires us to specify an option.
|
||||
|
||||
* The :meth:`parse_args` method actually returns some data from the
|
||||
* The :meth:`~ArgumentParser.parse_args` method actually returns some data from the
|
||||
options specified, in this case, ``echo``.
|
||||
|
||||
* The variable is some form of 'magic' that :mod:`argparse` performs for free
|
||||
|
@ -256,7 +258,7 @@ Here is what is happening:
|
|||
|
||||
* To show that the option is actually optional, there is no error when running
|
||||
the program without it. Note that by default, if an optional argument isn't
|
||||
used, the relevant variable, in this case :attr:`args.verbosity`, is
|
||||
used, the relevant variable, in this case ``args.verbosity``, is
|
||||
given ``None`` as a value, which is the reason it fails the truth
|
||||
test of the :keyword:`if` statement.
|
||||
|
||||
|
@ -299,7 +301,7 @@ Here is what is happening:
|
|||
We even changed the name of the option to match that idea.
|
||||
Note that we now specify a new keyword, ``action``, and give it the value
|
||||
``"store_true"``. This means that, if the option is specified,
|
||||
assign the value ``True`` to :data:`args.verbose`.
|
||||
assign the value ``True`` to ``args.verbose``.
|
||||
Not specifying it implies ``False``.
|
||||
|
||||
* It complains when you specify a value, in true spirit of what flags
|
||||
|
@ -698,7 +700,7 @@ Conflicting options
|
|||
|
||||
So far, we have been working with two methods of an
|
||||
:class:`argparse.ArgumentParser` instance. Let's introduce a third one,
|
||||
:meth:`add_mutually_exclusive_group`. It allows for us to specify options that
|
||||
:meth:`~ArgumentParser.add_mutually_exclusive_group`. It allows for us to specify options that
|
||||
conflict with each other. Let's also change the rest of the program so that
|
||||
the new functionality makes more sense:
|
||||
we'll introduce the ``--quiet`` option,
|
||||
|
|
|
@ -1273,11 +1273,14 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
|
||||
.. testcode::
|
||||
|
||||
import functools
|
||||
|
||||
class StaticMethod:
|
||||
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
functools.update_wrapper(self, f)
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
return self.f
|
||||
|
@ -1285,13 +1288,19 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
def __call__(self, *args, **kwds):
|
||||
return self.f(*args, **kwds)
|
||||
|
||||
The :func:`functools.update_wrapper` call adds a ``__wrapped__`` attribute
|
||||
that refers to the underlying function. Also it carries forward
|
||||
the attributes necessary to make the wrapper look like the wrapped
|
||||
function: ``__name__``, ``__qualname__``, ``__doc__``, and ``__annotations__``.
|
||||
|
||||
.. testcode::
|
||||
:hide:
|
||||
|
||||
class E_sim:
|
||||
@StaticMethod
|
||||
def f(x):
|
||||
return x * 10
|
||||
def f(x: int) -> str:
|
||||
"Simple function example"
|
||||
return "!" * x
|
||||
|
||||
wrapped_ord = StaticMethod(ord)
|
||||
|
||||
|
@ -1299,11 +1308,51 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
:hide:
|
||||
|
||||
>>> E_sim.f(3)
|
||||
30
|
||||
'!!!'
|
||||
>>> E_sim().f(3)
|
||||
30
|
||||
'!!!'
|
||||
|
||||
>>> sm = vars(E_sim)['f']
|
||||
>>> type(sm).__name__
|
||||
'StaticMethod'
|
||||
>>> f = E_sim.f
|
||||
>>> type(f).__name__
|
||||
'function'
|
||||
>>> sm.__name__
|
||||
'f'
|
||||
>>> f.__name__
|
||||
'f'
|
||||
>>> sm.__qualname__
|
||||
'E_sim.f'
|
||||
>>> f.__qualname__
|
||||
'E_sim.f'
|
||||
>>> sm.__doc__
|
||||
'Simple function example'
|
||||
>>> f.__doc__
|
||||
'Simple function example'
|
||||
>>> sm.__annotations__
|
||||
{'x': <class 'int'>, 'return': <class 'str'>}
|
||||
>>> f.__annotations__
|
||||
{'x': <class 'int'>, 'return': <class 'str'>}
|
||||
>>> sm.__module__ == f.__module__
|
||||
True
|
||||
>>> sm(3)
|
||||
'!!!'
|
||||
>>> f(3)
|
||||
'!!!'
|
||||
|
||||
>>> wrapped_ord('A')
|
||||
65
|
||||
>>> wrapped_ord.__module__ == ord.__module__
|
||||
True
|
||||
>>> wrapped_ord.__wrapped__ == ord
|
||||
True
|
||||
>>> wrapped_ord.__name__ == ord.__name__
|
||||
True
|
||||
>>> wrapped_ord.__qualname__ == ord.__qualname__
|
||||
True
|
||||
>>> wrapped_ord.__doc__ == ord.__doc__
|
||||
True
|
||||
|
||||
|
||||
Class methods
|
||||
|
@ -1359,11 +1408,14 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
|
||||
.. testcode::
|
||||
|
||||
import functools
|
||||
|
||||
class ClassMethod:
|
||||
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
functools.update_wrapper(self, f)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
if cls is None:
|
||||
|
@ -1380,8 +1432,9 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
# Verify the emulation works
|
||||
class T:
|
||||
@ClassMethod
|
||||
def cm(cls, x, y):
|
||||
return (cls, x, y)
|
||||
def cm(cls, x: int, y: str) -> tuple[str, int, str]:
|
||||
"Class method that returns a tuple"
|
||||
return (cls.__name__, x, y)
|
||||
|
||||
@ClassMethod
|
||||
@property
|
||||
|
@ -1393,17 +1446,40 @@ Using the non-data descriptor protocol, a pure Python version of
|
|||
:hide:
|
||||
|
||||
>>> T.cm(11, 22)
|
||||
(<class 'T'>, 11, 22)
|
||||
('T', 11, 22)
|
||||
|
||||
# Also call it from an instance
|
||||
>>> t = T()
|
||||
>>> t.cm(11, 22)
|
||||
(<class 'T'>, 11, 22)
|
||||
('T', 11, 22)
|
||||
|
||||
# Check the alternate path for chained descriptors
|
||||
>>> T.__doc__
|
||||
"A doc for 'T'"
|
||||
|
||||
# Verify that T uses our emulation
|
||||
>>> type(vars(T)['cm']).__name__
|
||||
'ClassMethod'
|
||||
|
||||
# Verify that update_wrapper() correctly copied attributes
|
||||
>>> T.cm.__name__
|
||||
'cm'
|
||||
>>> T.cm.__qualname__
|
||||
'T.cm'
|
||||
>>> T.cm.__doc__
|
||||
'Class method that returns a tuple'
|
||||
>>> T.cm.__annotations__
|
||||
{'x': <class 'int'>, 'y': <class 'str'>, 'return': tuple[str, int, str]}
|
||||
|
||||
# Verify that __wrapped__ was added and works correctly
|
||||
>>> f = vars(T)['cm'].__wrapped__
|
||||
>>> type(f).__name__
|
||||
'function'
|
||||
>>> f.__name__
|
||||
'cm'
|
||||
>>> f(T, 11, 22)
|
||||
('T', 11, 22)
|
||||
|
||||
|
||||
The code path for ``hasattr(type(self.f), '__get__')`` was added in
|
||||
Python 3.9 and makes it possible for :func:`classmethod` to support
|
||||
|
@ -1423,6 +1499,12 @@ chained together. In Python 3.11, this functionality was deprecated.
|
|||
>>> G.__doc__
|
||||
"A doc for 'G'"
|
||||
|
||||
The :func:`functools.update_wrapper` call in ``ClassMethod`` adds a
|
||||
``__wrapped__`` attribute that refers to the underlying function. Also
|
||||
it carries forward the attributes necessary to make the wrapper look
|
||||
like the wrapped function: ``__name__``, ``__qualname__``, ``__doc__``,
|
||||
and ``__annotations__``.
|
||||
|
||||
|
||||
Member objects and __slots__
|
||||
----------------------------
|
||||
|
|
|
@ -36,8 +36,10 @@ inherits from :class:`Enum` itself.
|
|||
|
||||
.. note:: Case of Enum Members
|
||||
|
||||
Because Enums are used to represent constants we recommend using
|
||||
UPPER_CASE names for members, and will be using that style in our examples.
|
||||
Because Enums are used to represent constants, and to help avoid issues
|
||||
with name clashes between mixin-class methods/attributes and enum names,
|
||||
we strongly recommend using UPPER_CASE names for members, and will be using
|
||||
that style in our examples.
|
||||
|
||||
Depending on the nature of the enum a member's value may or may not be
|
||||
important, but either way that value can be used to get the corresponding
|
||||
|
@ -490,6 +492,10 @@ the :meth:`~Enum.__repr__` omits the inherited class' name. For example::
|
|||
Use the :func:`!dataclass` argument ``repr=False``
|
||||
to use the standard :func:`repr`.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Only the dataclass fields are shown in the value area, not the dataclass'
|
||||
name.
|
||||
|
||||
|
||||
Pickling
|
||||
--------
|
||||
|
@ -992,7 +998,9 @@ but remain normal attributes.
|
|||
Enum members are instances of their enum class, and are normally accessed as
|
||||
``EnumClass.member``. In certain situations, such as writing custom enum
|
||||
behavior, being able to access one member directly from another is useful,
|
||||
and is supported.
|
||||
and is supported; however, in order to avoid name clashes between member names
|
||||
and attributes/methods from mixed-in classes, upper-case names are strongly
|
||||
recommended.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
||||
|
|
|
@ -1208,8 +1208,8 @@ General
|
|||
-------
|
||||
|
||||
**Structure and Interpretation of Computer Programs**, by Harold Abelson and
|
||||
Gerald Jay Sussman with Julie Sussman. Full text at
|
||||
https://mitpress.mit.edu/sicp/. In this classic textbook of computer science,
|
||||
Gerald Jay Sussman with Julie Sussman. The book can be found at
|
||||
https://mitpress.mit.edu/sicp. In this classic textbook of computer science,
|
||||
chapters 2 and 3 discuss the use of sequences and streams to organize the data
|
||||
flow inside a program. The book uses Scheme for its examples, but many of the
|
||||
design approaches described in these chapters are applicable to functional-style
|
||||
|
|
|
@ -86,7 +86,7 @@ response::
|
|||
|
||||
import urllib.request
|
||||
|
||||
req = urllib.request.Request('http://www.voidspace.org.uk')
|
||||
req = urllib.request.Request('http://python.org/')
|
||||
with urllib.request.urlopen(req) as response:
|
||||
the_page = response.read()
|
||||
|
||||
|
@ -458,7 +458,7 @@ To illustrate creating and installing a handler we will use the
|
|||
``HTTPBasicAuthHandler``. For a more detailed discussion of this subject --
|
||||
including an explanation of how Basic Authentication works - see the `Basic
|
||||
Authentication Tutorial
|
||||
<http://www.voidspace.org.uk/python/articles/authentication.shtml>`_.
|
||||
<https://web.archive.org/web/20201215133350/http://www.voidspace.org.uk/python/articles/authentication.shtml>`__.
|
||||
|
||||
When authentication is required, the server sends a header (as well as the 401
|
||||
error code) requesting authentication. This specifies the authentication scheme
|
||||
|
|
|
@ -67,7 +67,7 @@ default_ Default value used when an argument is not provided
|
|||
dest_ Specify the attribute name used in the result namespace
|
||||
help_ Help message for an argument
|
||||
metavar_ Alternate display name for the argument as shown in help
|
||||
nargs_ Number of times the argument can be used :class:`int`, ``'?'``, ``'*'``, ``'+'``, or ``argparse.REMAINDER``
|
||||
nargs_ Number of times the argument can be used :class:`int`, ``'?'``, ``'*'``, or ``'+'``
|
||||
required_ Indicate whether an argument is required or optional ``True`` or ``False``
|
||||
type_ Automatically convert an argument to the given type :class:`int`, :class:`float`, ``argparse.FileType('w')``, or callable function
|
||||
====================== =========================================================== ==========================================================================================================================
|
||||
|
@ -585,7 +585,7 @@ arguments will never be treated as file references.
|
|||
|
||||
.. versionchanged:: 3.12
|
||||
:class:`ArgumentParser` changed encoding and errors to read arguments files
|
||||
from default (e.g. :func:`locale.getpreferredencoding(False)` and
|
||||
from default (e.g. :func:`locale.getpreferredencoding(False) <locale.getpreferredencoding>` and
|
||||
``"strict"``) to :term:`filesystem encoding and error handler`.
|
||||
Arguments file should be encoded in UTF-8 instead of ANSI Codepage on Windows.
|
||||
|
||||
|
@ -1191,7 +1191,7 @@ done downstream after the arguments are parsed.
|
|||
For example, JSON or YAML conversions have complex error cases that require
|
||||
better reporting than can be given by the ``type`` keyword. A
|
||||
:exc:`~json.JSONDecodeError` would not be well formatted and a
|
||||
:exc:`FileNotFound` exception would not be handled at all.
|
||||
:exc:`FileNotFoundError` exception would not be handled at all.
|
||||
|
||||
Even :class:`~argparse.FileType` has its limitations for use with the ``type``
|
||||
keyword. If one argument uses *FileType* and then a subsequent argument fails,
|
||||
|
@ -1445,7 +1445,7 @@ Action classes
|
|||
Action classes implement the Action API, a callable which returns a callable
|
||||
which processes arguments from the command-line. Any object which follows
|
||||
this API may be passed as the ``action`` parameter to
|
||||
:meth:`add_argument`.
|
||||
:meth:`~ArgumentParser.add_argument`.
|
||||
|
||||
.. class:: Action(option_strings, dest, nargs=None, const=None, default=None, \
|
||||
type=None, choices=None, required=False, help=None, \
|
||||
|
@ -1723,7 +1723,7 @@ Sub-commands
|
|||
:class:`ArgumentParser` supports the creation of such sub-commands with the
|
||||
:meth:`add_subparsers` method. The :meth:`add_subparsers` method is normally
|
||||
called with no arguments and returns a special action object. This object
|
||||
has a single method, :meth:`~ArgumentParser.add_parser`, which takes a
|
||||
has a single method, :meth:`~_SubParsersAction.add_parser`, which takes a
|
||||
command name and any :class:`ArgumentParser` constructor arguments, and
|
||||
returns an :class:`ArgumentParser` object that can be modified as usual.
|
||||
|
||||
|
@ -1789,7 +1789,7 @@ Sub-commands
|
|||
for that particular parser will be printed. The help message will not
|
||||
include parent parser or sibling parser messages. (A help message for each
|
||||
subparser command, however, can be given by supplying the ``help=`` argument
|
||||
to :meth:`add_parser` as above.)
|
||||
to :meth:`~_SubParsersAction.add_parser` as above.)
|
||||
|
||||
::
|
||||
|
||||
|
@ -2157,7 +2157,7 @@ the populated namespace and the list of remaining argument strings.
|
|||
|
||||
.. warning::
|
||||
:ref:`Prefix matching <prefix-matching>` rules apply to
|
||||
:meth:`parse_known_args`. The parser may consume an option even if it's just
|
||||
:meth:`~ArgumentParser.parse_known_args`. The parser may consume an option even if it's just
|
||||
a prefix of one of its known options, instead of leaving it in the remaining
|
||||
arguments list.
|
||||
|
||||
|
@ -2218,7 +2218,7 @@ support this parsing style.
|
|||
|
||||
These parsers do not support all the argparse features, and will raise
|
||||
exceptions if unsupported features are used. In particular, subparsers,
|
||||
``argparse.REMAINDER``, and mutually exclusive groups that include both
|
||||
and mutually exclusive groups that include both
|
||||
optionals and positionals are not supported.
|
||||
|
||||
The following example shows the difference between
|
||||
|
@ -2295,3 +2295,17 @@ A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
|
|||
|
||||
* Replace the OptionParser constructor ``version`` argument with a call to
|
||||
``parser.add_argument('--version', action='version', version='<the version>')``.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
.. exception:: ArgumentError
|
||||
|
||||
An error from creating or using an argument (optional or positional).
|
||||
|
||||
The string value of this exception is the message, augmented with
|
||||
information about the argument that caused it.
|
||||
|
||||
.. exception:: ArgumentTypeError
|
||||
|
||||
Raised when something goes wrong converting a command line string to a type.
|
||||
|
|
|
@ -256,8 +256,9 @@ Creating Tasks
|
|||
|
||||
.. note::
|
||||
|
||||
:meth:`asyncio.TaskGroup.create_task` is a newer alternative
|
||||
that allows for convenient waiting for a group of related tasks.
|
||||
:meth:`asyncio.TaskGroup.create_task` is a new alternative
|
||||
leveraging structural concurrency; it allows for waiting
|
||||
for a group of related tasks with strong safety guarantees.
|
||||
|
||||
.. important::
|
||||
|
||||
|
@ -340,7 +341,7 @@ Example::
|
|||
async with asyncio.TaskGroup() as tg:
|
||||
task1 = tg.create_task(some_coro(...))
|
||||
task2 = tg.create_task(another_coro(...))
|
||||
print("Both tasks have completed now.")
|
||||
print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
|
||||
|
||||
The ``async with`` statement will wait for all tasks in the group to finish.
|
||||
While waiting, new tasks may still be added to the group
|
||||
|
@ -459,8 +460,12 @@ Running Tasks Concurrently
|
|||
Tasks/Futures to be cancelled.
|
||||
|
||||
.. note::
|
||||
A more modern way to create and run tasks concurrently and
|
||||
wait for their completion is :class:`asyncio.TaskGroup`.
|
||||
A new alternative to create and run tasks concurrently and
|
||||
wait for their completion is :class:`asyncio.TaskGroup`. *TaskGroup*
|
||||
provides stronger safety guarantees than *gather* for scheduling a nesting of subtasks:
|
||||
if a task (or a subtask, a task scheduled by a task)
|
||||
raises an exception, *TaskGroup* will, while *gather* will not,
|
||||
cancel the remaining scheduled tasks).
|
||||
|
||||
.. _asyncio_example_gather:
|
||||
|
||||
|
@ -829,6 +834,9 @@ Waiting Primitives
|
|||
Deprecation warning is emitted if not all awaitable objects in the *aws*
|
||||
iterable are Future-like objects and there is no running event loop.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Added support for generators yielding tasks.
|
||||
|
||||
|
||||
Running in Threads
|
||||
==================
|
||||
|
|
|
@ -304,8 +304,15 @@ Functions and classes provided:
|
|||
|
||||
This context manager is :ref:`reentrant <reentrant-cms>`.
|
||||
|
||||
If the code within the :keyword:`!with` block raises an
|
||||
:exc:`ExceptionGroup`, suppressed exceptions are removed from the
|
||||
group. If any exceptions in the group are not suppressed, a group containing them is re-raised.
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
``suppress`` now supports suppressing exceptions raised as
|
||||
part of an :exc:`ExceptionGroup`.
|
||||
|
||||
.. function:: redirect_stdout(new_target)
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ Such constructors may be factory functions or class instances.
|
|||
.. function:: pickle(type, function, constructor_ob=None)
|
||||
|
||||
Declares that *function* should be used as a "reduction" function for objects
|
||||
of type *type*. *function* should return either a string or a tuple
|
||||
containing two or three elements. See the :attr:`~pickle.Pickler.dispatch_table`
|
||||
of type *type*. *function* must return either a string or a tuple
|
||||
containing two or five elements. See the :attr:`~pickle.Pickler.dispatch_table`
|
||||
for more details on the interface of *function*.
|
||||
|
||||
The *constructor_ob* parameter is a legacy feature and is now ignored, but if
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
--------------
|
||||
|
||||
This module provides a decorator and functions for automatically
|
||||
adding generated :term:`special method`\s such as :meth:`__init__` and
|
||||
:meth:`__repr__` to user-defined classes. It was originally described
|
||||
adding generated :term:`special method`\s such as :meth:`~object.__init__` and
|
||||
:meth:`~object.__repr__` to user-defined classes. It was originally described
|
||||
in :pep:`557`.
|
||||
|
||||
The member variables to use in these generated methods are defined
|
||||
|
@ -31,7 +31,7 @@ using :pep:`526` type annotations. For example, this code::
|
|||
def total_cost(self) -> float:
|
||||
return self.unit_price * self.quantity_on_hand
|
||||
|
||||
will add, among other things, a :meth:`__init__` that looks like::
|
||||
will add, among other things, a :meth:`~object.__init__` that looks like::
|
||||
|
||||
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
|
||||
self.name = name
|
||||
|
@ -86,86 +86,86 @@ Module contents
|
|||
|
||||
The parameters to :func:`dataclass` are:
|
||||
|
||||
- ``init``: If true (the default), a :meth:`__init__` method will be
|
||||
- ``init``: If true (the default), a :meth:`~object.__init__` method will be
|
||||
generated.
|
||||
|
||||
If the class already defines :meth:`__init__`, this parameter is
|
||||
If the class already defines :meth:`~object.__init__`, this parameter is
|
||||
ignored.
|
||||
|
||||
- ``repr``: If true (the default), a :meth:`__repr__` method will be
|
||||
- ``repr``: If true (the default), a :meth:`~object.__repr__` method will be
|
||||
generated. The generated repr string will have the class name and
|
||||
the name and repr of each field, in the order they are defined in
|
||||
the class. Fields that are marked as being excluded from the repr
|
||||
are not included. For example:
|
||||
``InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)``.
|
||||
|
||||
If the class already defines :meth:`__repr__`, this parameter is
|
||||
If the class already defines :meth:`~object.__repr__`, this parameter is
|
||||
ignored.
|
||||
|
||||
- ``eq``: If true (the default), an :meth:`__eq__` method will be
|
||||
- ``eq``: If true (the default), an :meth:`~object.__eq__` method will be
|
||||
generated. This method compares the class as if it were a tuple
|
||||
of its fields, in order. Both instances in the comparison must
|
||||
be of the identical type.
|
||||
|
||||
If the class already defines :meth:`__eq__`, this parameter is
|
||||
If the class already defines :meth:`~object.__eq__`, this parameter is
|
||||
ignored.
|
||||
|
||||
- ``order``: If true (the default is ``False``), :meth:`__lt__`,
|
||||
:meth:`__le__`, :meth:`__gt__`, and :meth:`__ge__` methods will be
|
||||
- ``order``: If true (the default is ``False``), :meth:`~object.__lt__`,
|
||||
:meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be
|
||||
generated. These compare the class as if it were a tuple of its
|
||||
fields, in order. Both instances in the comparison must be of the
|
||||
identical type. If ``order`` is true and ``eq`` is false, a
|
||||
:exc:`ValueError` is raised.
|
||||
|
||||
If the class already defines any of :meth:`__lt__`,
|
||||
:meth:`__le__`, :meth:`__gt__`, or :meth:`__ge__`, then
|
||||
If the class already defines any of :meth:`~object.__lt__`,
|
||||
:meth:`~object.__le__`, :meth:`~object.__gt__`, or :meth:`~object.__ge__`, then
|
||||
:exc:`TypeError` is raised.
|
||||
|
||||
- ``unsafe_hash``: If ``False`` (the default), a :meth:`__hash__` method
|
||||
- ``unsafe_hash``: If ``False`` (the default), a :meth:`~object.__hash__` method
|
||||
is generated according to how ``eq`` and ``frozen`` are set.
|
||||
|
||||
:meth:`__hash__` is used by built-in :meth:`hash()`, and when objects are
|
||||
:meth:`~object.__hash__` is used by built-in :meth:`hash()`, and when objects are
|
||||
added to hashed collections such as dictionaries and sets. Having a
|
||||
:meth:`__hash__` implies that instances of the class are immutable.
|
||||
:meth:`~object.__hash__` implies that instances of the class are immutable.
|
||||
Mutability is a complicated property that depends on the programmer's
|
||||
intent, the existence and behavior of :meth:`__eq__`, and the values of
|
||||
intent, the existence and behavior of :meth:`~object.__eq__`, and the values of
|
||||
the ``eq`` and ``frozen`` flags in the :func:`dataclass` decorator.
|
||||
|
||||
By default, :func:`dataclass` will not implicitly add a :meth:`__hash__`
|
||||
By default, :func:`dataclass` will not implicitly add a :meth:`~object.__hash__`
|
||||
method unless it is safe to do so. Neither will it add or change an
|
||||
existing explicitly defined :meth:`__hash__` method. Setting the class
|
||||
existing explicitly defined :meth:`~object.__hash__` method. Setting the class
|
||||
attribute ``__hash__ = None`` has a specific meaning to Python, as
|
||||
described in the :meth:`__hash__` documentation.
|
||||
described in the :meth:`~object.__hash__` documentation.
|
||||
|
||||
If :meth:`__hash__` is not explicitly defined, or if it is set to ``None``,
|
||||
then :func:`dataclass` *may* add an implicit :meth:`__hash__` method.
|
||||
If :meth:`~object.__hash__` is not explicitly defined, or if it is set to ``None``,
|
||||
then :func:`dataclass` *may* add an implicit :meth:`~object.__hash__` method.
|
||||
Although not recommended, you can force :func:`dataclass` to create a
|
||||
:meth:`__hash__` method with ``unsafe_hash=True``. This might be the case
|
||||
:meth:`~object.__hash__` method with ``unsafe_hash=True``. This might be the case
|
||||
if your class is logically immutable but can nonetheless be mutated.
|
||||
This is a specialized use case and should be considered carefully.
|
||||
|
||||
Here are the rules governing implicit creation of a :meth:`__hash__`
|
||||
method. Note that you cannot both have an explicit :meth:`__hash__`
|
||||
Here are the rules governing implicit creation of a :meth:`~object.__hash__`
|
||||
method. Note that you cannot both have an explicit :meth:`~object.__hash__`
|
||||
method in your dataclass and set ``unsafe_hash=True``; this will result
|
||||
in a :exc:`TypeError`.
|
||||
|
||||
If ``eq`` and ``frozen`` are both true, by default :func:`dataclass` will
|
||||
generate a :meth:`__hash__` method for you. If ``eq`` is true and
|
||||
``frozen`` is false, :meth:`__hash__` will be set to ``None``, marking it
|
||||
generate a :meth:`~object.__hash__` method for you. If ``eq`` is true and
|
||||
``frozen`` is false, :meth:`~object.__hash__` will be set to ``None``, marking it
|
||||
unhashable (which it is, since it is mutable). If ``eq`` is false,
|
||||
:meth:`__hash__` will be left untouched meaning the :meth:`__hash__`
|
||||
:meth:`~object.__hash__` will be left untouched meaning the :meth:`~object.__hash__`
|
||||
method of the superclass will be used (if the superclass is
|
||||
:class:`object`, this means it will fall back to id-based hashing).
|
||||
|
||||
- ``frozen``: If true (the default is ``False``), assigning to fields will
|
||||
generate an exception. This emulates read-only frozen instances. If
|
||||
:meth:`__setattr__` or :meth:`__delattr__` is defined in the class, then
|
||||
:meth:`~object.__setattr__` or :meth:`~object.__delattr__` is defined in the class, then
|
||||
:exc:`TypeError` is raised. See the discussion below.
|
||||
|
||||
- ``match_args``: If true (the default is ``True``), the
|
||||
``__match_args__`` tuple will be created from the list of
|
||||
parameters to the generated :meth:`__init__` method (even if
|
||||
:meth:`__init__` is not generated, see above). If false, or if
|
||||
parameters to the generated :meth:`~object.__init__` method (even if
|
||||
:meth:`~object.__init__` is not generated, see above). If false, or if
|
||||
``__match_args__`` is already defined in the class, then
|
||||
``__match_args__`` will not be generated.
|
||||
|
||||
|
@ -173,18 +173,18 @@ Module contents
|
|||
|
||||
- ``kw_only``: If true (the default value is ``False``), then all
|
||||
fields will be marked as keyword-only. If a field is marked as
|
||||
keyword-only, then the only effect is that the :meth:`__init__`
|
||||
keyword-only, then the only effect is that the :meth:`~object.__init__`
|
||||
parameter generated from a keyword-only field must be specified
|
||||
with a keyword when :meth:`__init__` is called. There is no
|
||||
with a keyword when :meth:`~object.__init__` is called. There is no
|
||||
effect on any other aspect of dataclasses. See the
|
||||
:term:`parameter` glossary entry for details. Also see the
|
||||
:const:`KW_ONLY` section.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
- ``slots``: If true (the default is ``False``), :attr:`__slots__` attribute
|
||||
- ``slots``: If true (the default is ``False``), :attr:`~object.__slots__` attribute
|
||||
will be generated and new class will be returned instead of the original one.
|
||||
If :attr:`__slots__` is already defined in the class, then :exc:`TypeError`
|
||||
If :attr:`~object.__slots__` is already defined in the class, then :exc:`TypeError`
|
||||
is raised.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
@ -215,7 +215,7 @@ Module contents
|
|||
b: int = 0 # assign a default value for 'b'
|
||||
|
||||
In this example, both ``a`` and ``b`` will be included in the added
|
||||
:meth:`__init__` method, which will be defined as::
|
||||
:meth:`~object.__init__` method, which will be defined as::
|
||||
|
||||
def __init__(self, a: int, b: int = 0):
|
||||
|
||||
|
@ -256,13 +256,13 @@ Module contents
|
|||
error to specify both ``default`` and ``default_factory``.
|
||||
|
||||
- ``init``: If true (the default), this field is included as a
|
||||
parameter to the generated :meth:`__init__` method.
|
||||
parameter to the generated :meth:`~object.__init__` method.
|
||||
|
||||
- ``repr``: If true (the default), this field is included in the
|
||||
string returned by the generated :meth:`__repr__` method.
|
||||
string returned by the generated :meth:`~object.__repr__` method.
|
||||
|
||||
- ``hash``: This can be a bool or ``None``. If true, this field is
|
||||
included in the generated :meth:`__hash__` method. If ``None`` (the
|
||||
included in the generated :meth:`~object.__hash__` method. If ``None`` (the
|
||||
default), use the value of ``compare``: this would normally be
|
||||
the expected behavior. A field should be considered in the hash
|
||||
if it's used for comparisons. Setting this value to anything
|
||||
|
@ -275,8 +275,8 @@ Module contents
|
|||
is excluded from the hash, it will still be used for comparisons.
|
||||
|
||||
- ``compare``: If true (the default), this field is included in the
|
||||
generated equality and comparison methods (:meth:`__eq__`,
|
||||
:meth:`__gt__`, et al.).
|
||||
generated equality and comparison methods (:meth:`~object.__eq__`,
|
||||
:meth:`~object.__gt__`, et al.).
|
||||
|
||||
- ``metadata``: This can be a mapping or None. None is treated as
|
||||
an empty dict. This value is wrapped in
|
||||
|
@ -287,7 +287,7 @@ Module contents
|
|||
namespace in the metadata.
|
||||
|
||||
- ``kw_only``: If true, this field will be marked as keyword-only.
|
||||
This is used when the generated :meth:`__init__` method's
|
||||
This is used when the generated :meth:`~object.__init__` method's
|
||||
parameters are computed.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
@ -435,13 +435,13 @@ Module contents
|
|||
Class, raises :exc:`TypeError`. If values in ``changes`` do not
|
||||
specify fields, raises :exc:`TypeError`.
|
||||
|
||||
The newly returned object is created by calling the :meth:`__init__`
|
||||
The newly returned object is created by calling the :meth:`~object.__init__`
|
||||
method of the dataclass. This ensures that
|
||||
:meth:`__post_init__`, if present, is also called.
|
||||
:ref:`__post_init__ <post-init-processing>`, if present, is also called.
|
||||
|
||||
Init-only variables without default values, if any exist, must be
|
||||
specified on the call to :func:`replace` so that they can be passed to
|
||||
:meth:`__init__` and :meth:`__post_init__`.
|
||||
:meth:`~object.__init__` and :ref:`__post_init__ <post-init-processing>`.
|
||||
|
||||
It is an error for ``changes`` to contain any fields that are
|
||||
defined as having ``init=False``. A :exc:`ValueError` will be raised
|
||||
|
@ -449,7 +449,7 @@ Module contents
|
|||
|
||||
Be forewarned about how ``init=False`` fields work during a call to
|
||||
:func:`replace`. They are not copied from the source object, but
|
||||
rather are initialized in :meth:`__post_init__`, if they're
|
||||
rather are initialized in :ref:`__post_init__ <post-init-processing>`, if they're
|
||||
initialized at all. It is expected that ``init=False`` fields will
|
||||
be rarely and judiciously used. If they are used, it might be wise
|
||||
to have alternate class constructors, or perhaps a custom
|
||||
|
@ -480,7 +480,7 @@ Module contents
|
|||
:const:`KW_ONLY` is otherwise completely ignored. This includes the
|
||||
name of such a field. By convention, a name of ``_`` is used for a
|
||||
:const:`KW_ONLY` field. Keyword-only fields signify
|
||||
:meth:`__init__` parameters that must be specified as keywords when
|
||||
:meth:`~object.__init__` parameters that must be specified as keywords when
|
||||
the class is instantiated.
|
||||
|
||||
In this example, the fields ``y`` and ``z`` will be marked as keyword-only fields::
|
||||
|
@ -501,20 +501,22 @@ Module contents
|
|||
|
||||
.. exception:: FrozenInstanceError
|
||||
|
||||
Raised when an implicitly defined :meth:`__setattr__` or
|
||||
:meth:`__delattr__` is called on a dataclass which was defined with
|
||||
Raised when an implicitly defined :meth:`~object.__setattr__` or
|
||||
:meth:`~object.__delattr__` is called on a dataclass which was defined with
|
||||
``frozen=True``. It is a subclass of :exc:`AttributeError`.
|
||||
|
||||
.. _post-init-processing:
|
||||
|
||||
Post-init processing
|
||||
--------------------
|
||||
|
||||
The generated :meth:`__init__` code will call a method named
|
||||
:meth:`__post_init__`, if :meth:`__post_init__` is defined on the
|
||||
The generated :meth:`~object.__init__` code will call a method named
|
||||
:meth:`!__post_init__`, if :meth:`!__post_init__` is defined on the
|
||||
class. It will normally be called as ``self.__post_init__()``.
|
||||
However, if any ``InitVar`` fields are defined, they will also be
|
||||
passed to :meth:`__post_init__` in the order they were defined in the
|
||||
class. If no :meth:`__init__` method is generated, then
|
||||
:meth:`__post_init__` will not automatically be called.
|
||||
passed to :meth:`!__post_init__` in the order they were defined in the
|
||||
class. If no :meth:`~object.__init__` method is generated, then
|
||||
:meth:`!__post_init__` will not automatically be called.
|
||||
|
||||
Among other uses, this allows for initializing field values that
|
||||
depend on one or more other fields. For example::
|
||||
|
@ -528,10 +530,10 @@ depend on one or more other fields. For example::
|
|||
def __post_init__(self):
|
||||
self.c = self.a + self.b
|
||||
|
||||
The :meth:`__init__` method generated by :func:`dataclass` does not call base
|
||||
class :meth:`__init__` methods. If the base class has an :meth:`__init__` method
|
||||
The :meth:`~object.__init__` method generated by :func:`dataclass` does not call base
|
||||
class :meth:`~object.__init__` methods. If the base class has an :meth:`~object.__init__` method
|
||||
that has to be called, it is common to call this method in a
|
||||
:meth:`__post_init__` method::
|
||||
:meth:`!__post_init__` method::
|
||||
|
||||
@dataclass
|
||||
class Rectangle:
|
||||
|
@ -545,12 +547,12 @@ that has to be called, it is common to call this method in a
|
|||
def __post_init__(self):
|
||||
super().__init__(self.side, self.side)
|
||||
|
||||
Note, however, that in general the dataclass-generated :meth:`__init__` methods
|
||||
Note, however, that in general the dataclass-generated :meth:`~object.__init__` methods
|
||||
don't need to be called, since the derived dataclass will take care of
|
||||
initializing all fields of any base class that is a dataclass itself.
|
||||
|
||||
See the section below on init-only variables for ways to pass
|
||||
parameters to :meth:`__post_init__`. Also see the warning about how
|
||||
parameters to :meth:`!__post_init__`. Also see the warning about how
|
||||
:func:`replace` handles ``init=False`` fields.
|
||||
|
||||
Class variables
|
||||
|
@ -573,8 +575,8 @@ if the type of a field is of type ``dataclasses.InitVar``. If a field
|
|||
is an ``InitVar``, it is considered a pseudo-field called an init-only
|
||||
field. As it is not a true field, it is not returned by the
|
||||
module-level :func:`fields` function. Init-only fields are added as
|
||||
parameters to the generated :meth:`__init__` method, and are passed to
|
||||
the optional :meth:`__post_init__` method. They are not otherwise used
|
||||
parameters to the generated :meth:`~object.__init__` method, and are passed to
|
||||
the optional :ref:`__post_init__ <post-init-processing>` method. They are not otherwise used
|
||||
by dataclasses.
|
||||
|
||||
For example, suppose a field will be initialized from a database, if a
|
||||
|
@ -601,12 +603,12 @@ Frozen instances
|
|||
It is not possible to create truly immutable Python objects. However,
|
||||
by passing ``frozen=True`` to the :meth:`dataclass` decorator you can
|
||||
emulate immutability. In that case, dataclasses will add
|
||||
:meth:`__setattr__` and :meth:`__delattr__` methods to the class. These
|
||||
:meth:`~object.__setattr__` and :meth:`~object.__delattr__` methods to the class. These
|
||||
methods will raise a :exc:`FrozenInstanceError` when invoked.
|
||||
|
||||
There is a tiny performance penalty when using ``frozen=True``:
|
||||
:meth:`__init__` cannot use simple assignment to initialize fields, and
|
||||
must use :meth:`object.__setattr__`.
|
||||
:meth:`~object.__init__` cannot use simple assignment to initialize fields, and
|
||||
must use :meth:`~object.__setattr__`.
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
@ -634,14 +636,14 @@ example::
|
|||
The final list of fields is, in order, ``x``, ``y``, ``z``. The final
|
||||
type of ``x`` is ``int``, as specified in class ``C``.
|
||||
|
||||
The generated :meth:`__init__` method for ``C`` will look like::
|
||||
The generated :meth:`~object.__init__` method for ``C`` will look like::
|
||||
|
||||
def __init__(self, x: int = 15, y: int = 0, z: int = 10):
|
||||
|
||||
Re-ordering of keyword-only parameters in :meth:`__init__`
|
||||
----------------------------------------------------------
|
||||
Re-ordering of keyword-only parameters in :meth:`~object.__init__`
|
||||
------------------------------------------------------------------
|
||||
|
||||
After the parameters needed for :meth:`__init__` are computed, any
|
||||
After the parameters needed for :meth:`~object.__init__` are computed, any
|
||||
keyword-only parameters are moved to come after all regular
|
||||
(non-keyword-only) parameters. This is a requirement of how
|
||||
keyword-only parameters are implemented in Python: they must come
|
||||
|
@ -662,7 +664,7 @@ fields, and ``Base.x`` and ``D.z`` are regular fields::
|
|||
z: int = 10
|
||||
t: int = field(kw_only=True, default=0)
|
||||
|
||||
The generated :meth:`__init__` method for ``D`` will look like::
|
||||
The generated :meth:`~object.__init__` method for ``D`` will look like::
|
||||
|
||||
def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):
|
||||
|
||||
|
@ -671,7 +673,7 @@ the list of fields: parameters derived from regular fields are
|
|||
followed by parameters derived from keyword-only fields.
|
||||
|
||||
The relative ordering of keyword-only parameters is maintained in the
|
||||
re-ordered :meth:`__init__` parameter list.
|
||||
re-ordered :meth:`~object.__init__` parameter list.
|
||||
|
||||
|
||||
Default factory functions
|
||||
|
@ -683,10 +685,10 @@ example, to create a new instance of a list, use::
|
|||
|
||||
mylist: list = field(default_factory=list)
|
||||
|
||||
If a field is excluded from :meth:`__init__` (using ``init=False``)
|
||||
If a field is excluded from :meth:`~object.__init__` (using ``init=False``)
|
||||
and the field also specifies ``default_factory``, then the default
|
||||
factory function will always be called from the generated
|
||||
:meth:`__init__` function. This happens because there is no other
|
||||
:meth:`~object.__init__` function. This happens because there is no other
|
||||
way to give the field an initial value.
|
||||
|
||||
Mutable default values
|
||||
|
@ -714,7 +716,7 @@ Using dataclasses, *if* this code was valid::
|
|||
|
||||
@dataclass
|
||||
class D:
|
||||
x: List = []
|
||||
x: list = [] # This code raises ValueError
|
||||
def add(self, element):
|
||||
self.x += element
|
||||
|
||||
|
|
|
@ -1043,7 +1043,7 @@ Other constructors, all class methods:
|
|||
Return a :class:`.datetime` corresponding to *date_string*, parsed according to
|
||||
*format*.
|
||||
|
||||
This is equivalent to::
|
||||
If *format* does not contain microseconds or timezone information, this is equivalent to::
|
||||
|
||||
datetime(*(time.strptime(date_string, format)[0:6]))
|
||||
|
||||
|
@ -2510,10 +2510,7 @@ Notes:
|
|||
Because the format depends on the current locale, care should be taken when
|
||||
making assumptions about the output value. Field orderings will vary (for
|
||||
example, "month/day/year" versus "day/month/year"), and the output may
|
||||
contain Unicode characters encoded using the locale's default encoding (for
|
||||
example, if the current locale is ``ja_JP``, the default encoding could be
|
||||
any one of ``eucJP``, ``SJIS``, or ``utf-8``; use :meth:`locale.getlocale`
|
||||
to determine the current locale's encoding).
|
||||
contain non-ASCII characters.
|
||||
|
||||
(2)
|
||||
The :meth:`strptime` method can parse years in the full [1, 9999] range, but
|
||||
|
|
|
@ -926,7 +926,7 @@ Each thread has its own current context which is accessed or changed using the
|
|||
You can also use the :keyword:`with` statement and the :func:`localcontext`
|
||||
function to temporarily change the active context.
|
||||
|
||||
.. function:: localcontext(ctx=None, \*\*kwargs)
|
||||
.. function:: localcontext(ctx=None, **kwargs)
|
||||
|
||||
Return a context manager that will set the current context for the active thread
|
||||
to a copy of *ctx* on entry to the with-statement and restore the previous context
|
||||
|
|
|
@ -1036,6 +1036,24 @@ iterations of the loop.
|
|||
pushed to the stack before the attribute or unbound method respectively.
|
||||
|
||||
|
||||
.. opcode:: LOAD_SUPER_ATTR (namei)
|
||||
|
||||
This opcode implements :func:`super` (e.g. ``super().method()`` and
|
||||
``super().attr``). It works the same as :opcode:`LOAD_ATTR`, except that
|
||||
``namei`` is shifted left by 2 bits instead of 1, and instead of expecting a
|
||||
single receiver on the stack, it expects three objects (from top of stack
|
||||
down): ``self`` (the first argument to the current method), ``cls`` (the
|
||||
class within which the current method was defined), and the global ``super``.
|
||||
|
||||
The low bit of ``namei`` signals to attempt a method load, as with
|
||||
:opcode:`LOAD_ATTR`.
|
||||
|
||||
The second-low bit of ``namei``, if set, means that this was a two-argument
|
||||
call to :func:`super` (unset means zero-argument).
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
||||
.. opcode:: COMPARE_OP (opname)
|
||||
|
||||
Performs a Boolean operation. The operation name can be found in
|
||||
|
|
|
@ -119,7 +119,8 @@ Module Contents
|
|||
:func:`~enum.property`
|
||||
|
||||
Allows :class:`Enum` members to have attributes without conflicting with
|
||||
member names.
|
||||
member names. The ``value`` and ``name`` attributes are implemented this
|
||||
way.
|
||||
|
||||
:func:`unique`
|
||||
|
||||
|
@ -169,7 +170,7 @@ Data Types
|
|||
final *enum*, as well as creating the enum members, properly handling
|
||||
duplicates, providing iteration over the enum class, etc.
|
||||
|
||||
.. method:: EnumType.__call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
|
||||
.. method:: EnumType.__call__(cls, value, names=None, \*, module=None, qualname=None, type=None, start=1, boundary=None)
|
||||
|
||||
This method is called in two different ways:
|
||||
|
||||
|
@ -317,7 +318,7 @@ Data Types
|
|||
>>> PowersOfThree.SECOND.value
|
||||
9
|
||||
|
||||
.. method:: Enum.__init_subclass__(cls, **kwds)
|
||||
.. method:: Enum.__init_subclass__(cls, \**kwds)
|
||||
|
||||
A *classmethod* that is used to further configure subsequent subclasses.
|
||||
By default, does nothing.
|
||||
|
|
|
@ -49,8 +49,13 @@ The :mod:`functools` module defines the following functions:
|
|||
>>> factorial(12) # makes two new recursive calls, the other 10 are cached
|
||||
479001600
|
||||
|
||||
The cache is threadsafe so the wrapped function can be used in multiple
|
||||
threads.
|
||||
The cache is threadsafe so that the wrapped function can be used in
|
||||
multiple threads. This means that the underlying data structure will
|
||||
remain coherent during concurrent updates.
|
||||
|
||||
It is possible for the wrapped function to be called more than once if
|
||||
another thread makes an additional call before the initial call has been
|
||||
completed and cached.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
|
@ -118,6 +123,7 @@ The :mod:`functools` module defines the following functions:
|
|||
def stdev(self):
|
||||
return statistics.stdev(self._data)
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Prior to Python 3.12, ``cached_property`` included an undocumented lock to
|
||||
|
@ -126,8 +132,6 @@ The :mod:`functools` module defines the following functions:
|
|||
per-instance, which could result in unacceptably high lock contention. In
|
||||
Python 3.12+ this locking is removed.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. function:: cmp_to_key(func)
|
||||
|
||||
|
@ -159,8 +163,13 @@ The :mod:`functools` module defines the following functions:
|
|||
*maxsize* most recent calls. It can save time when an expensive or I/O bound
|
||||
function is periodically called with the same arguments.
|
||||
|
||||
The cache is threadsafe so the wrapped function can be used in multiple
|
||||
threads.
|
||||
The cache is threadsafe so that the wrapped function can be used in
|
||||
multiple threads. This means that the underlying data structure will
|
||||
remain coherent during concurrent updates.
|
||||
|
||||
It is possible for the wrapped function to be called more than once if
|
||||
another thread makes an additional call before the initial call has been
|
||||
completed and cached.
|
||||
|
||||
Since a dictionary is used to cache results, the positional and keyword
|
||||
arguments to the function must be :term:`hashable`.
|
||||
|
@ -233,7 +242,7 @@ The :mod:`functools` module defines the following functions:
|
|||
@lru_cache(maxsize=32)
|
||||
def get_pep(num):
|
||||
'Retrieve text of a Python Enhancement Proposal'
|
||||
resource = 'https://peps.python.org/pep-%04d/' % num
|
||||
resource = f'https://peps.python.org/pep-{num:04d}'
|
||||
try:
|
||||
with urllib.request.urlopen(resource) as s:
|
||||
return s.read()
|
||||
|
|
|
@ -308,6 +308,10 @@ Python module or `Import Package <https://packaging.python.org/en/latest/glossar
|
|||
>>> packages_distributions()
|
||||
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
|
||||
|
||||
Some editable installs, `do not supply top-level names
|
||||
<https://github.com/pypa/packaging-problems/issues/609>`_, and thus this
|
||||
function is not reliable with such installs.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
.. _distributions:
|
||||
|
|
|
@ -452,9 +452,7 @@ process which created it.
|
|||
importable by the children. This is covered in :ref:`multiprocessing-programming`
|
||||
however it is worth pointing out here. This means that some examples, such
|
||||
as the :class:`multiprocessing.pool.Pool` examples will not work in the
|
||||
interactive interpreter. For example:
|
||||
|
||||
.. code-block:: text
|
||||
interactive interpreter. For example::
|
||||
|
||||
>>> from multiprocessing import Pool
|
||||
>>> p = Pool(5)
|
||||
|
|
|
@ -954,7 +954,16 @@ The canonical way to create an :class:`Option` instance is with the
|
|||
|
||||
As you can see, most actions involve storing or updating a value somewhere.
|
||||
:mod:`optparse` always creates a special object for this, conventionally called
|
||||
``options`` (it happens to be an instance of :class:`optparse.Values`). Option
|
||||
``options``, which is an instance of :class:`optparse.Values`.
|
||||
|
||||
.. class:: Values
|
||||
|
||||
An object holding parsed argument names and values as attributes.
|
||||
Normally created by calling when calling :meth:`OptionParser.parse_args`,
|
||||
and can be overridden by a custom subclass passed to the *values* argument of
|
||||
:meth:`OptionParser.parse_args` (as described in :ref:`optparse-parsing-arguments`).
|
||||
|
||||
Option
|
||||
arguments (and various other values) are stored as attributes of this object,
|
||||
according to the :attr:`~Option.dest` (destination) option attribute.
|
||||
|
||||
|
@ -991,6 +1000,14 @@ one that makes sense for *all* options.
|
|||
Option attributes
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. class:: Option
|
||||
|
||||
A single command line argument,
|
||||
with various attributes passed by keyword to the constructor.
|
||||
Normally created with :meth:`OptionParser.add_option` rather than directly,
|
||||
and can be overridden by a custom class via the *option_class* argument
|
||||
to :class:`OptionParser`.
|
||||
|
||||
The following option attributes may be passed as keyword arguments to
|
||||
:meth:`OptionParser.add_option`. If you pass an option attribute that is not
|
||||
relevant to a particular option, or fail to pass a required option attribute,
|
||||
|
@ -2035,3 +2052,27 @@ Features of note:
|
|||
about setting a default value for the option destinations in question; they
|
||||
can just leave the default as ``None`` and :meth:`ensure_value` will take care of
|
||||
getting it right when it's needed.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
.. exception:: OptionError
|
||||
|
||||
Raised if an :class:`Option` instance is created with invalid or
|
||||
inconsistent arguments.
|
||||
|
||||
.. exception:: OptionConflictError
|
||||
|
||||
Raised if conflicting options are added to an :class:`OptionParser`.
|
||||
|
||||
.. exception:: OptionValueError
|
||||
|
||||
Raised if an invalid option value is encountered on the command line.
|
||||
|
||||
.. exception:: BadOptionError
|
||||
|
||||
Raised if an invalid option is passed on the command line.
|
||||
|
||||
.. exception:: AmbiguousOptionError
|
||||
|
||||
Raised if an ambiguous option is passed on the command line.
|
||||
|
|
|
@ -3919,7 +3919,8 @@ to be ignored.
|
|||
the :envvar:`PATH` variable. The other variants, :func:`execl`, :func:`execle`,
|
||||
:func:`execv`, and :func:`execve`, will not use the :envvar:`PATH` variable to
|
||||
locate the executable; *path* must contain an appropriate absolute or relative
|
||||
path.
|
||||
path. Relative paths must include at least one slash, even on Windows, as
|
||||
plain names will not be resolved.
|
||||
|
||||
For :func:`execle`, :func:`execlpe`, :func:`execve`, and :func:`execvpe` (note
|
||||
that these all end in "e"), the *env* parameter must be a mapping which is
|
||||
|
|
|
@ -25,9 +25,9 @@ support.
|
|||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
|
||||
This will add to the package's ``__path__`` all subdirectories of directories
|
||||
on :data:`sys.path` named after the package. This is useful if one wants to
|
||||
distribute different parts of a single logical package as multiple
|
||||
For each directory on :data:`sys.path` that has a subdirectory that matches the
|
||||
package name, add the subdirectory to the package's :attr:`__path__`. This is useful
|
||||
if one wants to distribute different parts of a single logical package as multiple
|
||||
directories.
|
||||
|
||||
It also looks for :file:`\*.pkg` files beginning where ``*`` matches the
|
||||
|
@ -82,7 +82,7 @@ support.
|
|||
This is a backwards compatibility wrapper around
|
||||
:func:`importlib.util.find_spec` that converts most failures to
|
||||
:exc:`ImportError` and only returns the loader rather than the full
|
||||
:class:`ModuleSpec`.
|
||||
:class:`importlib.machinery.ModuleSpec`.
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
Updated to be based directly on :mod:`importlib` rather than relying
|
||||
|
|
|
@ -19,7 +19,7 @@ function.
|
|||
|
||||
Readline keybindings may be configured via an initialization file, typically
|
||||
``.inputrc`` in your home directory. See `Readline Init File
|
||||
<https://tiswww.cwru.edu/php/chet/readline/rluserman.html#SEC9>`_
|
||||
<https://tiswww.cwru.edu/php/chet/readline/rluserman.html#Readline-Init-File>`_
|
||||
in the GNU Readline manual for information about the format and
|
||||
allowable constructs of that file, and the capabilities of the
|
||||
Readline library in general.
|
||||
|
|
|
@ -662,7 +662,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
|
|||
Remove the archive format *name* from the list of supported formats.
|
||||
|
||||
|
||||
.. function:: unpack_archive(filename[, extract_dir[, format]])
|
||||
.. function:: unpack_archive(filename[, extract_dir[, format[, filter]]])
|
||||
|
||||
Unpack an archive. *filename* is the full path of the archive.
|
||||
|
||||
|
@ -676,6 +676,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
|
|||
registered for that extension. In case none is found,
|
||||
a :exc:`ValueError` is raised.
|
||||
|
||||
The keyword-only *filter* argument is passed to the underlying unpacking
|
||||
function. For zip files, *filter* is not accepted.
|
||||
For tar files, it is recommended to set it to ``'data'``,
|
||||
unless using features specific to tar and UNIX-like filesystems.
|
||||
(See :ref:`tarfile-extraction-filter` for details.)
|
||||
The ``'data'`` filter will become the default for tar files
|
||||
in Python 3.14.
|
||||
|
||||
.. audit-event:: shutil.unpack_archive filename,extract_dir,format shutil.unpack_archive
|
||||
|
||||
.. warning::
|
||||
|
@ -688,6 +696,9 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
|
|||
.. versionchanged:: 3.7
|
||||
Accepts a :term:`path-like object` for *filename* and *extract_dir*.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Added the *filter* argument.
|
||||
|
||||
.. function:: register_unpack_format(name, extensions, function[, extra_args[, description]])
|
||||
|
||||
Registers an unpack format. *name* is the name of the format and
|
||||
|
@ -695,11 +706,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
|
|||
``.zip`` for Zip files.
|
||||
|
||||
*function* is the callable that will be used to unpack archives. The
|
||||
callable will receive the path of the archive, followed by the directory
|
||||
the archive must be extracted to.
|
||||
callable will receive:
|
||||
|
||||
When provided, *extra_args* is a sequence of ``(name, value)`` tuples that
|
||||
will be passed as keywords arguments to the callable.
|
||||
- the path of the archive, as a positional argument;
|
||||
- the directory the archive must be extracted to, as a positional argument;
|
||||
- possibly a *filter* keyword argument, if it was given to
|
||||
:func:`unpack_archive`;
|
||||
- additional keyword arguments, specified by *extra_args* as a sequence
|
||||
of ``(name, value)`` tuples.
|
||||
|
||||
*description* can be provided to describe the format, and will be returned
|
||||
by the :func:`get_unpack_formats` function.
|
||||
|
|
|
@ -140,9 +140,16 @@ server is the address family.
|
|||
ForkingUDPServer
|
||||
ThreadingTCPServer
|
||||
ThreadingUDPServer
|
||||
ForkingUnixStreamServer
|
||||
ForkingUnixDatagramServer
|
||||
ThreadingUnixStreamServer
|
||||
ThreadingUnixDatagramServer
|
||||
|
||||
These classes are pre-defined using the mix-in classes.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
The ``ForkingUnixStreamServer`` and ``ForkingUnixDatagramServer`` classes
|
||||
were added.
|
||||
|
||||
To implement a service, you must derive a class from :class:`BaseRequestHandler`
|
||||
and redefine its :meth:`~BaseRequestHandler.handle` method.
|
||||
|
|
|
@ -1605,8 +1605,8 @@ expression support in the :mod:`re` module).
|
|||
converts it to ``"ss"``.
|
||||
|
||||
The casefolding algorithm is
|
||||
`described in section 3.13 of the Unicode Standard
|
||||
<http://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G53253>`__.
|
||||
`described in section 3.13 'Default Case Folding' of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf>`__.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
@ -1768,8 +1768,9 @@ expression support in the :mod:`re` module).
|
|||
one character, ``False`` otherwise. Alphabetic characters are those characters defined
|
||||
in the Unicode character database as "Letter", i.e., those with general category
|
||||
property being one of "Lm", "Lt", "Lu", "Ll", or "Lo". Note that this is different
|
||||
from the `Alphabetic property defined in the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf#G91002>`_.
|
||||
from the `Alphabetic property defined in the section 4.10 'Letters, Alphabetic, and
|
||||
Ideographic' of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf>`_.
|
||||
|
||||
|
||||
.. method:: str.isascii()
|
||||
|
@ -1904,8 +1905,8 @@ expression support in the :mod:`re` module).
|
|||
lowercase.
|
||||
|
||||
The lowercasing algorithm used is
|
||||
`described in section 3.13 of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G34078>`__.
|
||||
`described in section 3.13 'Default Case Folding' of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf>`__.
|
||||
|
||||
|
||||
.. method:: str.lstrip([chars])
|
||||
|
@ -2250,8 +2251,8 @@ expression support in the :mod:`re` module).
|
|||
titlecase).
|
||||
|
||||
The uppercasing algorithm used is
|
||||
`described in section 3.13 of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G34078>`__.
|
||||
`described in section 3.13 'Default Case Folding' of the Unicode Standard
|
||||
<https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf>`__.
|
||||
|
||||
|
||||
.. method:: str.zfill(width)
|
||||
|
@ -3714,12 +3715,15 @@ copying.
|
|||
types such as :class:`bytes` and :class:`bytearray`, an element is a single
|
||||
byte, but other types such as :class:`array.array` may have bigger elements.
|
||||
|
||||
``len(view)`` is equal to the length of :class:`~memoryview.tolist`.
|
||||
If ``view.ndim = 0``, the length is 1. If ``view.ndim = 1``, the length
|
||||
is equal to the number of elements in the view. For higher dimensions,
|
||||
the length is equal to the length of the nested list representation of
|
||||
the view. The :class:`~memoryview.itemsize` attribute will give you the
|
||||
number of bytes in a single element.
|
||||
``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which
|
||||
is the nested list representation of the view. If ``view.ndim = 1``,
|
||||
this is equal to the number of elements in the view.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
If ``view.ndim == 0``, ``len(view)`` now raises :exc:`TypeError` instead of returning 1.
|
||||
|
||||
The :class:`~memoryview.itemsize` attribute will give you the number of
|
||||
bytes in a single element.
|
||||
|
||||
A :class:`memoryview` supports slicing and indexing to expose its data.
|
||||
One-dimensional slicing will result in a subview::
|
||||
|
|
|
@ -919,9 +919,12 @@ Reassigning them to new values is unsupported:
|
|||
|
||||
.. attribute:: Popen.returncode
|
||||
|
||||
The child return code, set by :meth:`poll` and :meth:`wait` (and indirectly
|
||||
by :meth:`communicate`). A ``None`` value indicates that the process
|
||||
hasn't terminated yet.
|
||||
The child return code. Initially ``None``, :attr:`returncode` is set by
|
||||
a call to the :meth:`poll`, :meth:`wait`, or :meth:`communicate` methods
|
||||
if they detect that the process has terminated.
|
||||
|
||||
A ``None`` value indicates that the process hadn't yet terminated at the
|
||||
time of the last method call.
|
||||
|
||||
A negative value ``-N`` indicates that the child was terminated by signal
|
||||
``N`` (POSIX only).
|
||||
|
|
|
@ -670,6 +670,13 @@ always available.
|
|||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
.. function:: getunicodeinternedsize()
|
||||
|
||||
Return the number of unicode objects that have been interned.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
||||
.. function:: getandroidapilevel()
|
||||
|
||||
Return the build time API version of Android as an integer.
|
||||
|
|
|
@ -36,6 +36,13 @@ Some facts and figures:
|
|||
.. versionchanged:: 3.3
|
||||
Added support for :mod:`lzma` compression.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Archives are extracted using a :ref:`filter <tarfile-extraction-filter>`,
|
||||
which makes it possible to either limit surprising/dangerous features,
|
||||
or to acknowledge that they are expected and the archive is fully trusted.
|
||||
By default, archives are fully trusted, but this default is deprecated
|
||||
and slated to change in Python 3.14.
|
||||
|
||||
|
||||
.. function:: open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)
|
||||
|
||||
|
@ -209,6 +216,38 @@ The :mod:`tarfile` module defines the following exceptions:
|
|||
Is raised by :meth:`TarInfo.frombuf` if the buffer it gets is invalid.
|
||||
|
||||
|
||||
.. exception:: FilterError
|
||||
|
||||
Base class for members :ref:`refused <tarfile-extraction-refuse>` by
|
||||
filters.
|
||||
|
||||
.. attribute:: tarinfo
|
||||
|
||||
Information about the member that the filter refused to extract,
|
||||
as :ref:`TarInfo <tarinfo-objects>`.
|
||||
|
||||
.. exception:: AbsolutePathError
|
||||
|
||||
Raised to refuse extracting a member with an absolute path.
|
||||
|
||||
.. exception:: OutsideDestinationError
|
||||
|
||||
Raised to refuse extracting a member outside the destination directory.
|
||||
|
||||
.. exception:: SpecialFileError
|
||||
|
||||
Raised to refuse extracting a special file (e.g. a device or pipe).
|
||||
|
||||
.. exception:: AbsoluteLinkError
|
||||
|
||||
Raised to refuse extracting a symbolic link with an absolute path.
|
||||
|
||||
.. exception:: LinkOutsideDestinationError
|
||||
|
||||
Raised to refuse extracting a symbolic link pointing outside the destination
|
||||
directory.
|
||||
|
||||
|
||||
The following constants are available at the module level:
|
||||
|
||||
.. data:: ENCODING
|
||||
|
@ -319,11 +358,8 @@ be finalized; only the internally used file object will be closed. See the
|
|||
*debug* can be set from ``0`` (no debug messages) up to ``3`` (all debug
|
||||
messages). The messages are written to ``sys.stderr``.
|
||||
|
||||
If *errorlevel* is ``0``, all errors are ignored when using :meth:`TarFile.extract`.
|
||||
Nevertheless, they appear as error messages in the debug output, when debugging
|
||||
is enabled. If ``1``, all *fatal* errors are raised as :exc:`OSError`
|
||||
exceptions. If ``2``, all *non-fatal* errors are raised as :exc:`TarError`
|
||||
exceptions as well.
|
||||
*errorlevel* controls how extraction errors are handled,
|
||||
see :attr:`the corresponding attribute <~TarFile.errorlevel>`.
|
||||
|
||||
The *encoding* and *errors* arguments define the character encoding to be
|
||||
used for reading or writing the archive and how conversion errors are going
|
||||
|
@ -390,7 +426,7 @@ be finalized; only the internally used file object will be closed. See the
|
|||
available.
|
||||
|
||||
|
||||
.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False)
|
||||
.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False, filter=None)
|
||||
|
||||
Extract all members from the archive to the current working directory or
|
||||
directory *path*. If optional *members* is given, it must be a subset of the
|
||||
|
@ -404,6 +440,12 @@ be finalized; only the internally used file object will be closed. See the
|
|||
are used to set the owner/group for the extracted files. Otherwise, the named
|
||||
values from the tarfile are used.
|
||||
|
||||
The *filter* argument specifies how ``members`` are modified or rejected
|
||||
before extraction.
|
||||
See :ref:`tarfile-extraction-filter` for details.
|
||||
It is recommended to set this explicitly depending on which *tar* features
|
||||
you need to support.
|
||||
|
||||
.. warning::
|
||||
|
||||
Never extract archives from untrusted sources without prior inspection.
|
||||
|
@ -411,14 +453,20 @@ be finalized; only the internally used file object will be closed. See the
|
|||
that have absolute filenames starting with ``"/"`` or filenames with two
|
||||
dots ``".."``.
|
||||
|
||||
Set ``filter='data'`` to prevent the most dangerous security issues,
|
||||
and read the :ref:`tarfile-extraction-filter` section for details.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
Added the *numeric_owner* parameter.
|
||||
|
||||
.. versionchanged:: 3.6
|
||||
The *path* parameter accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Added the *filter* parameter.
|
||||
|
||||
.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False)
|
||||
|
||||
.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False, filter=None)
|
||||
|
||||
Extract a member from the archive to the current working directory, using its
|
||||
full name. Its file information is extracted as accurately as possible. *member*
|
||||
|
@ -426,9 +474,8 @@ be finalized; only the internally used file object will be closed. See the
|
|||
directory using *path*. *path* may be a :term:`path-like object`.
|
||||
File attributes (owner, mtime, mode) are set unless *set_attrs* is false.
|
||||
|
||||
If *numeric_owner* is :const:`True`, the uid and gid numbers from the tarfile
|
||||
are used to set the owner/group for the extracted files. Otherwise, the named
|
||||
values from the tarfile are used.
|
||||
The *numeric_owner* and *filter* arguments are the same as
|
||||
for :meth:`extractall`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -439,6 +486,9 @@ be finalized; only the internally used file object will be closed. See the
|
|||
|
||||
See the warning for :meth:`extractall`.
|
||||
|
||||
Set ``filter='data'`` to prevent the most dangerous security issues,
|
||||
and read the :ref:`tarfile-extraction-filter` section for details.
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
Added the *set_attrs* parameter.
|
||||
|
||||
|
@ -448,6 +498,9 @@ be finalized; only the internally used file object will be closed. See the
|
|||
.. versionchanged:: 3.6
|
||||
The *path* parameter accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
Added the *filter* parameter.
|
||||
|
||||
|
||||
.. method:: TarFile.extractfile(member)
|
||||
|
||||
|
@ -460,6 +513,55 @@ be finalized; only the internally used file object will be closed. See the
|
|||
.. versionchanged:: 3.3
|
||||
Return an :class:`io.BufferedReader` object.
|
||||
|
||||
.. attribute:: TarFile.errorlevel
|
||||
:type: int
|
||||
|
||||
If *errorlevel* is ``0``, errors are ignored when using :meth:`TarFile.extract`
|
||||
and :meth:`TarFile.extractall`.
|
||||
Nevertheless, they appear as error messages in the debug output when
|
||||
*debug* is greater than 0.
|
||||
If ``1`` (the default), all *fatal* errors are raised as :exc:`OSError` or
|
||||
:exc:`FilterError` exceptions. If ``2``, all *non-fatal* errors are raised
|
||||
as :exc:`TarError` exceptions as well.
|
||||
|
||||
Some exceptions, e.g. ones caused by wrong argument types or data
|
||||
corruption, are always raised.
|
||||
|
||||
Custom :ref:`extraction filters <tarfile-extraction-filter>`
|
||||
should raise :exc:`FilterError` for *fatal* errors
|
||||
and :exc:`ExtractError` for *non-fatal* ones.
|
||||
|
||||
Note that when an exception is raised, the archive may be partially
|
||||
extracted. It is the user’s responsibility to clean up.
|
||||
|
||||
.. attribute:: TarFile.extraction_filter
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
The :ref:`extraction filter <tarfile-extraction-filter>` used
|
||||
as a default for the *filter* argument of :meth:`~TarFile.extract`
|
||||
and :meth:`~TarFile.extractall`.
|
||||
|
||||
The attribute may be ``None`` or a callable.
|
||||
String names are not allowed for this attribute, unlike the *filter*
|
||||
argument to :meth:`~TarFile.extract`.
|
||||
|
||||
If ``extraction_filter`` is ``None`` (the default),
|
||||
calling an extraction method without a *filter* argument will raise a
|
||||
``DeprecationWarning``,
|
||||
and fall back to the :func:`fully_trusted <fully_trusted_filter>` filter,
|
||||
whose dangerous behavior matches previous versions of Python.
|
||||
|
||||
In Python 3.14+, leaving ``extraction_filter=None`` will cause
|
||||
extraction methods to use the :func:`data <data_filter>` filter by default.
|
||||
|
||||
The attribute may be set on instances or overridden in subclasses.
|
||||
It also is possible to set it on the ``TarFile`` class itself to set a
|
||||
global default, although, since it affects all uses of *tarfile*,
|
||||
it is best practice to only do so in top-level applications or
|
||||
:mod:`site configuration <site>`.
|
||||
To set a global default this way, a filter function needs to be wrapped in
|
||||
:func:`staticmethod()` to prevent injection of a ``self`` argument.
|
||||
|
||||
.. method:: TarFile.add(name, arcname=None, recursive=True, *, filter=None)
|
||||
|
||||
|
@ -535,8 +637,23 @@ permissions, owner etc.), it provides some useful methods to determine its type.
|
|||
It does *not* contain the file's data itself.
|
||||
|
||||
:class:`TarInfo` objects are returned by :class:`TarFile`'s methods
|
||||
:meth:`getmember`, :meth:`getmembers` and :meth:`gettarinfo`.
|
||||
:meth:`~TarFile.getmember`, :meth:`~TarFile.getmembers` and
|
||||
:meth:`~TarFile.gettarinfo`.
|
||||
|
||||
Modifying the objects returned by :meth:`~!TarFile.getmember` or
|
||||
:meth:`~!TarFile.getmembers` will affect all subsequent
|
||||
operations on the archive.
|
||||
For cases where this is unwanted, you can use :mod:`copy.copy() <copy>` or
|
||||
call the :meth:`~TarInfo.replace` method to create a modified copy in one step.
|
||||
|
||||
Several attributes can be set to ``None`` to indicate that a piece of metadata
|
||||
is unused or unknown.
|
||||
Different :class:`TarInfo` methods handle ``None`` differently:
|
||||
|
||||
- The :meth:`~TarFile.extract` or :meth:`~TarFile.extractall` methods will
|
||||
ignore the corresponding metadata, leaving it set to a default.
|
||||
- :meth:`~TarFile.addfile` will fail.
|
||||
- :meth:`~TarFile.list` will print a placeholder string.
|
||||
|
||||
.. class:: TarInfo(name="")
|
||||
|
||||
|
@ -569,24 +686,39 @@ A ``TarInfo`` object has the following public data attributes:
|
|||
|
||||
|
||||
.. attribute:: TarInfo.name
|
||||
:type: str
|
||||
|
||||
Name of the archive member.
|
||||
|
||||
|
||||
.. attribute:: TarInfo.size
|
||||
:type: int
|
||||
|
||||
Size in bytes.
|
||||
|
||||
|
||||
.. attribute:: TarInfo.mtime
|
||||
:type: int | float
|
||||
|
||||
Time of last modification.
|
||||
Time of last modification in seconds since the :ref:`epoch <epoch>`,
|
||||
as in :attr:`os.stat_result.st_mtime`.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.mode
|
||||
:type: int
|
||||
|
||||
Permission bits.
|
||||
Permission bits, as for :func:`os.chmod`.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.type
|
||||
|
||||
|
@ -598,35 +730,76 @@ A ``TarInfo`` object has the following public data attributes:
|
|||
|
||||
|
||||
.. attribute:: TarInfo.linkname
|
||||
:type: str
|
||||
|
||||
Name of the target file name, which is only present in :class:`TarInfo` objects
|
||||
of type :const:`LNKTYPE` and :const:`SYMTYPE`.
|
||||
|
||||
|
||||
.. attribute:: TarInfo.uid
|
||||
:type: int
|
||||
|
||||
User ID of the user who originally stored this member.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.gid
|
||||
:type: int
|
||||
|
||||
Group ID of the user who originally stored this member.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.uname
|
||||
:type: str
|
||||
|
||||
User name.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.gname
|
||||
:type: str
|
||||
|
||||
Group name.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
Can be set to ``None`` for :meth:`~TarFile.extract` and
|
||||
:meth:`~TarFile.extractall`, causing extraction to skip applying this
|
||||
attribute.
|
||||
|
||||
.. attribute:: TarInfo.pax_headers
|
||||
:type: dict
|
||||
|
||||
A dictionary containing key-value pairs of an associated pax extended header.
|
||||
|
||||
.. method:: TarInfo.replace(name=..., mtime=..., mode=..., linkname=...,
|
||||
uid=..., gid=..., uname=..., gname=...,
|
||||
deep=True)
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
Return a *new* copy of the :class:`!TarInfo` object with the given attributes
|
||||
changed. For example, to return a ``TarInfo`` with the group name set to
|
||||
``'staff'``, use::
|
||||
|
||||
new_tarinfo = old_tarinfo.replace(gname='staff')
|
||||
|
||||
By default, a deep copy is made.
|
||||
If *deep* is false, the copy is shallow, i.e. ``pax_headers``
|
||||
and any custom attributes are shared with the original ``TarInfo`` object.
|
||||
|
||||
A :class:`TarInfo` object also provides some convenient query methods:
|
||||
|
||||
|
@ -676,9 +849,258 @@ A :class:`TarInfo` object also provides some convenient query methods:
|
|||
Return :const:`True` if it is one of character device, block device or FIFO.
|
||||
|
||||
|
||||
.. _tarfile-extraction-filter:
|
||||
|
||||
Extraction filters
|
||||
------------------
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
The *tar* format is designed to capture all details of a UNIX-like filesystem,
|
||||
which makes it very powerful.
|
||||
Unfortunately, the features make it easy to create tar files that have
|
||||
unintended -- and possibly malicious -- effects when extracted.
|
||||
For example, extracting a tar file can overwrite arbitrary files in various
|
||||
ways (e.g. by using absolute paths, ``..`` path components, or symlinks that
|
||||
affect later members).
|
||||
|
||||
In most cases, the full functionality is not needed.
|
||||
Therefore, *tarfile* supports extraction filters: a mechanism to limit
|
||||
functionality, and thus mitigate some of the security issues.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:pep:`706`
|
||||
Contains further motivation and rationale behind the design.
|
||||
|
||||
The *filter* argument to :meth:`TarFile.extract` or :meth:`~TarFile.extractall`
|
||||
can be:
|
||||
|
||||
* the string ``'fully_trusted'``: Honor all metadata as specified in the
|
||||
archive.
|
||||
Should be used if the user trusts the archive completely, or implements
|
||||
their own complex verification.
|
||||
|
||||
* the string ``'tar'``: Honor most *tar*-specific features (i.e. features of
|
||||
UNIX-like filesystems), but block features that are very likely to be
|
||||
surprising or malicious. See :func:`tar_filter` for details.
|
||||
|
||||
* the string ``'data'``: Ignore or block most features specific to UNIX-like
|
||||
filesystems. Intended for extracting cross-platform data archives.
|
||||
See :func:`data_filter` for details.
|
||||
|
||||
* ``None`` (default): Use :attr:`TarFile.extraction_filter`.
|
||||
|
||||
If that is also ``None`` (the default), raise a ``DeprecationWarning``,
|
||||
and fall back to the ``'fully_trusted'`` filter, whose dangerous behavior
|
||||
matches previous versions of Python.
|
||||
|
||||
In Python 3.14, the ``'data'`` filter will become the default instead.
|
||||
It's possible to switch earlier; see :attr:`TarFile.extraction_filter`.
|
||||
|
||||
* A callable which will be called for each extracted member with a
|
||||
:ref:`TarInfo <tarinfo-objects>` describing the member and the destination
|
||||
path to where the archive is extracted (i.e. the same path is used for all
|
||||
members)::
|
||||
|
||||
filter(/, member: TarInfo, path: str) -> TarInfo | None
|
||||
|
||||
The callable is called just before each member is extracted, so it can
|
||||
take the current state of the disk into account.
|
||||
It can:
|
||||
|
||||
- return a :class:`TarInfo` object which will be used instead of the metadata
|
||||
in the archive, or
|
||||
- return ``None``, in which case the member will be skipped, or
|
||||
- raise an exception to abort the operation or skip the member,
|
||||
depending on :attr:`~TarFile.errorlevel`.
|
||||
Note that when extraction is aborted, :meth:`~TarFile.extractall` may leave
|
||||
the archive partially extracted. It does not attempt to clean up.
|
||||
|
||||
Default named filters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The pre-defined, named filters are available as functions, so they can be
|
||||
reused in custom filters:
|
||||
|
||||
.. function:: fully_trusted_filter(/, member, path)
|
||||
|
||||
Return *member* unchanged.
|
||||
|
||||
This implements the ``'fully_trusted'`` filter.
|
||||
|
||||
.. function:: tar_filter(/, member, path)
|
||||
|
||||
Implements the ``'tar'`` filter.
|
||||
|
||||
- Strip leading slashes (``/`` and :attr:`os.sep`) from filenames.
|
||||
- :ref:`Refuse <tarfile-extraction-refuse>` to extract files with absolute
|
||||
paths (in case the name is absolute
|
||||
even after stripping slashes, e.g. ``C:/foo`` on Windows).
|
||||
This raises :class:`~tarfile.AbsolutePathError`.
|
||||
- :ref:`Refuse <tarfile-extraction-refuse>` to extract files whose absolute
|
||||
path (after following symlinks) would end up outside the destination.
|
||||
This raises :class:`~tarfile.OutsideDestinationError`.
|
||||
- Clear high mode bits (setuid, setgid, sticky) and group/other write bits
|
||||
(:attr:`~stat.S_IWGRP`|:attr:`~stat.S_IWOTH`).
|
||||
|
||||
Return the modified ``TarInfo`` member.
|
||||
|
||||
.. function:: data_filter(/, member, path)
|
||||
|
||||
Implements the ``'data'`` filter.
|
||||
In addition to what ``tar_filter`` does:
|
||||
|
||||
- :ref:`Refuse <tarfile-extraction-refuse>` to extract links (hard or soft)
|
||||
that link to absolute paths, or ones that link outside the destination.
|
||||
|
||||
This raises :class:`~tarfile.AbsoluteLinkError` or
|
||||
:class:`~tarfile.LinkOutsideDestinationError`.
|
||||
|
||||
Note that such files are refused even on platforms that do not support
|
||||
symbolic links.
|
||||
|
||||
- :ref:`Refuse <tarfile-extraction-refuse>` to extract device files
|
||||
(including pipes).
|
||||
This raises :class:`~tarfile.SpecialFileError`.
|
||||
|
||||
- For regular files, including hard links:
|
||||
|
||||
- Set the owner read and write permissions
|
||||
(:attr:`~stat.S_IRUSR`|:attr:`~stat.S_IWUSR`).
|
||||
- Remove the group & other executable permission
|
||||
(:attr:`~stat.S_IXGRP`|:attr:`~stat.S_IXOTH`)
|
||||
if the owner doesn’t have it (:attr:`~stat.S_IXUSR`).
|
||||
|
||||
- For other files (directories), set ``mode`` to ``None``, so
|
||||
that extraction methods skip applying permission bits.
|
||||
- Set user and group info (``uid``, ``gid``, ``uname``, ``gname``)
|
||||
to ``None``, so that extraction methods skip setting it.
|
||||
|
||||
Return the modified ``TarInfo`` member.
|
||||
|
||||
|
||||
.. _tarfile-extraction-refuse:
|
||||
|
||||
Filter errors
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
When a filter refuses to extract a file, it will raise an appropriate exception,
|
||||
a subclass of :class:`~tarfile.FilterError`.
|
||||
This will abort the extraction if :attr:`TarFile.errorlevel` is 1 or more.
|
||||
With ``errorlevel=0`` the error will be logged and the member will be skipped,
|
||||
but extraction will continue.
|
||||
|
||||
|
||||
Hints for further verification
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Even with ``filter='data'``, *tarfile* is not suited for extracting untrusted
|
||||
files without prior inspection.
|
||||
Among other issues, the pre-defined filters do not prevent denial-of-service
|
||||
attacks. Users should do additional checks.
|
||||
|
||||
Here is an incomplete list of things to consider:
|
||||
|
||||
* Extract to a :func:`new temporary directory <tempfile.mkdtemp>`
|
||||
to prevent e.g. exploiting pre-existing links, and to make it easier to
|
||||
clean up after a failed extraction.
|
||||
* When working with untrusted data, use external (e.g. OS-level) limits on
|
||||
disk, memory and CPU usage.
|
||||
* Check filenames against an allow-list of characters
|
||||
(to filter out control characters, confusables, foreign path separators,
|
||||
etc.).
|
||||
* Check that filenames have expected extensions (discouraging files that
|
||||
execute when you “click on them”, or extension-less files like Windows special device names).
|
||||
* Limit the number of extracted files, total size of extracted data,
|
||||
filename length (including symlink length), and size of individual files.
|
||||
* Check for files that would be shadowed on case-insensitive filesystems.
|
||||
|
||||
Also note that:
|
||||
|
||||
* Tar files may contain multiple versions of the same file.
|
||||
Later ones are expected to overwrite any earlier ones.
|
||||
This feature is crucial to allow updating tape archives, but can be abused
|
||||
maliciously.
|
||||
* *tarfile* does not protect against issues with “live” data,
|
||||
e.g. an attacker tinkering with the destination (or source) directory while
|
||||
extraction (or archiving) is in progress.
|
||||
|
||||
|
||||
Supporting older Python versions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Extraction filters were added to Python 3.12, but may be backported to older
|
||||
versions as security updates.
|
||||
To check whether the feature is available, use e.g.
|
||||
``hasattr(tarfile, 'data_filter')`` rather than checking the Python version.
|
||||
|
||||
The following examples show how to support Python versions with and without
|
||||
the feature.
|
||||
Note that setting ``extraction_filter`` will affect any subsequent operations.
|
||||
|
||||
* Fully trusted archive::
|
||||
|
||||
my_tarfile.extraction_filter = (lambda member, path: member)
|
||||
my_tarfile.extractall()
|
||||
|
||||
* Use the ``'data'`` filter if available, but revert to Python 3.11 behavior
|
||||
(``'fully_trusted'``) if this feature is not available::
|
||||
|
||||
my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
|
||||
(lambda member, path: member))
|
||||
my_tarfile.extractall()
|
||||
|
||||
* Use the ``'data'`` filter; *fail* if it is not available::
|
||||
|
||||
my_tarfile.extractall(filter=tarfile.data_filter)
|
||||
|
||||
or::
|
||||
|
||||
my_tarfile.extraction_filter = tarfile.data_filter
|
||||
my_tarfile.extractall()
|
||||
|
||||
* Use the ``'data'`` filter; *warn* if it is not available::
|
||||
|
||||
if hasattr(tarfile, 'data_filter'):
|
||||
my_tarfile.extractall(filter='data')
|
||||
else:
|
||||
# remove this when no longer needed
|
||||
warn_the_user('Extracting may be unsafe; consider updating Python')
|
||||
my_tarfile.extractall()
|
||||
|
||||
|
||||
Stateful extraction filter example
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While *tarfile*'s extraction methods take a simple *filter* callable,
|
||||
custom filters may be more complex objects with an internal state.
|
||||
It may be useful to write these as context managers, to be used like this::
|
||||
|
||||
with StatefulFilter() as filter_func:
|
||||
tar.extractall(path, filter=filter_func)
|
||||
|
||||
Such a filter can be written as, for example::
|
||||
|
||||
class StatefulFilter:
|
||||
def __init__(self):
|
||||
self.file_count = 0
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __call__(self, member, path):
|
||||
self.file_count += 1
|
||||
return member
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
print(f'{self.file_count} files extracted')
|
||||
|
||||
|
||||
.. _tarfile-commandline:
|
||||
.. program:: tarfile
|
||||
|
||||
|
||||
Command-Line Interface
|
||||
----------------------
|
||||
|
||||
|
@ -748,6 +1170,13 @@ Command-line options
|
|||
|
||||
Verbose output.
|
||||
|
||||
.. cmdoption:: --filter <filtername>
|
||||
|
||||
Specifies the *filter* for ``--extract``.
|
||||
See :ref:`tarfile-extraction-filter` for details.
|
||||
Only string names are accepted (that is, ``fully_trusted``, ``tar``,
|
||||
and ``data``).
|
||||
|
||||
.. _tar-examples:
|
||||
|
||||
Examples
|
||||
|
@ -757,7 +1186,7 @@ How to extract an entire tar archive to the current working directory::
|
|||
|
||||
import tarfile
|
||||
tar = tarfile.open("sample.tar.gz")
|
||||
tar.extractall()
|
||||
tar.extractall(filter='data')
|
||||
tar.close()
|
||||
|
||||
How to extract a subset of a tar archive with :meth:`TarFile.extractall` using
|
||||
|
|
10
Doc/library/token-list.inc
generated
10
Doc/library/token-list.inc
generated
|
@ -201,6 +201,10 @@
|
|||
|
||||
Token value for ``":="``.
|
||||
|
||||
.. data:: EXCLAMATION
|
||||
|
||||
Token value for ``"!"``.
|
||||
|
||||
.. data:: OP
|
||||
|
||||
.. data:: AWAIT
|
||||
|
@ -213,6 +217,12 @@
|
|||
|
||||
.. data:: SOFT_KEYWORD
|
||||
|
||||
.. data:: FSTRING_START
|
||||
|
||||
.. data:: FSTRING_MIDDLE
|
||||
|
||||
.. data:: FSTRING_END
|
||||
|
||||
.. data:: ERRORTOKEN
|
||||
|
||||
.. data:: N_TOKENS
|
||||
|
|
|
@ -82,6 +82,46 @@ Dynamic Type Creation
|
|||
|
||||
.. versionadded:: 3.7
|
||||
|
||||
.. function:: get_original_bases(cls, /)
|
||||
|
||||
Return the tuple of objects originally given as the bases of *cls* before
|
||||
the :meth:`~object.__mro_entries__` method has been called on any bases
|
||||
(following the mechanisms laid out in :pep:`560`). This is useful for
|
||||
introspecting :ref:`Generics <user-defined-generics>`.
|
||||
|
||||
For classes that have an ``__orig_bases__`` attribute, this
|
||||
function returns the value of ``cls.__orig_bases__``.
|
||||
For classes without the ``__orig_bases__`` attribute, ``cls.__bases__`` is
|
||||
returned.
|
||||
|
||||
Examples::
|
||||
|
||||
from typing import TypeVar, Generic, NamedTuple, TypedDict
|
||||
|
||||
T = TypeVar("T")
|
||||
class Foo(Generic[T]): ...
|
||||
class Bar(Foo[int], float): ...
|
||||
class Baz(list[str]): ...
|
||||
Eggs = NamedTuple("Eggs", [("a", int), ("b", str)])
|
||||
Spam = TypedDict("Spam", {"a": int, "b": str})
|
||||
|
||||
assert Bar.__bases__ == (Foo, float)
|
||||
assert get_original_bases(Bar) == (Foo[int], float)
|
||||
|
||||
assert Baz.__bases__ == (list,)
|
||||
assert get_original_bases(Baz) == (list[str],)
|
||||
|
||||
assert Eggs.__bases__ == (tuple,)
|
||||
assert get_original_bases(Eggs) == (NamedTuple,)
|
||||
|
||||
assert Spam.__bases__ == (dict,)
|
||||
assert get_original_bases(Spam) == (TypedDict,)
|
||||
|
||||
assert int.__bases__ == (object,)
|
||||
assert get_original_bases(int) == (object,)
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
.. seealso::
|
||||
|
||||
:pep:`560` - Core support for typing module and generic types
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.. moduleauthor:: James C. Ahlstrom <jim@interet.com>
|
||||
.. sectionauthor:: James C. Ahlstrom <jim@interet.com>
|
||||
|
||||
**Source code:** :source:`Lib/zipfile.py`
|
||||
**Source code:** :source:`Lib/zipfile/`
|
||||
|
||||
--------------
|
||||
|
||||
|
|
|
@ -2102,6 +2102,10 @@ Resolving MRO entries
|
|||
:func:`types.resolve_bases`
|
||||
Dynamically resolve bases that are not instances of :class:`type`.
|
||||
|
||||
:func:`types.get_original_bases`
|
||||
Retrieve a class's "original bases" prior to modifications by
|
||||
:meth:`~object.__mro_entries__`.
|
||||
|
||||
:pep:`560`
|
||||
Core support for typing module and generic types.
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ Doc/faq/gui.rst
|
|||
Doc/faq/library.rst
|
||||
Doc/faq/programming.rst
|
||||
Doc/glossary.rst
|
||||
Doc/howto/argparse.rst
|
||||
Doc/howto/curses.rst
|
||||
Doc/howto/descriptor.rst
|
||||
Doc/howto/enum.rst
|
||||
|
@ -78,7 +77,6 @@ Doc/library/__future__.rst
|
|||
Doc/library/_thread.rst
|
||||
Doc/library/abc.rst
|
||||
Doc/library/aifc.rst
|
||||
Doc/library/argparse.rst
|
||||
Doc/library/ast.rst
|
||||
Doc/library/asyncio-dev.rst
|
||||
Doc/library/asyncio-eventloop.rst
|
||||
|
@ -113,7 +111,6 @@ Doc/library/csv.rst
|
|||
Doc/library/ctypes.rst
|
||||
Doc/library/curses.ascii.rst
|
||||
Doc/library/curses.rst
|
||||
Doc/library/dataclasses.rst
|
||||
Doc/library/datetime.rst
|
||||
Doc/library/dbm.rst
|
||||
Doc/library/decimal.rst
|
||||
|
@ -180,7 +177,6 @@ Doc/library/os.rst
|
|||
Doc/library/ossaudiodev.rst
|
||||
Doc/library/pickle.rst
|
||||
Doc/library/pickletools.rst
|
||||
Doc/library/pkgutil.rst
|
||||
Doc/library/platform.rst
|
||||
Doc/library/plistlib.rst
|
||||
Doc/library/poplib.rst
|
||||
|
|
|
@ -54,13 +54,6 @@ On FreeBSD and OpenBSD
|
|||
pkg_add ftp://ftp.openbsd.org/pub/OpenBSD/4.2/packages/i386/python-2.5.1p2.tgz
|
||||
|
||||
|
||||
On OpenSolaris
|
||||
--------------
|
||||
|
||||
You can get Python from `OpenCSW <https://www.opencsw.org/>`_. Various versions
|
||||
of Python are available and can be installed with e.g. ``pkgutil -i python27``.
|
||||
|
||||
|
||||
.. _building-python-on-unix:
|
||||
|
||||
Building Python
|
||||
|
|
|
@ -470,7 +470,7 @@ user's system, including environment variables, system registry settings, and
|
|||
installed packages. The standard library is included as pre-compiled and
|
||||
optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``,
|
||||
``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all
|
||||
dependants, such as Idle), pip and the Python documentation are not included.
|
||||
dependents, such as Idle), pip and the Python documentation are not included.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ this edition of "What's New in Python" links to the bug/patch
|
|||
item for each change.
|
||||
|
||||
Hosting of the Python bug tracker is kindly provided by
|
||||
`Upfront Systems <http://www.upfrontsoftware.co.za>`__
|
||||
`Upfront Systems <https://upfrontsoftware.co.za>`__
|
||||
of Stellenbosch, South Africa. Martin von Löwis put a
|
||||
lot of effort into importing existing bugs and patches from
|
||||
SourceForge; his scripts for this import operation are at
|
||||
|
|
|
@ -2104,7 +2104,7 @@ Changes to Python's build process and to the C API include:
|
|||
|
||||
* The latest release of the GNU Debugger, GDB 7, can be `scripted
|
||||
using Python
|
||||
<https://sourceware.org/gdb/current/onlinedocs/gdb/Python.html>`__.
|
||||
<https://web.archive.org/web/20110715084810/http://sourceware.org/gdb/current/onlinedocs/gdb/Python.html>`__.
|
||||
When you begin debugging an executable program P, GDB will look for
|
||||
a file named ``P-gdb.py`` and automatically read it. Dave Malcolm
|
||||
contributed a :file:`python-gdb.py` that adds a number of
|
||||
|
|
|
@ -137,6 +137,13 @@ New Features
|
|||
(Design by Pablo Galindo. Contributed by Pablo Galindo and Christian Heimes
|
||||
with contributions from Gregory P. Smith [Google] and Mark Shannon
|
||||
in :gh:`96123`.)
|
||||
* The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`,
|
||||
have a new a *filter* argument that allows limiting tar features than may be
|
||||
surprising or dangerous, such as creating files outside the destination
|
||||
directory.
|
||||
See :ref:`tarfile-extraction-filter` for details.
|
||||
In Python 3.14, the default will switch to ``'data'``.
|
||||
(Contributed by Petr Viktorin in :pep:`706`.)
|
||||
|
||||
|
||||
Other Language Changes
|
||||
|
@ -250,7 +257,8 @@ asyncio
|
|||
:mod:`asyncio` does not support legacy generator-based coroutines.
|
||||
(Contributed by Kumar Aditya in :gh:`102748`.)
|
||||
|
||||
* :func:`asyncio.wait` now accepts generators yielding tasks.
|
||||
* :func:`asyncio.wait` and :func:`asyncio.as_completed` now accepts generators
|
||||
yielding tasks.
|
||||
(Contributed by Kumar Aditya in :gh:`78530`.)
|
||||
|
||||
csv
|
||||
|
@ -308,6 +316,13 @@ fractions
|
|||
* Objects of type :class:`fractions.Fraction` now support float-style
|
||||
formatting. (Contributed by Mark Dickinson in :gh:`100161`.)
|
||||
|
||||
itertools
|
||||
---------
|
||||
|
||||
* Added :class:`itertools.batched()` for collecting into even-sized
|
||||
tuples where the last batch may be shorter than the rest.
|
||||
(Contributed by Raymond Hettinger in :gh:`98363`.)
|
||||
|
||||
math
|
||||
----
|
||||
|
||||
|
@ -399,6 +414,13 @@ threading
|
|||
profiling functions in all running threads in addition to the calling one.
|
||||
(Contributed by Pablo Galindo in :gh:`93503`.)
|
||||
|
||||
types
|
||||
-----
|
||||
|
||||
* Add :func:`types.get_original_bases` to allow for further introspection of
|
||||
:ref:`user-defined-generics` when subclassed. (Contributed by
|
||||
James Hilton-Balfe and Alex Waygood in :gh:`101827`.)
|
||||
|
||||
unicodedata
|
||||
-----------
|
||||
|
||||
|
@ -615,6 +637,10 @@ Deprecated
|
|||
* The *onerror* argument of :func:`shutil.rmtree` is deprecated as will be removed
|
||||
in Python 3.14. Use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.)
|
||||
|
||||
* Extracting tar archives without specifying *filter* is deprecated until
|
||||
Python 3.14, when ``'data'`` filter will become the default.
|
||||
See :ref:`tarfile-extraction-filter` for details.
|
||||
|
||||
|
||||
Pending Removal in Python 3.13
|
||||
------------------------------
|
||||
|
@ -977,6 +1003,10 @@ Changes in the Python API
|
|||
exception instance, rather than to a ``(typ, exc, tb)`` tuple.
|
||||
(Contributed by Irit Katriel in :gh:`103176`.)
|
||||
|
||||
* When extracting tar files using :mod:`tarfile` or
|
||||
:func:`shutil.unpack_archive`, pass the *filter* argument to limit features
|
||||
that may be surprising or dangerous.
|
||||
See :ref:`tarfile-extraction-filter` for details.
|
||||
|
||||
Build Changes
|
||||
=============
|
||||
|
@ -1121,6 +1151,24 @@ New Features
|
|||
to replace the legacy-api :c:func:`!PyErr_Display`. (Contributed by
|
||||
Irit Katriel in :gh:`102755`).
|
||||
|
||||
* :pep:`683`: Introduced Immortal Objects to Python which allows objects
|
||||
to bypass reference counts and introduced changes to the C-API:
|
||||
|
||||
- ``_Py_IMMORTAL_REFCNT``: The reference count that defines an object
|
||||
as immortal.
|
||||
- ``_Py_IsImmortal`` Checks if an object has the immortal reference count.
|
||||
- ``PyObject_HEAD_INIT`` This will now initialize reference count to
|
||||
``_Py_IMMORTAL_REFCNT`` when used with ``Py_BUILD_CORE``.
|
||||
- ``SSTATE_INTERNED_IMMORTAL`` An identifier for interned unicode objects
|
||||
that are immortal.
|
||||
- ``SSTATE_INTERNED_IMMORTAL_STATIC`` An identifier for interned unicode
|
||||
objects that are immortal and static
|
||||
- ``sys.getunicodeinternedsize`` This returns the total number of unicode
|
||||
objects that have been interned. This is now needed for refleak.py to
|
||||
correctly track reference counts and allocated blocks
|
||||
|
||||
(Contributed by Eddie Elizondo in :gh:`84436`.)
|
||||
|
||||
Porting to Python 3.12
|
||||
----------------------
|
||||
|
||||
|
@ -1285,8 +1333,7 @@ Removed
|
|||
* :c:func:`!PyUnicode_GetSize`
|
||||
* :c:func:`!PyUnicode_GET_DATA_SIZE`
|
||||
|
||||
* Remove the ``PyUnicode_InternImmortal()`` function and the
|
||||
``SSTATE_INTERNED_IMMORTAL`` macro.
|
||||
* Remove the ``PyUnicode_InternImmortal()`` function macro.
|
||||
(Contributed by Victor Stinner in :gh:`85858`.)
|
||||
|
||||
* Remove ``Jython`` compatibility hacks from several stdlib modules and tests.
|
||||
|
|
|
@ -53,6 +53,7 @@ ATEQUAL '@='
|
|||
RARROW '->'
|
||||
ELLIPSIS '...'
|
||||
COLONEQUAL ':='
|
||||
EXCLAMATION '!'
|
||||
|
||||
OP
|
||||
AWAIT
|
||||
|
@ -60,6 +61,9 @@ ASYNC
|
|||
TYPE_IGNORE
|
||||
TYPE_COMMENT
|
||||
SOFT_KEYWORD
|
||||
FSTRING_START
|
||||
FSTRING_MIDDLE
|
||||
FSTRING_END
|
||||
ERRORTOKEN
|
||||
|
||||
# These aren't used by the C tokenizer but are needed for tokenize.py
|
||||
|
|
|
@ -194,7 +194,7 @@ yield_stmt[stmt_ty]: y=yield_expr { _PyAST_Expr(y, EXTRA) }
|
|||
|
||||
assert_stmt[stmt_ty]: 'assert' a=expression b=[',' z=expression { z }] { _PyAST_Assert(a, b, EXTRA) }
|
||||
|
||||
import_stmt[stmt_ty]:
|
||||
import_stmt[stmt_ty]:
|
||||
| invalid_import
|
||||
| import_name
|
||||
| import_from
|
||||
|
@ -415,8 +415,8 @@ try_stmt[stmt_ty]:
|
|||
| invalid_try_stmt
|
||||
| 'try' &&':' b=block f=finally_block { _PyAST_Try(b, NULL, NULL, f, EXTRA) }
|
||||
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_block+ el=[else_block] f=[finally_block] { _PyAST_Try(b, ex, el, f, EXTRA) }
|
||||
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_star_block+ el=[else_block] f=[finally_block] {
|
||||
CHECK_VERSION(stmt_ty, 11, "Exception groups are",
|
||||
| 'try' &&':' b=block ex[asdl_excepthandler_seq*]=except_star_block+ el=[else_block] f=[finally_block] {
|
||||
CHECK_VERSION(stmt_ty, 11, "Exception groups are",
|
||||
_PyAST_TryStar(b, ex, el, f, EXTRA)) }
|
||||
|
||||
|
||||
|
@ -807,7 +807,7 @@ atom[expr_ty]:
|
|||
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
|
||||
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
|
||||
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
|
||||
| &STRING strings
|
||||
| &(STRING|FSTRING_START) strings
|
||||
| NUMBER
|
||||
| &'(' (tuple | group | genexp)
|
||||
| &'[' (list | listcomp)
|
||||
|
@ -877,7 +877,26 @@ lambda_param[arg_ty]: a=NAME { _PyAST_arg(a->v.Name.id, NULL, NULL, EXTRA) }
|
|||
# LITERALS
|
||||
# ========
|
||||
|
||||
strings[expr_ty] (memo): a=STRING+ { _PyPegen_concatenate_strings(p, a) }
|
||||
fstring_middle[expr_ty]:
|
||||
| fstring_replacement_field
|
||||
| t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
|
||||
fstring_replacement_field[expr_ty]:
|
||||
| '{' a=(yield_expr | star_expressions) debug_expr="="? conversion=[fstring_conversion] format=[fstring_full_format_spec] '}' {
|
||||
_PyPegen_formatted_value(p, a, debug_expr, conversion, format, EXTRA)
|
||||
}
|
||||
| invalid_replacement_field
|
||||
fstring_conversion[expr_ty]:
|
||||
| conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
|
||||
fstring_full_format_spec[expr_ty]:
|
||||
| ':' spec=fstring_format_spec* { spec ? _PyAST_JoinedStr((asdl_expr_seq*)spec, EXTRA) : NULL }
|
||||
fstring_format_spec[expr_ty]:
|
||||
| t=FSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
|
||||
| fstring_replacement_field
|
||||
fstring[expr_ty]:
|
||||
| a=FSTRING_START b=fstring_middle* c=FSTRING_END { _PyPegen_joined_str(p, a, (asdl_expr_seq*)b, c) }
|
||||
|
||||
string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) }
|
||||
strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
|
||||
|
||||
list[expr_ty]:
|
||||
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
|
||||
|
@ -1118,6 +1137,8 @@ invalid_expression:
|
|||
_PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL :
|
||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") }
|
||||
| a=disjunction 'if' b=disjunction !('else'|':') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected 'else' after 'if' expression") }
|
||||
| a='lambda' [lambda_params] b=':' &(FSTRING_MIDDLE | fstring_replacement_field) {
|
||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
|
||||
|
||||
invalid_named_expression(memo):
|
||||
| a=expression ':=' expression {
|
||||
|
@ -1241,7 +1262,7 @@ invalid_group:
|
|||
invalid_import:
|
||||
| a='import' dotted_name 'from' dotted_name {
|
||||
RAISE_SYNTAX_ERROR_STARTING_FROM(a, "Did you mean to use 'from ... import ...' instead?") }
|
||||
|
||||
|
||||
invalid_import_from_targets:
|
||||
| import_from_as_names ',' NEWLINE {
|
||||
RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") }
|
||||
|
@ -1335,3 +1356,24 @@ invalid_kvpair:
|
|||
| expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
|
||||
invalid_starred_expression:
|
||||
| a='*' expression '=' b=expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to iterable argument unpacking") }
|
||||
invalid_replacement_field:
|
||||
| '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
|
||||
| '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }
|
||||
| '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") }
|
||||
| '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '}'") }
|
||||
| '{' !(yield_expr | star_expressions) { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")}
|
||||
| '{' (yield_expr | star_expressions) !('=' | '!' | ':' | '}') {
|
||||
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") }
|
||||
| '{' (yield_expr | star_expressions) '=' !('!' | ':' | '}') {
|
||||
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") }
|
||||
| '{' (yield_expr | star_expressions) '='? invalid_conversion_character
|
||||
| '{' (yield_expr | star_expressions) '='? ['!' NAME] !(':' | '}') {
|
||||
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") }
|
||||
| '{' (yield_expr | star_expressions) '='? ['!' NAME] ':' fstring_format_spec* !'}' {
|
||||
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}', or format specs") }
|
||||
| '{' (yield_expr | star_expressions) '='? ['!' NAME] !'}' {
|
||||
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") }
|
||||
|
||||
invalid_conversion_character:
|
||||
| '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: missing conversion character") }
|
||||
| '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: invalid conversion character") }
|
||||
|
|
|
@ -11,8 +11,7 @@ PyAPI_DATA(PyTypeObject) PyBool_Type;
|
|||
|
||||
#define PyBool_Check(x) Py_IS_TYPE((x), &PyBool_Type)
|
||||
|
||||
/* Py_False and Py_True are the only two bools in existence.
|
||||
Don't forget to apply Py_INCREF() when returning either!!! */
|
||||
/* Py_False and Py_True are the only two bools in existence. */
|
||||
|
||||
/* Don't use these directly */
|
||||
PyAPI_DATA(PyLongObject) _Py_FalseStruct;
|
||||
|
@ -31,8 +30,8 @@ PyAPI_FUNC(int) Py_IsFalse(PyObject *x);
|
|||
#define Py_IsFalse(x) Py_Is((x), Py_False)
|
||||
|
||||
/* Macros for returning Py_True or Py_False, respectively */
|
||||
#define Py_RETURN_TRUE return Py_NewRef(Py_True)
|
||||
#define Py_RETURN_FALSE return Py_NewRef(Py_False)
|
||||
#define Py_RETURN_TRUE return Py_True
|
||||
#define Py_RETURN_FALSE return Py_False
|
||||
|
||||
/* Function to return a bool from a C long */
|
||||
PyAPI_FUNC(PyObject *) PyBool_FromLong(long);
|
||||
|
|
|
@ -245,6 +245,8 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
|
|||
/* --- PyInterpreterConfig ------------------------------------ */
|
||||
|
||||
typedef struct {
|
||||
// XXX "allow_object_sharing"? "own_objects"?
|
||||
int use_main_obmalloc;
|
||||
int allow_fork;
|
||||
int allow_exec;
|
||||
int allow_threads;
|
||||
|
@ -254,6 +256,7 @@ typedef struct {
|
|||
|
||||
#define _PyInterpreterConfig_INIT \
|
||||
{ \
|
||||
.use_main_obmalloc = 0, \
|
||||
.allow_fork = 0, \
|
||||
.allow_exec = 0, \
|
||||
.allow_threads = 1, \
|
||||
|
@ -263,6 +266,7 @@ typedef struct {
|
|||
|
||||
#define _PyInterpreterConfig_LEGACY_INIT \
|
||||
{ \
|
||||
.use_main_obmalloc = 1, \
|
||||
.allow_fork = 1, \
|
||||
.allow_exec = 1, \
|
||||
.allow_threads = 1, \
|
||||
|
|
|
@ -564,3 +564,10 @@ PyAPI_FUNC(int) PyType_AddWatcher(PyType_WatchCallback callback);
|
|||
PyAPI_FUNC(int) PyType_ClearWatcher(int watcher_id);
|
||||
PyAPI_FUNC(int) PyType_Watch(int watcher_id, PyObject *type);
|
||||
PyAPI_FUNC(int) PyType_Unwatch(int watcher_id, PyObject *type);
|
||||
|
||||
/* Attempt to assign a version tag to the given type.
|
||||
*
|
||||
* Returns 1 if the type already had a valid version tag or a new one was
|
||||
* assigned, or 0 if a new tag could not be assigned.
|
||||
*/
|
||||
PyAPI_FUNC(int) PyUnstable_Type_AssignVersionTag(PyTypeObject *type);
|
||||
|
|
|
@ -11,6 +11,10 @@ is available in a given context. For example, forking the process
|
|||
might not be allowed in the current interpreter (i.e. os.fork() would fail).
|
||||
*/
|
||||
|
||||
/* Set if the interpreter share obmalloc runtime state
|
||||
with the main interpreter. */
|
||||
#define Py_RTFLAGS_USE_MAIN_OBMALLOC (1UL << 5)
|
||||
|
||||
/* Set if import should check a module for subinterpreter support. */
|
||||
#define Py_RTFLAGS_MULTI_INTERP_EXTENSIONS (1UL << 8)
|
||||
|
||||
|
|
|
@ -98,9 +98,16 @@ typedef struct {
|
|||
Py_ssize_t length; /* Number of code points in the string */
|
||||
Py_hash_t hash; /* Hash value; -1 if not set */
|
||||
struct {
|
||||
/* If interned is set, the two references from the
|
||||
dictionary to this object are *not* counted in ob_refcnt. */
|
||||
unsigned int interned:1;
|
||||
/* If interned is non-zero, the two references from the
|
||||
dictionary to this object are *not* counted in ob_refcnt.
|
||||
The possible values here are:
|
||||
0: Not Interned
|
||||
1: Interned
|
||||
2: Interned and Immortal
|
||||
3: Interned, Immortal, and Static
|
||||
This categorization allows the runtime to determine the right
|
||||
cleanup mechanism at runtime shutdown. */
|
||||
unsigned int interned:2;
|
||||
/* Character size:
|
||||
|
||||
- PyUnicode_1BYTE_KIND (1):
|
||||
|
@ -135,7 +142,7 @@ typedef struct {
|
|||
unsigned int ascii:1;
|
||||
/* Padding to ensure that PyUnicode_DATA() is always aligned to
|
||||
4 bytes (see issue #19537 on m68k). */
|
||||
unsigned int :26;
|
||||
unsigned int :25;
|
||||
} state;
|
||||
} PyASCIIObject;
|
||||
|
||||
|
@ -183,6 +190,8 @@ PyAPI_FUNC(int) _PyUnicode_CheckConsistency(
|
|||
/* Interning state. */
|
||||
#define SSTATE_NOT_INTERNED 0
|
||||
#define SSTATE_INTERNED_MORTAL 1
|
||||
#define SSTATE_INTERNED_IMMORTAL 2
|
||||
#define SSTATE_INTERNED_IMMORTAL_STATIC 3
|
||||
|
||||
/* Use only if you know it's a string */
|
||||
static inline unsigned int PyUnicode_CHECK_INTERNED(PyObject *op) {
|
||||
|
|
|
@ -8,15 +8,13 @@ extern "C" {
|
|||
# error "this header requires Py_BUILD_CORE define"
|
||||
#endif
|
||||
|
||||
#include "pycore_object.h" // _PyObject_IMMORTAL_REFCNT
|
||||
|
||||
#ifdef Py_DEBUG
|
||||
static inline void
|
||||
_PyStaticObject_CheckRefcnt(PyObject *obj) {
|
||||
if (Py_REFCNT(obj) < _PyObject_IMMORTAL_REFCNT) {
|
||||
if (Py_REFCNT(obj) < _Py_IMMORTAL_REFCNT) {
|
||||
_PyObject_ASSERT_FAILED_MSG(obj,
|
||||
"immortal object has less refcnt than expected "
|
||||
"_PyObject_IMMORTAL_REFCNT");
|
||||
"_Py_IMMORTAL_REFCNT");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,8 @@ struct _import_runtime_state {
|
|||
used exclusively for when the extensions dict is access/modified
|
||||
from an arbitrary thread. */
|
||||
PyThreadState main_tstate;
|
||||
/* A lock to guard the dict. */
|
||||
PyThread_type_lock mutex;
|
||||
/* A dict mapping (filename, name) to PyModuleDef for modules.
|
||||
Only legacy (single-phase init) extension modules are added
|
||||
and only if they support multiple initialization (m_size >- 0)
|
||||
|
|
|
@ -23,11 +23,12 @@ extern "C" {
|
|||
#include "pycore_function.h" // FUNC_MAX_WATCHERS
|
||||
#include "pycore_genobject.h" // struct _Py_async_gen_state
|
||||
#include "pycore_gc.h" // struct _gc_runtime_state
|
||||
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
|
||||
#include "pycore_import.h" // struct _import_state
|
||||
#include "pycore_instruments.h" // PY_MONITORING_EVENTS
|
||||
#include "pycore_list.h" // struct _Py_list_state
|
||||
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
|
||||
#include "pycore_object_state.h" // struct _py_object_state
|
||||
#include "pycore_obmalloc.h" // struct obmalloc_state
|
||||
#include "pycore_tuple.h" // struct _Py_tuple_state
|
||||
#include "pycore_typeobject.h" // struct type_cache
|
||||
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
|
||||
|
@ -82,6 +83,8 @@ struct _is {
|
|||
int _initialized;
|
||||
int finalizing;
|
||||
|
||||
struct _obmalloc_state obmalloc;
|
||||
|
||||
struct _ceval_state ceval;
|
||||
struct _gc_runtime_state gc;
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ _PyLong_FlipSign(PyLongObject *op) {
|
|||
|
||||
#define _PyLong_DIGIT_INIT(val) \
|
||||
{ \
|
||||
.ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
|
||||
.ob_base = _PyObject_HEAD_INIT(&PyLong_Type) \
|
||||
.long_value = { \
|
||||
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
|
||||
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
|
||||
|
|
|
@ -14,21 +14,25 @@ extern "C" {
|
|||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
#include "pycore_runtime.h" // _PyRuntime
|
||||
|
||||
/* This value provides *effective* immortality, meaning the object should never
|
||||
be deallocated (until runtime finalization). See PEP 683 for more details about
|
||||
immortality, as well as a proposed mechanism for proper immortality. */
|
||||
#define _PyObject_IMMORTAL_REFCNT 999999999
|
||||
|
||||
#define _PyObject_IMMORTAL_INIT(type) \
|
||||
{ \
|
||||
.ob_refcnt = _PyObject_IMMORTAL_REFCNT, \
|
||||
.ob_type = (type), \
|
||||
}
|
||||
#define _PyVarObject_IMMORTAL_INIT(type, size) \
|
||||
{ \
|
||||
.ob_base = _PyObject_IMMORTAL_INIT(type), \
|
||||
.ob_size = size, \
|
||||
}
|
||||
/* We need to maintain an internal copy of Py{Var}Object_HEAD_INIT to avoid
|
||||
designated initializer conflicts in C++20. If we use the deinition in
|
||||
object.h, we will be mixing designated and non-designated initializers in
|
||||
pycore objects which is forbiddent in C++20. However, if we then use
|
||||
designated initializers in object.h then Extensions without designated break.
|
||||
Furthermore, we can't use designated initializers in Extensions since these
|
||||
are not supported pre-C++20. Thus, keeping an internal copy here is the most
|
||||
backwards compatible solution */
|
||||
#define _PyObject_HEAD_INIT(type) \
|
||||
{ \
|
||||
_PyObject_EXTRA_INIT \
|
||||
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
|
||||
.ob_type = (type) \
|
||||
},
|
||||
#define _PyVarObject_HEAD_INIT(type, size) \
|
||||
{ \
|
||||
.ob_base = _PyObject_HEAD_INIT(type) \
|
||||
.ob_size = size \
|
||||
},
|
||||
|
||||
PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
|
||||
const char *func,
|
||||
|
@ -61,9 +65,20 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
|
|||
}
|
||||
#define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n)
|
||||
|
||||
static inline void _Py_SetImmortal(PyObject *op)
|
||||
{
|
||||
if (op) {
|
||||
op->ob_refcnt = _Py_IMMORTAL_REFCNT;
|
||||
}
|
||||
}
|
||||
#define _Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))
|
||||
|
||||
static inline void
|
||||
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
|
||||
{
|
||||
if (_Py_IsImmortal(op)) {
|
||||
return;
|
||||
}
|
||||
_Py_DECREF_STAT_INC();
|
||||
#ifdef Py_REF_DEBUG
|
||||
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
|
||||
|
@ -82,6 +97,9 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
|
|||
static inline void
|
||||
_Py_DECREF_NO_DEALLOC(PyObject *op)
|
||||
{
|
||||
if (_Py_IsImmortal(op)) {
|
||||
return;
|
||||
}
|
||||
_Py_DECREF_STAT_INC();
|
||||
#ifdef Py_REF_DEBUG
|
||||
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
|
||||
|
|
|
@ -657,8 +657,12 @@ struct _obmalloc_usage {
|
|||
#endif /* WITH_PYMALLOC_RADIX_TREE */
|
||||
|
||||
|
||||
struct _obmalloc_state {
|
||||
struct _obmalloc_global_state {
|
||||
int dump_debug_stats;
|
||||
Py_ssize_t interpreter_leaks;
|
||||
};
|
||||
|
||||
struct _obmalloc_state {
|
||||
struct _obmalloc_pools pools;
|
||||
struct _obmalloc_mgmt mgmt;
|
||||
struct _obmalloc_usage usage;
|
||||
|
@ -675,7 +679,11 @@ void _PyObject_VirtualFree(void *, size_t size);
|
|||
|
||||
|
||||
/* This function returns the number of allocated memory blocks, regardless of size */
|
||||
PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void);
|
||||
extern Py_ssize_t _Py_GetGlobalAllocatedBlocks(void);
|
||||
#define _Py_GetAllocatedBlocks() \
|
||||
_Py_GetGlobalAllocatedBlocks()
|
||||
extern Py_ssize_t _PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *);
|
||||
extern void _PyInterpreterState_FinalizeAllocatedBlocks(PyInterpreterState *);
|
||||
|
||||
|
||||
#ifdef WITH_PYMALLOC
|
||||
|
|
|
@ -54,9 +54,13 @@ extern "C" {
|
|||
# error "NB_SMALL_SIZE_CLASSES should be less than 64"
|
||||
#endif
|
||||
|
||||
#define _obmalloc_state_INIT(obmalloc) \
|
||||
#define _obmalloc_global_state_INIT \
|
||||
{ \
|
||||
.dump_debug_stats = -1, \
|
||||
}
|
||||
|
||||
#define _obmalloc_state_INIT(obmalloc) \
|
||||
{ \
|
||||
.pools = { \
|
||||
.used = _obmalloc_pools_INIT(obmalloc.pools), \
|
||||
}, \
|
||||
|
|
15
Include/internal/pycore_opcode.h
generated
15
Include/internal/pycore_opcode.h
generated
|
@ -179,6 +179,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
|
|||
[LOAD_GLOBAL_BUILTIN] = LOAD_GLOBAL,
|
||||
[LOAD_GLOBAL_MODULE] = LOAD_GLOBAL,
|
||||
[LOAD_NAME] = LOAD_NAME,
|
||||
[LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR,
|
||||
[MAKE_CELL] = MAKE_CELL,
|
||||
[MAKE_FUNCTION] = MAKE_FUNCTION,
|
||||
[MAP_ADD] = MAP_ADD,
|
||||
|
@ -236,7 +237,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
|
|||
#endif // NEED_OPCODE_TABLES
|
||||
|
||||
#ifdef Py_DEBUG
|
||||
static const char *const _PyOpcode_OpName[263] = {
|
||||
static const char *const _PyOpcode_OpName[266] = {
|
||||
[CACHE] = "CACHE",
|
||||
[POP_TOP] = "POP_TOP",
|
||||
[PUSH_NULL] = "PUSH_NULL",
|
||||
|
@ -378,9 +379,9 @@ static const char *const _PyOpcode_OpName[263] = {
|
|||
[STORE_DEREF] = "STORE_DEREF",
|
||||
[DELETE_DEREF] = "DELETE_DEREF",
|
||||
[JUMP_BACKWARD] = "JUMP_BACKWARD",
|
||||
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
|
||||
[LOAD_SUPER_ATTR] = "LOAD_SUPER_ATTR",
|
||||
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
|
||||
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
|
||||
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
|
||||
[EXTENDED_ARG] = "EXTENDED_ARG",
|
||||
[LIST_APPEND] = "LIST_APPEND",
|
||||
[SET_ADD] = "SET_ADD",
|
||||
|
@ -390,15 +391,15 @@ static const char *const _PyOpcode_OpName[263] = {
|
|||
[YIELD_VALUE] = "YIELD_VALUE",
|
||||
[RESUME] = "RESUME",
|
||||
[MATCH_CLASS] = "MATCH_CLASS",
|
||||
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
|
||||
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
|
||||
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
|
||||
[FORMAT_VALUE] = "FORMAT_VALUE",
|
||||
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
|
||||
[BUILD_STRING] = "BUILD_STRING",
|
||||
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
|
||||
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
|
||||
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
|
||||
[SEND_GEN] = "SEND_GEN",
|
||||
[161] = "<161>",
|
||||
[LIST_EXTEND] = "LIST_EXTEND",
|
||||
[SET_UPDATE] = "SET_UPDATE",
|
||||
[DICT_MERGE] = "DICT_MERGE",
|
||||
|
@ -500,11 +501,13 @@ static const char *const _PyOpcode_OpName[263] = {
|
|||
[JUMP] = "JUMP",
|
||||
[JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
|
||||
[LOAD_METHOD] = "LOAD_METHOD",
|
||||
[LOAD_SUPER_METHOD] = "LOAD_SUPER_METHOD",
|
||||
[LOAD_ZERO_SUPER_METHOD] = "LOAD_ZERO_SUPER_METHOD",
|
||||
[LOAD_ZERO_SUPER_ATTR] = "LOAD_ZERO_SUPER_ATTR",
|
||||
};
|
||||
#endif
|
||||
|
||||
#define EXTRA_CASES \
|
||||
case 161: \
|
||||
case 166: \
|
||||
case 167: \
|
||||
case 168: \
|
||||
|
|
|
@ -64,6 +64,7 @@ extern void _PyAtExit_Fini(PyInterpreterState *interp);
|
|||
extern void _PyThread_FiniType(PyInterpreterState *interp);
|
||||
extern void _Py_Deepfreeze_Fini(void);
|
||||
extern void _PyArg_Fini(void);
|
||||
extern void _Py_FinalizeAllocatedBlocks(_PyRuntimeState *);
|
||||
|
||||
extern PyStatus _PyGILState_Init(PyInterpreterState *interp);
|
||||
extern PyStatus _PyGILState_SetTstate(PyThreadState *tstate);
|
||||
|
|
|
@ -33,6 +33,13 @@ _Py_IsMainInterpreter(PyInterpreterState *interp)
|
|||
return (interp == _PyInterpreterState_Main());
|
||||
}
|
||||
|
||||
static inline int
|
||||
_Py_IsMainInterpreterFinalizing(PyInterpreterState *interp)
|
||||
{
|
||||
return (_PyRuntimeState_GetFinalizing(interp->runtime) != NULL &&
|
||||
interp == &interp->runtime->_main_interpreter);
|
||||
}
|
||||
|
||||
|
||||
static inline const PyConfig *
|
||||
_Py_GetMainConfig(void)
|
||||
|
@ -64,17 +71,14 @@ _Py_ThreadCanHandlePendingCalls(void)
|
|||
/* Variable and macro for in-line access to current thread
|
||||
and interpreter state */
|
||||
|
||||
static inline PyThreadState*
|
||||
_PyRuntimeState_GetThreadState(_PyRuntimeState *runtime)
|
||||
{
|
||||
return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->tstate_current);
|
||||
}
|
||||
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
|
||||
extern _Py_thread_local PyThreadState *_Py_tss_tstate;
|
||||
#endif
|
||||
PyAPI_DATA(PyThreadState *) _PyThreadState_GetCurrent(void);
|
||||
|
||||
/* Get the current Python thread state.
|
||||
|
||||
Efficient macro reading directly the 'tstate_current' atomic
|
||||
variable. The macro is unsafe: it does not check for error and it can
|
||||
return NULL.
|
||||
This function is unsafe: it does not check for error and it can return NULL.
|
||||
|
||||
The caller must hold the GIL.
|
||||
|
||||
|
@ -82,9 +86,20 @@ _PyRuntimeState_GetThreadState(_PyRuntimeState *runtime)
|
|||
static inline PyThreadState*
|
||||
_PyThreadState_GET(void)
|
||||
{
|
||||
return _PyRuntimeState_GetThreadState(&_PyRuntime);
|
||||
#if defined(HAVE_THREAD_LOCAL) && !defined(Py_BUILD_CORE_MODULE)
|
||||
return _Py_tss_tstate;
|
||||
#else
|
||||
return _PyThreadState_GetCurrent();
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline PyThreadState*
|
||||
_PyRuntimeState_GetThreadState(_PyRuntimeState *Py_UNUSED(runtime))
|
||||
{
|
||||
return _PyThreadState_GET();
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
_Py_EnsureFuncTstateNotNULL(const char *func, PyThreadState *tstate)
|
||||
{
|
||||
|
|
|
@ -21,10 +21,10 @@ extern "C" {
|
|||
#include "pycore_pymem.h" // struct _pymem_allocators
|
||||
#include "pycore_pyhash.h" // struct pyhash_runtime_state
|
||||
#include "pycore_pythread.h" // struct _pythread_runtime_state
|
||||
#include "pycore_obmalloc.h" // struct obmalloc_state
|
||||
#include "pycore_signal.h" // struct _signals_runtime_state
|
||||
#include "pycore_time.h" // struct _time_runtime_state
|
||||
#include "pycore_tracemalloc.h" // struct _tracemalloc_runtime_state
|
||||
#include "pycore_typeobject.h" // struct types_runtime_state
|
||||
#include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_ids
|
||||
|
||||
struct _getargs_runtime_state {
|
||||
|
@ -87,7 +87,7 @@ typedef struct pyruntimestate {
|
|||
_Py_atomic_address _finalizing;
|
||||
|
||||
struct _pymem_allocators allocators;
|
||||
struct _obmalloc_state obmalloc;
|
||||
struct _obmalloc_global_state obmalloc;
|
||||
struct pyhash_runtime_state pyhash_state;
|
||||
struct _time_runtime_state time;
|
||||
struct _pythread_runtime_state threads;
|
||||
|
@ -119,9 +119,6 @@ typedef struct pyruntimestate {
|
|||
|
||||
unsigned long main_thread;
|
||||
|
||||
/* Assuming the current thread holds the GIL, this is the
|
||||
PyThreadState for the current thread. */
|
||||
_Py_atomic_address tstate_current;
|
||||
/* Used for the thread state bound to the current thread. */
|
||||
Py_tss_t autoTSSkey;
|
||||
|
||||
|
@ -153,13 +150,7 @@ typedef struct pyruntimestate {
|
|||
struct _py_object_runtime_state object_state;
|
||||
struct _Py_float_runtime_state float_state;
|
||||
struct _Py_unicode_runtime_state unicode_state;
|
||||
|
||||
struct {
|
||||
/* Used to set PyTypeObject.tp_version_tag */
|
||||
// bpo-42745: next_version_tag remains shared by all interpreters
|
||||
// because of static types.
|
||||
unsigned int next_version_tag;
|
||||
} types;
|
||||
struct _types_runtime_state types;
|
||||
|
||||
/* All the objects that are shared by the runtime's interpreters. */
|
||||
struct _Py_static_objects static_objects;
|
||||
|
|
|
@ -29,7 +29,7 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
_pymem_allocators_debug_INIT, \
|
||||
_pymem_allocators_obj_arena_INIT, \
|
||||
}, \
|
||||
.obmalloc = _obmalloc_state_INIT(runtime.obmalloc), \
|
||||
.obmalloc = _obmalloc_global_state_INIT, \
|
||||
.pyhash_state = pyhash_state_INIT, \
|
||||
.signals = _signals_RUNTIME_INIT, \
|
||||
.interpreters = { \
|
||||
|
@ -76,13 +76,13 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
.latin1 = _Py_str_latin1_INIT, \
|
||||
}, \
|
||||
.tuple_empty = { \
|
||||
.ob_base = _PyVarObject_IMMORTAL_INIT(&PyTuple_Type, 0) \
|
||||
.ob_base = _PyVarObject_HEAD_INIT(&PyTuple_Type, 0) \
|
||||
}, \
|
||||
.hamt_bitmap_node_empty = { \
|
||||
.ob_base = _PyVarObject_IMMORTAL_INIT(&_PyHamt_BitmapNode_Type, 0) \
|
||||
.ob_base = _PyVarObject_HEAD_INIT(&_PyHamt_BitmapNode_Type, 0) \
|
||||
}, \
|
||||
.context_token_missing = { \
|
||||
.ob_base = _PyObject_IMMORTAL_INIT(&_PyContextTokenMissing_Type), \
|
||||
.ob_base = _PyObject_HEAD_INIT(&_PyContextTokenMissing_Type) \
|
||||
}, \
|
||||
}, \
|
||||
}, \
|
||||
|
@ -93,6 +93,7 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
{ \
|
||||
.id_refcount = -1, \
|
||||
.imports = IMPORTS_INIT, \
|
||||
.obmalloc = _obmalloc_state_INIT(INTERP.obmalloc), \
|
||||
.ceval = { \
|
||||
.recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \
|
||||
}, \
|
||||
|
@ -112,15 +113,18 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
.func_state = { \
|
||||
.next_version = 1, \
|
||||
}, \
|
||||
.types = { \
|
||||
.next_version_tag = _Py_TYPE_BASE_VERSION_TAG, \
|
||||
}, \
|
||||
.static_objects = { \
|
||||
.singletons = { \
|
||||
._not_used = 1, \
|
||||
.hamt_empty = { \
|
||||
.ob_base = _PyObject_IMMORTAL_INIT(&_PyHamt_Type), \
|
||||
.ob_base = _PyObject_HEAD_INIT(&_PyHamt_Type) \
|
||||
.h_root = (PyHamtNode*)&_Py_SINGLETON(hamt_bitmap_node_empty), \
|
||||
}, \
|
||||
.last_resort_memory_error = { \
|
||||
_PyObject_IMMORTAL_INIT(&_PyExc_MemoryError), \
|
||||
_PyObject_HEAD_INIT(&_PyExc_MemoryError) \
|
||||
}, \
|
||||
}, \
|
||||
}, \
|
||||
|
@ -138,7 +142,7 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
|
||||
#define _PyBytes_SIMPLE_INIT(CH, LEN) \
|
||||
{ \
|
||||
_PyVarObject_IMMORTAL_INIT(&PyBytes_Type, (LEN)), \
|
||||
_PyVarObject_HEAD_INIT(&PyBytes_Type, (LEN)) \
|
||||
.ob_shash = -1, \
|
||||
.ob_sval = { (CH) }, \
|
||||
}
|
||||
|
@ -149,7 +153,7 @@ extern PyTypeObject _PyExc_MemoryError;
|
|||
|
||||
#define _PyUnicode_ASCII_BASE_INIT(LITERAL, ASCII) \
|
||||
{ \
|
||||
.ob_base = _PyObject_IMMORTAL_INIT(&PyUnicode_Type), \
|
||||
.ob_base = _PyObject_HEAD_INIT(&PyUnicode_Type) \
|
||||
.length = sizeof(LITERAL) - 1, \
|
||||
.hash = -1, \
|
||||
.state = { \
|
||||
|
|
|
@ -67,14 +67,18 @@ extern "C" {
|
|||
#define RARROW 51
|
||||
#define ELLIPSIS 52
|
||||
#define COLONEQUAL 53
|
||||
#define OP 54
|
||||
#define AWAIT 55
|
||||
#define ASYNC 56
|
||||
#define TYPE_IGNORE 57
|
||||
#define TYPE_COMMENT 58
|
||||
#define SOFT_KEYWORD 59
|
||||
#define ERRORTOKEN 60
|
||||
#define N_TOKENS 64
|
||||
#define EXCLAMATION 54
|
||||
#define OP 55
|
||||
#define AWAIT 56
|
||||
#define ASYNC 57
|
||||
#define TYPE_IGNORE 58
|
||||
#define TYPE_COMMENT 59
|
||||
#define SOFT_KEYWORD 60
|
||||
#define FSTRING_START 61
|
||||
#define FSTRING_MIDDLE 62
|
||||
#define FSTRING_END 63
|
||||
#define ERRORTOKEN 64
|
||||
#define N_TOKENS 68
|
||||
#define NT_OFFSET 256
|
||||
|
||||
/* Special definitions for cooperation with parser */
|
||||
|
@ -86,6 +90,8 @@ extern "C" {
|
|||
(x) == NEWLINE || \
|
||||
(x) == INDENT || \
|
||||
(x) == DEDENT)
|
||||
#define ISSTRINGLIT(x) ((x) == STRING || \
|
||||
(x) == FSTRING_MIDDLE)
|
||||
|
||||
|
||||
// Symbols exported for test_peg_generator
|
||||
|
|
|
@ -11,22 +11,17 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
/* runtime lifecycle */
|
||||
/* state */
|
||||
|
||||
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
|
||||
extern void _PyTypes_FiniTypes(PyInterpreterState *);
|
||||
extern void _PyTypes_Fini(PyInterpreterState *);
|
||||
#define _Py_TYPE_BASE_VERSION_TAG (2<<16)
|
||||
#define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1)
|
||||
|
||||
|
||||
/* other API */
|
||||
|
||||
/* Length of array of slotdef pointers used to store slots with the
|
||||
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
|
||||
the same __name__, for any __name__. Since that's a static property, it is
|
||||
appropriate to declare fixed-size arrays for this. */
|
||||
#define MAX_EQUIV 10
|
||||
|
||||
typedef struct wrapperbase pytype_slotdef;
|
||||
struct _types_runtime_state {
|
||||
/* Used to set PyTypeObject.tp_version_tag for core static types. */
|
||||
// bpo-42745: next_version_tag remains shared by all interpreters
|
||||
// because of static types.
|
||||
unsigned int next_version_tag;
|
||||
};
|
||||
|
||||
|
||||
// Type attribute lookup cache: speed up attribute and method lookups,
|
||||
|
@ -57,6 +52,36 @@ typedef struct {
|
|||
PyObject *tp_weaklist;
|
||||
} static_builtin_state;
|
||||
|
||||
struct types_state {
|
||||
/* Used to set PyTypeObject.tp_version_tag.
|
||||
It starts at _Py_MAX_GLOBAL_TYPE_VERSION_TAG + 1,
|
||||
where all those lower numbers are used for core static types. */
|
||||
unsigned int next_version_tag;
|
||||
|
||||
struct type_cache type_cache;
|
||||
size_t num_builtins_initialized;
|
||||
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
|
||||
};
|
||||
|
||||
|
||||
/* runtime lifecycle */
|
||||
|
||||
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
|
||||
extern void _PyTypes_FiniTypes(PyInterpreterState *);
|
||||
extern void _PyTypes_Fini(PyInterpreterState *);
|
||||
|
||||
|
||||
/* other API */
|
||||
|
||||
/* Length of array of slotdef pointers used to store slots with the
|
||||
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
|
||||
the same __name__, for any __name__. Since that's a static property, it is
|
||||
appropriate to declare fixed-size arrays for this. */
|
||||
#define MAX_EQUIV 10
|
||||
|
||||
typedef struct wrapperbase pytype_slotdef;
|
||||
|
||||
|
||||
static inline PyObject **
|
||||
_PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state)
|
||||
{
|
||||
|
@ -78,12 +103,6 @@ _PyType_GetModuleState(PyTypeObject *type)
|
|||
return mod->md_state;
|
||||
}
|
||||
|
||||
struct types_state {
|
||||
struct type_cache type_cache;
|
||||
size_t num_builtins_initialized;
|
||||
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
|
||||
};
|
||||
|
||||
|
||||
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
|
||||
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
|
||||
|
@ -98,6 +117,9 @@ _Py_type_getattro(PyTypeObject *type, PyObject *name);
|
|||
PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
|
||||
PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
|
||||
|
||||
PyObject *
|
||||
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@ extern "C" {
|
|||
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
|
||||
|
||||
void _PyUnicode_ExactDealloc(PyObject *op);
|
||||
Py_ssize_t _PyUnicode_InternedSize(void);
|
||||
|
||||
/* runtime lifecycle */
|
||||
|
||||
|
|
133
Include/object.h
133
Include/object.h
|
@ -78,12 +78,76 @@ whose size is determined when the object is allocated.
|
|||
/* PyObject_HEAD defines the initial segment of every PyObject. */
|
||||
#define PyObject_HEAD PyObject ob_base;
|
||||
|
||||
#define PyObject_HEAD_INIT(type) \
|
||||
{ _PyObject_EXTRA_INIT \
|
||||
1, (type) },
|
||||
/*
|
||||
Immortalization:
|
||||
|
||||
#define PyVarObject_HEAD_INIT(type, size) \
|
||||
{ PyObject_HEAD_INIT(type) (size) },
|
||||
The following indicates the immortalization strategy depending on the amount
|
||||
of available bits in the reference count field. All strategies are backwards
|
||||
compatible but the specific reference count value or immortalization check
|
||||
might change depending on the specializations for the underlying system.
|
||||
|
||||
Proper deallocation of immortal instances requires distinguishing between
|
||||
statically allocated immortal instances vs those promoted by the runtime to be
|
||||
immortal. The latter should be the only instances that require
|
||||
cleanup during runtime finalization.
|
||||
*/
|
||||
|
||||
#if SIZEOF_VOID_P > 4
|
||||
/*
|
||||
In 64+ bit systems, an object will be marked as immortal by setting all of the
|
||||
lower 32 bits of the reference count field, which is equal to: 0xFFFFFFFF
|
||||
|
||||
Using the lower 32 bits makes the value backwards compatible by allowing
|
||||
C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
|
||||
increase and decrease the objects reference count. The object would lose its
|
||||
immortality, but the execution would still be correct.
|
||||
|
||||
Reference count increases will use saturated arithmetic, taking advantage of
|
||||
having all the lower 32 bits set, which will avoid the reference count to go
|
||||
beyond the refcount limit. Immortality checks for reference count decreases will
|
||||
be done by checking the bit sign flag in the lower 32 bits.
|
||||
*/
|
||||
#define _Py_IMMORTAL_REFCNT UINT_MAX
|
||||
|
||||
#else
|
||||
/*
|
||||
In 32 bit systems, an object will be marked as immortal by setting all of the
|
||||
lower 30 bits of the reference count field, which is equal to: 0x3FFFFFFF
|
||||
|
||||
Using the lower 30 bits makes the value backwards compatible by allowing
|
||||
C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
|
||||
increase and decrease the objects reference count. The object would lose its
|
||||
immortality, but the execution would still be correct.
|
||||
|
||||
Reference count increases and decreases will first go through an immortality
|
||||
check by comparing the reference count field to the immortality reference count.
|
||||
*/
|
||||
#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2)
|
||||
#endif
|
||||
|
||||
// Make all internal uses of PyObject_HEAD_INIT immortal while preserving the
|
||||
// C-API expectation that the refcnt will be set to 1.
|
||||
#ifdef Py_BUILD_CORE
|
||||
#define PyObject_HEAD_INIT(type) \
|
||||
{ \
|
||||
_PyObject_EXTRA_INIT \
|
||||
{ _Py_IMMORTAL_REFCNT }, \
|
||||
(type) \
|
||||
},
|
||||
#else
|
||||
#define PyObject_HEAD_INIT(type) \
|
||||
{ \
|
||||
_PyObject_EXTRA_INIT \
|
||||
{ 1 }, \
|
||||
(type) \
|
||||
},
|
||||
#endif /* Py_BUILD_CORE */
|
||||
|
||||
#define PyVarObject_HEAD_INIT(type, size) \
|
||||
{ \
|
||||
PyObject_HEAD_INIT(type) \
|
||||
(size) \
|
||||
},
|
||||
|
||||
/* PyObject_VAR_HEAD defines the initial segment of all variable-size
|
||||
* container objects. These end with a declaration of an array with 1
|
||||
|
@ -101,7 +165,12 @@ whose size is determined when the object is allocated.
|
|||
*/
|
||||
struct _object {
|
||||
_PyObject_HEAD_EXTRA
|
||||
Py_ssize_t ob_refcnt;
|
||||
union {
|
||||
Py_ssize_t ob_refcnt;
|
||||
#if SIZEOF_VOID_P > 4
|
||||
PY_UINT32_T ob_refcnt_split[2];
|
||||
#endif
|
||||
};
|
||||
PyTypeObject *ob_type;
|
||||
};
|
||||
|
||||
|
@ -152,6 +221,15 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) {
|
|||
# define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob))
|
||||
#endif
|
||||
|
||||
static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
|
||||
{
|
||||
#if SIZEOF_VOID_P > 4
|
||||
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
|
||||
#else
|
||||
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
|
||||
#endif
|
||||
}
|
||||
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))
|
||||
|
||||
static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
|
||||
return Py_TYPE(ob) == type;
|
||||
|
@ -162,6 +240,13 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
|
|||
|
||||
|
||||
static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
|
||||
// This immortal check is for code that is unaware of immortal objects.
|
||||
// The runtime tracks these objects and we should avoid as much
|
||||
// as possible having extensions inadvertently change the refcnt
|
||||
// of an immortalized object.
|
||||
if (_Py_IsImmortal(ob)) {
|
||||
return;
|
||||
}
|
||||
ob->ob_refcnt = refcnt;
|
||||
}
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
|
||||
|
@ -524,19 +609,33 @@ PyAPI_FUNC(void) Py_DecRef(PyObject *);
|
|||
PyAPI_FUNC(void) _Py_IncRef(PyObject *);
|
||||
PyAPI_FUNC(void) _Py_DecRef(PyObject *);
|
||||
|
||||
static inline void Py_INCREF(PyObject *op)
|
||||
static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
|
||||
{
|
||||
#if defined(Py_REF_DEBUG) && defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000
|
||||
// Stable ABI for Python 3.10 built in debug mode.
|
||||
_Py_IncRef(op);
|
||||
#else
|
||||
_Py_INCREF_STAT_INC();
|
||||
// Non-limited C API and limited C API for Python 3.9 and older access
|
||||
// directly PyObject.ob_refcnt.
|
||||
#if SIZEOF_VOID_P > 4
|
||||
// Portable saturated add, branching on the carry flag and set low bits
|
||||
PY_UINT32_T cur_refcnt = op->ob_refcnt_split[PY_BIG_ENDIAN];
|
||||
PY_UINT32_T new_refcnt = cur_refcnt + 1;
|
||||
if (new_refcnt == 0) {
|
||||
return;
|
||||
}
|
||||
op->ob_refcnt_split[PY_BIG_ENDIAN] = new_refcnt;
|
||||
#else
|
||||
// Explicitly check immortality against the immortal value
|
||||
if (_Py_IsImmortal(op)) {
|
||||
return;
|
||||
}
|
||||
op->ob_refcnt++;
|
||||
#endif
|
||||
_Py_INCREF_STAT_INC();
|
||||
#ifdef Py_REF_DEBUG
|
||||
_Py_INC_REFTOTAL();
|
||||
#endif // Py_REF_DEBUG
|
||||
op->ob_refcnt++;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
|
||||
|
@ -553,6 +652,9 @@ static inline void Py_DECREF(PyObject *op) {
|
|||
#elif defined(Py_REF_DEBUG)
|
||||
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
|
||||
{
|
||||
if (_Py_IsImmortal(op)) {
|
||||
return;
|
||||
}
|
||||
_Py_DECREF_STAT_INC();
|
||||
_Py_DEC_REFTOTAL();
|
||||
if (--op->ob_refcnt != 0) {
|
||||
|
@ -567,11 +669,14 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
|
|||
#define Py_DECREF(op) Py_DECREF(__FILE__, __LINE__, _PyObject_CAST(op))
|
||||
|
||||
#else
|
||||
static inline void Py_DECREF(PyObject *op)
|
||||
static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
|
||||
{
|
||||
_Py_DECREF_STAT_INC();
|
||||
// Non-limited C API and limited C API for Python 3.9 and older access
|
||||
// directly PyObject.ob_refcnt.
|
||||
if (_Py_IsImmortal(op)) {
|
||||
return;
|
||||
}
|
||||
_Py_DECREF_STAT_INC();
|
||||
if (--op->ob_refcnt == 0) {
|
||||
_Py_Dealloc(op);
|
||||
}
|
||||
|
@ -721,7 +826,7 @@ PyAPI_FUNC(int) Py_IsNone(PyObject *x);
|
|||
#define Py_IsNone(x) Py_Is((x), Py_None)
|
||||
|
||||
/* Macro for returning Py_None from a function */
|
||||
#define Py_RETURN_NONE return Py_NewRef(Py_None)
|
||||
#define Py_RETURN_NONE return Py_None
|
||||
|
||||
/*
|
||||
Py_NotImplemented is a singleton used to signal that an operation is
|
||||
|
@ -731,7 +836,7 @@ PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */
|
|||
#define Py_NotImplemented (&_Py_NotImplementedStruct)
|
||||
|
||||
/* Macro for returning Py_NotImplemented from a function */
|
||||
#define Py_RETURN_NOTIMPLEMENTED return Py_NewRef(Py_NotImplemented)
|
||||
#define Py_RETURN_NOTIMPLEMENTED return Py_NotImplemented
|
||||
|
||||
/* Rich comparison opcodes */
|
||||
#define Py_LT 0
|
||||
|
|
23
Include/opcode.h
generated
23
Include/opcode.h
generated
|
@ -95,6 +95,7 @@ extern "C" {
|
|||
#define STORE_DEREF 138
|
||||
#define DELETE_DEREF 139
|
||||
#define JUMP_BACKWARD 140
|
||||
#define LOAD_SUPER_ATTR 141
|
||||
#define CALL_FUNCTION_EX 142
|
||||
#define EXTENDED_ARG 144
|
||||
#define LIST_APPEND 145
|
||||
|
@ -142,7 +143,10 @@ extern "C" {
|
|||
#define JUMP 260
|
||||
#define JUMP_NO_INTERRUPT 261
|
||||
#define LOAD_METHOD 262
|
||||
#define MAX_PSEUDO_OPCODE 262
|
||||
#define LOAD_SUPER_METHOD 263
|
||||
#define LOAD_ZERO_SUPER_METHOD 264
|
||||
#define LOAD_ZERO_SUPER_ATTR 265
|
||||
#define MAX_PSEUDO_OPCODE 265
|
||||
#define BINARY_OP_ADD_FLOAT 6
|
||||
#define BINARY_OP_ADD_INT 7
|
||||
#define BINARY_OP_ADD_UNICODE 8
|
||||
|
@ -198,18 +202,21 @@ extern "C" {
|
|||
#define STORE_ATTR_SLOT 111
|
||||
#define STORE_ATTR_WITH_HINT 112
|
||||
#define STORE_FAST__LOAD_FAST 113
|
||||
#define STORE_FAST__STORE_FAST 141
|
||||
#define STORE_SUBSCR_DICT 143
|
||||
#define STORE_SUBSCR_LIST_INT 153
|
||||
#define UNPACK_SEQUENCE_LIST 154
|
||||
#define UNPACK_SEQUENCE_TUPLE 158
|
||||
#define UNPACK_SEQUENCE_TWO_TUPLE 159
|
||||
#define SEND_GEN 160
|
||||
#define STORE_FAST__STORE_FAST 143
|
||||
#define STORE_SUBSCR_DICT 153
|
||||
#define STORE_SUBSCR_LIST_INT 154
|
||||
#define UNPACK_SEQUENCE_LIST 158
|
||||
#define UNPACK_SEQUENCE_TUPLE 159
|
||||
#define UNPACK_SEQUENCE_TWO_TUPLE 160
|
||||
#define SEND_GEN 161
|
||||
|
||||
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|
||||
|| ((op) == JUMP) \
|
||||
|| ((op) == JUMP_NO_INTERRUPT) \
|
||||
|| ((op) == LOAD_METHOD) \
|
||||
|| ((op) == LOAD_SUPER_METHOD) \
|
||||
|| ((op) == LOAD_ZERO_SUPER_METHOD) \
|
||||
|| ((op) == LOAD_ZERO_SUPER_ATTR) \
|
||||
)
|
||||
|
||||
#define HAS_CONST(op) (false\
|
||||
|
|
|
@ -184,7 +184,6 @@ typedef Py_ssize_t Py_ssize_clean_t;
|
|||
# define Py_LOCAL_INLINE(type) static inline type
|
||||
#endif
|
||||
|
||||
// bpo-28126: Py_MEMCPY is kept for backwards compatibility,
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
|
||||
# define Py_MEMCPY memcpy
|
||||
#endif
|
||||
|
@ -663,6 +662,27 @@ extern char * _getpty(int *, int, mode_t, int);
|
|||
# define WITH_THREAD
|
||||
#endif
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
# ifdef Py_BUILD_CORE
|
||||
# ifdef HAVE_THREAD_LOCAL
|
||||
# error "HAVE_THREAD_LOCAL is already defined"
|
||||
# endif
|
||||
# define HAVE_THREAD_LOCAL 1
|
||||
# ifdef thread_local
|
||||
# define _Py_thread_local thread_local
|
||||
# elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
|
||||
# define _Py_thread_local _Thread_local
|
||||
# elif defined(_MSC_VER) /* AKA NT_THREADS */
|
||||
# define _Py_thread_local __declspec(thread)
|
||||
# elif defined(__GNUC__) /* includes clang */
|
||||
# define _Py_thread_local __thread
|
||||
# else
|
||||
// fall back to the PyThread_tss_*() API, or ignore.
|
||||
# undef HAVE_THREAD_LOCAL
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Check that ALT_SOABI is consistent with Py_TRACE_REFS:
|
||||
./configure --with-trace-refs should must be used to define Py_TRACE_REFS */
|
||||
#if defined(ALT_SOABI) && defined(Py_TRACE_REFS)
|
||||
|
|
26
Lib/ast.py
26
Lib/ast.py
|
@ -25,6 +25,7 @@
|
|||
:license: Python License.
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
from _ast import *
|
||||
from contextlib import contextmanager, nullcontext
|
||||
from enum import IntEnum, auto, _simple_enum
|
||||
|
@ -305,28 +306,17 @@ def get_docstring(node, clean=True):
|
|||
return text
|
||||
|
||||
|
||||
def _splitlines_no_ff(source):
|
||||
_line_pattern = re.compile(r"(.*?(?:\r\n|\n|\r|$))")
|
||||
def _splitlines_no_ff(source, maxlines=None):
|
||||
"""Split a string into lines ignoring form feed and other chars.
|
||||
|
||||
This mimics how the Python parser splits source code.
|
||||
"""
|
||||
idx = 0
|
||||
lines = []
|
||||
next_line = ''
|
||||
while idx < len(source):
|
||||
c = source[idx]
|
||||
next_line += c
|
||||
idx += 1
|
||||
# Keep \r\n together
|
||||
if c == '\r' and idx < len(source) and source[idx] == '\n':
|
||||
next_line += '\n'
|
||||
idx += 1
|
||||
if c in '\r\n':
|
||||
lines.append(next_line)
|
||||
next_line = ''
|
||||
|
||||
if next_line:
|
||||
lines.append(next_line)
|
||||
for lineno, match in enumerate(_line_pattern.finditer(source), 1):
|
||||
if maxlines is not None and lineno > maxlines:
|
||||
break
|
||||
lines.append(match[0])
|
||||
return lines
|
||||
|
||||
|
||||
|
@ -360,7 +350,7 @@ def get_source_segment(source, node, *, padded=False):
|
|||
except AttributeError:
|
||||
return None
|
||||
|
||||
lines = _splitlines_no_ff(source)
|
||||
lines = _splitlines_no_ff(source, maxlines=end_lineno+1)
|
||||
if end_lineno == lineno:
|
||||
return lines[lineno].encode()[col_offset:end_col_offset].decode()
|
||||
|
||||
|
|
|
@ -574,6 +574,8 @@ class Bdb:
|
|||
line = linecache.getline(filename, lineno, frame.f_globals)
|
||||
if line:
|
||||
s += lprefix + line.strip()
|
||||
else:
|
||||
s += f'{lprefix}Warning: lineno is None'
|
||||
return s
|
||||
|
||||
# The following methods can be called by clients to use
|
||||
|
|
|
@ -7,6 +7,7 @@ set the first day of the week (0=Monday, 6=Sunday)."""
|
|||
|
||||
import sys
|
||||
import datetime
|
||||
from enum import IntEnum, global_enum
|
||||
import locale as _locale
|
||||
from itertools import repeat
|
||||
|
||||
|
@ -16,6 +17,9 @@ __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
|
|||
"timegm", "month_name", "month_abbr", "day_name", "day_abbr",
|
||||
"Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar",
|
||||
"LocaleHTMLCalendar", "weekheader",
|
||||
"Day", "Month", "JANUARY", "FEBRUARY", "MARCH",
|
||||
"APRIL", "MAY", "JUNE", "JULY",
|
||||
"AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
|
||||
"MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY",
|
||||
"SATURDAY", "SUNDAY"]
|
||||
|
||||
|
@ -37,9 +41,35 @@ class IllegalWeekdayError(ValueError):
|
|||
return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday
|
||||
|
||||
|
||||
# Constants for months referenced later
|
||||
January = 1
|
||||
February = 2
|
||||
# Constants for months
|
||||
@global_enum
|
||||
class Month(IntEnum):
|
||||
JANUARY = 1
|
||||
FEBRUARY = 2
|
||||
MARCH = 3
|
||||
APRIL = 4
|
||||
MAY = 5
|
||||
JUNE = 6
|
||||
JULY = 7
|
||||
AUGUST = 8
|
||||
SEPTEMBER = 9
|
||||
OCTOBER = 10
|
||||
NOVEMBER = 11
|
||||
DECEMBER = 12
|
||||
|
||||
|
||||
# Constants for days
|
||||
@global_enum
|
||||
class Day(IntEnum):
|
||||
MONDAY = 0
|
||||
TUESDAY = 1
|
||||
WEDNESDAY = 2
|
||||
THURSDAY = 3
|
||||
FRIDAY = 4
|
||||
SATURDAY = 5
|
||||
SUNDAY = 6
|
||||
|
||||
|
||||
|
||||
# Number of days per month (except for February in leap years)
|
||||
mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
|
@ -95,9 +125,6 @@ day_abbr = _localized_day('%a')
|
|||
month_name = _localized_month('%B')
|
||||
month_abbr = _localized_month('%b')
|
||||
|
||||
# Constants for weekdays
|
||||
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
|
||||
|
||||
|
||||
def isleap(year):
|
||||
"""Return True for leap years, False for non-leap years."""
|
||||
|
@ -125,12 +152,12 @@ def monthrange(year, month):
|
|||
if not 1 <= month <= 12:
|
||||
raise IllegalMonthError(month)
|
||||
day1 = weekday(year, month, 1)
|
||||
ndays = mdays[month] + (month == February and isleap(year))
|
||||
ndays = mdays[month] + (month == FEBRUARY and isleap(year))
|
||||
return day1, ndays
|
||||
|
||||
|
||||
def _monthlen(year, month):
|
||||
return mdays[month] + (month == February and isleap(year))
|
||||
return mdays[month] + (month == FEBRUARY and isleap(year))
|
||||
|
||||
|
||||
def _prevmonth(year, month):
|
||||
|
@ -260,10 +287,7 @@ class Calendar(object):
|
|||
Each month contains between 4 and 6 weeks and each week contains 1-7
|
||||
days. Days are datetime.date objects.
|
||||
"""
|
||||
months = [
|
||||
self.monthdatescalendar(year, i)
|
||||
for i in range(January, January+12)
|
||||
]
|
||||
months = [self.monthdatescalendar(year, m) for m in Month]
|
||||
return [months[i:i+width] for i in range(0, len(months), width) ]
|
||||
|
||||
def yeardays2calendar(self, year, width=3):
|
||||
|
@ -273,10 +297,7 @@ class Calendar(object):
|
|||
(day number, weekday number) tuples. Day numbers outside this month are
|
||||
zero.
|
||||
"""
|
||||
months = [
|
||||
self.monthdays2calendar(year, i)
|
||||
for i in range(January, January+12)
|
||||
]
|
||||
months = [self.monthdays2calendar(year, m) for m in Month]
|
||||
return [months[i:i+width] for i in range(0, len(months), width) ]
|
||||
|
||||
def yeardayscalendar(self, year, width=3):
|
||||
|
@ -285,10 +306,7 @@ class Calendar(object):
|
|||
yeardatescalendar()). Entries in the week lists are day numbers.
|
||||
Day numbers outside this month are zero.
|
||||
"""
|
||||
months = [
|
||||
self.monthdayscalendar(year, i)
|
||||
for i in range(January, January+12)
|
||||
]
|
||||
months = [self.monthdayscalendar(year, m) for m in Month]
|
||||
return [months[i:i+width] for i in range(0, len(months), width) ]
|
||||
|
||||
|
||||
|
@ -509,7 +527,7 @@ class HTMLCalendar(Calendar):
|
|||
a('\n')
|
||||
a('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
|
||||
width, self.cssclass_year_head, theyear))
|
||||
for i in range(January, January+12, width):
|
||||
for i in range(JANUARY, JANUARY+12, width):
|
||||
# months in this row
|
||||
months = range(i, min(i+width, 13))
|
||||
a('<tr>')
|
||||
|
|
|
@ -441,7 +441,16 @@ class suppress(AbstractContextManager):
|
|||
# exactly reproduce the limitations of the CPython interpreter.
|
||||
#
|
||||
# See http://bugs.python.org/issue12029 for more details
|
||||
return exctype is not None and issubclass(exctype, self._exceptions)
|
||||
if exctype is None:
|
||||
return
|
||||
if issubclass(exctype, self._exceptions):
|
||||
return True
|
||||
if issubclass(exctype, ExceptionGroup):
|
||||
match, rest = excinst.split(self._exceptions)
|
||||
if rest is None:
|
||||
return True
|
||||
raise rest
|
||||
return False
|
||||
|
||||
|
||||
class _BaseExitStack:
|
||||
|
|
|
@ -1965,6 +1965,11 @@ class datetime(date):
|
|||
def _local_timezone(self):
|
||||
if self.tzinfo is None:
|
||||
ts = self._mktime()
|
||||
# Detect gap
|
||||
ts2 = self.replace(fold=1-self.fold)._mktime()
|
||||
if ts2 != ts: # This happens in a gap or a fold
|
||||
if (ts2 > ts) == self.fold:
|
||||
ts = ts2
|
||||
else:
|
||||
ts = (self - _EPOCH) // timedelta(seconds=1)
|
||||
localtm = _time.localtime(ts)
|
||||
|
|
|
@ -41,6 +41,7 @@ JUMP_BACKWARD = opmap['JUMP_BACKWARD']
|
|||
FOR_ITER = opmap['FOR_ITER']
|
||||
SEND = opmap['SEND']
|
||||
LOAD_ATTR = opmap['LOAD_ATTR']
|
||||
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
|
||||
|
||||
CACHE = opmap["CACHE"]
|
||||
|
||||
|
@ -475,6 +476,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
|
|||
argval, argrepr = _get_name_info(arg//2, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = "NULL|self + " + argrepr
|
||||
elif deop == LOAD_SUPER_ATTR:
|
||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = "NULL|self + " + argrepr
|
||||
else:
|
||||
argval, argrepr = _get_name_info(arg, get_name)
|
||||
elif deop in hasjabs:
|
||||
|
|
|
@ -1987,7 +1987,7 @@ def get_address_list(value):
|
|||
try:
|
||||
token, value = get_address(value)
|
||||
address_list.append(token)
|
||||
except errors.HeaderParseError as err:
|
||||
except errors.HeaderParseError:
|
||||
leader = None
|
||||
if value[0] in CFWS_LEADER:
|
||||
leader, value = get_cfws(value)
|
||||
|
@ -2096,7 +2096,7 @@ def get_msg_id(value):
|
|||
except errors.HeaderParseError:
|
||||
try:
|
||||
token, value = get_no_fold_literal(value)
|
||||
except errors.HeaderParseError as e:
|
||||
except errors.HeaderParseError:
|
||||
try:
|
||||
token, value = get_domain(value)
|
||||
msg_id.defects.append(errors.ObsoleteHeaderDefect(
|
||||
|
@ -2443,7 +2443,6 @@ def get_parameter(value):
|
|||
raise errors.HeaderParseError("Parameter not followed by '='")
|
||||
param.append(ValueTerminal('=', 'parameter-separator'))
|
||||
value = value[1:]
|
||||
leader = None
|
||||
if value and value[0] in CFWS_LEADER:
|
||||
token, value = get_cfws(value)
|
||||
param.append(token)
|
||||
|
@ -2568,7 +2567,7 @@ def parse_mime_parameters(value):
|
|||
try:
|
||||
token, value = get_parameter(value)
|
||||
mime_parameters.append(token)
|
||||
except errors.HeaderParseError as err:
|
||||
except errors.HeaderParseError:
|
||||
leader = None
|
||||
if value[0] in CFWS_LEADER:
|
||||
leader, value = get_cfws(value)
|
||||
|
@ -2626,7 +2625,6 @@ def parse_content_type_header(value):
|
|||
don't do that.
|
||||
"""
|
||||
ctype = ContentType()
|
||||
recover = False
|
||||
if not value:
|
||||
ctype.defects.append(errors.HeaderMissingRequiredValue(
|
||||
"Missing content type specification"))
|
||||
|
|
|
@ -341,7 +341,6 @@ class Charset:
|
|||
if not lines and not current_line:
|
||||
lines.append(None)
|
||||
else:
|
||||
separator = (' ' if lines else '')
|
||||
joined_line = EMPTYSTRING.join(current_line)
|
||||
header_bytes = _encode(joined_line, codec)
|
||||
lines.append(encoder(header_bytes))
|
||||
|
|
|
@ -264,7 +264,7 @@ class FeedParser:
|
|||
yield NeedMoreData
|
||||
continue
|
||||
break
|
||||
msg = self._pop_message()
|
||||
self._pop_message()
|
||||
# We need to pop the EOF matcher in order to tell if we're at
|
||||
# the end of the current file, not the end of the last block
|
||||
# of message headers.
|
||||
|
|
|
@ -14,7 +14,7 @@ from io import BytesIO, StringIO
|
|||
# Intrapackage imports
|
||||
from email import utils
|
||||
from email import errors
|
||||
from email._policybase import Policy, compat32
|
||||
from email._policybase import compat32
|
||||
from email import charset as _charset
|
||||
from email._encoded_words import decode_b
|
||||
Charset = _charset.Charset
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
__all__ = ['MIMEText']
|
||||
|
||||
from email.charset import Charset
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
|
||||
|
||||
|
@ -36,6 +35,6 @@ class MIMEText(MIMENonMultipart):
|
|||
_charset = 'utf-8'
|
||||
|
||||
MIMENonMultipart.__init__(self, 'text', _subtype, policy=policy,
|
||||
**{'charset': str(_charset)})
|
||||
charset=str(_charset))
|
||||
|
||||
self.set_payload(_text, _charset)
|
||||
|
|
|
@ -10,7 +10,7 @@ from importlib import resources
|
|||
|
||||
__all__ = ["version", "bootstrap"]
|
||||
_PACKAGE_NAMES = ('pip',)
|
||||
_PIP_VERSION = "23.0.1"
|
||||
_PIP_VERSION = "23.1.1"
|
||||
_PROJECTS = [
|
||||
("pip", _PIP_VERSION, "py3"),
|
||||
]
|
||||
|
|
Binary file not shown.
86
Lib/enum.py
86
Lib/enum.py
|
@ -190,6 +190,8 @@ class property(DynamicClassAttribute):
|
|||
"""
|
||||
|
||||
member = None
|
||||
_attr_type = None
|
||||
_cls_type = None
|
||||
|
||||
def __get__(self, instance, ownerclass=None):
|
||||
if instance is None:
|
||||
|
@ -199,33 +201,36 @@ class property(DynamicClassAttribute):
|
|||
raise AttributeError(
|
||||
'%r has no attribute %r' % (ownerclass, self.name)
|
||||
)
|
||||
else:
|
||||
if self.fget is None:
|
||||
# look for a member by this name.
|
||||
try:
|
||||
return ownerclass._member_map_[self.name]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
'%r has no attribute %r' % (ownerclass, self.name)
|
||||
) from None
|
||||
else:
|
||||
return self.fget(instance)
|
||||
if self.fget is not None:
|
||||
# use previous enum.property
|
||||
return self.fget(instance)
|
||||
elif self._attr_type == 'attr':
|
||||
# look up previous attibute
|
||||
return getattr(self._cls_type, self.name)
|
||||
elif self._attr_type == 'desc':
|
||||
# use previous descriptor
|
||||
return getattr(instance._value_, self.name)
|
||||
# look for a member by this name.
|
||||
try:
|
||||
return ownerclass._member_map_[self.name]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
'%r has no attribute %r' % (ownerclass, self.name)
|
||||
) from None
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if self.fset is None:
|
||||
raise AttributeError(
|
||||
"<enum %r> cannot set attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
else:
|
||||
if self.fset is not None:
|
||||
return self.fset(instance, value)
|
||||
raise AttributeError(
|
||||
"<enum %r> cannot set attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
|
||||
def __delete__(self, instance):
|
||||
if self.fdel is None:
|
||||
raise AttributeError(
|
||||
"<enum %r> cannot delete attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
else:
|
||||
if self.fdel is not None:
|
||||
return self.fdel(instance)
|
||||
raise AttributeError(
|
||||
"<enum %r> cannot delete attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
|
||||
def __set_name__(self, ownerclass, name):
|
||||
self.name = name
|
||||
|
@ -313,27 +318,38 @@ class _proto_member:
|
|||
enum_class._member_names_.append(member_name)
|
||||
# if necessary, get redirect in place and then add it to _member_map_
|
||||
found_descriptor = None
|
||||
descriptor_type = None
|
||||
class_type = None
|
||||
for base in enum_class.__mro__[1:]:
|
||||
descriptor = base.__dict__.get(member_name)
|
||||
if descriptor is not None:
|
||||
if isinstance(descriptor, (property, DynamicClassAttribute)):
|
||||
found_descriptor = descriptor
|
||||
attr = base.__dict__.get(member_name)
|
||||
if attr is not None:
|
||||
if isinstance(attr, (property, DynamicClassAttribute)):
|
||||
found_descriptor = attr
|
||||
class_type = base
|
||||
descriptor_type = 'enum'
|
||||
break
|
||||
elif (
|
||||
hasattr(descriptor, 'fget') and
|
||||
hasattr(descriptor, 'fset') and
|
||||
hasattr(descriptor, 'fdel')
|
||||
):
|
||||
found_descriptor = descriptor
|
||||
elif _is_descriptor(attr):
|
||||
found_descriptor = attr
|
||||
descriptor_type = descriptor_type or 'desc'
|
||||
class_type = class_type or base
|
||||
continue
|
||||
else:
|
||||
descriptor_type = 'attr'
|
||||
class_type = base
|
||||
if found_descriptor:
|
||||
redirect = property()
|
||||
redirect.member = enum_member
|
||||
redirect.__set_name__(enum_class, member_name)
|
||||
# earlier descriptor found; copy fget, fset, fdel to this one.
|
||||
redirect.fget = found_descriptor.fget
|
||||
redirect.fset = found_descriptor.fset
|
||||
redirect.fdel = found_descriptor.fdel
|
||||
if descriptor_type in ('enum','desc'):
|
||||
# earlier descriptor found; copy fget, fset, fdel to this one.
|
||||
redirect.fget = getattr(found_descriptor, 'fget', None)
|
||||
redirect._get = getattr(found_descriptor, '__get__', None)
|
||||
redirect.fset = getattr(found_descriptor, 'fset', None)
|
||||
redirect._set = getattr(found_descriptor, '__set__', None)
|
||||
redirect.fdel = getattr(found_descriptor, 'fdel', None)
|
||||
redirect._del = getattr(found_descriptor, '__delete__', None)
|
||||
redirect._attr_type = descriptor_type
|
||||
redirect._cls_type = class_type
|
||||
setattr(enum_class, member_name, redirect)
|
||||
else:
|
||||
setattr(enum_class, member_name, enum_member)
|
||||
|
|
|
@ -25,7 +25,7 @@ class CalltipWindow(TooltipBase):
|
|||
text_widget: a Text widget with code for which call-tips are desired
|
||||
"""
|
||||
# Note: The Text widget will be accessible as self.anchor_widget
|
||||
super(CalltipWindow, self).__init__(text_widget)
|
||||
super().__init__(text_widget)
|
||||
|
||||
self.label = self.text = None
|
||||
self.parenline = self.parencol = self.lastline = None
|
||||
|
@ -54,7 +54,7 @@ class CalltipWindow(TooltipBase):
|
|||
return
|
||||
self.lastline = curline
|
||||
self.anchor_widget.see("insert")
|
||||
super(CalltipWindow, self).position_window()
|
||||
super().position_window()
|
||||
|
||||
def showtip(self, text, parenleft, parenright):
|
||||
"""Show the call-tip, bind events which will close it and reposition it.
|
||||
|
@ -73,7 +73,7 @@ class CalltipWindow(TooltipBase):
|
|||
self.parenline, self.parencol = map(
|
||||
int, self.anchor_widget.index(parenleft).split("."))
|
||||
|
||||
super(CalltipWindow, self).showtip()
|
||||
super().showtip()
|
||||
|
||||
self._bind_events()
|
||||
|
||||
|
@ -143,7 +143,7 @@ class CalltipWindow(TooltipBase):
|
|||
# ValueError may be raised by MultiCall
|
||||
pass
|
||||
|
||||
super(CalltipWindow, self).hidetip()
|
||||
super().hidetip()
|
||||
|
||||
def _bind_events(self):
|
||||
"""Bind event handlers."""
|
||||
|
|
|
@ -49,9 +49,9 @@ class Idb(bdb.Bdb):
|
|||
filename = code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
basename = os.path.basename(filename)
|
||||
message = "%s:%s" % (basename, lineno)
|
||||
message = f"{basename}:{lineno}"
|
||||
if code.co_name != "?":
|
||||
message = "%s: %s()" % (message, code.co_name)
|
||||
message = f"{message}: {code.co_name}()"
|
||||
return message
|
||||
|
||||
|
||||
|
@ -213,7 +213,8 @@ class Debugger:
|
|||
m1 = "%s" % str(type)
|
||||
if value is not None:
|
||||
try:
|
||||
m1 = "%s: %s" % (m1, str(value))
|
||||
# TODO redo entire section, tries not needed.
|
||||
m1 = f"{m1}: {value}"
|
||||
except:
|
||||
pass
|
||||
bg = "yellow"
|
||||
|
|
|
@ -87,7 +87,7 @@ class SequenceTreeItem(ObjectTreeItem):
|
|||
continue
|
||||
def setfunction(value, key=key, object=self.object):
|
||||
object[key] = value
|
||||
item = make_objecttreeitem("%r:" % (key,), value, setfunction)
|
||||
item = make_objecttreeitem(f"{key!r}:", value, setfunction)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
|
|
|
@ -38,12 +38,13 @@ darwin = sys.platform == 'darwin'
|
|||
def _sphinx_version():
|
||||
"Format sys.version_info to produce the Sphinx version string used to install the chm docs"
|
||||
major, minor, micro, level, serial = sys.version_info
|
||||
release = '%s%s' % (major, minor)
|
||||
release += '%s' % (micro,)
|
||||
# TODO remove unneeded function since .chm no longer installed
|
||||
release = f'{major}{minor}'
|
||||
release += f'{micro}'
|
||||
if level == 'candidate':
|
||||
release += 'rc%s' % (serial,)
|
||||
release += f'rc{serial}'
|
||||
elif level != 'final':
|
||||
release += '%s%s' % (level[0], serial)
|
||||
release += f'{level[0]}{serial}'
|
||||
return release
|
||||
|
||||
|
||||
|
@ -950,7 +951,7 @@ class EditorWindow:
|
|||
rf_list = []
|
||||
file_path = self.recent_files_path
|
||||
if file_path and os.path.exists(file_path):
|
||||
with open(file_path, 'r',
|
||||
with open(file_path,
|
||||
encoding='utf_8', errors='replace') as rf_list_file:
|
||||
rf_list = rf_list_file.readlines()
|
||||
if new_file:
|
||||
|
@ -1458,7 +1459,7 @@ class EditorWindow:
|
|||
else:
|
||||
self.reindent_to(y.compute_backslash_indent())
|
||||
else:
|
||||
assert 0, "bogus continuation type %r" % (c,)
|
||||
assert 0, f"bogus continuation type {c!r}"
|
||||
return "break"
|
||||
|
||||
# This line starts a brand new statement; indent relative to
|
||||
|
|
|
@ -22,7 +22,7 @@ class FileList:
|
|||
# This can happen when bad filename is passed on command line:
|
||||
messagebox.showerror(
|
||||
"File Error",
|
||||
"%r is a directory." % (filename,),
|
||||
f"{filename!r} is a directory.",
|
||||
master=self.root)
|
||||
return None
|
||||
key = os.path.normcase(filename)
|
||||
|
@ -90,7 +90,7 @@ class FileList:
|
|||
self.inversedict[conflict] = None
|
||||
messagebox.showerror(
|
||||
"Name Conflict",
|
||||
"You now have multiple edit windows open for %r" % (filename,),
|
||||
f"You now have multiple edit windows open for {filename!r}",
|
||||
master=self.root)
|
||||
self.dict[newkey] = edit
|
||||
self.inversedict[edit] = newkey
|
||||
|
|
|
@ -191,7 +191,7 @@ class IdleConfTest(unittest.TestCase):
|
|||
idle_dir = os.path.abspath(sys.path[0])
|
||||
for ctype in conf.config_types:
|
||||
config_path = os.path.join(idle_dir, '../config-%s.def' % ctype)
|
||||
with open(config_path, 'r') as f:
|
||||
with open(config_path) as f:
|
||||
cls.config_string[ctype] = f.read()
|
||||
|
||||
cls.orig_warn = config._warn
|
||||
|
|
|
@ -159,7 +159,7 @@ class ModuleFunctionTest(unittest.TestCase):
|
|||
for line, expected_output in test_lines:
|
||||
self.assertEqual(flh(line), expected_output)
|
||||
if expected_output:
|
||||
mock_open.assert_called_with(expected_output[0], 'r')
|
||||
mock_open.assert_called_with(expected_output[0])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -328,7 +328,7 @@ class LineNumbersTest(unittest.TestCase):
|
|||
self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0')
|
||||
|
||||
# Generate a mouse-wheel event and make sure it scrolled up or down.
|
||||
# The meaning of the "delta" is OS-dependant, so this just checks for
|
||||
# The meaning of the "delta" is OS-dependent, so this just checks for
|
||||
# any change.
|
||||
self.linenumber.sidebar_text.event_generate('<MouseWheel>',
|
||||
x=0, y=0,
|
||||
|
@ -691,7 +691,7 @@ class ShellSidebarTest(unittest.TestCase):
|
|||
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
|
||||
|
||||
# Scroll up using the <MouseWheel> event.
|
||||
# The meaning delta is platform-dependant.
|
||||
# The meaning of delta is platform-dependent.
|
||||
delta = -1 if sys.platform == 'darwin' else 120
|
||||
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=delta)
|
||||
yield
|
||||
|
|
|
@ -52,9 +52,9 @@ else:
|
|||
_modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
|
||||
|
||||
# a dictionary to map a modifier name into its number
|
||||
_modifier_names = dict([(name, number)
|
||||
_modifier_names = {name: number
|
||||
for number in range(len(_modifiers))
|
||||
for name in _modifiers[number]])
|
||||
for name in _modifiers[number]}
|
||||
|
||||
# In 3.4, if no shell window is ever open, the underlying Tk widget is
|
||||
# destroyed before .__del__ methods here are called. The following
|
||||
|
@ -134,7 +134,7 @@ def expand_substates(states):
|
|||
return nb
|
||||
statelist = []
|
||||
for state in states:
|
||||
substates = list(set(state & x for x in states))
|
||||
substates = list({state & x for x in states})
|
||||
substates.sort(key=nbits, reverse=True)
|
||||
statelist.append(substates)
|
||||
return statelist
|
||||
|
@ -258,9 +258,9 @@ _types = (
|
|||
_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
|
||||
|
||||
# A dictionary to map a type name into its number
|
||||
_type_names = dict([(name, number)
|
||||
_type_names = {name: number
|
||||
for number in range(len(_types))
|
||||
for name in _types[number]])
|
||||
for name in _types[number]}
|
||||
|
||||
_keysym_re = re.compile(r"^\w+$")
|
||||
_button_re = re.compile(r"^[1-5]$")
|
||||
|
|
|
@ -42,7 +42,7 @@ def file_line_helper(line):
|
|||
if match:
|
||||
filename, lineno = match.group(1, 2)
|
||||
try:
|
||||
f = open(filename, "r")
|
||||
f = open(filename)
|
||||
f.close()
|
||||
break
|
||||
except OSError:
|
||||
|
|
|
@ -249,7 +249,7 @@ class PyShellEditorWindow(EditorWindow):
|
|||
breaks = self.breakpoints
|
||||
filename = self.io.filename
|
||||
try:
|
||||
with open(self.breakpointPath, "r") as fp:
|
||||
with open(self.breakpointPath) as fp:
|
||||
lines = fp.readlines()
|
||||
except OSError:
|
||||
lines = []
|
||||
|
@ -279,7 +279,7 @@ class PyShellEditorWindow(EditorWindow):
|
|||
if filename is None:
|
||||
return
|
||||
if os.path.isfile(self.breakpointPath):
|
||||
with open(self.breakpointPath, "r") as fp:
|
||||
with open(self.breakpointPath) as fp:
|
||||
lines = fp.readlines()
|
||||
for line in lines:
|
||||
if line.startswith(filename + '='):
|
||||
|
@ -441,7 +441,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
|||
# run from the IDLE source directory.
|
||||
del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
|
||||
default=False, type='bool')
|
||||
command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
|
||||
command = f"__import__('idlelib.run').run.main({del_exitf!r})"
|
||||
return [sys.executable] + w + ["-c", command, str(self.port)]
|
||||
|
||||
def start_subprocess(self):
|
||||
|
@ -574,9 +574,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
|||
|
||||
self.runcommand("""if 1:
|
||||
import sys as _sys
|
||||
_sys.path = %r
|
||||
_sys.path = {!r}
|
||||
del _sys
|
||||
\n""" % (path,))
|
||||
\n""".format(path))
|
||||
|
||||
active_seq = None
|
||||
|
||||
|
@ -703,14 +703,14 @@ class ModifiedInterpreter(InteractiveInterpreter):
|
|||
def prepend_syspath(self, filename):
|
||||
"Prepend sys.path with file's directory if not already included"
|
||||
self.runcommand("""if 1:
|
||||
_filename = %r
|
||||
_filename = {!r}
|
||||
import sys as _sys
|
||||
from os.path import dirname as _dirname
|
||||
_dir = _dirname(_filename)
|
||||
if not _dir in _sys.path:
|
||||
_sys.path.insert(0, _dir)
|
||||
del _filename, _sys, _dirname, _dir
|
||||
\n""" % (filename,))
|
||||
\n""".format(filename))
|
||||
|
||||
def showsyntaxerror(self, filename=None):
|
||||
"""Override Interactive Interpreter method: Use Colorizing
|
||||
|
@ -1536,7 +1536,7 @@ def main():
|
|||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
|
||||
except getopt.error as msg:
|
||||
print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
|
||||
print(f"Error: {msg}\n{usage_msg}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
for o, a in opts:
|
||||
if o == '-c':
|
||||
|
@ -1668,9 +1668,9 @@ def main():
|
|||
if cmd or script:
|
||||
shell.interp.runcommand("""if 1:
|
||||
import sys as _sys
|
||||
_sys.argv = %r
|
||||
_sys.argv = {!r}
|
||||
del _sys
|
||||
\n""" % (sys.argv,))
|
||||
\n""".format(sys.argv))
|
||||
if cmd:
|
||||
shell.interp.execsource(cmd)
|
||||
elif script:
|
||||
|
|
|
@ -47,9 +47,8 @@ class WidgetRedirector:
|
|||
tk.createcommand(w, self.dispatch)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s<%s>)" % (self.__class__.__name__,
|
||||
self.widget.__class__.__name__,
|
||||
self.widget._w)
|
||||
w = self.widget
|
||||
return f"{self.__class__.__name__,}({w.__class__.__name__}<{w._w}>)"
|
||||
|
||||
def close(self):
|
||||
"Unregister operations and revert redirection created by .__init__."
|
||||
|
@ -143,8 +142,7 @@ class OriginalCommand:
|
|||
self.orig_and_operation = (redir.orig, operation)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (self.__class__.__name__,
|
||||
self.redir, self.operation)
|
||||
return f"{self.__class__.__name__,}({self.redir!r}, {self.operation!r})"
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.tk_call(self.orig_and_operation + args)
|
||||
|
|
|
@ -174,7 +174,7 @@ class SocketIO:
|
|||
except TypeError:
|
||||
return ("ERROR", "Bad request format")
|
||||
if oid not in self.objtable:
|
||||
return ("ERROR", "Unknown object id: %r" % (oid,))
|
||||
return ("ERROR", f"Unknown object id: {oid!r}")
|
||||
obj = self.objtable[oid]
|
||||
if methodname == "__methods__":
|
||||
methods = {}
|
||||
|
@ -185,7 +185,7 @@ class SocketIO:
|
|||
_getattributes(obj, attributes)
|
||||
return ("OK", attributes)
|
||||
if not hasattr(obj, methodname):
|
||||
return ("ERROR", "Unsupported method name: %r" % (methodname,))
|
||||
return ("ERROR", f"Unsupported method name: {methodname!r}")
|
||||
method = getattr(obj, methodname)
|
||||
try:
|
||||
if how == 'CALL':
|
||||
|
|
|
@ -52,13 +52,13 @@ def idle_formatwarning(message, category, filename, lineno, line=None):
|
|||
"""Format warnings the IDLE way."""
|
||||
|
||||
s = "\nWarning (from warnings module):\n"
|
||||
s += ' File \"%s\", line %s\n' % (filename, lineno)
|
||||
s += f' File \"{filename}\", line {lineno}\n'
|
||||
if line is None:
|
||||
line = linecache.getline(filename, lineno)
|
||||
line = line.strip()
|
||||
if line:
|
||||
s += " %s\n" % line
|
||||
s += "%s: %s\n" % (category.__name__, message)
|
||||
s += f"{category.__name__}: {message}\n"
|
||||
return s
|
||||
|
||||
def idle_showwarning_subproc(
|
||||
|
@ -239,6 +239,7 @@ def print_exception():
|
|||
efile = sys.stderr
|
||||
typ, val, tb = excinfo = sys.exc_info()
|
||||
sys.last_type, sys.last_value, sys.last_traceback = excinfo
|
||||
sys.last_exc = val
|
||||
seen = set()
|
||||
|
||||
def print_exc(typ, exc, tb):
|
||||
|
|
|
@ -169,7 +169,7 @@ def view_file(parent, title, filename, encoding, modal=True, wrap='word',
|
|||
with contents of the file.
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'r', encoding=encoding) as file:
|
||||
with open(filename, encoding=encoding) as file:
|
||||
contents = file.read()
|
||||
except OSError:
|
||||
showerror(title='File Load Error',
|
||||
|
|
|
@ -92,7 +92,7 @@ class OnHoverTooltipBase(TooltipBase):
|
|||
e.g. after hovering over the anchor widget with the mouse for enough
|
||||
time.
|
||||
"""
|
||||
super(OnHoverTooltipBase, self).__init__(anchor_widget)
|
||||
super().__init__(anchor_widget)
|
||||
self.hover_delay = hover_delay
|
||||
|
||||
self._after_id = None
|
||||
|
@ -107,7 +107,7 @@ class OnHoverTooltipBase(TooltipBase):
|
|||
self.anchor_widget.unbind("<Button>", self._id3) # pragma: no cover
|
||||
except TclError:
|
||||
pass
|
||||
super(OnHoverTooltipBase, self).__del__()
|
||||
super().__del__()
|
||||
|
||||
def _show_event(self, event=None):
|
||||
"""event handler to display the tooltip"""
|
||||
|
@ -139,7 +139,7 @@ class OnHoverTooltipBase(TooltipBase):
|
|||
self.unschedule()
|
||||
except TclError: # pragma: no cover
|
||||
pass
|
||||
super(OnHoverTooltipBase, self).hidetip()
|
||||
super().hidetip()
|
||||
|
||||
|
||||
class Hovertip(OnHoverTooltipBase):
|
||||
|
@ -154,7 +154,7 @@ class Hovertip(OnHoverTooltipBase):
|
|||
e.g. after hovering over the anchor widget with the mouse for enough
|
||||
time.
|
||||
"""
|
||||
super(Hovertip, self).__init__(anchor_widget, hover_delay=hover_delay)
|
||||
super().__init__(anchor_widget, hover_delay=hover_delay)
|
||||
self.text = text
|
||||
|
||||
def showcontents(self):
|
||||
|
|
|
@ -32,7 +32,7 @@ except NameError:
|
|||
if os.path.isdir(_icondir):
|
||||
ICONDIR = _icondir
|
||||
elif not os.path.isdir(ICONDIR):
|
||||
raise RuntimeError("can't find icon directory (%r)" % (ICONDIR,))
|
||||
raise RuntimeError(f"can't find icon directory ({ICONDIR!r})")
|
||||
|
||||
def listicons(icondir=ICONDIR):
|
||||
"""Utility to display the available icons."""
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue