Compare commits
98 Commits
revert-239
...
master
Author | SHA1 | Date |
---|---|---|
Cheryl Sabella | e40e2a2cc9 | |
Serhiy Storchaka | 59f9b4e450 | |
Brandt Bucher | 27f9dafc2b | |
Brandt Bucher | cde988e893 | |
Erlend Egeberg Aasland | f7f0ed59bc | |
Erlend Egeberg Aasland | 0b858cdd5d | |
Pablo Galindo | 958acd2da0 | |
Pablo Galindo | ef2d371dee | |
Steve Dower | af4cd16479 | |
Mark Shannon | 127dde5916 | |
Pablo Galindo | 445f7f54b1 | |
Pablo Galindo | de833b6013 | |
Mohamed Koubaa | c8a87addb1 | |
Mark Shannon | bf06b209da | |
Joshua Root | df21f502fd | |
Serhiy Storchaka | b6fc0c406e | |
Ned Deily | 0f3b96b368 | |
Ned Deily | a38e04b566 | |
Ned Deily | 14097a2785 | |
Erlend Egeberg Aasland | c94ee13ad5 | |
Serhiy Storchaka | 1470edd613 | |
Hai Shi | 7c83eaa536 | |
Erlend Egeberg Aasland | b8eb376590 | |
Zackery Spytz | 6613676861 | |
Zackery Spytz | 5d3553b0a8 | |
Pablo Galindo | 9e8fe1986c | |
Pablo Galindo | bd2728b1e8 | |
Lysandros Nikolaou | 2ea320dddd | |
Raymond Hettinger | 8f8de7380c | |
Raymond Hettinger | 768fa145cf | |
Serhiy Storchaka | 607501abb4 | |
Serhiy Storchaka | a25011be8c | |
Erlend Egeberg Aasland | 75bf107c62 | |
Ken Jin | 49cd68fb1e | |
Kurochan | d9142831ba | |
Ken Jin | 11276cd9c4 | |
Ross | 3bf05327c2 | |
Dong-hee Na | de6f20a6de | |
Dong-hee Na | ec3165320e | |
Raymond Hettinger | c8a7b8fa1b | |
Jason R. Coombs | b5711c940f | |
Tao He | 3631d6deab | |
Jason R. Coombs | a6fd0f414c | |
Jason R. Coombs | dfdca85dfa | |
Erlend Egeberg Aasland | f4936ad1c4 | |
Brandon Stansbury | 9655434cca | |
Raymond Hettinger | f421bfce80 | |
Filipe Laíns | 4ac923f275 | |
pxinwr | 277ce3060b | |
Yurii Karabas | c56387f80c | |
Victor Stinner | ba0e49a464 | |
Petr Viktorin | 056c08211b | |
Andre Delfino | 2edfc86f69 | |
Erlend Egeberg Aasland | 84d79cfda9 | |
Jakub Kulík | 0159e5efee | |
Hai Shi | dd39123970 | |
Michael Wayne Goodman | 84402eb110 | |
Jero Bado | a4258e8cd7 | |
Senthil Kumaran | 030a713183 | |
Ross | c1af128f5a | |
Zackery Spytz | 40c2c83899 | |
Serhiy Storchaka | 1df56bc059 | |
Serhiy Storchaka | 156b7f7052 | |
Zackery Spytz | c56988b88f | |
Ken Jin | efb1f0918f | |
Pablo Galindo | a6d63a20df | |
Pablo Galindo | 290f5ae997 | |
Ken Jin | 4140f10a16 | |
Raymond Hettinger | a9621bb301 | |
Ammar Askar | 1031f23fc3 | |
Erlend Egeberg Aasland | bf108bb21e | |
Erlend Egeberg Aasland | 897387d2c8 | |
Erlend Egeberg Aasland | abba83b4b9 | |
Erlend Egeberg Aasland | bf64d9064a | |
Erlend Egeberg Aasland | 3ccef1ca47 | |
Serhiy Storchaka | b02ad2458b | |
Victor Stinner | f4507231e3 | |
Pablo Galindo | 3bcc4ead3f | |
Shantanu | 7865f516f3 | |
Dong-hee Na | 0b281f94b9 | |
Victor Stinner | ea251806b8 | |
Victor Stinner | 993e88cf08 | |
Victor Stinner | 4101018488 | |
Raymond Hettinger | 77fde8dc16 | |
Victor Stinner | ba3d67c2fb | |
Ken Jin | f0853bcedf | |
Eric Snow | 5ae9be68d9 | |
Serhiy Storchaka | c1ae21c965 | |
Desmond Cheong | 36a779e64c | |
Serhiy Storchaka | 675c97eb6c | |
Irit Katriel | 586f3dbe15 | |
Serhiy Storchaka | bb70b2afe3 | |
Serhiy Storchaka | 954a7427ba | |
Erlend Egeberg Aasland | 7f162e867c | |
Gregory P. Smith | 64abf37344 | |
Gregory P. Smith | 8badadec53 | |
Ethan Furman | 786d97a66c | |
Serhiy Storchaka | c6c43b2874 |
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"__comment": "Taken from vscode's vs/workbench/contrib/tasks/common/problemMatcher.ts msCompile rule",
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "msvc-problem-matcher",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+),?(\\d+)?(?:,\\d+,\\d+)?\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"code": 5,
|
||||
"message": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- name: Check for source changes
|
||||
id: check
|
||||
run: |
|
||||
if [ -z "GITHUB_BASE_REF" ]; then
|
||||
if [ -z "$GITHUB_BASE_REF" ]; then
|
||||
echo '::set-output name=run_tests::true'
|
||||
else
|
||||
git fetch origin $GITHUB_BASE_REF --depth=1
|
||||
|
@ -99,6 +99,8 @@ jobs:
|
|||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Register MSVC problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/msvc.json"
|
||||
- name: Build CPython
|
||||
run: .\PCbuild\build.bat -e -p x64
|
||||
- name: Display build info
|
||||
|
|
|
@ -4,7 +4,7 @@ Copyright
|
|||
|
||||
Python and this documentation is:
|
||||
|
||||
Copyright © 2001-2020 Python Software Foundation. All rights reserved.
|
||||
Copyright © 2001-2021 Python Software Foundation. All rights reserved.
|
||||
|
||||
Copyright © 2000 BeOpen.com. All rights reserved.
|
||||
|
||||
|
|
|
@ -934,32 +934,42 @@ here is a pure Python equivalent:
|
|||
if doc is None and fget is not None:
|
||||
doc = fget.__doc__
|
||||
self.__doc__ = doc
|
||||
self._name = ''
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
self._name = name
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
if self.fget is None:
|
||||
raise AttributeError("unreadable attribute")
|
||||
raise AttributeError(f'unreadable attribute {self._name}')
|
||||
return self.fget(obj)
|
||||
|
||||
def __set__(self, obj, value):
|
||||
if self.fset is None:
|
||||
raise AttributeError("can't set attribute")
|
||||
raise AttributeError(f"can't set attribute {self._name}")
|
||||
self.fset(obj, value)
|
||||
|
||||
def __delete__(self, obj):
|
||||
if self.fdel is None:
|
||||
raise AttributeError("can't delete attribute")
|
||||
raise AttributeError(f"can't delete attribute {self._name}")
|
||||
self.fdel(obj)
|
||||
|
||||
def getter(self, fget):
|
||||
return type(self)(fget, self.fset, self.fdel, self.__doc__)
|
||||
prop = type(self)(fget, self.fset, self.fdel, self.__doc__)
|
||||
prop._name = self._name
|
||||
return prop
|
||||
|
||||
def setter(self, fset):
|
||||
return type(self)(self.fget, fset, self.fdel, self.__doc__)
|
||||
prop = type(self)(self.fget, fset, self.fdel, self.__doc__)
|
||||
prop._name = self._name
|
||||
return prop
|
||||
|
||||
def deleter(self, fdel):
|
||||
return type(self)(self.fget, self.fset, fdel, self.__doc__)
|
||||
prop = type(self)(self.fget, self.fset, fdel, self.__doc__)
|
||||
prop._name = self._name
|
||||
return prop
|
||||
|
||||
.. testcode::
|
||||
:hide:
|
||||
|
|
|
@ -112,14 +112,15 @@ The module :mod:`curses` defines the following functions:
|
|||
.. function:: color_content(color_number)
|
||||
|
||||
Return the intensity of the red, green, and blue (RGB) components in the color
|
||||
*color_number*, which must be between ``0`` and :const:`COLORS`. Return a 3-tuple,
|
||||
*color_number*, which must be between ``0`` and ``COLORS - 1``. Return a 3-tuple,
|
||||
containing the R,G,B values for the given color, which will be between
|
||||
``0`` (no component) and ``1000`` (maximum amount of component).
|
||||
|
||||
|
||||
.. function:: color_pair(color_number)
|
||||
.. function:: color_pair(pair_number)
|
||||
|
||||
Return the attribute value for displaying text in the specified color. This
|
||||
Return the attribute value for displaying text in the specified color pair.
|
||||
Only the first 256 color pairs are supported. This
|
||||
attribute value can be combined with :const:`A_STANDOUT`, :const:`A_REVERSE`,
|
||||
and the other :const:`A_\*` attributes. :func:`pair_number` is the counterpart
|
||||
to this function.
|
||||
|
@ -287,7 +288,7 @@ The module :mod:`curses` defines the following functions:
|
|||
Change the definition of a color, taking the number of the color to be changed
|
||||
followed by three RGB values (for the amounts of red, green, and blue
|
||||
components). The value of *color_number* must be between ``0`` and
|
||||
:const:`COLORS`. Each of *r*, *g*, *b*, must be a value between ``0`` and
|
||||
`COLORS - 1`. Each of *r*, *g*, *b*, must be a value between ``0`` and
|
||||
``1000``. When :func:`init_color` is used, all occurrences of that color on the
|
||||
screen immediately change to the new definition. This function is a no-op on
|
||||
most terminals; it is active only if :func:`can_change_color` returns ``True``.
|
||||
|
@ -300,7 +301,8 @@ The module :mod:`curses` defines the following functions:
|
|||
color number. The value of *pair_number* must be between ``1`` and
|
||||
``COLOR_PAIRS - 1`` (the ``0`` color pair is wired to white on black and cannot
|
||||
be changed). The value of *fg* and *bg* arguments must be between ``0`` and
|
||||
:const:`COLORS`. If the color-pair was previously initialized, the screen is
|
||||
``COLORS - 1``, or, after calling :func:`use_default_colors`, ``-1``.
|
||||
If the color-pair was previously initialized, the screen is
|
||||
refreshed and all occurrences of that color-pair are changed to the new
|
||||
definition.
|
||||
|
||||
|
@ -450,7 +452,7 @@ The module :mod:`curses` defines the following functions:
|
|||
.. function:: pair_content(pair_number)
|
||||
|
||||
Return a tuple ``(fg, bg)`` containing the colors for the requested color pair.
|
||||
The value of *pair_number* must be between ``1`` and ``COLOR_PAIRS - 1``.
|
||||
The value of *pair_number* must be between ``0`` and ``COLOR_PAIRS - 1``.
|
||||
|
||||
|
||||
.. function:: pair_number(attr)
|
||||
|
|
|
@ -289,7 +289,7 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1.
|
|||
A :class:`ParameterizedMIMEHeader` class that handles the
|
||||
:mailheader:`Content-Disposition` header.
|
||||
|
||||
.. attribute:: content-disposition
|
||||
.. attribute:: content_disposition
|
||||
|
||||
``inline`` and ``attachment`` are the only valid values in common use.
|
||||
|
||||
|
|
|
@ -62,16 +62,26 @@ The :mod:`functools` module defines the following functions:
|
|||
Example::
|
||||
|
||||
class DataSet:
|
||||
|
||||
def __init__(self, sequence_of_numbers):
|
||||
self._data = sequence_of_numbers
|
||||
self._data = tuple(sequence_of_numbers)
|
||||
|
||||
@cached_property
|
||||
def stdev(self):
|
||||
return statistics.stdev(self._data)
|
||||
|
||||
@cached_property
|
||||
def variance(self):
|
||||
return statistics.variance(self._data)
|
||||
The mechanics of :func:`cached_property` are somewhat different from
|
||||
:func:`property`. A regular property blocks attribute writes unless a
|
||||
setter is defined. In contrast, a *cached_property* allows writes.
|
||||
|
||||
The *cached_property* decorator only runs on lookups and only when an
|
||||
attribute of the same name doesn't exist. When it does run, the
|
||||
*cached_property* writes to the attribute with the same name. Subsequent
|
||||
attribute reads and writes take precedence over the *cached_property*
|
||||
method and it works like a normal attribute.
|
||||
|
||||
The cached value can be cleared by deleting the attribute. This
|
||||
allows the *cached_property* method to run again.
|
||||
|
||||
Note, this decorator interferes with the operation of :pep:`412`
|
||||
key-sharing dictionaries. This means that instance dictionaries
|
||||
|
|
|
@ -115,8 +115,9 @@ Every distribution includes some metadata, which you can extract using the
|
|||
|
||||
>>> wheel_metadata = metadata('wheel') # doctest: +SKIP
|
||||
|
||||
The keys of the returned data structure [#f1]_ name the metadata keywords, and
|
||||
their values are returned unparsed from the distribution metadata::
|
||||
The keys of the returned data structure, a ``PackageMetadata``,
|
||||
name the metadata keywords, and
|
||||
the values are returned unparsed from the distribution metadata::
|
||||
|
||||
>>> wheel_metadata['Requires-Python'] # doctest: +SKIP
|
||||
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
|
||||
|
@ -206,9 +207,9 @@ Thus, an alternative way to get the version number is through the
|
|||
There are all kinds of additional metadata available on the ``Distribution``
|
||||
instance::
|
||||
|
||||
>>> d.metadata['Requires-Python'] # doctest: +SKIP
|
||||
>>> dist.metadata['Requires-Python'] # doctest: +SKIP
|
||||
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
|
||||
>>> d.metadata['License'] # doctest: +SKIP
|
||||
>>> dist.metadata['License'] # doctest: +SKIP
|
||||
'MIT'
|
||||
|
||||
The full set of available metadata is not described here. See :pep:`566`
|
||||
|
@ -259,9 +260,3 @@ a custom finder, return instances of this derived ``Distribution`` in the
|
|||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] Technically, the returned distribution metadata object is an
|
||||
:class:`email.message.EmailMessage`
|
||||
instance, but this is an implementation detail, and not part of the
|
||||
stable API. You should only use dictionary-like methods and syntax
|
||||
to access the metadata contents.
|
||||
|
|
|
@ -786,6 +786,18 @@ which incur interpreter overhead.
|
|||
def dotproduct(vec1, vec2):
|
||||
return sum(map(operator.mul, vec1, vec2))
|
||||
|
||||
def convolve(signal, kernel):
|
||||
# See: https://betterexplained.com/articles/intuitive-convolution/
|
||||
# convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur)
|
||||
# convolve(data, [1, -1]) --> 1st finite difference (1st derivative)
|
||||
# convolve(data, [1, -2, 1]) --> 2nd finite difference (2nd derivative)
|
||||
kernel = tuple(kernel)[::-1]
|
||||
n = len(kernel)
|
||||
window = collections.deque([0], maxlen=n) * n
|
||||
for x in chain(signal, repeat(0, n-1)):
|
||||
window.append(x)
|
||||
yield sum(map(operator.mul, kernel, window))
|
||||
|
||||
def flatten(list_of_lists):
|
||||
"Flatten one level of nesting"
|
||||
return chain.from_iterable(list_of_lists)
|
||||
|
|
|
@ -665,14 +665,14 @@ The ``errors`` module has the following attributes:
|
|||
|
||||
.. data:: codes
|
||||
|
||||
A dictionary mapping numeric error codes to their string descriptions.
|
||||
A dictionary mapping string descriptions to their error codes.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. data:: messages
|
||||
|
||||
A dictionary mapping string descriptions to their error codes.
|
||||
A dictionary mapping numeric error codes to their string descriptions.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
|
|
@ -135,6 +135,15 @@ Functions for integers
|
|||
values. Formerly it used a style like ``int(random()*n)`` which could produce
|
||||
slightly uneven distributions.
|
||||
|
||||
.. deprecated:: 3.10
|
||||
The automatic conversion of non-integer types to equivalent integers is
|
||||
deprecated. Currently ``randrange(10.0)`` is losslessly converted to
|
||||
``randrange(10)``. In the future, this will raise a :exc:`TypeError`.
|
||||
|
||||
.. deprecated:: 3.10
|
||||
The exception raised for non-integral values such as ``range(10.5)``
|
||||
will be changed from :exc:`ValueError` to :exc:`TypeError`.
|
||||
|
||||
.. function:: randint(a, b)
|
||||
|
||||
Return a random integer *N* such that ``a <= N <= b``. Alias for
|
||||
|
|
|
@ -907,11 +907,9 @@ The :mod:`socket` module also offers various network-related services:
|
|||
where the host byte order is the same as network byte order, this is a no-op;
|
||||
otherwise, it performs a 2-byte swap operation.
|
||||
|
||||
.. deprecated:: 3.7
|
||||
In case *x* does not fit in 16-bit unsigned integer, but does fit in a
|
||||
positive C int, it is silently truncated to 16-bit unsigned integer.
|
||||
This silent truncation feature is deprecated, and will raise an
|
||||
exception in future versions of Python.
|
||||
.. versionchanged:: 3.10
|
||||
Raises :exc:`OverflowError` if *x* does not fit in a 16-bit unsigned
|
||||
integer.
|
||||
|
||||
|
||||
.. function:: htonl(x)
|
||||
|
@ -927,11 +925,9 @@ The :mod:`socket` module also offers various network-related services:
|
|||
where the host byte order is the same as network byte order, this is a no-op;
|
||||
otherwise, it performs a 2-byte swap operation.
|
||||
|
||||
.. deprecated:: 3.7
|
||||
In case *x* does not fit in 16-bit unsigned integer, but does fit in a
|
||||
positive C int, it is silently truncated to 16-bit unsigned integer.
|
||||
This silent truncation feature is deprecated, and will raise an
|
||||
exception in future versions of Python.
|
||||
.. versionchanged:: 3.10
|
||||
Raises :exc:`OverflowError` if *x* does not fit in a 16-bit unsigned
|
||||
integer.
|
||||
|
||||
|
||||
.. function:: inet_aton(ip_string)
|
||||
|
|
|
@ -546,7 +546,7 @@ Connection Objects
|
|||
con.close()
|
||||
|
||||
|
||||
.. method:: backup(target, *, pages=0, progress=None, name="main", sleep=0.250)
|
||||
.. method:: backup(target, *, pages=-1, progress=None, name="main", sleep=0.250)
|
||||
|
||||
This method makes a backup of a SQLite database even while it's being accessed
|
||||
by other clients, or concurrently by the same connection. The copy will be
|
||||
|
|
|
@ -198,7 +198,7 @@ However, for reading convenience, most of the examples show sorted sequences.
|
|||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
.. versionchanged:: 3.10
|
||||
Added support for *weights*.
|
||||
|
||||
.. function:: median(data)
|
||||
|
|
|
@ -4959,6 +4959,11 @@ All parameterized generics implement special read-only attributes.
|
|||
(~T,)
|
||||
|
||||
|
||||
.. note::
|
||||
A ``GenericAlias`` object with :class:`typing.ParamSpec` parameters may not
|
||||
have correct ``__parameters__`` after substitution because
|
||||
:class:`typing.ParamSpec` is intended primarily for static type checking.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :pep:`585` -- "Type Hinting Generics In Standard Collections"
|
||||
|
|
|
@ -1187,8 +1187,9 @@ calls these functions.
|
|||
The arguments shown above are merely some common ones.
|
||||
The full function signature is largely the same as that of :func:`run` -
|
||||
most arguments are passed directly through to that interface.
|
||||
However, explicitly passing ``input=None`` to inherit the parent's
|
||||
standard input file handle is not supported.
|
||||
One API deviation from :func:`run` behavior exists: passing ``input=None``
|
||||
will behave the same as ``input=b''`` (or ``input=''``, depending on other
|
||||
arguments) rather than using the parent's standard input file handle.
|
||||
|
||||
By default, this function will return the data as encoded bytes. The actual
|
||||
encoding of the output data may depend on the command being invoked, so the
|
||||
|
|
|
@ -160,6 +160,8 @@ Examining Symbol Tables
|
|||
|
||||
Return ``True`` if the symbol is annotated.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
.. method:: is_free()
|
||||
|
||||
Return ``True`` if the symbol is referenced in its block, but not assigned
|
||||
|
|
|
@ -91,6 +91,9 @@ The different font weights and slants are:
|
|||
|
||||
Return the names of defined fonts.
|
||||
|
||||
.. function:: nametofont(name)
|
||||
.. function:: nametofont(name, root=None)
|
||||
|
||||
Return a :class:`Font` representation of a tk named font.
|
||||
Return a :class:`Font` representation of a tk named font.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
The *root* parameter was added.
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
--------------
|
||||
|
||||
This module provides runtime support for type hints as specified by
|
||||
:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, and :pep:`613`.
|
||||
:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`,
|
||||
:pep:`612` and :pep:`613`.
|
||||
The most fundamental support consists of the types :data:`Any`, :data:`Union`,
|
||||
:data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and
|
||||
:class:`Generic`. For full specification please see :pep:`484`. For
|
||||
|
@ -171,6 +172,22 @@ It is possible to declare the return type of a callable without specifying
|
|||
the call signature by substituting a literal ellipsis
|
||||
for the list of arguments in the type hint: ``Callable[..., ReturnType]``.
|
||||
|
||||
Callables which take other callables as arguments may indicate that their
|
||||
parameter types are dependent on each other using :class:`ParamSpec`.
|
||||
Additionally, if that callable adds or removes arguments from other
|
||||
callables, the :data:`Concatenate` operator may be used. They
|
||||
take the form ``Callable[ParamSpecVariable, ReturnType]`` and
|
||||
``Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]``
|
||||
respectively.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
``Callable`` now supports :class:`ParamSpec` and :data:`Concatenate`.
|
||||
See :pep:`612` for more information.
|
||||
|
||||
.. seealso::
|
||||
The documentation for :class:`ParamSpec` and :class:`Concatenate` provide
|
||||
examples of usage in ``Callable``.
|
||||
|
||||
.. _generics:
|
||||
|
||||
Generics
|
||||
|
@ -316,6 +333,43 @@ User defined generic type aliases are also supported. Examples::
|
|||
.. versionchanged:: 3.7
|
||||
:class:`Generic` no longer has a custom metaclass.
|
||||
|
||||
User-defined generics for parameter expressions are also supported via parameter
|
||||
specification variables in the form ``Generic[P]``. The behavior is consistent
|
||||
with type variables' described above as parameter specification variables are
|
||||
treated by the typing module as a specialized type variable. The one exception
|
||||
to this is that a list of types can be used to substitute a :class:`ParamSpec`::
|
||||
|
||||
>>> from typing import Generic, ParamSpec, TypeVar
|
||||
|
||||
>>> T = TypeVar('T')
|
||||
>>> P = ParamSpec('P')
|
||||
|
||||
>>> class Z(Generic[T, P]): ...
|
||||
...
|
||||
>>> Z[int, [dict, float]]
|
||||
__main__.Z[int, (<class 'dict'>, <class 'float'>)]
|
||||
|
||||
|
||||
Furthermore, a generic with only one parameter specification variable will accept
|
||||
parameter lists in the forms ``X[[Type1, Type2, ...]]`` and also
|
||||
``X[Type1, Type2, ...]`` for aesthetic reasons. Internally, the latter is converted
|
||||
to the former and are thus equivalent::
|
||||
|
||||
>>> class X(Generic[P]): ...
|
||||
...
|
||||
>>> X[int, str]
|
||||
__main__.X[(<class 'int'>, <class 'str'>)]
|
||||
>>> X[[int, str]]
|
||||
__main__.X[(<class 'int'>, <class 'str'>)]
|
||||
|
||||
Do note that generics with :class:`ParamSpec` may not have correct
|
||||
``__parameters__`` after substitution in some cases because they
|
||||
are intended primarily for static type checking.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
:class:`Generic` can now be parameterized over parameter expressions.
|
||||
See :class:`ParamSpec` and :pep:`612` for more details.
|
||||
|
||||
A user-defined generic class can have ABCs as base classes without a metaclass
|
||||
conflict. Generic metaclasses are not supported. The outcome of parameterizing
|
||||
generics is cached, and most types in the typing module are hashable and
|
||||
|
@ -602,10 +656,80 @@ These can be used as types in annotations using ``[]``, each having a unique syn
|
|||
``Callable[..., Any]``, and in turn to
|
||||
:class:`collections.abc.Callable`.
|
||||
|
||||
Callables which take other callables as arguments may indicate that their
|
||||
parameter types are dependent on each other using :class:`ParamSpec`.
|
||||
Additionally, if that callable adds or removes arguments from other
|
||||
callables, the :data:`Concatenate` operator may be used. They
|
||||
take the form ``Callable[ParamSpecVariable, ReturnType]`` and
|
||||
``Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]``
|
||||
respectively.
|
||||
|
||||
.. deprecated:: 3.9
|
||||
:class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and
|
||||
:ref:`types-genericalias`.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
``Callable`` now supports :class:`ParamSpec` and :data:`Concatenate`.
|
||||
See :pep:`612` for more information.
|
||||
|
||||
.. seealso::
|
||||
The documentation for :class:`ParamSpec` and :class:`Concatenate` provide
|
||||
examples of usage with ``Callable``.
|
||||
|
||||
.. data:: Concatenate
|
||||
|
||||
Used with :data:`Callable` and :class:`ParamSpec` to type annotate a higher
|
||||
order callable which adds, removes, or transforms parameters of another
|
||||
callable. Usage is in the form
|
||||
``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate``
|
||||
is currently only valid when used as the first argument to a :data:`Callable`.
|
||||
The last parameter to ``Concatenate`` must be a :class:`ParamSpec`.
|
||||
|
||||
For example, to annotate a decorator ``with_lock`` which provides a
|
||||
:class:`threading.Lock` to the decorated function, ``Concatenate`` can be
|
||||
used to indicate that ``with_lock`` expects a callable which takes in a
|
||||
``Lock`` as the first argument, and returns a callable with a different type
|
||||
signature. In this case, the :class:`ParamSpec` indicates that the returned
|
||||
callable's parameter types are dependent on the parameter types of the
|
||||
callable being passed in::
|
||||
|
||||
from collections.abc import Callable
|
||||
from threading import Lock
|
||||
from typing import Any, Concatenate, ParamSpec
|
||||
|
||||
P = ParamSpec('P')
|
||||
R = ParamSpec('R')
|
||||
|
||||
# Use this lock to ensure that only one thread is executing a function
|
||||
# at any time.
|
||||
my_lock = Lock()
|
||||
|
||||
def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]:
|
||||
'''A type-safe decorator which provides a lock.'''
|
||||
global my_lock
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> T:
|
||||
# Provide the lock as the first argument.
|
||||
return f(my_lock, *args, **kwargs)
|
||||
return inner
|
||||
|
||||
@with_lock
|
||||
def sum_threadsafe(lock: Lock, numbers: list[float]) -> float:
|
||||
'''Add a list of numbers together in a thread-safe manner.'''
|
||||
with lock:
|
||||
return sum(numbers)
|
||||
|
||||
# We don't need to pass in the lock ourselves thanks to the decorator.
|
||||
sum_threadsafe([1.1, 2.2, 3.3])
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :pep:`612` -- Parameter Specification Variables (the PEP which introduced
|
||||
``ParamSpec`` and ``Concatenate``).
|
||||
* :class:`ParamSpec` and :class:`Callable`.
|
||||
|
||||
|
||||
.. class:: Type(Generic[CT_co])
|
||||
|
||||
A variable annotated with ``C`` may accept a value of type ``C``. In
|
||||
|
@ -876,6 +1000,84 @@ These are not used in annotations. They are building blocks for creating generic
|
|||
for the type variable must be a subclass of the boundary type,
|
||||
see :pep:`484`.
|
||||
|
||||
.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
|
||||
|
||||
Parameter specification variable. A specialized version of
|
||||
:class:`type variables <TypeVar>`.
|
||||
|
||||
Usage::
|
||||
|
||||
P = ParamSpec('P')
|
||||
|
||||
Parameter specification variables exist primarily for the benefit of static
|
||||
type checkers. They are used to forward the parameter types of one
|
||||
callable to another callable -- a pattern commonly found in higher order
|
||||
functions and decorators. They are only valid when used in ``Concatenate``,
|
||||
or as the first argument to ``Callable``, or as parameters for user-defined
|
||||
Generics. See :class:`Generic` for more information on generic types.
|
||||
|
||||
For example, to add basic logging to a function, one can create a decorator
|
||||
``add_logging`` to log function calls. The parameter specification variable
|
||||
tells the type checker that the callable passed into the decorator and the
|
||||
new callable returned by it have inter-dependent type parameters::
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar, ParamSpec
|
||||
import logging
|
||||
|
||||
T = TypeVar('T')
|
||||
P = ParamSpec('P')
|
||||
|
||||
def add_logging(f: Callable[P, T]) -> Callable[P, T]:
|
||||
'''A type-safe decorator to add logging to a function.'''
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> T:
|
||||
logging.info(f'{f.__name__} was called')
|
||||
return f(*args, **kwargs)
|
||||
return inner
|
||||
|
||||
@add_logging
|
||||
def add_two(x: float, y: float) -> float:
|
||||
'''Add two numbers together.'''
|
||||
return x + y
|
||||
|
||||
Without ``ParamSpec``, the simplest way to annotate this previously was to
|
||||
use a :class:`TypeVar` with bound ``Callable[..., Any]``. However this
|
||||
causes two problems:
|
||||
|
||||
1. The type checker can't type check the ``inner`` function because
|
||||
``*args`` and ``**kwargs`` have to be typed :data:`Any`.
|
||||
2. :func:`~cast` may be required in the body of the ``add_logging``
|
||||
decorator when returning the ``inner`` function, or the static type
|
||||
checker must be told to ignore the ``return inner``.
|
||||
|
||||
.. attribute:: args
|
||||
.. attribute:: kwargs
|
||||
|
||||
Since ``ParamSpec`` captures both positional and keyword parameters,
|
||||
``P.args`` and ``P.kwargs`` can be used to split a ``ParamSpec`` into its
|
||||
components. ``P.args`` represents the tuple of positional parameters in a
|
||||
given call and should only be used to annotate ``*args``. ``P.kwargs``
|
||||
represents the mapping of keyword parameters to their values in a given call,
|
||||
and should be only be used to annotate ``**kwargs`` or ``**kwds``. Both
|
||||
attributes require the annotated parameter to be in scope.
|
||||
|
||||
Parameter specification variables created with ``covariant=True`` or
|
||||
``contravariant=True`` can be used to declare covariant or contravariant
|
||||
generic types. The ``bound`` argument is also accepted, similar to
|
||||
:class:`TypeVar`. However the actual semantics of these keywords are yet to
|
||||
be decided.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
.. note::
|
||||
Only parameter specification variables defined in global scope can
|
||||
be pickled.
|
||||
|
||||
.. seealso::
|
||||
* :pep:`612` -- Parameter Specification Variables (the PEP which introduced
|
||||
``ParamSpec`` and ``Concatenate``).
|
||||
* :class:`Callable` and :class:`Concatenate`.
|
||||
|
||||
.. data:: AnyStr
|
||||
|
||||
``AnyStr`` is a type variable defined as
|
||||
|
|
|
@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release|
|
|||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python |release| alone or in any derivative
|
||||
version, provided, however, that PSF's License Agreement and PSF's notice of
|
||||
copyright, i.e., "Copyright © 2001-2020 Python Software Foundation; All Rights
|
||||
copyright, i.e., "Copyright © 2001-2021 Python Software Foundation; All Rights
|
||||
Reserved" are retained in Python |release| alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@ library/ipaddress,,::,2001:db00::0/24
|
|||
library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
|
||||
library/ipaddress,,::,2001:db00::0/ffff:ff00::
|
||||
library/itertools,,:step,elements from seq[start:stop:step]
|
||||
library/itertools,,::,kernel = tuple(kernel)[::-1]
|
||||
library/itertools,,:stop,elements from seq[start:stop:step]
|
||||
library/logging.handlers,,:port,host:port
|
||||
library/mmap,,:i2,obj[i1:i2]
|
||||
|
|
|
|
@ -144,6 +144,28 @@ See :pep:`604` for more details.
|
|||
|
||||
(Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.)
|
||||
|
||||
PEP 612: Parameter Specification Variables
|
||||
------------------------------------------
|
||||
|
||||
Two new options to improve the information provided to static type checkers for
|
||||
:pep:`484`\ 's ``Callable`` have been added to the :mod:`typing` module.
|
||||
|
||||
The first is the parameter specification variable. They are used to forward the
|
||||
parameter types of one callable to another callable -- a pattern commonly
|
||||
found in higher order functions and decorators. Examples of usage can be found
|
||||
in :class:`typing.ParamSpec`. Previously, there was no easy way to type annotate
|
||||
dependency of parameter types in such a precise manner.
|
||||
|
||||
The second option is the new ``Concatenate`` operator. It's used in conjunction
|
||||
with parameter specification variables to type annotate a higher order callable
|
||||
which adds or removes parameters of another callable. Examples of usage can
|
||||
be found in :class:`typing.Concatenate`.
|
||||
|
||||
See :class:`typing.Callable`, :class:`typing.ParamSpec`,
|
||||
:class:`typing.Concatenate` and :pep:`612` for more details.
|
||||
|
||||
(Contributed by Ken Jin in :issue:`41559`.)
|
||||
|
||||
Other Language Changes
|
||||
======================
|
||||
|
||||
|
@ -404,9 +426,11 @@ Optimizations
|
|||
average.
|
||||
(Contributed by Victor Stinner in :issue:`41006`.)
|
||||
|
||||
* The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism.
|
||||
It is about 36% faster now. (Contributed by Pablo Galindo and Yury Selivanov
|
||||
in :issue:`42093`, based on ideas implemented originally in PyPy and MicroPython.)
|
||||
* The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism. It
|
||||
is about 36% faster now. This makes optimized ``LOAD_ATTR`` instructions the
|
||||
current most performance attribute access method (faster than slots).
|
||||
(Contributed by Pablo Galindo and Yury Selivanov in :issue:`42093`, based on
|
||||
ideas implemented originally in PyPy and MicroPython.)
|
||||
|
||||
* When building Python with ``--enable-optimizations`` now
|
||||
``-fno-semantic-interposition`` is added to both the compile and link line.
|
||||
|
@ -537,6 +561,12 @@ Changes in the Python API
|
|||
silently in Python 3.9.
|
||||
(Contributed by Ken Jin in :issue:`42195`.)
|
||||
|
||||
* :meth:`socket.htons` and :meth:`socket.ntohs` now raise :exc:`OverflowError`
|
||||
instead of :exc:`DeprecationWarning` if the given parameter will not fit in
|
||||
a 16-bit unsigned integer.
|
||||
(Contributed by Erlend E. Aasland in :issue:`42393`.)
|
||||
|
||||
|
||||
CPython bytecode changes
|
||||
========================
|
||||
|
||||
|
@ -558,6 +588,10 @@ Build Changes
|
|||
* The :mod:`atexit` module must now always be built as a built-in module.
|
||||
(Contributed by Victor Stinner in :issue:`42639`.)
|
||||
|
||||
* Added ``--disable-test-modules`` option to the ``configure`` script:
|
||||
don't build nor install test modules.
|
||||
(Contributed by Xavier de Gaye, Thomas Petazzoni and Peixing Xin in :issue:`27640`.)
|
||||
|
||||
|
||||
C API Changes
|
||||
=============
|
||||
|
|
|
@ -1482,4 +1482,37 @@ and to match the behavior of static type checkers specified in the PEP.
|
|||
File "<stdin>", line 1, in <module>
|
||||
TypeError: unhashable type: 'set'
|
||||
|
||||
(Contributed by Yurii Karabas in :issue:`42345`.)
|
||||
(Contributed by Yurii Karabas in :issue:`42345`.)
|
||||
|
||||
macOS 11.0 (Big Sur) and Apple Silicon Mac support
|
||||
--------------------------------------------------
|
||||
|
||||
As of 3.9.1, Python now fully supports building and running on macOS 11.0
|
||||
(Big Sur) and on Apple Silicon Macs (based on the ``ARM64`` architecture).
|
||||
A new universal build variant, ``universal2``, is now available to natively
|
||||
support both ``ARM64`` and ``Intel 64`` in one set of executables. Binaries
|
||||
can also now be built on current versions of macOS to be deployed on a range
|
||||
of older macOS versions (tested to 10.9) while making some newer OS
|
||||
functions and options conditionally available based on the operating system
|
||||
version in use at runtime ("weaklinking").
|
||||
|
||||
(Contributed by Ronald Oussoren and Lawrence D'Anna in :issue:`41100`.)
|
||||
|
||||
Notable changes in Python 3.9.2
|
||||
===============================
|
||||
|
||||
collections.abc
|
||||
---------------
|
||||
|
||||
:class:`collections.abc.Callable` generic now flattens type parameters, similar
|
||||
to what :data:`typing.Callable` currently does. This means that
|
||||
``collections.abc.Callable[[int, str], str]`` will have ``__args__`` of
|
||||
``(int, str, str)``; previously this was ``([int, str], str)``. To allow this
|
||||
change, :class:`types.GenericAlias` can now be subclassed, and a subclass will
|
||||
be returned when subscripting the :class:`collections.abc.Callable` type.
|
||||
Code which accesses the arguments via :func:`typing.get_args` or ``__args__``
|
||||
need to account for this change. A :exc:`DeprecationWarning` may be emitted for
|
||||
invalid forms of parameterizing :class:`collections.abc.Callable` which may have
|
||||
passed silently in Python 3.9.1. This :exc:`DeprecationWarning` will
|
||||
become a :exc:`TypeError` in Python 3.10.
|
||||
(Contributed by Ken Jin in :issue:`42195`.)
|
|
@ -580,18 +580,23 @@ star_targets[expr_ty]:
|
|||
| a=star_target !',' { a }
|
||||
| a=star_target b=(',' c=star_target { c })* [','] {
|
||||
_Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) }
|
||||
star_targets_seq[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_target+ [','] { a }
|
||||
star_targets_list_seq[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_target+ [','] { a }
|
||||
star_targets_tuple_seq[asdl_expr_seq*]:
|
||||
| a=star_target b=(',' c=star_target { c })+ [','] { (asdl_expr_seq*) _PyPegen_seq_insert_in_front(p, a, b) }
|
||||
| a=star_target ',' { (asdl_expr_seq*) _PyPegen_singleton_seq(p, a) }
|
||||
star_target[expr_ty] (memo):
|
||||
| '*' a=(!'*' star_target) {
|
||||
_Py_Starred(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), Store, EXTRA) }
|
||||
| target_with_star_atom
|
||||
target_with_star_atom[expr_ty] (memo):
|
||||
| a=t_primary '.' b=NAME !t_lookahead { _Py_Attribute(a, b->v.Name.id, Store, EXTRA) }
|
||||
| a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) }
|
||||
| star_atom
|
||||
star_atom[expr_ty]:
|
||||
| a=NAME { _PyPegen_set_expr_context(p, a, Store) }
|
||||
| '(' a=star_target ')' { _PyPegen_set_expr_context(p, a, Store) }
|
||||
| '(' a=[star_targets_seq] ')' { _Py_Tuple(a, Store, EXTRA) }
|
||||
| '[' a=[star_targets_seq] ']' { _Py_List(a, Store, EXTRA) }
|
||||
| '(' a=target_with_star_atom ')' { _PyPegen_set_expr_context(p, a, Store) }
|
||||
| '(' a=[star_targets_tuple_seq] ')' { _Py_Tuple(a, Store, EXTRA) }
|
||||
| '[' a=[star_targets_list_seq] ']' { _Py_List(a, Store, EXTRA) }
|
||||
|
||||
single_target[expr_ty]:
|
||||
| single_subscript_attribute_target
|
||||
|
|
|
@ -63,7 +63,7 @@ PyVectorcall_Function(PyObject *callable)
|
|||
{
|
||||
PyTypeObject *tp;
|
||||
Py_ssize_t offset;
|
||||
vectorcallfunc *ptr;
|
||||
vectorcallfunc ptr;
|
||||
|
||||
assert(callable != NULL);
|
||||
tp = Py_TYPE(callable);
|
||||
|
@ -73,8 +73,8 @@ PyVectorcall_Function(PyObject *callable)
|
|||
assert(PyCallable_Check(callable));
|
||||
offset = tp->tp_vectorcall_offset;
|
||||
assert(offset > 0);
|
||||
ptr = (vectorcallfunc *)(((char *)callable) + offset);
|
||||
return *ptr;
|
||||
memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Call the callable object 'callable' with the "vectorcall" calling
|
||||
|
|
|
@ -35,12 +35,13 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
|
|||
_PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*.
|
||||
*/
|
||||
typedef struct _Py_Identifier {
|
||||
struct _Py_Identifier *next;
|
||||
const char* string;
|
||||
PyObject *object;
|
||||
// Index in PyInterpreterState.unicode.ids.array. It is process-wide
|
||||
// unique and must be initialized to -1.
|
||||
Py_ssize_t index;
|
||||
} _Py_Identifier;
|
||||
|
||||
#define _Py_static_string_init(value) { .next = NULL, .string = value, .object = NULL }
|
||||
#define _Py_static_string_init(value) { .string = value, .index = -1 }
|
||||
#define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value)
|
||||
#define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
|
||||
|
||||
|
|
|
@ -64,6 +64,11 @@ struct _Py_bytes_state {
|
|||
PyBytesObject *characters[256];
|
||||
};
|
||||
|
||||
struct _Py_unicode_ids {
|
||||
Py_ssize_t size;
|
||||
PyObject **array;
|
||||
};
|
||||
|
||||
struct _Py_unicode_state {
|
||||
// The empty Unicode object is a singleton to improve performance.
|
||||
PyObject *empty_string;
|
||||
|
@ -71,6 +76,19 @@ struct _Py_unicode_state {
|
|||
shared as well. */
|
||||
PyObject *latin1[256];
|
||||
struct _Py_unicode_fs_codec fs_codec;
|
||||
|
||||
/* This dictionary holds all interned unicode strings. Note that references
|
||||
to strings in this dictionary are *not* counted in the string's ob_refcnt.
|
||||
When the interned string reaches a refcnt of 0 the string deallocation
|
||||
function will delete the reference from this dictionary.
|
||||
|
||||
Another way to look at this is that to say that the actual reference
|
||||
count of a string is: s->ob_refcnt + (s->state ? 2 : 0)
|
||||
*/
|
||||
PyObject *interned;
|
||||
|
||||
// Unicode identifiers (_Py_Identifier): see _PyUnicode_FromId()
|
||||
struct _Py_unicode_ids ids;
|
||||
};
|
||||
|
||||
struct _Py_float_state {
|
||||
|
@ -173,6 +191,27 @@ struct atexit_state {
|
|||
};
|
||||
|
||||
|
||||
// Type attribute lookup cache: speed up attribute and method lookups,
|
||||
// see _PyType_Lookup().
|
||||
struct type_cache_entry {
|
||||
unsigned int version; // initialized from type->tp_version_tag
|
||||
PyObject *name; // reference to exactly a str or None
|
||||
PyObject *value; // borrowed reference or NULL
|
||||
};
|
||||
|
||||
#define MCACHE_SIZE_EXP 12
|
||||
#define MCACHE_STATS 0
|
||||
|
||||
struct type_cache {
|
||||
struct type_cache_entry hashtable[1 << MCACHE_SIZE_EXP];
|
||||
#if MCACHE_STATS
|
||||
size_t hits;
|
||||
size_t misses;
|
||||
size_t collisions;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/* interpreter state */
|
||||
|
||||
#define _PY_NSMALLPOSINTS 257
|
||||
|
@ -277,6 +316,7 @@ struct _is {
|
|||
struct _Py_exc_state exc_state;
|
||||
|
||||
struct ast_state ast;
|
||||
struct type_cache type_cache;
|
||||
};
|
||||
|
||||
extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp);
|
||||
|
|
|
@ -27,6 +27,9 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
|
|||
return ((type->tp_flags & feature) != 0);
|
||||
}
|
||||
|
||||
extern void _PyType_InitCache(PyInterpreterState *interp);
|
||||
|
||||
|
||||
/* Inline functions trading binary compatibility for speed:
|
||||
_PyObject_Init() is the fast version of PyObject_Init(), and
|
||||
_PyObject_InitVar() is the fast version of PyObject_InitVar().
|
||||
|
|
|
@ -76,7 +76,7 @@ extern void _PyExc_Fini(PyThreadState *tstate);
|
|||
extern void _PyImport_Fini(void);
|
||||
extern void _PyImport_Fini2(void);
|
||||
extern void _PyGC_Fini(PyThreadState *tstate);
|
||||
extern void _PyType_Fini(void);
|
||||
extern void _PyType_Fini(PyThreadState *tstate);
|
||||
extern void _Py_HashRandomization_Fini(void);
|
||||
extern void _PyUnicode_Fini(PyThreadState *tstate);
|
||||
extern void _PyUnicode_ClearInterned(PyThreadState *tstate);
|
||||
|
|
|
@ -49,6 +49,11 @@ typedef struct _Py_AuditHookEntry {
|
|||
void *userData;
|
||||
} _Py_AuditHookEntry;
|
||||
|
||||
struct _Py_unicode_runtime_ids {
|
||||
PyThread_type_lock lock;
|
||||
Py_ssize_t next_index;
|
||||
};
|
||||
|
||||
/* Full Python runtime state */
|
||||
|
||||
typedef struct pyruntimestate {
|
||||
|
@ -106,6 +111,8 @@ typedef struct pyruntimestate {
|
|||
void *open_code_userdata;
|
||||
_Py_AuditHookEntry *audit_hook_head;
|
||||
|
||||
struct _Py_unicode_runtime_ids unicode_ids;
|
||||
|
||||
// XXX Consolidate globals found via the check-c-globals script.
|
||||
} _PyRuntimeState;
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
#define PY_MINOR_VERSION 10
|
||||
#define PY_MICRO_VERSION 0
|
||||
#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA
|
||||
#define PY_RELEASE_SERIAL 3
|
||||
#define PY_RELEASE_SERIAL 4
|
||||
|
||||
/* Version as a string */
|
||||
#define PY_VERSION "3.10.0a3+"
|
||||
#define PY_VERSION "3.10.0a4+"
|
||||
/*--end constants--*/
|
||||
|
||||
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works,
|
|||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation;
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
|
|
|
@ -63,7 +63,10 @@ def literal_eval(node_or_string):
|
|||
if isinstance(node_or_string, Expression):
|
||||
node_or_string = node_or_string.body
|
||||
def _raise_malformed_node(node):
|
||||
raise ValueError(f'malformed node or string: {node!r}')
|
||||
msg = "malformed node or string"
|
||||
if lno := getattr(node, 'lineno', None):
|
||||
msg += f' on line {lno}'
|
||||
raise ValueError(msg + f': {node!r}')
|
||||
def _convert_num(node):
|
||||
if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
|
||||
_raise_malformed_node(node)
|
||||
|
|
|
@ -344,7 +344,7 @@ def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
|
|||
global _a85chars, _a85chars2
|
||||
# Delay the initialization of tables to not waste memory
|
||||
# if the function is never called
|
||||
if _a85chars is None:
|
||||
if _a85chars2 is None:
|
||||
_a85chars = [bytes((i,)) for i in range(33, 118)]
|
||||
_a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
|
||||
|
||||
|
@ -452,7 +452,7 @@ def b85encode(b, pad=False):
|
|||
global _b85chars, _b85chars2
|
||||
# Delay the initialization of tables to not waste memory
|
||||
# if the function is never called
|
||||
if _b85chars is None:
|
||||
if _b85chars2 is None:
|
||||
_b85chars = [bytes((i,)) for i in _b85alphabet]
|
||||
_b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
|
||||
return _85encode(b, _b85chars, _b85chars2, pad)
|
||||
|
|
|
@ -178,7 +178,7 @@ class EnumMeta(type):
|
|||
Metaclass for Enum
|
||||
"""
|
||||
@classmethod
|
||||
def __prepare__(metacls, cls, bases):
|
||||
def __prepare__(metacls, cls, bases, **kwds):
|
||||
# check that previous enum members do not exist
|
||||
metacls._check_for_existing_members(cls, bases)
|
||||
# create the namespace dict
|
||||
|
@ -235,10 +235,10 @@ class EnumMeta(type):
|
|||
# create our new Enum type
|
||||
if bases:
|
||||
bases = (_NoInitSubclass, ) + bases
|
||||
enum_class = type.__new__(metacls, cls, bases, classdict)
|
||||
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
|
||||
enum_class.__bases__ = enum_class.__bases__[1:] #or (object, )
|
||||
else:
|
||||
enum_class = type.__new__(metacls, cls, bases, classdict)
|
||||
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
|
||||
old_init_subclass = getattr(enum_class, '__init_subclass__', None)
|
||||
# and restore the new one (if there was one)
|
||||
if new_init_subclass is not None:
|
||||
|
|
|
@ -3,6 +3,9 @@ Released on 2021-10-04?
|
|||
======================================
|
||||
|
||||
|
||||
bpo-32631: Finish zzdummy example extension module: make menu entries
|
||||
work; add docstrings and tests with 100% coverage.
|
||||
|
||||
bpo-42508: Keep IDLE running on macOS. Remove obsolete workaround
|
||||
that prevented running files with shortcuts when using new universal2
|
||||
installers built on macOS 11.
|
||||
|
|
|
@ -2316,7 +2316,15 @@ display when Code Context is turned on for an editor window.
|
|||
|
||||
Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines
|
||||
of output to automatically "squeeze".
|
||||
'''
|
||||
''',
|
||||
'Extensions': '''
|
||||
ZzDummy: This extension is provided as an example for how to create and
|
||||
use an extension. Enable indicates whether the extension is active or
|
||||
not; likewise enable_editor and enable_shell indicate which windows it
|
||||
will be active on. For this extension, z-text is the text that will be
|
||||
inserted at or removed from the beginning of the lines of selected text,
|
||||
or the current line if no selection.
|
||||
''',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ variables:
|
|||
(There are a few more, but they are rarely useful.)
|
||||
|
||||
The extension class must not directly bind Window Manager (e.g. X) events.
|
||||
Rather, it must define one or more virtual events, e.g. <<zoom-height>>, and
|
||||
corresponding methods, e.g. zoom_height_event(). The virtual events will be
|
||||
Rather, it must define one or more virtual events, e.g. <<z-in>>, and
|
||||
corresponding methods, e.g. z_in_event(). The virtual events will be
|
||||
bound to the corresponding methods, and Window Manager events can then be bound
|
||||
to the virtual events. (This indirection is done so that the key bindings can
|
||||
easily be changed, and so that other sources of virtual events can exist, such
|
||||
|
@ -54,21 +54,21 @@ Extensions are not required to define menu entries for all the events they
|
|||
implement. (They are also not required to create keybindings, but in that
|
||||
case there must be empty bindings in cofig-extensions.def)
|
||||
|
||||
Here is a complete example:
|
||||
Here is a partial example from zzdummy.py:
|
||||
|
||||
class ZoomHeight:
|
||||
class ZzDummy:
|
||||
|
||||
menudefs = [
|
||||
('edit', [
|
||||
None, # Separator
|
||||
('_Zoom Height', '<<zoom-height>>'),
|
||||
])
|
||||
('format', [
|
||||
('Z in', '<<z-in>>'),
|
||||
('Z out', '<<z-out>>'),
|
||||
] )
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
|
||||
def zoom_height_event(self, event):
|
||||
def z_in_event(self, event=None):
|
||||
"...Do what you want here..."
|
||||
|
||||
The final piece of the puzzle is the file "config-extensions.def", which is
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
"Test zzdummy, coverage 100%."
|
||||
|
||||
from idlelib import zzdummy
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from unittest import mock
|
||||
from idlelib import config
|
||||
from idlelib import editor
|
||||
from idlelib import format
|
||||
|
||||
|
||||
usercfg = zzdummy.idleConf.userCfg
|
||||
testcfg = {
|
||||
'main': config.IdleUserConfParser(''),
|
||||
'highlight': config.IdleUserConfParser(''),
|
||||
'keys': config.IdleUserConfParser(''),
|
||||
'extensions': config.IdleUserConfParser(''),
|
||||
}
|
||||
code_sample = """\
|
||||
|
||||
class C1():
|
||||
# Class comment.
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
"""
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
get_selection_indices = editor.EditorWindow.get_selection_indices
|
||||
def __init__(self, root, text):
|
||||
self.root = root
|
||||
self.top = root
|
||||
self.text = text
|
||||
self.fregion = format.FormatRegion(self)
|
||||
self.text.undo_block_start = mock.Mock()
|
||||
self.text.undo_block_stop = mock.Mock()
|
||||
|
||||
|
||||
class ZZDummyTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
text = cls.text = Text(cls.root)
|
||||
cls.editor = DummyEditwin(root, text)
|
||||
zzdummy.idleConf.userCfg = testcfg
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
zzdummy.idleConf.userCfg = usercfg
|
||||
del cls.editor, cls.text
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
text = self.text
|
||||
text.insert('1.0', code_sample)
|
||||
text.undo_block_start.reset_mock()
|
||||
text.undo_block_stop.reset_mock()
|
||||
zz = self.zz = zzdummy.ZzDummy(self.editor)
|
||||
zzdummy.ZzDummy.ztext = '# ignore #'
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
del self.zz
|
||||
|
||||
def checklines(self, text, value):
|
||||
# Verify that there are lines being checked.
|
||||
end_line = int(float(text.index('end')))
|
||||
|
||||
# Check each line for the starting text.
|
||||
actual = []
|
||||
for line in range(1, end_line):
|
||||
txt = text.get(f'{line}.0', f'{line}.end')
|
||||
actual.append(txt.startswith(value))
|
||||
return actual
|
||||
|
||||
def test_init(self):
|
||||
zz = self.zz
|
||||
self.assertEqual(zz.editwin, self.editor)
|
||||
self.assertEqual(zz.text, self.editor.text)
|
||||
|
||||
def test_reload(self):
|
||||
self.assertEqual(self.zz.ztext, '# ignore #')
|
||||
testcfg['extensions'].SetOption('ZzDummy', 'z-text', 'spam')
|
||||
zzdummy.ZzDummy.reload()
|
||||
self.assertEqual(self.zz.ztext, 'spam')
|
||||
|
||||
def test_z_in_event(self):
|
||||
eq = self.assertEqual
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
eq(self.zz.ztext, '# ignore #')
|
||||
|
||||
# No lines have the leading text.
|
||||
expected = [False, False, False, False, False, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.tag_add('sel', '2.0', '4.end')
|
||||
eq(zz.z_in_event(), 'break')
|
||||
expected = [False, True, True, True, False, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.undo_block_start.assert_called_once()
|
||||
text.undo_block_stop.assert_called_once()
|
||||
|
||||
def test_z_out_event(self):
|
||||
eq = self.assertEqual
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
eq(self.zz.ztext, '# ignore #')
|
||||
|
||||
# Prepend text.
|
||||
text.tag_add('sel', '2.0', '5.end')
|
||||
zz.z_in_event()
|
||||
text.undo_block_start.reset_mock()
|
||||
text.undo_block_stop.reset_mock()
|
||||
|
||||
# Select a few lines to remove text.
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
text.tag_add('sel', '3.0', '4.end')
|
||||
eq(zz.z_out_event(), 'break')
|
||||
expected = [False, True, False, False, True, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.undo_block_start.assert_called_once()
|
||||
text.undo_block_stop.assert_called_once()
|
||||
|
||||
def test_roundtrip(self):
|
||||
# Insert and remove to all code should give back original text.
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
|
||||
text.tag_add('sel', '1.0', 'end-1c')
|
||||
zz.z_in_event()
|
||||
zz.z_out_event()
|
||||
|
||||
self.assertEqual(text.get('1.0', 'end-1c'), code_sample)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
|
@ -1,42 +1,73 @@
|
|||
"Example extension, also used for testing."
|
||||
"""Example extension, also used for testing.
|
||||
|
||||
See extend.txt for more details on creating an extension.
|
||||
See config-extension.def for configuring an extension.
|
||||
"""
|
||||
|
||||
from idlelib.config import idleConf
|
||||
from functools import wraps
|
||||
|
||||
ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
|
||||
|
||||
def format_selection(format_line):
|
||||
"Apply a formatting function to all of the selected lines."
|
||||
|
||||
@wraps(format_line)
|
||||
def apply(self, event=None):
|
||||
head, tail, chars, lines = self.formatter.get_region()
|
||||
for pos in range(len(lines) - 1):
|
||||
line = lines[pos]
|
||||
lines[pos] = format_line(self, line)
|
||||
self.formatter.set_region(head, tail, chars, lines)
|
||||
return 'break'
|
||||
|
||||
return apply
|
||||
|
||||
|
||||
class ZzDummy:
|
||||
"""Prepend or remove initial text from selected lines."""
|
||||
|
||||
## menudefs = [
|
||||
## ('format', [
|
||||
## ('Z in', '<<z-in>>'),
|
||||
## ('Z out', '<<z-out>>'),
|
||||
## ] )
|
||||
## ]
|
||||
# Extend the format menu.
|
||||
menudefs = [
|
||||
('format', [
|
||||
('Z in', '<<z-in>>'),
|
||||
('Z out', '<<z-out>>'),
|
||||
] )
|
||||
]
|
||||
|
||||
def __init__(self, editwin):
|
||||
"Initialize the settings for this extension."
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
z_in = False
|
||||
self.formatter = editwin.fregion
|
||||
|
||||
@classmethod
|
||||
def reload(cls):
|
||||
"Load class variables from config."
|
||||
cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
|
||||
|
||||
def z_in_event(self, event):
|
||||
"""
|
||||
"""
|
||||
text = self.text
|
||||
text.undo_block_start()
|
||||
for line in range(1, text.index('end')):
|
||||
text.insert('%d.0', ztext)
|
||||
text.undo_block_stop()
|
||||
return "break"
|
||||
@format_selection
|
||||
def z_in_event(self, line):
|
||||
"""Insert text at the beginning of each selected line.
|
||||
|
||||
This is bound to the <<z-in>> virtual event when the extensions
|
||||
are loaded.
|
||||
"""
|
||||
return f'{self.ztext}{line}'
|
||||
|
||||
@format_selection
|
||||
def z_out_event(self, line):
|
||||
"""Remove specific text from the beginning of each selected line.
|
||||
|
||||
This is bound to the <<z-out>> virtual event when the extensions
|
||||
are loaded.
|
||||
"""
|
||||
zlength = 0 if not line.startswith(self.ztext) else len(self.ztext)
|
||||
return line[zlength:]
|
||||
|
||||
def z_out_event(self, event): pass
|
||||
|
||||
ZzDummy.reload()
|
||||
|
||||
##if __name__ == "__main__":
|
||||
## import unittest
|
||||
## unittest.main('idlelib.idle_test.test_zzdummy',
|
||||
## verbosity=2, exit=False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_zzdummy', verbosity=2, exit=False)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import io
|
||||
import os
|
||||
import re
|
||||
import abc
|
||||
|
@ -18,6 +17,7 @@ from contextlib import suppress
|
|||
from importlib import import_module
|
||||
from importlib.abc import MetaPathFinder
|
||||
from itertools import starmap
|
||||
from typing import Any, List, Optional, Protocol, TypeVar, Union
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -31,7 +31,7 @@ __all__ = [
|
|||
'metadata',
|
||||
'requires',
|
||||
'version',
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class PackageNotFoundError(ModuleNotFoundError):
|
||||
|
@ -43,7 +43,7 @@ class PackageNotFoundError(ModuleNotFoundError):
|
|||
|
||||
@property
|
||||
def name(self):
|
||||
name, = self.args
|
||||
(name,) = self.args
|
||||
return name
|
||||
|
||||
|
||||
|
@ -60,7 +60,7 @@ class EntryPoint(
|
|||
r'(?P<module>[\w.]+)\s*'
|
||||
r'(:\s*(?P<attr>[\w.]+))?\s*'
|
||||
r'(?P<extras>\[.*\])?\s*$'
|
||||
)
|
||||
)
|
||||
"""
|
||||
A regular expression describing the syntax for an entry point,
|
||||
which might look like:
|
||||
|
@ -77,6 +77,8 @@ class EntryPoint(
|
|||
following the attr, and following any extras.
|
||||
"""
|
||||
|
||||
dist: Optional['Distribution'] = None
|
||||
|
||||
def load(self):
|
||||
"""Load the entry point from its definition. If only a module
|
||||
is indicated by the value, return that module. Otherwise,
|
||||
|
@ -104,23 +106,27 @@ class EntryPoint(
|
|||
|
||||
@classmethod
|
||||
def _from_config(cls, config):
|
||||
return [
|
||||
return (
|
||||
cls(name, value, group)
|
||||
for group in config.sections()
|
||||
for name, value in config.items(group)
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _from_text(cls, text):
|
||||
config = ConfigParser(delimiters='=')
|
||||
# case sensitive: https://stackoverflow.com/q/1611799/812183
|
||||
config.optionxform = str
|
||||
try:
|
||||
config.read_string(text)
|
||||
except AttributeError: # pragma: nocover
|
||||
# Python 2 has no read_string
|
||||
config.readfp(io.StringIO(text))
|
||||
return EntryPoint._from_config(config)
|
||||
config.read_string(text)
|
||||
return cls._from_config(config)
|
||||
|
||||
@classmethod
|
||||
def _from_text_for(cls, text, dist):
|
||||
return (ep._for(dist) for ep in cls._from_text(text))
|
||||
|
||||
def _for(self, dist):
|
||||
self.dist = dist
|
||||
return self
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
|
@ -132,7 +138,7 @@ class EntryPoint(
|
|||
return (
|
||||
self.__class__,
|
||||
(self.name, self.value, self.group),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PackagePath(pathlib.PurePosixPath):
|
||||
|
@ -159,6 +165,25 @@ class FileHash:
|
|||
return '<FileHash mode: {} value: {}>'.format(self.mode, self.value)
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class PackageMetadata(Protocol):
|
||||
def __len__(self) -> int:
|
||||
... # pragma: no cover
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
... # pragma: no cover
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
... # pragma: no cover
|
||||
|
||||
def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]:
|
||||
"""
|
||||
Return all values associated with a possibly multi-valued key.
|
||||
"""
|
||||
|
||||
|
||||
class Distribution:
|
||||
"""A Python distribution package."""
|
||||
|
||||
|
@ -210,9 +235,8 @@ class Distribution:
|
|||
raise ValueError("cannot accept context and kwargs")
|
||||
context = context or DistributionFinder.Context(**kwargs)
|
||||
return itertools.chain.from_iterable(
|
||||
resolver(context)
|
||||
for resolver in cls._discover_resolvers()
|
||||
)
|
||||
resolver(context) for resolver in cls._discover_resolvers()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def at(path):
|
||||
|
@ -227,24 +251,24 @@ class Distribution:
|
|||
def _discover_resolvers():
|
||||
"""Search the meta_path for resolvers."""
|
||||
declared = (
|
||||
getattr(finder, 'find_distributions', None)
|
||||
for finder in sys.meta_path
|
||||
)
|
||||
getattr(finder, 'find_distributions', None) for finder in sys.meta_path
|
||||
)
|
||||
return filter(None, declared)
|
||||
|
||||
@classmethod
|
||||
def _local(cls, root='.'):
|
||||
from pep517 import build, meta
|
||||
|
||||
system = build.compat_system(root)
|
||||
builder = functools.partial(
|
||||
meta.build,
|
||||
source_dir=root,
|
||||
system=system,
|
||||
)
|
||||
)
|
||||
return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
def metadata(self) -> PackageMetadata:
|
||||
"""Return the parsed metadata for this Distribution.
|
||||
|
||||
The returned object will have keys that name the various bits of
|
||||
|
@ -257,9 +281,14 @@ class Distribution:
|
|||
# effect is to just end up using the PathDistribution's self._path
|
||||
# (which points to the egg-info file) attribute unchanged.
|
||||
or self.read_text('')
|
||||
)
|
||||
)
|
||||
return email.message_from_string(text)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the 'Name' metadata for the distribution package."""
|
||||
return self.metadata['Name']
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Return the 'Version' metadata for the distribution package."""
|
||||
|
@ -267,7 +296,7 @@ class Distribution:
|
|||
|
||||
@property
|
||||
def entry_points(self):
|
||||
return EntryPoint._from_text(self.read_text('entry_points.txt'))
|
||||
return list(EntryPoint._from_text_for(self.read_text('entry_points.txt'), self))
|
||||
|
||||
@property
|
||||
def files(self):
|
||||
|
@ -324,9 +353,10 @@ class Distribution:
|
|||
section_pairs = cls._read_sections(source.splitlines())
|
||||
sections = {
|
||||
section: list(map(operator.itemgetter('line'), results))
|
||||
for section, results in
|
||||
itertools.groupby(section_pairs, operator.itemgetter('section'))
|
||||
}
|
||||
for section, results in itertools.groupby(
|
||||
section_pairs, operator.itemgetter('section')
|
||||
)
|
||||
}
|
||||
return cls._convert_egg_info_reqs_to_simple_reqs(sections)
|
||||
|
||||
@staticmethod
|
||||
|
@ -350,6 +380,7 @@ class Distribution:
|
|||
requirement. This method converts the former to the
|
||||
latter. See _test_deps_from_requires_text for an example.
|
||||
"""
|
||||
|
||||
def make_condition(name):
|
||||
return name and 'extra == "{name}"'.format(name=name)
|
||||
|
||||
|
@ -438,48 +469,69 @@ class FastPath:
|
|||
names = zip_path.root.namelist()
|
||||
self.joinpath = zip_path.joinpath
|
||||
|
||||
return dict.fromkeys(
|
||||
child.split(posixpath.sep, 1)[0]
|
||||
for child in names
|
||||
)
|
||||
|
||||
def is_egg(self, search):
|
||||
base = self.base
|
||||
return (
|
||||
base == search.versionless_egg_name
|
||||
or base.startswith(search.prefix)
|
||||
and base.endswith('.egg'))
|
||||
return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
|
||||
|
||||
def search(self, name):
|
||||
for child in self.children():
|
||||
n_low = child.lower()
|
||||
if (n_low in name.exact_matches
|
||||
or n_low.startswith(name.prefix)
|
||||
and n_low.endswith(name.suffixes)
|
||||
# legacy case:
|
||||
or self.is_egg(name) and n_low == 'egg-info'):
|
||||
yield self.joinpath(child)
|
||||
return (
|
||||
self.joinpath(child)
|
||||
for child in self.children()
|
||||
if name.matches(child, self.base)
|
||||
)
|
||||
|
||||
|
||||
class Prepared:
|
||||
"""
|
||||
A prepared search for metadata on a possibly-named package.
|
||||
"""
|
||||
normalized = ''
|
||||
prefix = ''
|
||||
|
||||
normalized = None
|
||||
suffixes = '.dist-info', '.egg-info'
|
||||
exact_matches = [''][:0]
|
||||
versionless_egg_name = ''
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
if name is None:
|
||||
return
|
||||
self.normalized = name.lower().replace('-', '_')
|
||||
self.prefix = self.normalized + '-'
|
||||
self.exact_matches = [
|
||||
self.normalized + suffix for suffix in self.suffixes]
|
||||
self.versionless_egg_name = self.normalized + '.egg'
|
||||
self.normalized = self.normalize(name)
|
||||
self.exact_matches = [self.normalized + suffix for suffix in self.suffixes]
|
||||
|
||||
@staticmethod
|
||||
def normalize(name):
|
||||
"""
|
||||
PEP 503 normalization plus dashes as underscores.
|
||||
"""
|
||||
return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
|
||||
|
||||
@staticmethod
|
||||
def legacy_normalize(name):
|
||||
"""
|
||||
Normalize the package name as found in the convention in
|
||||
older packaging tools versions and specs.
|
||||
"""
|
||||
return name.lower().replace('-', '_')
|
||||
|
||||
def matches(self, cand, base):
|
||||
low = cand.lower()
|
||||
pre, ext = os.path.splitext(low)
|
||||
name, sep, rest = pre.partition('-')
|
||||
return (
|
||||
low in self.exact_matches
|
||||
or ext in self.suffixes
|
||||
and (not self.normalized or name.replace('.', '_') == self.normalized)
|
||||
# legacy case:
|
||||
or self.is_egg(base)
|
||||
and low == 'egg-info'
|
||||
)
|
||||
|
||||
def is_egg(self, base):
|
||||
normalized = self.legacy_normalize(self.name or '')
|
||||
prefix = normalized + '-' if normalized else ''
|
||||
versionless_egg_name = normalized + '.egg' if self.name else ''
|
||||
return (
|
||||
base == versionless_egg_name
|
||||
or base.startswith(prefix)
|
||||
and base.endswith('.egg')
|
||||
)
|
||||
|
||||
|
||||
class MetadataPathFinder(DistributionFinder):
|
||||
|
@ -500,9 +552,8 @@ class MetadataPathFinder(DistributionFinder):
|
|||
def _search_paths(cls, name, paths):
|
||||
"""Find metadata directories in paths heuristically."""
|
||||
return itertools.chain.from_iterable(
|
||||
path.search(Prepared(name))
|
||||
for path in map(FastPath, paths)
|
||||
)
|
||||
path.search(Prepared(name)) for path in map(FastPath, paths)
|
||||
)
|
||||
|
||||
|
||||
class PathDistribution(Distribution):
|
||||
|
@ -515,9 +566,15 @@ class PathDistribution(Distribution):
|
|||
self._path = path
|
||||
|
||||
def read_text(self, filename):
|
||||
with suppress(FileNotFoundError, IsADirectoryError, KeyError,
|
||||
NotADirectoryError, PermissionError):
|
||||
with suppress(
|
||||
FileNotFoundError,
|
||||
IsADirectoryError,
|
||||
KeyError,
|
||||
NotADirectoryError,
|
||||
PermissionError,
|
||||
):
|
||||
return self._path.joinpath(filename).read_text(encoding='utf-8')
|
||||
|
||||
read_text.__doc__ = Distribution.read_text.__doc__
|
||||
|
||||
def locate_file(self, path):
|
||||
|
@ -541,11 +598,11 @@ def distributions(**kwargs):
|
|||
return Distribution.discover(**kwargs)
|
||||
|
||||
|
||||
def metadata(distribution_name):
|
||||
def metadata(distribution_name) -> PackageMetadata:
|
||||
"""Get the metadata for the named package.
|
||||
|
||||
:param distribution_name: The name of the distribution package to query.
|
||||
:return: An email.Message containing the parsed metadata.
|
||||
:return: A PackageMetadata containing the parsed metadata.
|
||||
"""
|
||||
return Distribution.from_name(distribution_name).metadata
|
||||
|
||||
|
@ -565,15 +622,11 @@ def entry_points():
|
|||
|
||||
:return: EntryPoint objects for all installed packages.
|
||||
"""
|
||||
eps = itertools.chain.from_iterable(
|
||||
dist.entry_points for dist in distributions())
|
||||
eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions())
|
||||
by_group = operator.attrgetter('group')
|
||||
ordered = sorted(eps, key=by_group)
|
||||
grouped = itertools.groupby(ordered, by_group)
|
||||
return {
|
||||
group: tuple(eps)
|
||||
for group, eps in grouped
|
||||
}
|
||||
return {group: tuple(eps) for group, eps in grouped}
|
||||
|
||||
|
||||
def files(distribution_name):
|
||||
|
|
|
@ -174,7 +174,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
|
|||
The file is read and scanned in chunks of chunksize bytes.
|
||||
|
||||
"""
|
||||
if executable is None:
|
||||
if not executable:
|
||||
try:
|
||||
ver = os.confstr('CS_GNU_LIBC_VERSION')
|
||||
# parse 'glibc 2.28' as ('glibc', '2.28')
|
||||
|
@ -769,7 +769,7 @@ class uname_result(
|
|||
):
|
||||
"""
|
||||
A uname_result that's largely compatible with a
|
||||
simple namedtuple except that 'platform' is
|
||||
simple namedtuple except that 'processor' is
|
||||
resolved late and cached to avoid calling "uname"
|
||||
except when needed.
|
||||
"""
|
||||
|
@ -784,12 +784,25 @@ class uname_result(
|
|||
(self.processor,)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _make(cls, iterable):
|
||||
# override factory to affect length check
|
||||
num_fields = len(cls._fields)
|
||||
result = cls.__new__(cls, *iterable)
|
||||
if len(result) != num_fields + 1:
|
||||
msg = f'Expected {num_fields} arguments, got {len(result)}'
|
||||
raise TypeError(msg)
|
||||
return result
|
||||
|
||||
def __getitem__(self, key):
|
||||
return tuple(iter(self))[key]
|
||||
return tuple(self)[key]
|
||||
|
||||
def __len__(self):
|
||||
return len(tuple(iter(self)))
|
||||
|
||||
def __reduce__(self):
|
||||
return uname_result, tuple(self)[:len(self._fields)]
|
||||
|
||||
|
||||
_uname_cache = None
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Autogenerated by Sphinx on Mon Dec 7 19:34:00 2020
|
||||
# Autogenerated by Sphinx on Mon Jan 4 17:25:50 2021
|
||||
topics = {'assert': 'The "assert" statement\n'
|
||||
'**********************\n'
|
||||
'\n'
|
||||
|
@ -461,13 +461,12 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'\n'
|
||||
' async_for_stmt ::= "async" for_stmt\n'
|
||||
'\n'
|
||||
'An *asynchronous iterable* is able to call asynchronous code in '
|
||||
'its\n'
|
||||
'*iter* implementation, and *asynchronous iterator* can call\n'
|
||||
'asynchronous code in its *next* method.\n'
|
||||
'An *asynchronous iterable* provides an "__aiter__" method that\n'
|
||||
'directly returns an *asynchronous iterator*, which can call\n'
|
||||
'asynchronous code in its "__anext__" method.\n'
|
||||
'\n'
|
||||
'The "async for" statement allows convenient iteration over\n'
|
||||
'asynchronous iterators.\n'
|
||||
'asynchronous iterables.\n'
|
||||
'\n'
|
||||
'The following code:\n'
|
||||
'\n'
|
||||
|
@ -2383,8 +2382,9 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'compatible\n'
|
||||
'with an exception if it is the class or a base class of the '
|
||||
'exception\n'
|
||||
'object or a tuple containing an item compatible with the '
|
||||
'exception.\n'
|
||||
'object, or a tuple containing an item that is the class or a '
|
||||
'base\n'
|
||||
'class of the exception object.\n'
|
||||
'\n'
|
||||
'If no except clause matches the exception, the search for an '
|
||||
'exception\n'
|
||||
|
@ -2451,11 +2451,32 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'(see\n'
|
||||
'section The standard type hierarchy) identifying the point in '
|
||||
'the\n'
|
||||
'program where the exception occurred. "sys.exc_info()" values '
|
||||
'are\n'
|
||||
'restored to their previous values (before the call) when '
|
||||
'returning\n'
|
||||
'from a function that handled an exception.\n'
|
||||
'program where the exception occurred. The details about the '
|
||||
'exception\n'
|
||||
'accessed via "sys.exc_info()" are restored to their previous '
|
||||
'values\n'
|
||||
'when leaving an exception handler:\n'
|
||||
'\n'
|
||||
' >>> print(sys.exc_info())\n'
|
||||
' (None, None, None)\n'
|
||||
' >>> try:\n'
|
||||
' ... raise TypeError\n'
|
||||
' ... except:\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ... try:\n'
|
||||
' ... raise ValueError\n'
|
||||
' ... except:\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ...\n'
|
||||
" (<class 'TypeError'>, TypeError(), <traceback object at "
|
||||
'0x10efad080>)\n'
|
||||
" (<class 'ValueError'>, ValueError(), <traceback object at "
|
||||
'0x10efad040>)\n'
|
||||
" (<class 'TypeError'>, TypeError(), <traceback object at "
|
||||
'0x10efad080>)\n'
|
||||
' >>> print(sys.exc_info())\n'
|
||||
' (None, None, None)\n'
|
||||
'\n'
|
||||
'The optional "else" clause is executed if the control flow '
|
||||
'leaves the\n'
|
||||
|
@ -2985,13 +3006,12 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'\n'
|
||||
' async_for_stmt ::= "async" for_stmt\n'
|
||||
'\n'
|
||||
'An *asynchronous iterable* is able to call asynchronous code in '
|
||||
'its\n'
|
||||
'*iter* implementation, and *asynchronous iterator* can call\n'
|
||||
'asynchronous code in its *next* method.\n'
|
||||
'An *asynchronous iterable* provides an "__aiter__" method that\n'
|
||||
'directly returns an *asynchronous iterator*, which can call\n'
|
||||
'asynchronous code in its "__anext__" method.\n'
|
||||
'\n'
|
||||
'The "async for" statement allows convenient iteration over\n'
|
||||
'asynchronous iterators.\n'
|
||||
'asynchronous iterables.\n'
|
||||
'\n'
|
||||
'The following code:\n'
|
||||
'\n'
|
||||
|
@ -5524,44 +5544,51 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
' | | formats the result in either fixed-point '
|
||||
'format or in |\n'
|
||||
' | | scientific notation, depending on its '
|
||||
'magnitude. The |\n'
|
||||
' | | precise rules are as follows: suppose that '
|
||||
'the result |\n'
|
||||
'magnitude. A |\n'
|
||||
' | | precision of "0" is treated as equivalent '
|
||||
'to a precision |\n'
|
||||
' | | of "1". The precise rules are as follows: '
|
||||
'suppose that |\n'
|
||||
' | | the result formatted with presentation '
|
||||
'type "\'e\'" and |\n'
|
||||
' | | precision "p-1" would have exponent '
|
||||
'"exp". Then, if "m <= |\n'
|
||||
' | | exp < p", where "m" is -4 for floats and '
|
||||
'-6 for |\n'
|
||||
' | | "Decimals", the number is formatted with '
|
||||
'presentation type |\n'
|
||||
' | | "\'f\'" and precision "p-1-exp". '
|
||||
'Otherwise, the number is |\n'
|
||||
' | | formatted with presentation type "\'e\'" '
|
||||
'and precision "p-1" |\n'
|
||||
' | | would have exponent "exp". Then, if "m <= '
|
||||
'exp < p", where |\n'
|
||||
' | | "m" is -4 for floats and -6 for '
|
||||
'"Decimals", the number is |\n'
|
||||
' | | formatted with presentation type "\'f\'" '
|
||||
'and precision |\n'
|
||||
' | | "p-1-exp". Otherwise, the number is '
|
||||
'formatted with |\n'
|
||||
' | | presentation type "\'e\'" and precision '
|
||||
'"p-1". In both cases |\n'
|
||||
' | | insignificant trailing zeros are removed '
|
||||
'from the |\n'
|
||||
' | | significand, and the decimal point is also '
|
||||
'removed if |\n'
|
||||
' | | there are no remaining digits following '
|
||||
'it, unless the |\n'
|
||||
' | | "\'#\'" option is used. Positive and '
|
||||
'negative infinity, |\n'
|
||||
' | | positive and negative zero, and nans, are '
|
||||
'formatted as |\n'
|
||||
' | | "inf", "-inf", "0", "-0" and "nan" '
|
||||
'respectively, |\n'
|
||||
' | | regardless of the precision. A precision '
|
||||
'of "0" is |\n'
|
||||
' | | treated as equivalent to a precision of '
|
||||
'"1". With no |\n'
|
||||
' | | precision given, uses a precision of "6" '
|
||||
'significant |\n'
|
||||
' | | digits for "float", and shows all '
|
||||
'coefficient digits for |\n'
|
||||
' | | '
|
||||
'"Decimal". '
|
||||
'|\n'
|
||||
' | | "p-1". In both cases insignificant '
|
||||
'trailing zeros are |\n'
|
||||
' | | removed from the significand, and the '
|
||||
'decimal point is |\n'
|
||||
' | | also removed if there are no remaining '
|
||||
'digits following |\n'
|
||||
' | | it, unless the "\'#\'" option is used. '
|
||||
'With no precision |\n'
|
||||
' | | given, uses a precision of "6" significant '
|
||||
'digits for |\n'
|
||||
' | | "float". For "Decimal", the coefficient of '
|
||||
'the result is |\n'
|
||||
' | | formed from the coefficient digits of the '
|
||||
'value; |\n'
|
||||
' | | scientific notation is used for values '
|
||||
'smaller than "1e-6" |\n'
|
||||
' | | in absolute value and values where the '
|
||||
'place value of the |\n'
|
||||
' | | least significant digit is larger than 1, '
|
||||
'and fixed-point |\n'
|
||||
' | | notation is used otherwise. Positive and '
|
||||
'negative |\n'
|
||||
' | | infinity, positive and negative zero, and '
|
||||
'nans, are |\n'
|
||||
' | | formatted as "inf", "-inf", "0", "-0" and '
|
||||
'"nan" |\n'
|
||||
' | | respectively, regardless of the '
|
||||
'precision. |\n'
|
||||
' '
|
||||
'+-----------+------------------------------------------------------------+\n'
|
||||
' | "\'G\'" | General format. Same as "\'g\'" except '
|
||||
|
@ -5586,19 +5613,24 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'percent sign. |\n'
|
||||
' '
|
||||
'+-----------+------------------------------------------------------------+\n'
|
||||
' | None | Similar to "\'g\'", except that '
|
||||
'fixed-point notation, when |\n'
|
||||
' | | used, has at least one digit past the '
|
||||
'decimal point. The |\n'
|
||||
' | | default precision is as high as needed to '
|
||||
'represent the |\n'
|
||||
' | | particular value. The overall effect is to '
|
||||
'match the |\n'
|
||||
' | | output of "str()" as altered by the other '
|
||||
'format |\n'
|
||||
' | | '
|
||||
'modifiers. '
|
||||
'|\n'
|
||||
' | None | For "float" this is the same as "\'g\'", '
|
||||
'except that when |\n'
|
||||
' | | fixed-point notation is used to format the '
|
||||
'result, it |\n'
|
||||
' | | always includes at least one digit past '
|
||||
'the decimal point. |\n'
|
||||
' | | The precision used is as large as needed '
|
||||
'to represent the |\n'
|
||||
' | | given value faithfully. For "Decimal", '
|
||||
'this is the same |\n'
|
||||
' | | as either "\'g\'" or "\'G\'" depending on '
|
||||
'the value of |\n'
|
||||
' | | "context.capitals" for the current decimal '
|
||||
'context. The |\n'
|
||||
' | | overall effect is to match the output of '
|
||||
'"str()" as |\n'
|
||||
' | | altered by the other format '
|
||||
'modifiers. |\n'
|
||||
' '
|
||||
'+-----------+------------------------------------------------------------+\n'
|
||||
'\n'
|
||||
|
@ -5972,8 +6004,10 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'\n'
|
||||
'Names listed in a "global" statement must not be defined as '
|
||||
'formal\n'
|
||||
'parameters or in a "for" loop control target, "class" definition,\n'
|
||||
'function definition, "import" statement, or variable annotation.\n'
|
||||
'parameters, or as targets in "with" statements or "except" '
|
||||
'clauses, or\n'
|
||||
'in a "for" target list, "class" definition, function definition,\n'
|
||||
'"import" statement, or variable annotation.\n'
|
||||
'\n'
|
||||
'**CPython implementation detail:** The current implementation does '
|
||||
'not\n'
|
||||
|
@ -7925,7 +7959,7 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'immediate\n'
|
||||
' subclasses. This method returns a list of all those '
|
||||
'references\n'
|
||||
' still alive. Example:\n'
|
||||
' still alive. The list is in definition order. Example:\n'
|
||||
'\n'
|
||||
' >>> int.__subclasses__()\n'
|
||||
" [<class 'bool'>]\n"
|
||||
|
@ -11224,7 +11258,8 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'object is “compatible” with the exception. An object is compatible\n'
|
||||
'with an exception if it is the class or a base class of the '
|
||||
'exception\n'
|
||||
'object or a tuple containing an item compatible with the exception.\n'
|
||||
'object, or a tuple containing an item that is the class or a base\n'
|
||||
'class of the exception object.\n'
|
||||
'\n'
|
||||
'If no except clause matches the exception, the search for an '
|
||||
'exception\n'
|
||||
|
@ -11279,9 +11314,31 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
'the\n'
|
||||
'exception class, the exception instance and a traceback object (see\n'
|
||||
'section The standard type hierarchy) identifying the point in the\n'
|
||||
'program where the exception occurred. "sys.exc_info()" values are\n'
|
||||
'restored to their previous values (before the call) when returning\n'
|
||||
'from a function that handled an exception.\n'
|
||||
'program where the exception occurred. The details about the '
|
||||
'exception\n'
|
||||
'accessed via "sys.exc_info()" are restored to their previous values\n'
|
||||
'when leaving an exception handler:\n'
|
||||
'\n'
|
||||
' >>> print(sys.exc_info())\n'
|
||||
' (None, None, None)\n'
|
||||
' >>> try:\n'
|
||||
' ... raise TypeError\n'
|
||||
' ... except:\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ... try:\n'
|
||||
' ... raise ValueError\n'
|
||||
' ... except:\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ... print(sys.exc_info())\n'
|
||||
' ...\n'
|
||||
" (<class 'TypeError'>, TypeError(), <traceback object at "
|
||||
'0x10efad080>)\n'
|
||||
" (<class 'ValueError'>, ValueError(), <traceback object at "
|
||||
'0x10efad040>)\n'
|
||||
" (<class 'TypeError'>, TypeError(), <traceback object at "
|
||||
'0x10efad080>)\n'
|
||||
' >>> print(sys.exc_info())\n'
|
||||
' (None, None, None)\n'
|
||||
'\n'
|
||||
'The optional "else" clause is executed if the control flow leaves '
|
||||
'the\n'
|
||||
|
@ -11445,7 +11502,6 @@ topics = {'assert': 'The "assert" statement\n'
|
|||
' There are two types of integers:\n'
|
||||
'\n'
|
||||
' Integers ("int")\n'
|
||||
'\n'
|
||||
' These represent numbers in an unlimited range, subject to\n'
|
||||
' available (virtual) memory only. For the purpose of '
|
||||
'shift\n'
|
||||
|
|
|
@ -51,6 +51,7 @@ from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
|
|||
from math import tau as TWOPI, floor as _floor, isfinite as _isfinite
|
||||
from os import urandom as _urandom
|
||||
from _collections_abc import Set as _Set, Sequence as _Sequence
|
||||
from operator import index as _index
|
||||
from itertools import accumulate as _accumulate, repeat as _repeat
|
||||
from bisect import bisect as _bisect
|
||||
import os as _os
|
||||
|
@ -95,6 +96,7 @@ LOG4 = _log(4.0)
|
|||
SG_MAGICCONST = 1.0 + _log(4.5)
|
||||
BPF = 53 # Number of bits in a float
|
||||
RECIP_BPF = 2 ** -BPF
|
||||
_ONE = 1
|
||||
|
||||
|
||||
class Random(_random.Random):
|
||||
|
@ -287,7 +289,7 @@ class Random(_random.Random):
|
|||
|
||||
## -------------------- integer methods -------------------
|
||||
|
||||
def randrange(self, start, stop=None, step=1):
|
||||
def randrange(self, start, stop=None, step=_ONE):
|
||||
"""Choose a random item from range(start, stop[, step]).
|
||||
|
||||
This fixes the problem with randint() which includes the
|
||||
|
@ -297,38 +299,72 @@ class Random(_random.Random):
|
|||
|
||||
# This code is a bit messy to make it fast for the
|
||||
# common case while still doing adequate error checking.
|
||||
istart = int(start)
|
||||
if istart != start:
|
||||
raise ValueError("non-integer arg 1 for randrange()")
|
||||
try:
|
||||
istart = _index(start)
|
||||
except TypeError:
|
||||
if int(start) == start:
|
||||
istart = int(start)
|
||||
_warn('Float arguments to randrange() have been deprecated\n'
|
||||
'since Python 3.10 and will be removed in a subsequent '
|
||||
'version.',
|
||||
DeprecationWarning, 2)
|
||||
else:
|
||||
_warn('randrange() will raise TypeError in the future',
|
||||
DeprecationWarning, 2)
|
||||
raise ValueError("non-integer arg 1 for randrange()")
|
||||
|
||||
if stop is None:
|
||||
# We don't check for "step != 1" because it hasn't been
|
||||
# type checked and converted to an integer yet.
|
||||
if step is not _ONE:
|
||||
raise TypeError('Missing a non-None stop argument')
|
||||
if istart > 0:
|
||||
return self._randbelow(istart)
|
||||
raise ValueError("empty range for randrange()")
|
||||
|
||||
# stop argument supplied.
|
||||
istop = int(stop)
|
||||
if istop != stop:
|
||||
raise ValueError("non-integer stop for randrange()")
|
||||
try:
|
||||
istop = _index(stop)
|
||||
except TypeError:
|
||||
if int(stop) == stop:
|
||||
istop = int(stop)
|
||||
_warn('Float arguments to randrange() have been deprecated\n'
|
||||
'since Python 3.10 and will be removed in a subsequent '
|
||||
'version.',
|
||||
DeprecationWarning, 2)
|
||||
else:
|
||||
_warn('randrange() will raise TypeError in the future',
|
||||
DeprecationWarning, 2)
|
||||
raise ValueError("non-integer stop for randrange()")
|
||||
|
||||
try:
|
||||
istep = _index(step)
|
||||
except TypeError:
|
||||
if int(step) == step:
|
||||
istep = int(step)
|
||||
_warn('Float arguments to randrange() have been deprecated\n'
|
||||
'since Python 3.10 and will be removed in a subsequent '
|
||||
'version.',
|
||||
DeprecationWarning, 2)
|
||||
else:
|
||||
_warn('randrange() will raise TypeError in the future',
|
||||
DeprecationWarning, 2)
|
||||
raise ValueError("non-integer step for randrange()")
|
||||
width = istop - istart
|
||||
if step == 1 and width > 0:
|
||||
return istart + self._randbelow(width)
|
||||
if step == 1:
|
||||
if istep == 1:
|
||||
if width > 0:
|
||||
return istart + self._randbelow(width)
|
||||
raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
|
||||
|
||||
# Non-unit step argument supplied.
|
||||
istep = int(step)
|
||||
if istep != step:
|
||||
raise ValueError("non-integer step for randrange()")
|
||||
if istep > 0:
|
||||
n = (width + istep - 1) // istep
|
||||
elif istep < 0:
|
||||
n = (width + istep + 1) // istep
|
||||
else:
|
||||
raise ValueError("zero step for randrange()")
|
||||
|
||||
if n <= 0:
|
||||
raise ValueError("empty range for randrange()")
|
||||
|
||||
return istart + istep * self._randbelow(n)
|
||||
|
||||
def randint(self, a, b):
|
||||
|
|
|
@ -1082,7 +1082,8 @@ class LMTP(SMTP):
|
|||
# Handle Unix-domain sockets.
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(self.timeout)
|
||||
if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
|
||||
self.sock.settimeout(self.timeout)
|
||||
self.file = None
|
||||
self.sock.connect(host)
|
||||
except OSError:
|
||||
|
|
|
@ -628,6 +628,39 @@ if hasattr(os, "fork"):
|
|||
self.collect_children(blocking=self.block_on_close)
|
||||
|
||||
|
||||
class _Threads(list):
|
||||
"""
|
||||
Joinable list of all non-daemon threads.
|
||||
"""
|
||||
def append(self, thread):
|
||||
self.reap()
|
||||
if thread.daemon:
|
||||
return
|
||||
super().append(thread)
|
||||
|
||||
def pop_all(self):
|
||||
self[:], result = [], self[:]
|
||||
return result
|
||||
|
||||
def join(self):
|
||||
for thread in self.pop_all():
|
||||
thread.join()
|
||||
|
||||
def reap(self):
|
||||
self[:] = (thread for thread in self if thread.is_alive())
|
||||
|
||||
|
||||
class _NoThreads:
|
||||
"""
|
||||
Degenerate version of _Threads.
|
||||
"""
|
||||
def append(self, thread):
|
||||
pass
|
||||
|
||||
def join(self):
|
||||
pass
|
||||
|
||||
|
||||
class ThreadingMixIn:
|
||||
"""Mix-in class to handle each request in a new thread."""
|
||||
|
||||
|
@ -636,9 +669,9 @@ class ThreadingMixIn:
|
|||
daemon_threads = False
|
||||
# If true, server_close() waits until all non-daemonic threads terminate.
|
||||
block_on_close = True
|
||||
# For non-daemonic threads, list of threading.Threading objects
|
||||
# Threads object
|
||||
# used by server_close() to wait for all threads completion.
|
||||
_threads = None
|
||||
_threads = _NoThreads()
|
||||
|
||||
def process_request_thread(self, request, client_address):
|
||||
"""Same as in BaseServer but as a thread.
|
||||
|
@ -655,23 +688,17 @@ class ThreadingMixIn:
|
|||
|
||||
def process_request(self, request, client_address):
|
||||
"""Start a new thread to process the request."""
|
||||
if self.block_on_close:
|
||||
vars(self).setdefault('_threads', _Threads())
|
||||
t = threading.Thread(target = self.process_request_thread,
|
||||
args = (request, client_address))
|
||||
t.daemon = self.daemon_threads
|
||||
if not t.daemon and self.block_on_close:
|
||||
if self._threads is None:
|
||||
self._threads = []
|
||||
self._threads.append(t)
|
||||
self._threads.append(t)
|
||||
t.start()
|
||||
|
||||
def server_close(self):
|
||||
super().server_close()
|
||||
if self.block_on_close:
|
||||
threads = self._threads
|
||||
self._threads = None
|
||||
if threads:
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
self._threads.join()
|
||||
|
||||
|
||||
if hasattr(os, "fork"):
|
||||
|
|
|
@ -260,6 +260,14 @@ class TraceCallbackTests(unittest.TestCase):
|
|||
cur.execute(queries[0])
|
||||
con2.execute("create table bar(x)")
|
||||
cur.execute(queries[1])
|
||||
|
||||
# Extract from SQLite 3.7.15 changelog:
|
||||
# Avoid invoking the sqlite3_trace() callback multiple times when a
|
||||
# statement is automatically reprepared due to SQLITE_SCHEMA errors.
|
||||
#
|
||||
# See bpo-40810
|
||||
if sqlite.sqlite_version_info < (3, 7, 15):
|
||||
queries.append(queries[-1])
|
||||
self.assertEqual(traced_statements, queries)
|
||||
|
||||
|
||||
|
|
|
@ -420,7 +420,11 @@ def check_output(*popenargs, timeout=None, **kwargs):
|
|||
if 'input' in kwargs and kwargs['input'] is None:
|
||||
# Explicitly passing input=None was previously equivalent to passing an
|
||||
# empty string. That is maintained here for backwards compatibility.
|
||||
kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b''
|
||||
if kwargs.get('universal_newlines') or kwargs.get('text'):
|
||||
empty = ''
|
||||
else:
|
||||
empty = b''
|
||||
kwargs['input'] = empty
|
||||
|
||||
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
|
||||
**kwargs).stdout
|
||||
|
|
|
@ -14,6 +14,6 @@ the user build or load random bytecodes anyway. Otherwise, this is a
|
|||
|
||||
import types
|
||||
|
||||
co = types.CodeType(0, 0, 0, 0, 0, b'\x04\x71\x00\x00',
|
||||
co = types.CodeType(0, 0, 0, 0, 0, 0, b'\x04\x00\x71\x00',
|
||||
(), (), (), '', '', 1, b'')
|
||||
exec(co)
|
||||
|
|
|
@ -107,6 +107,9 @@ class MockSocket:
|
|||
def close(self):
|
||||
pass
|
||||
|
||||
def connect(self, host):
|
||||
pass
|
||||
|
||||
|
||||
def socket(family=None, type=None, proto=None):
|
||||
return MockSocket(family)
|
||||
|
@ -152,8 +155,12 @@ error = socket_module.error
|
|||
|
||||
|
||||
# Constants
|
||||
_GLOBAL_DEFAULT_TIMEOUT = socket_module._GLOBAL_DEFAULT_TIMEOUT
|
||||
AF_INET = socket_module.AF_INET
|
||||
AF_INET6 = socket_module.AF_INET6
|
||||
SOCK_STREAM = socket_module.SOCK_STREAM
|
||||
SOL_SOCKET = None
|
||||
SO_REUSEADDR = None
|
||||
|
||||
if hasattr(socket_module, 'AF_UNIX'):
|
||||
AF_UNIX = socket_module.AF_UNIX
|
||||
|
|
|
@ -69,6 +69,10 @@ def count_opcode(code, pickle):
|
|||
return n
|
||||
|
||||
|
||||
def identity(x):
|
||||
return x
|
||||
|
||||
|
||||
class UnseekableIO(io.BytesIO):
|
||||
def peek(self, *args):
|
||||
raise NotImplementedError
|
||||
|
@ -138,11 +142,12 @@ class E(C):
|
|||
def __getinitargs__(self):
|
||||
return ()
|
||||
|
||||
class H(object):
|
||||
# Simple mutable object.
|
||||
class Object:
|
||||
pass
|
||||
|
||||
# Hashable mutable key
|
||||
class K(object):
|
||||
# Hashable immutable key object containing unheshable mutable data.
|
||||
class K:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
|
@ -157,10 +162,6 @@ __main__.D = D
|
|||
D.__module__ = "__main__"
|
||||
__main__.E = E
|
||||
E.__module__ = "__main__"
|
||||
__main__.H = H
|
||||
H.__module__ = "__main__"
|
||||
__main__.K = K
|
||||
K.__module__ = "__main__"
|
||||
|
||||
class myint(int):
|
||||
def __init__(self, x):
|
||||
|
@ -1496,54 +1497,182 @@ class AbstractPickleTests(unittest.TestCase):
|
|||
got = filelike.getvalue()
|
||||
self.assertEqual(expected, got)
|
||||
|
||||
def test_recursive_list(self):
|
||||
l = []
|
||||
def _test_recursive_list(self, cls, aslist=identity, minprotocol=0):
|
||||
# List containing itself.
|
||||
l = cls()
|
||||
l.append(l)
|
||||
for proto in protocols:
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(l, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, list)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIs(x[0], x)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = aslist(x)
|
||||
self.assertEqual(len(y), 1)
|
||||
self.assertIs(y[0], x)
|
||||
|
||||
def test_recursive_tuple_and_list(self):
|
||||
t = ([],)
|
||||
def test_recursive_list(self):
|
||||
self._test_recursive_list(list)
|
||||
|
||||
def test_recursive_list_subclass(self):
|
||||
self._test_recursive_list(MyList, minprotocol=2)
|
||||
|
||||
def test_recursive_list_like(self):
|
||||
self._test_recursive_list(REX_six, aslist=lambda x: x.items)
|
||||
|
||||
def _test_recursive_tuple_and_list(self, cls, aslist=identity, minprotocol=0):
|
||||
# Tuple containing a list containing the original tuple.
|
||||
t = (cls(),)
|
||||
t[0].append(t)
|
||||
for proto in protocols:
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, tuple)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIsInstance(x[0], list)
|
||||
self.assertEqual(len(x[0]), 1)
|
||||
self.assertIs(x[0][0], x)
|
||||
self.assertIsInstance(x[0], cls)
|
||||
y = aslist(x[0])
|
||||
self.assertEqual(len(y), 1)
|
||||
self.assertIs(y[0], x)
|
||||
|
||||
# List containing a tuple containing the original list.
|
||||
t, = t
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = aslist(x)
|
||||
self.assertEqual(len(y), 1)
|
||||
self.assertIsInstance(y[0], tuple)
|
||||
self.assertEqual(len(y[0]), 1)
|
||||
self.assertIs(y[0][0], x)
|
||||
|
||||
def test_recursive_tuple_and_list(self):
|
||||
self._test_recursive_tuple_and_list(list)
|
||||
|
||||
def test_recursive_tuple_and_list_subclass(self):
|
||||
self._test_recursive_tuple_and_list(MyList, minprotocol=2)
|
||||
|
||||
def test_recursive_tuple_and_list_like(self):
|
||||
self._test_recursive_tuple_and_list(REX_six, aslist=lambda x: x.items)
|
||||
|
||||
def _test_recursive_dict(self, cls, asdict=identity, minprotocol=0):
|
||||
# Dict containing itself.
|
||||
d = cls()
|
||||
d[1] = d
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(d, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = asdict(x)
|
||||
self.assertEqual(list(y.keys()), [1])
|
||||
self.assertIs(y[1], x)
|
||||
|
||||
def test_recursive_dict(self):
|
||||
d = {}
|
||||
d[1] = d
|
||||
for proto in protocols:
|
||||
self._test_recursive_dict(dict)
|
||||
|
||||
def test_recursive_dict_subclass(self):
|
||||
self._test_recursive_dict(MyDict, minprotocol=2)
|
||||
|
||||
def test_recursive_dict_like(self):
|
||||
self._test_recursive_dict(REX_seven, asdict=lambda x: x.table)
|
||||
|
||||
def _test_recursive_tuple_and_dict(self, cls, asdict=identity, minprotocol=0):
|
||||
# Tuple containing a dict containing the original tuple.
|
||||
t = (cls(),)
|
||||
t[0][1] = t
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, tuple)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIsInstance(x[0], cls)
|
||||
y = asdict(x[0])
|
||||
self.assertEqual(list(y), [1])
|
||||
self.assertIs(y[1], x)
|
||||
|
||||
# Dict containing a tuple containing the original dict.
|
||||
t, = t
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = asdict(x)
|
||||
self.assertEqual(list(y), [1])
|
||||
self.assertIsInstance(y[1], tuple)
|
||||
self.assertEqual(len(y[1]), 1)
|
||||
self.assertIs(y[1][0], x)
|
||||
|
||||
def test_recursive_tuple_and_dict(self):
|
||||
self._test_recursive_tuple_and_dict(dict)
|
||||
|
||||
def test_recursive_tuple_and_dict_subclass(self):
|
||||
self._test_recursive_tuple_and_dict(MyDict, minprotocol=2)
|
||||
|
||||
def test_recursive_tuple_and_dict_like(self):
|
||||
self._test_recursive_tuple_and_dict(REX_seven, asdict=lambda x: x.table)
|
||||
|
||||
def _test_recursive_dict_key(self, cls, asdict=identity, minprotocol=0):
|
||||
# Dict containing an immutable object (as key) containing the original
|
||||
# dict.
|
||||
d = cls()
|
||||
d[K(d)] = 1
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(d, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, dict)
|
||||
self.assertEqual(list(x.keys()), [1])
|
||||
self.assertIs(x[1], x)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = asdict(x)
|
||||
self.assertEqual(len(y.keys()), 1)
|
||||
self.assertIsInstance(list(y.keys())[0], K)
|
||||
self.assertIs(list(y.keys())[0].value, x)
|
||||
|
||||
def test_recursive_dict_key(self):
|
||||
d = {}
|
||||
k = K(d)
|
||||
d[k] = 1
|
||||
for proto in protocols:
|
||||
s = self.dumps(d, proto)
|
||||
self._test_recursive_dict_key(dict)
|
||||
|
||||
def test_recursive_dict_subclass_key(self):
|
||||
self._test_recursive_dict_key(MyDict, minprotocol=2)
|
||||
|
||||
def test_recursive_dict_like_key(self):
|
||||
self._test_recursive_dict_key(REX_seven, asdict=lambda x: x.table)
|
||||
|
||||
def _test_recursive_tuple_and_dict_key(self, cls, asdict=identity, minprotocol=0):
|
||||
# Tuple containing a dict containing an immutable object (as key)
|
||||
# containing the original tuple.
|
||||
t = (cls(),)
|
||||
t[0][K(t)] = 1
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, dict)
|
||||
self.assertEqual(len(x.keys()), 1)
|
||||
self.assertIsInstance(list(x.keys())[0], K)
|
||||
self.assertIs(list(x.keys())[0].value, x)
|
||||
self.assertIsInstance(x, tuple)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIsInstance(x[0], cls)
|
||||
y = asdict(x[0])
|
||||
self.assertEqual(len(y), 1)
|
||||
self.assertIsInstance(list(y.keys())[0], K)
|
||||
self.assertIs(list(y.keys())[0].value, x)
|
||||
|
||||
# Dict containing an immutable object (as key) containing a tuple
|
||||
# containing the original dict.
|
||||
t, = t
|
||||
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, cls)
|
||||
y = asdict(x)
|
||||
self.assertEqual(len(y), 1)
|
||||
self.assertIsInstance(list(y.keys())[0], K)
|
||||
self.assertIs(list(y.keys())[0].value[0], x)
|
||||
|
||||
def test_recursive_tuple_and_dict_key(self):
|
||||
self._test_recursive_tuple_and_dict_key(dict)
|
||||
|
||||
def test_recursive_tuple_and_dict_subclass_key(self):
|
||||
self._test_recursive_tuple_and_dict_key(MyDict, minprotocol=2)
|
||||
|
||||
def test_recursive_tuple_and_dict_like_key(self):
|
||||
self._test_recursive_tuple_and_dict_key(REX_seven, asdict=lambda x: x.table)
|
||||
|
||||
def test_recursive_set(self):
|
||||
# Set containing an immutable object containing the original set.
|
||||
y = set()
|
||||
k = K(y)
|
||||
y.add(k)
|
||||
y.add(K(y))
|
||||
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(y, proto)
|
||||
x = self.loads(s)
|
||||
|
@ -1552,52 +1681,31 @@ class AbstractPickleTests(unittest.TestCase):
|
|||
self.assertIsInstance(list(x)[0], K)
|
||||
self.assertIs(list(x)[0].value, x)
|
||||
|
||||
def test_recursive_list_subclass(self):
|
||||
y = MyList()
|
||||
y.append(y)
|
||||
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
|
||||
# Immutable object containing a set containing the original object.
|
||||
y, = y
|
||||
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(y, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, MyList)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIs(x[0], x)
|
||||
|
||||
def test_recursive_dict_subclass(self):
|
||||
d = MyDict()
|
||||
d[1] = d
|
||||
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(d, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, MyDict)
|
||||
self.assertEqual(list(x.keys()), [1])
|
||||
self.assertIs(x[1], x)
|
||||
|
||||
def test_recursive_dict_subclass_key(self):
|
||||
d = MyDict()
|
||||
k = K(d)
|
||||
d[k] = 1
|
||||
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = self.dumps(d, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, MyDict)
|
||||
self.assertEqual(len(list(x.keys())), 1)
|
||||
self.assertIsInstance(list(x.keys())[0], K)
|
||||
self.assertIs(list(x.keys())[0].value, x)
|
||||
self.assertIsInstance(x, K)
|
||||
self.assertIsInstance(x.value, set)
|
||||
self.assertEqual(len(x.value), 1)
|
||||
self.assertIs(list(x.value)[0], x)
|
||||
|
||||
def test_recursive_inst(self):
|
||||
i = C()
|
||||
# Mutable object containing itself.
|
||||
i = Object()
|
||||
i.attr = i
|
||||
for proto in protocols:
|
||||
s = self.dumps(i, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, C)
|
||||
self.assertIsInstance(x, Object)
|
||||
self.assertEqual(dir(x), dir(i))
|
||||
self.assertIs(x.attr, x)
|
||||
|
||||
def test_recursive_multi(self):
|
||||
l = []
|
||||
d = {1:l}
|
||||
i = C()
|
||||
i = Object()
|
||||
i.attr = d
|
||||
l.append(i)
|
||||
for proto in protocols:
|
||||
|
@ -1607,49 +1715,94 @@ class AbstractPickleTests(unittest.TestCase):
|
|||
self.assertEqual(len(x), 1)
|
||||
self.assertEqual(dir(x[0]), dir(i))
|
||||
self.assertEqual(list(x[0].attr.keys()), [1])
|
||||
self.assertTrue(x[0].attr[1] is x)
|
||||
self.assertIs(x[0].attr[1], x)
|
||||
|
||||
def check_recursive_collection_and_inst(self, factory):
|
||||
h = H()
|
||||
y = factory([h])
|
||||
h.attr = y
|
||||
def _test_recursive_collection_and_inst(self, factory):
|
||||
# Mutable object containing a collection containing the original
|
||||
# object.
|
||||
o = Object()
|
||||
o.attr = factory([o])
|
||||
t = type(o.attr)
|
||||
for proto in protocols:
|
||||
s = self.dumps(y, proto)
|
||||
s = self.dumps(o, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, type(y))
|
||||
self.assertIsInstance(x.attr, t)
|
||||
self.assertEqual(len(x.attr), 1)
|
||||
self.assertIsInstance(list(x.attr)[0], Object)
|
||||
self.assertIs(list(x.attr)[0], x)
|
||||
|
||||
# Collection containing a mutable object containing the original
|
||||
# collection.
|
||||
o = o.attr
|
||||
for proto in protocols:
|
||||
s = self.dumps(o, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, t)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIsInstance(list(x)[0], H)
|
||||
self.assertIsInstance(list(x)[0], Object)
|
||||
self.assertIs(list(x)[0].attr, x)
|
||||
|
||||
def test_recursive_list_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(list)
|
||||
self._test_recursive_collection_and_inst(list)
|
||||
|
||||
def test_recursive_tuple_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(tuple)
|
||||
self._test_recursive_collection_and_inst(tuple)
|
||||
|
||||
def test_recursive_dict_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(dict.fromkeys)
|
||||
self._test_recursive_collection_and_inst(dict.fromkeys)
|
||||
|
||||
def test_recursive_set_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(set)
|
||||
self._test_recursive_collection_and_inst(set)
|
||||
|
||||
def test_recursive_frozenset_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(frozenset)
|
||||
self._test_recursive_collection_and_inst(frozenset)
|
||||
|
||||
def test_recursive_list_subclass_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(MyList)
|
||||
self._test_recursive_collection_and_inst(MyList)
|
||||
|
||||
def test_recursive_tuple_subclass_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(MyTuple)
|
||||
self._test_recursive_collection_and_inst(MyTuple)
|
||||
|
||||
def test_recursive_dict_subclass_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(MyDict.fromkeys)
|
||||
self._test_recursive_collection_and_inst(MyDict.fromkeys)
|
||||
|
||||
def test_recursive_set_subclass_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(MySet)
|
||||
self._test_recursive_collection_and_inst(MySet)
|
||||
|
||||
def test_recursive_frozenset_subclass_and_inst(self):
|
||||
self.check_recursive_collection_and_inst(MyFrozenSet)
|
||||
self._test_recursive_collection_and_inst(MyFrozenSet)
|
||||
|
||||
def test_recursive_inst_state(self):
|
||||
# Mutable object containing itself.
|
||||
y = REX_state()
|
||||
y.state = y
|
||||
for proto in protocols:
|
||||
s = self.dumps(y, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, REX_state)
|
||||
self.assertIs(x.state, x)
|
||||
|
||||
def test_recursive_tuple_and_inst_state(self):
|
||||
# Tuple containing a mutable object containing the original tuple.
|
||||
t = (REX_state(),)
|
||||
t[0].state = t
|
||||
for proto in protocols:
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, tuple)
|
||||
self.assertEqual(len(x), 1)
|
||||
self.assertIsInstance(x[0], REX_state)
|
||||
self.assertIs(x[0].state, x)
|
||||
|
||||
# Mutable object containing a tuple containing the object.
|
||||
t, = t
|
||||
for proto in protocols:
|
||||
s = self.dumps(t, proto)
|
||||
x = self.loads(s)
|
||||
self.assertIsInstance(x, REX_state)
|
||||
self.assertIsInstance(x.state, tuple)
|
||||
self.assertEqual(len(x.state), 1)
|
||||
self.assertIs(x.state[0], x)
|
||||
|
||||
def test_unicode(self):
|
||||
endcases = ['', '<\\u>', '<\\\u1234>', '<\n>',
|
||||
|
@ -3062,6 +3215,19 @@ class REX_seven(object):
|
|||
def __reduce__(self):
|
||||
return type(self), (), None, None, iter(self.table.items())
|
||||
|
||||
class REX_state(object):
|
||||
"""This class is used to check the 3th argument (state) of
|
||||
the reduce protocol.
|
||||
"""
|
||||
def __init__(self, state=None):
|
||||
self.state = state
|
||||
def __eq__(self, other):
|
||||
return type(self) is type(other) and self.state == other.state
|
||||
def __setstate__(self, state):
|
||||
self.state = state
|
||||
def __reduce__(self):
|
||||
return type(self), (), self.state
|
||||
|
||||
|
||||
# Test classes for newobj
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""Tests for the asdl parser in Parser/asdl.py"""
|
||||
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import os
|
||||
from os.path import dirname
|
||||
import sys
|
||||
|
@ -26,7 +27,10 @@ class TestAsdlParser(unittest.TestCase):
|
|||
sys.path.insert(0, parser_dir)
|
||||
loader = importlib.machinery.SourceFileLoader(
|
||||
'asdl', os.path.join(parser_dir, 'asdl.py'))
|
||||
cls.asdl = loader.load_module()
|
||||
spec = importlib.util.spec_from_loader('asdl', loader)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
loader.exec_module(module)
|
||||
cls.asdl = module
|
||||
cls.mod = cls.asdl.parse(os.path.join(parser_dir, 'Python.asdl'))
|
||||
cls.assertTrue(cls.asdl.check(cls.mod), 'Module validation failed')
|
||||
|
||||
|
|
|
@ -1011,6 +1011,18 @@ Module(
|
|||
self.assertEqual(ast.literal_eval(" \t -1"), -1)
|
||||
self.assertRaises(IndentationError, ast.literal_eval, "\n -1")
|
||||
|
||||
def test_literal_eval_malformed_lineno(self):
|
||||
msg = r'malformed node or string on line 3:'
|
||||
with self.assertRaisesRegex(ValueError, msg):
|
||||
ast.literal_eval("{'a': 1,\n'b':2,\n'c':++3,\n'd':4}")
|
||||
|
||||
node = ast.UnaryOp(
|
||||
ast.UAdd(), ast.UnaryOp(ast.UAdd(), ast.Constant(6)))
|
||||
self.assertIsNone(getattr(node, 'lineno', None))
|
||||
msg = r'malformed node or string:'
|
||||
with self.assertRaisesRegex(ValueError, msg):
|
||||
ast.literal_eval(node)
|
||||
|
||||
def test_bad_integer(self):
|
||||
# issue13436: Bad error message with invalid numeric values
|
||||
body = [ast.ImportFrom(module='time',
|
||||
|
|
|
@ -1621,48 +1621,6 @@ class BuiltinTest(unittest.TestCase):
|
|||
self.assertEqual(self.iter_error(z1, ValueError), t)
|
||||
self.assertEqual(self.iter_error(z2, ValueError), t)
|
||||
|
||||
def test_zip_pickle_stability(self):
|
||||
# Pickles of zip((1, 2, 3), (4, 5, 6)) dumped from 3.9:
|
||||
pickles = [
|
||||
b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\nI6\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\n.',
|
||||
b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05K\x06tq\x05tq\x06Rq\x07K\x00btq\x08Rq\t.',
|
||||
b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.',
|
||||
b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05K\x06\x87q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t.',
|
||||
b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.',
|
||||
b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05K\x06\x87\x94\x85\x94R\x94K\x00b\x86\x94R\x94.',
|
||||
]
|
||||
for protocol, dump in enumerate(pickles):
|
||||
z1 = zip((1, 2, 3), (4, 5, 6))
|
||||
z2 = zip((1, 2, 3), (4, 5, 6), strict=False)
|
||||
z3 = pickle.loads(dump)
|
||||
l3 = list(z3)
|
||||
self.assertEqual(type(z3), zip)
|
||||
self.assertEqual(pickle.dumps(z1, protocol), dump)
|
||||
self.assertEqual(pickle.dumps(z2, protocol), dump)
|
||||
self.assertEqual(list(z1), l3)
|
||||
self.assertEqual(list(z2), l3)
|
||||
|
||||
def test_zip_pickle_strict_stability(self):
|
||||
# Pickles of zip((1, 2, 3), (4, 5), strict=True) dumped from 3.10:
|
||||
pickles = [
|
||||
b'citertools\nizip\np0\n(c__builtin__\niter\np1\n((I1\nI2\nI3\ntp2\ntp3\nRp4\nI0\nbg1\n((I4\nI5\ntp5\ntp6\nRp7\nI0\nbtp8\nRp9\nI01\nb.',
|
||||
b'citertools\nizip\nq\x00(c__builtin__\niter\nq\x01((K\x01K\x02K\x03tq\x02tq\x03Rq\x04K\x00bh\x01((K\x04K\x05tq\x05tq\x06Rq\x07K\x00btq\x08Rq\tI01\nb.',
|
||||
b'\x80\x02citertools\nizip\nq\x00c__builtin__\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.',
|
||||
b'\x80\x03cbuiltins\nzip\nq\x00cbuiltins\niter\nq\x01K\x01K\x02K\x03\x87q\x02\x85q\x03Rq\x04K\x00bh\x01K\x04K\x05\x86q\x05\x85q\x06Rq\x07K\x00b\x86q\x08Rq\t\x88b.',
|
||||
b'\x80\x04\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.',
|
||||
b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x03zip\x94\x93\x94\x8c\x08builtins\x94\x8c\x04iter\x94\x93\x94K\x01K\x02K\x03\x87\x94\x85\x94R\x94K\x00bh\x05K\x04K\x05\x86\x94\x85\x94R\x94K\x00b\x86\x94R\x94\x88b.',
|
||||
]
|
||||
a = (1, 2, 3)
|
||||
b = (4, 5)
|
||||
t = [(1, 4), (2, 5)]
|
||||
for protocol, dump in enumerate(pickles):
|
||||
z1 = zip(a, b, strict=True)
|
||||
z2 = pickle.loads(dump)
|
||||
self.assertEqual(pickle.dumps(z1, protocol), dump)
|
||||
self.assertEqual(type(z2), zip)
|
||||
self.assertEqual(self.iter_error(z1, ValueError), t)
|
||||
self.assertEqual(self.iter_error(z2, ValueError), t)
|
||||
|
||||
def test_zip_bad_iterable(self):
|
||||
exception = TypeError()
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
# This script doesn't actually display anything very coherent. but it
|
||||
# does call (nearly) every method and function.
|
||||
#
|
||||
# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
|
||||
# init_color()
|
||||
# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr()
|
||||
# Only called, not tested: getmouse(), ungetmouse()
|
||||
#
|
||||
|
||||
|
@ -13,6 +12,7 @@ import os
|
|||
import string
|
||||
import sys
|
||||
import tempfile
|
||||
import functools
|
||||
import unittest
|
||||
|
||||
from test.support import requires, verbose, SaveSignals
|
||||
|
@ -37,7 +37,17 @@ def requires_curses_func(name):
|
|||
return unittest.skipUnless(hasattr(curses, name),
|
||||
'requires curses.%s' % name)
|
||||
|
||||
def requires_colors(test):
|
||||
@functools.wraps(test)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
if not curses.has_colors():
|
||||
self.skipTest('requires colors support')
|
||||
curses.start_color()
|
||||
test(self, *args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
term = os.environ.get('TERM')
|
||||
SHORT_MAX = 0x7fff
|
||||
|
||||
# If newterm was supported we could use it instead of initscr and not exit
|
||||
@unittest.skipIf(not term or term == 'unknown',
|
||||
|
@ -48,37 +58,59 @@ class TestCurses(unittest.TestCase):
|
|||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
if not sys.__stdout__.isatty():
|
||||
# Temporary skip tests on non-tty
|
||||
raise unittest.SkipTest('sys.__stdout__ is not a tty')
|
||||
cls.tmp = tempfile.TemporaryFile()
|
||||
fd = cls.tmp.fileno()
|
||||
else:
|
||||
cls.tmp = None
|
||||
fd = sys.__stdout__.fileno()
|
||||
if verbose:
|
||||
print(f'TERM={term}', file=sys.stderr, flush=True)
|
||||
# testing setupterm() inside initscr/endwin
|
||||
# causes terminal breakage
|
||||
curses.setupterm(fd=fd)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if cls.tmp:
|
||||
cls.tmp.close()
|
||||
del cls.tmp
|
||||
stdout_fd = sys.__stdout__.fileno()
|
||||
curses.setupterm(fd=stdout_fd)
|
||||
|
||||
def setUp(self):
|
||||
self.isatty = True
|
||||
self.output = sys.__stdout__
|
||||
stdout_fd = sys.__stdout__.fileno()
|
||||
if not sys.__stdout__.isatty():
|
||||
# initstr() unconditionally uses C stdout.
|
||||
# If it is redirected to file or pipe, try to attach it
|
||||
# to terminal.
|
||||
# First, save a copy of the file descriptor of stdout, so it
|
||||
# can be restored after finishing the test.
|
||||
dup_fd = os.dup(stdout_fd)
|
||||
self.addCleanup(os.close, dup_fd)
|
||||
self.addCleanup(os.dup2, dup_fd, stdout_fd)
|
||||
|
||||
if sys.__stderr__.isatty():
|
||||
# If stderr is connected to terminal, use it.
|
||||
tmp = sys.__stderr__
|
||||
self.output = sys.__stderr__
|
||||
else:
|
||||
try:
|
||||
# Try to open the terminal device.
|
||||
tmp = open('/dev/tty', 'wb', buffering=0)
|
||||
except OSError:
|
||||
# As a fallback, use regular file to write control codes.
|
||||
# Some functions (like savetty) will not work, but at
|
||||
# least the garbage control sequences will not be mixed
|
||||
# with the testing report.
|
||||
tmp = tempfile.TemporaryFile(mode='wb', buffering=0)
|
||||
self.isatty = False
|
||||
self.addCleanup(tmp.close)
|
||||
self.output = None
|
||||
os.dup2(tmp.fileno(), stdout_fd)
|
||||
|
||||
self.save_signals = SaveSignals()
|
||||
self.save_signals.save()
|
||||
if verbose:
|
||||
self.addCleanup(self.save_signals.restore)
|
||||
if verbose and self.output is not None:
|
||||
# just to make the test output a little more readable
|
||||
print()
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
print(file=self.output, flush=True)
|
||||
self.stdscr = curses.initscr()
|
||||
curses.savetty()
|
||||
|
||||
def tearDown(self):
|
||||
curses.resetty()
|
||||
curses.endwin()
|
||||
self.save_signals.restore()
|
||||
if self.isatty:
|
||||
curses.savetty()
|
||||
self.addCleanup(curses.endwin)
|
||||
self.addCleanup(curses.resetty)
|
||||
|
||||
def test_window_funcs(self):
|
||||
"Test the methods of windows"
|
||||
|
@ -96,7 +128,7 @@ class TestCurses(unittest.TestCase):
|
|||
for meth in [stdscr.clear, stdscr.clrtobot,
|
||||
stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
|
||||
stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
|
||||
stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
|
||||
stdscr.getbkgd, stdscr.getmaxyx,
|
||||
stdscr.getparyx, stdscr.getyx, stdscr.inch,
|
||||
stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
|
||||
win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
|
||||
|
@ -207,6 +239,11 @@ class TestCurses(unittest.TestCase):
|
|||
if hasattr(stdscr, 'enclose'):
|
||||
stdscr.enclose(10, 10)
|
||||
|
||||
with tempfile.TemporaryFile() as f:
|
||||
self.stdscr.putwin(f)
|
||||
f.seek(0)
|
||||
curses.getwin(f)
|
||||
|
||||
self.assertRaises(ValueError, stdscr.getstr, -400)
|
||||
self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
|
||||
self.assertRaises(ValueError, stdscr.instr, -2)
|
||||
|
@ -225,17 +262,20 @@ class TestCurses(unittest.TestCase):
|
|||
def test_module_funcs(self):
|
||||
"Test module-level functions"
|
||||
for func in [curses.baudrate, curses.beep, curses.can_change_color,
|
||||
curses.cbreak, curses.def_prog_mode, curses.doupdate,
|
||||
curses.flash, curses.flushinp,
|
||||
curses.doupdate, curses.flash, curses.flushinp,
|
||||
curses.has_colors, curses.has_ic, curses.has_il,
|
||||
curses.isendwin, curses.killchar, curses.longname,
|
||||
curses.nocbreak, curses.noecho, curses.nonl,
|
||||
curses.noqiflush, curses.noraw,
|
||||
curses.reset_prog_mode, curses.termattrs,
|
||||
curses.termname, curses.erasechar,
|
||||
curses.noecho, curses.nonl, curses.noqiflush,
|
||||
curses.termattrs, curses.termname, curses.erasechar,
|
||||
curses.has_extended_color_support]:
|
||||
with self.subTest(func=func.__qualname__):
|
||||
func()
|
||||
if self.isatty:
|
||||
for func in [curses.cbreak, curses.def_prog_mode,
|
||||
curses.nocbreak, curses.noraw,
|
||||
curses.reset_prog_mode]:
|
||||
with self.subTest(func=func.__qualname__):
|
||||
func()
|
||||
if hasattr(curses, 'filter'):
|
||||
curses.filter()
|
||||
if hasattr(curses, 'getsyx'):
|
||||
|
@ -247,13 +287,9 @@ class TestCurses(unittest.TestCase):
|
|||
curses.delay_output(1)
|
||||
curses.echo() ; curses.echo(1)
|
||||
|
||||
with tempfile.TemporaryFile() as f:
|
||||
self.stdscr.putwin(f)
|
||||
f.seek(0)
|
||||
curses.getwin(f)
|
||||
|
||||
curses.halfdelay(1)
|
||||
curses.intrflush(1)
|
||||
if self.isatty:
|
||||
curses.intrflush(1)
|
||||
curses.meta(1)
|
||||
curses.napms(100)
|
||||
curses.newpad(50,50)
|
||||
|
@ -262,7 +298,8 @@ class TestCurses(unittest.TestCase):
|
|||
curses.nl() ; curses.nl(1)
|
||||
curses.putp(b'abc')
|
||||
curses.qiflush()
|
||||
curses.raw() ; curses.raw(1)
|
||||
if self.isatty:
|
||||
curses.raw() ; curses.raw(1)
|
||||
curses.set_escdelay(25)
|
||||
self.assertEqual(curses.get_escdelay(), 25)
|
||||
curses.set_tabsize(4)
|
||||
|
@ -281,31 +318,127 @@ class TestCurses(unittest.TestCase):
|
|||
curses.use_env(1)
|
||||
|
||||
# Functions only available on a few platforms
|
||||
def test_colors_funcs(self):
|
||||
|
||||
def bad_colors(self):
|
||||
return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
||||
|
||||
def bad_colors2(self):
|
||||
return (curses.COLORS, 2**31, 2**63, 2**64)
|
||||
|
||||
def bad_pairs(self):
|
||||
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
||||
|
||||
def test_start_color(self):
|
||||
if not curses.has_colors():
|
||||
self.skipTest('requires colors support')
|
||||
curses.start_color()
|
||||
curses.init_pair(2, 1,1)
|
||||
curses.color_content(1)
|
||||
curses.color_pair(2)
|
||||
curses.pair_content(curses.COLOR_PAIRS - 1)
|
||||
curses.pair_number(0)
|
||||
if verbose:
|
||||
print(f'COLORS = {curses.COLORS}', file=sys.stderr)
|
||||
print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
|
||||
|
||||
if hasattr(curses, 'use_default_colors'):
|
||||
curses.use_default_colors()
|
||||
@requires_colors
|
||||
def test_color_content(self):
|
||||
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
||||
curses.color_content(0)
|
||||
maxcolor = curses.COLORS - 1
|
||||
curses.color_content(maxcolor)
|
||||
|
||||
self.assertRaises(ValueError, curses.color_content, -1)
|
||||
self.assertRaises(ValueError, curses.color_content, curses.COLORS + 1)
|
||||
self.assertRaises(ValueError, curses.color_content, -2**31 - 1)
|
||||
self.assertRaises(ValueError, curses.color_content, 2**31)
|
||||
self.assertRaises(ValueError, curses.color_content, -2**63 - 1)
|
||||
self.assertRaises(ValueError, curses.color_content, 2**63 - 1)
|
||||
self.assertRaises(ValueError, curses.pair_content, -1)
|
||||
self.assertRaises(ValueError, curses.pair_content, curses.COLOR_PAIRS)
|
||||
self.assertRaises(ValueError, curses.pair_content, -2**31 - 1)
|
||||
self.assertRaises(ValueError, curses.pair_content, 2**31)
|
||||
self.assertRaises(ValueError, curses.pair_content, -2**63 - 1)
|
||||
self.assertRaises(ValueError, curses.pair_content, 2**63 - 1)
|
||||
for color in self.bad_colors():
|
||||
self.assertRaises(ValueError, curses.color_content, color)
|
||||
|
||||
@requires_colors
|
||||
def test_init_color(self):
|
||||
if not curses.can_change_color:
|
||||
self.skipTest('cannot change color')
|
||||
|
||||
old = curses.color_content(0)
|
||||
try:
|
||||
curses.init_color(0, *old)
|
||||
except curses.error:
|
||||
self.skipTest('cannot change color (init_color() failed)')
|
||||
self.addCleanup(curses.init_color, 0, *old)
|
||||
curses.init_color(0, 0, 0, 0)
|
||||
self.assertEqual(curses.color_content(0), (0, 0, 0))
|
||||
curses.init_color(0, 1000, 1000, 1000)
|
||||
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
||||
|
||||
maxcolor = curses.COLORS - 1
|
||||
old = curses.color_content(maxcolor)
|
||||
curses.init_color(maxcolor, *old)
|
||||
self.addCleanup(curses.init_color, maxcolor, *old)
|
||||
curses.init_color(maxcolor, 0, 500, 1000)
|
||||
self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
|
||||
|
||||
for color in self.bad_colors():
|
||||
self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
|
||||
for comp in (-1, 1001):
|
||||
self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
|
||||
self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
|
||||
self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
|
||||
|
||||
def get_pair_limit(self):
|
||||
pair_limit = curses.COLOR_PAIRS
|
||||
if hasattr(curses, 'ncurses_version'):
|
||||
if curses.has_extended_color_support():
|
||||
pair_limit += 2*curses.COLORS + 1
|
||||
if (not curses.has_extended_color_support()
|
||||
or (6, 1) <= curses.ncurses_version < (6, 2)):
|
||||
pair_limit = min(pair_limit, SHORT_MAX)
|
||||
return pair_limit
|
||||
|
||||
@requires_colors
|
||||
def test_pair_content(self):
|
||||
if not hasattr(curses, 'use_default_colors'):
|
||||
self.assertEqual(curses.pair_content(0),
|
||||
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
||||
curses.pair_content(0)
|
||||
maxpair = self.get_pair_limit() - 1
|
||||
if maxpair > 0:
|
||||
curses.pair_content(maxpair)
|
||||
|
||||
for pair in self.bad_pairs():
|
||||
self.assertRaises(ValueError, curses.pair_content, pair)
|
||||
|
||||
@requires_colors
|
||||
def test_init_pair(self):
|
||||
old = curses.pair_content(1)
|
||||
curses.init_pair(1, *old)
|
||||
self.addCleanup(curses.init_pair, 1, *old)
|
||||
|
||||
curses.init_pair(1, 0, 0)
|
||||
self.assertEqual(curses.pair_content(1), (0, 0))
|
||||
maxcolor = curses.COLORS - 1
|
||||
curses.init_pair(1, maxcolor, 0)
|
||||
self.assertEqual(curses.pair_content(1), (maxcolor, 0))
|
||||
curses.init_pair(1, 0, maxcolor)
|
||||
self.assertEqual(curses.pair_content(1), (0, maxcolor))
|
||||
maxpair = self.get_pair_limit() - 1
|
||||
if maxpair > 1:
|
||||
curses.init_pair(maxpair, 0, 0)
|
||||
self.assertEqual(curses.pair_content(maxpair), (0, 0))
|
||||
|
||||
for pair in self.bad_pairs():
|
||||
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
||||
for color in self.bad_colors2():
|
||||
self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
|
||||
self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
|
||||
|
||||
@requires_colors
|
||||
def test_color_attrs(self):
|
||||
for pair in 0, 1, 255:
|
||||
attr = curses.color_pair(pair)
|
||||
self.assertEqual(curses.pair_number(attr), pair, attr)
|
||||
self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
|
||||
self.assertEqual(curses.color_pair(0), 0)
|
||||
self.assertEqual(curses.pair_number(0), 0)
|
||||
|
||||
@requires_curses_func('use_default_colors')
|
||||
@requires_colors
|
||||
def test_use_default_colors(self):
|
||||
self.assertIn(curses.pair_content(0),
|
||||
((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
|
||||
curses.use_default_colors()
|
||||
self.assertEqual(curses.pair_content(0), (-1, -1))
|
||||
|
||||
@requires_curses_func('keyname')
|
||||
def test_keyname(self):
|
||||
|
@ -373,7 +506,6 @@ class TestCurses(unittest.TestCase):
|
|||
|
||||
@requires_curses_func('resizeterm')
|
||||
def test_resizeterm(self):
|
||||
stdscr = self.stdscr
|
||||
lines, cols = curses.LINES, curses.COLS
|
||||
new_lines = lines - 1
|
||||
new_cols = cols + 1
|
||||
|
@ -477,6 +609,8 @@ class MiscTests(unittest.TestCase):
|
|||
@requires_curses_func('ncurses_version')
|
||||
def test_ncurses_version(self):
|
||||
v = curses.ncurses_version
|
||||
if verbose:
|
||||
print(f'ncurses_version = {curses.ncurses_version}', flush=True)
|
||||
self.assertIsInstance(v[:], tuple)
|
||||
self.assertEqual(len(v), 3)
|
||||
self.assertIsInstance(v[0], int)
|
||||
|
|
|
@ -2119,7 +2119,7 @@ class TestEnum(unittest.TestCase):
|
|||
one = '1'
|
||||
two = b'2', 'ascii', 9
|
||||
|
||||
def test_init_subclass(self):
|
||||
def test_init_subclass_calling(self):
|
||||
class MyEnum(Enum):
|
||||
def __init_subclass__(cls, **kwds):
|
||||
super(MyEnum, cls).__init_subclass__(**kwds)
|
||||
|
@ -2155,6 +2155,16 @@ class TestEnum(unittest.TestCase):
|
|||
self.assertFalse(NeverEnum.__dict__.get('_test1', False))
|
||||
self.assertFalse(NeverEnum.__dict__.get('_test2', False))
|
||||
|
||||
def test_init_subclass_parameter(self):
|
||||
class multiEnum(Enum):
|
||||
def __init_subclass__(cls, multi):
|
||||
for member in cls:
|
||||
member._as_parameter_ = multi * member.value
|
||||
class E(multiEnum, multi=3):
|
||||
A = 1
|
||||
B = 2
|
||||
self.assertEqual(E.A._as_parameter_, 3)
|
||||
self.assertEqual(E.B._as_parameter_, 6)
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.version_info[:2] == (3, 9),
|
||||
|
|
|
@ -332,6 +332,59 @@ non-important content
|
|||
self.assertEqual(binop.left.col_offset, 4)
|
||||
self.assertEqual(binop.right.col_offset, 7)
|
||||
|
||||
def test_ast_line_numbers_with_parentheses(self):
|
||||
expr = """
|
||||
x = (
|
||||
f" {test(t)}"
|
||||
)"""
|
||||
t = ast.parse(expr)
|
||||
self.assertEqual(type(t), ast.Module)
|
||||
self.assertEqual(len(t.body), 1)
|
||||
# check the test(t) location
|
||||
call = t.body[0].value.values[1].value
|
||||
self.assertEqual(type(call), ast.Call)
|
||||
self.assertEqual(call.lineno, 3)
|
||||
self.assertEqual(call.end_lineno, 3)
|
||||
self.assertEqual(call.col_offset, 8)
|
||||
self.assertEqual(call.end_col_offset, 15)
|
||||
|
||||
expr = """
|
||||
x = (
|
||||
'PERL_MM_OPT', (
|
||||
f'wat'
|
||||
f'some_string={f(x)} '
|
||||
f'wat'
|
||||
),
|
||||
)
|
||||
"""
|
||||
t = ast.parse(expr)
|
||||
self.assertEqual(type(t), ast.Module)
|
||||
self.assertEqual(len(t.body), 1)
|
||||
# check the fstring
|
||||
fstring = t.body[0].value.elts[1]
|
||||
self.assertEqual(type(fstring), ast.JoinedStr)
|
||||
self.assertEqual(len(fstring.values), 3)
|
||||
wat1, middle, wat2 = fstring.values
|
||||
# check the first wat
|
||||
self.assertEqual(type(wat1), ast.Constant)
|
||||
self.assertEqual(wat1.lineno, 4)
|
||||
self.assertEqual(wat1.end_lineno, 6)
|
||||
self.assertEqual(wat1.col_offset, 12)
|
||||
self.assertEqual(wat1.end_col_offset, 18)
|
||||
# check the call
|
||||
call = middle.value
|
||||
self.assertEqual(type(call), ast.Call)
|
||||
self.assertEqual(call.lineno, 5)
|
||||
self.assertEqual(call.end_lineno, 5)
|
||||
self.assertEqual(call.col_offset, 27)
|
||||
self.assertEqual(call.end_col_offset, 31)
|
||||
# check the second wat
|
||||
self.assertEqual(type(wat2), ast.Constant)
|
||||
self.assertEqual(wat2.lineno, 4)
|
||||
self.assertEqual(wat2.end_lineno, 6)
|
||||
self.assertEqual(wat2.col_offset, 12)
|
||||
self.assertEqual(wat2.end_col_offset, 18)
|
||||
|
||||
def test_docstring(self):
|
||||
def f():
|
||||
f'''Not a docstring'''
|
||||
|
|
|
@ -27,8 +27,7 @@ import functools
|
|||
|
||||
py_functools = import_helper.import_fresh_module('functools',
|
||||
blocked=['_functools'])
|
||||
c_functools = import_helper.import_fresh_module('functools',
|
||||
fresh=['_functools'])
|
||||
c_functools = import_helper.import_fresh_module('functools')
|
||||
|
||||
decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal'])
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import textwrap
|
|||
import contextlib
|
||||
|
||||
from test.support.os_helper import FS_NONASCII
|
||||
from typing import Dict, Union
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
@ -71,8 +72,13 @@ class OnSysPath(Fixtures):
|
|||
self.fixtures.enter_context(self.add_sys_path(self.site_dir))
|
||||
|
||||
|
||||
# Except for python/mypy#731, prefer to define
|
||||
# FilesDef = Dict[str, Union['FilesDef', str]]
|
||||
FilesDef = Dict[str, Union[Dict[str, Union[Dict[str, str], str]], str]]
|
||||
|
||||
|
||||
class DistInfoPkg(OnSysPath, SiteDir):
|
||||
files = {
|
||||
files: FilesDef = {
|
||||
"distinfo_pkg-1.0.0.dist-info": {
|
||||
"METADATA": """
|
||||
Name: distinfo-pkg
|
||||
|
@ -86,19 +92,55 @@ class DistInfoPkg(OnSysPath, SiteDir):
|
|||
[entries]
|
||||
main = mod:main
|
||||
ns:sub = mod:main
|
||||
"""
|
||||
},
|
||||
""",
|
||||
},
|
||||
"mod.py": """
|
||||
def main():
|
||||
print("hello world")
|
||||
""",
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(DistInfoPkg, self).setUp()
|
||||
build_files(DistInfoPkg.files, self.site_dir)
|
||||
|
||||
|
||||
class DistInfoPkgWithDot(OnSysPath, SiteDir):
|
||||
files: FilesDef = {
|
||||
"pkg_dot-1.0.0.dist-info": {
|
||||
"METADATA": """
|
||||
Name: pkg.dot
|
||||
Version: 1.0.0
|
||||
""",
|
||||
},
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(DistInfoPkgWithDot, self).setUp()
|
||||
build_files(DistInfoPkgWithDot.files, self.site_dir)
|
||||
|
||||
|
||||
class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir):
|
||||
files: FilesDef = {
|
||||
"pkg.dot-1.0.0.dist-info": {
|
||||
"METADATA": """
|
||||
Name: pkg.dot
|
||||
Version: 1.0.0
|
||||
""",
|
||||
},
|
||||
"pkg.lot.egg-info": {
|
||||
"METADATA": """
|
||||
Name: pkg.lot
|
||||
Version: 1.0.0
|
||||
""",
|
||||
},
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(DistInfoPkgWithDotLegacy, self).setUp()
|
||||
build_files(DistInfoPkgWithDotLegacy.files, self.site_dir)
|
||||
|
||||
|
||||
class DistInfoPkgOffPath(SiteDir):
|
||||
def setUp(self):
|
||||
super(DistInfoPkgOffPath, self).setUp()
|
||||
|
@ -106,7 +148,7 @@ class DistInfoPkgOffPath(SiteDir):
|
|||
|
||||
|
||||
class EggInfoPkg(OnSysPath, SiteDir):
|
||||
files = {
|
||||
files: FilesDef = {
|
||||
"egginfo_pkg.egg-info": {
|
||||
"PKG-INFO": """
|
||||
Name: egginfo-pkg
|
||||
|
@ -129,13 +171,13 @@ class EggInfoPkg(OnSysPath, SiteDir):
|
|||
[test]
|
||||
pytest
|
||||
""",
|
||||
"top_level.txt": "mod\n"
|
||||
},
|
||||
"top_level.txt": "mod\n",
|
||||
},
|
||||
"mod.py": """
|
||||
def main():
|
||||
print("hello world")
|
||||
""",
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(EggInfoPkg, self).setUp()
|
||||
|
@ -143,7 +185,7 @@ class EggInfoPkg(OnSysPath, SiteDir):
|
|||
|
||||
|
||||
class EggInfoFile(OnSysPath, SiteDir):
|
||||
files = {
|
||||
files: FilesDef = {
|
||||
"egginfo_file.egg-info": """
|
||||
Metadata-Version: 1.0
|
||||
Name: egginfo_file
|
||||
|
@ -156,7 +198,7 @@ class EggInfoFile(OnSysPath, SiteDir):
|
|||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
""",
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(EggInfoFile, self).setUp()
|
||||
|
@ -164,12 +206,12 @@ class EggInfoFile(OnSysPath, SiteDir):
|
|||
|
||||
|
||||
class LocalPackage:
|
||||
files = {
|
||||
files: FilesDef = {
|
||||
"setup.py": """
|
||||
import setuptools
|
||||
setuptools.setup(name="local-pkg", version="2.0.1")
|
||||
""",
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.fixtures = contextlib.ExitStack()
|
||||
|
@ -214,8 +256,7 @@ def build_files(file_defs, prefix=pathlib.Path()):
|
|||
|
||||
class FileBuilder:
|
||||
def unicode_filename(self):
|
||||
return FS_NONASCII or \
|
||||
self.skip("File system does not support non-ascii.")
|
||||
return FS_NONASCII or self.skip("File system does not support non-ascii.")
|
||||
|
||||
|
||||
def DALS(str):
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# coding: utf-8
|
||||
|
||||
import re
|
||||
import json
|
||||
import pickle
|
||||
|
@ -14,10 +12,14 @@ except ImportError:
|
|||
|
||||
from . import fixtures
|
||||
from importlib.metadata import (
|
||||
Distribution, EntryPoint,
|
||||
PackageNotFoundError, distributions,
|
||||
entry_points, metadata, version,
|
||||
)
|
||||
Distribution,
|
||||
EntryPoint,
|
||||
PackageNotFoundError,
|
||||
distributions,
|
||||
entry_points,
|
||||
metadata,
|
||||
version,
|
||||
)
|
||||
|
||||
|
||||
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
|
||||
|
@ -70,12 +72,11 @@ class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):
|
|||
name='ep',
|
||||
value='importlib.metadata',
|
||||
group='grp',
|
||||
)
|
||||
)
|
||||
assert ep.load() is importlib.metadata
|
||||
|
||||
|
||||
class NameNormalizationTests(
|
||||
fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
||||
class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
||||
@staticmethod
|
||||
def pkg_with_dashes(site_dir):
|
||||
"""
|
||||
|
@ -144,11 +145,15 @@ class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
|||
metadata_dir.mkdir()
|
||||
metadata = metadata_dir / 'METADATA'
|
||||
with metadata.open('w', encoding='utf-8') as fp:
|
||||
fp.write(textwrap.dedent("""
|
||||
fp.write(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Name: portend
|
||||
|
||||
pôrˈtend
|
||||
""").lstrip())
|
||||
"""
|
||||
).lstrip()
|
||||
)
|
||||
return 'portend'
|
||||
|
||||
def test_metadata_loads(self):
|
||||
|
@ -162,24 +167,12 @@ class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
|||
assert meta.get_payload() == 'pôrˈtend\n'
|
||||
|
||||
|
||||
class DiscoveryTests(fixtures.EggInfoPkg,
|
||||
fixtures.DistInfoPkg,
|
||||
unittest.TestCase):
|
||||
|
||||
class DiscoveryTests(fixtures.EggInfoPkg, fixtures.DistInfoPkg, unittest.TestCase):
|
||||
def test_package_discovery(self):
|
||||
dists = list(distributions())
|
||||
assert all(
|
||||
isinstance(dist, Distribution)
|
||||
for dist in dists
|
||||
)
|
||||
assert any(
|
||||
dist.metadata['Name'] == 'egginfo-pkg'
|
||||
for dist in dists
|
||||
)
|
||||
assert any(
|
||||
dist.metadata['Name'] == 'distinfo-pkg'
|
||||
for dist in dists
|
||||
)
|
||||
assert all(isinstance(dist, Distribution) for dist in dists)
|
||||
assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists)
|
||||
assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
|
||||
|
||||
def test_invalid_usage(self):
|
||||
with self.assertRaises(ValueError):
|
||||
|
@ -265,10 +258,21 @@ class TestEntryPoints(unittest.TestCase):
|
|||
def test_attr(self):
|
||||
assert self.ep.attr is None
|
||||
|
||||
def test_sortable(self):
|
||||
"""
|
||||
EntryPoint objects are sortable, but result is undefined.
|
||||
"""
|
||||
sorted(
|
||||
[
|
||||
EntryPoint('b', 'val', 'group'),
|
||||
EntryPoint('a', 'val', 'group'),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class FileSystem(
|
||||
fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder,
|
||||
unittest.TestCase):
|
||||
fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase
|
||||
):
|
||||
def test_unicode_dir_on_sys_path(self):
|
||||
"""
|
||||
Ensure a Unicode subdirectory of a directory on sys.path
|
||||
|
@ -277,5 +281,5 @@ class FileSystem(
|
|||
fixtures.build_files(
|
||||
{self.unicode_filename(): {}},
|
||||
prefix=self.site_dir,
|
||||
)
|
||||
)
|
||||
list(distributions())
|
||||
|
|
|
@ -2,20 +2,26 @@ import re
|
|||
import textwrap
|
||||
import unittest
|
||||
|
||||
from collections.abc import Iterator
|
||||
|
||||
from . import fixtures
|
||||
from importlib.metadata import (
|
||||
Distribution, PackageNotFoundError, distribution,
|
||||
entry_points, files, metadata, requires, version,
|
||||
)
|
||||
Distribution,
|
||||
PackageNotFoundError,
|
||||
distribution,
|
||||
entry_points,
|
||||
files,
|
||||
metadata,
|
||||
requires,
|
||||
version,
|
||||
)
|
||||
|
||||
|
||||
class APITests(
|
||||
fixtures.EggInfoPkg,
|
||||
fixtures.DistInfoPkg,
|
||||
fixtures.EggInfoFile,
|
||||
unittest.TestCase):
|
||||
fixtures.EggInfoPkg,
|
||||
fixtures.DistInfoPkg,
|
||||
fixtures.DistInfoPkgWithDot,
|
||||
fixtures.EggInfoFile,
|
||||
unittest.TestCase,
|
||||
):
|
||||
|
||||
version_pattern = r'\d+\.\d+(\.\d)?'
|
||||
|
||||
|
@ -33,16 +39,28 @@ class APITests(
|
|||
with self.assertRaises(PackageNotFoundError):
|
||||
distribution('does-not-exist')
|
||||
|
||||
def test_name_normalization(self):
|
||||
names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot'
|
||||
for name in names:
|
||||
with self.subTest(name):
|
||||
assert distribution(name).metadata['Name'] == 'pkg.dot'
|
||||
|
||||
def test_prefix_not_matched(self):
|
||||
prefixes = 'p', 'pkg', 'pkg.'
|
||||
for prefix in prefixes:
|
||||
with self.subTest(prefix):
|
||||
with self.assertRaises(PackageNotFoundError):
|
||||
distribution(prefix)
|
||||
|
||||
def test_for_top_level(self):
|
||||
self.assertEqual(
|
||||
distribution('egginfo-pkg').read_text('top_level.txt').strip(),
|
||||
'mod')
|
||||
distribution('egginfo-pkg').read_text('top_level.txt').strip(), 'mod'
|
||||
)
|
||||
|
||||
def test_read_text(self):
|
||||
top_level = [
|
||||
path for path in files('egginfo-pkg')
|
||||
if path.name == 'top_level.txt'
|
||||
][0]
|
||||
path for path in files('egginfo-pkg') if path.name == 'top_level.txt'
|
||||
][0]
|
||||
self.assertEqual(top_level.read_text(), 'mod\n')
|
||||
|
||||
def test_entry_points(self):
|
||||
|
@ -51,6 +69,13 @@ class APITests(
|
|||
self.assertEqual(ep.value, 'mod:main')
|
||||
self.assertEqual(ep.extras, [])
|
||||
|
||||
def test_entry_points_distribution(self):
|
||||
entries = dict(entry_points()['entries'])
|
||||
for entry in ("main", "ns:sub"):
|
||||
ep = entries[entry]
|
||||
self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg'))
|
||||
self.assertEqual(ep.dist.version, "1.0.0")
|
||||
|
||||
def test_metadata_for_this_package(self):
|
||||
md = metadata('egginfo-pkg')
|
||||
assert md['author'] == 'Steven Ma'
|
||||
|
@ -75,13 +100,8 @@ class APITests(
|
|||
def test_file_hash_repr(self):
|
||||
assertRegex = self.assertRegex
|
||||
|
||||
util = [
|
||||
p for p in files('distinfo-pkg')
|
||||
if p.name == 'mod.py'
|
||||
][0]
|
||||
assertRegex(
|
||||
repr(util.hash),
|
||||
'<FileHash mode: sha256 value: .*>')
|
||||
util = [p for p in files('distinfo-pkg') if p.name == 'mod.py'][0]
|
||||
assertRegex(repr(util.hash), '<FileHash mode: sha256 value: .*>')
|
||||
|
||||
def test_files_dist_info(self):
|
||||
self._test_files(files('distinfo-pkg'))
|
||||
|
@ -99,10 +119,7 @@ class APITests(
|
|||
def test_requires_egg_info(self):
|
||||
deps = requires('egginfo-pkg')
|
||||
assert len(deps) == 2
|
||||
assert any(
|
||||
dep == 'wheel >= 1.0; python_version >= "2.7"'
|
||||
for dep in deps
|
||||
)
|
||||
assert any(dep == 'wheel >= 1.0; python_version >= "2.7"' for dep in deps)
|
||||
|
||||
def test_requires_dist_info(self):
|
||||
deps = requires('distinfo-pkg')
|
||||
|
@ -112,7 +129,8 @@ class APITests(
|
|||
assert "pytest; extra == 'test'" in deps
|
||||
|
||||
def test_more_complex_deps_requires_text(self):
|
||||
requires = textwrap.dedent("""
|
||||
requires = textwrap.dedent(
|
||||
"""
|
||||
dep1
|
||||
dep2
|
||||
|
||||
|
@ -124,7 +142,8 @@ class APITests(
|
|||
|
||||
[extra2:python_version < "3"]
|
||||
dep5
|
||||
""")
|
||||
"""
|
||||
)
|
||||
deps = sorted(Distribution._deps_from_requires_text(requires))
|
||||
expected = [
|
||||
'dep1',
|
||||
|
@ -132,7 +151,7 @@ class APITests(
|
|||
'dep3; python_version < "3"',
|
||||
'dep4; extra == "extra1"',
|
||||
'dep5; (python_version < "3") and extra == "extra2"',
|
||||
]
|
||||
]
|
||||
# It's important that the environment marker expression be
|
||||
# wrapped in parentheses to avoid the following 'and' binding more
|
||||
# tightly than some other part of the environment expression.
|
||||
|
@ -140,17 +159,27 @@ class APITests(
|
|||
assert deps == expected
|
||||
|
||||
|
||||
class LegacyDots(fixtures.DistInfoPkgWithDotLegacy, unittest.TestCase):
|
||||
def test_name_normalization(self):
|
||||
names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot'
|
||||
for name in names:
|
||||
with self.subTest(name):
|
||||
assert distribution(name).metadata['Name'] == 'pkg.dot'
|
||||
|
||||
def test_name_normalization_versionless_egg_info(self):
|
||||
names = 'pkg.lot', 'pkg_lot', 'pkg-lot', 'pkg..lot', 'Pkg.Lot'
|
||||
for name in names:
|
||||
with self.subTest(name):
|
||||
assert distribution(name).metadata['Name'] == 'pkg.lot'
|
||||
|
||||
|
||||
class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
|
||||
def test_find_distributions_specified_path(self):
|
||||
dists = Distribution.discover(path=[str(self.site_dir)])
|
||||
assert any(
|
||||
dist.metadata['Name'] == 'distinfo-pkg'
|
||||
for dist in dists
|
||||
)
|
||||
assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
|
||||
|
||||
def test_distribution_at_pathlib(self):
|
||||
"""Demonstrate how to load metadata direct from a directory.
|
||||
"""
|
||||
"""Demonstrate how to load metadata direct from a directory."""
|
||||
dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info'
|
||||
dist = Distribution.at(dist_info_path)
|
||||
assert dist.version == '1.0.0'
|
||||
|
|
|
@ -3,8 +3,12 @@ import unittest
|
|||
|
||||
from contextlib import ExitStack
|
||||
from importlib.metadata import (
|
||||
distribution, entry_points, files, PackageNotFoundError,
|
||||
version, distributions,
|
||||
PackageNotFoundError,
|
||||
distribution,
|
||||
distributions,
|
||||
entry_points,
|
||||
files,
|
||||
version,
|
||||
)
|
||||
from importlib import resources
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ class NetworkedNNTPTestsMixin:
|
|||
desc = self.server.description(self.GROUP_NAME)
|
||||
_check_desc(desc)
|
||||
# Another sanity check
|
||||
self.assertIn("Python", desc)
|
||||
self.assertIn(self.DESC, desc)
|
||||
# With a pattern
|
||||
desc = self.server.description(self.GROUP_PAT)
|
||||
_check_desc(desc)
|
||||
|
@ -309,6 +309,7 @@ class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase):
|
|||
NNTP_HOST = 'news.trigofacile.com'
|
||||
GROUP_NAME = 'fr.comp.lang.python'
|
||||
GROUP_PAT = 'fr.comp.lang.*'
|
||||
DESC = 'Python'
|
||||
|
||||
NNTP_CLASS = NNTP
|
||||
|
||||
|
@ -343,8 +344,11 @@ class NetworkedNNTP_SSLTests(NetworkedNNTPTests):
|
|||
# 400 connections per day are accepted from each IP address."
|
||||
|
||||
NNTP_HOST = 'nntp.aioe.org'
|
||||
GROUP_NAME = 'comp.lang.python'
|
||||
GROUP_PAT = 'comp.lang.*'
|
||||
# bpo-42794: aioe.test is one of the official groups on this server
|
||||
# used for testing: https://news.aioe.org/manual/aioe-hierarchy/
|
||||
GROUP_NAME = 'aioe.test'
|
||||
GROUP_PAT = 'aioe.*'
|
||||
DESC = 'test'
|
||||
|
||||
NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None)
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import unittest
|
||||
from test import test_tools
|
||||
|
||||
test_tools.skip_if_missing('peg_generator')
|
||||
with test_tools.imports_under_tool('peg_generator'):
|
||||
from pegen.grammar_parser import GeneratedParser as GrammarParser
|
||||
from pegen.validator import SubRuleValidator, ValidationError
|
||||
from pegen.testutil import parse_string
|
||||
from pegen.grammar import Grammar
|
||||
|
||||
|
||||
class TestPegen(unittest.TestCase):
|
||||
def test_rule_with_no_collision(self) -> None:
|
||||
grammar_source = """
|
||||
start: bad_rule
|
||||
sum:
|
||||
| NAME '-' NAME
|
||||
| NAME '+' NAME
|
||||
"""
|
||||
grammar: Grammar = parse_string(grammar_source, GrammarParser)
|
||||
validator = SubRuleValidator(grammar)
|
||||
for rule_name, rule in grammar.rules.items():
|
||||
validator.validate_rule(rule_name, rule)
|
||||
|
||||
def test_rule_with_simple_collision(self) -> None:
|
||||
grammar_source = """
|
||||
start: bad_rule
|
||||
sum:
|
||||
| NAME '+' NAME
|
||||
| NAME '+' NAME ';'
|
||||
"""
|
||||
grammar: Grammar = parse_string(grammar_source, GrammarParser)
|
||||
validator = SubRuleValidator(grammar)
|
||||
with self.assertRaises(ValidationError):
|
||||
for rule_name, rule in grammar.rules.items():
|
||||
validator.validate_rule(rule_name, rule)
|
||||
|
||||
def test_rule_with_collision_after_some_other_rules(self) -> None:
|
||||
grammar_source = """
|
||||
start: bad_rule
|
||||
sum:
|
||||
| NAME '+' NAME
|
||||
| NAME '*' NAME ';'
|
||||
| NAME '-' NAME
|
||||
| NAME '+' NAME ';'
|
||||
"""
|
||||
grammar: Grammar = parse_string(grammar_source, GrammarParser)
|
||||
validator = SubRuleValidator(grammar)
|
||||
with self.assertRaises(ValidationError):
|
||||
for rule_name, rule in grammar.rules.items():
|
||||
validator.validate_rule(rule_name, rule)
|
|
@ -1,4 +1,6 @@
|
|||
import os
|
||||
import copy
|
||||
import pickle
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
|
@ -234,6 +236,38 @@ class PlatformTest(unittest.TestCase):
|
|||
)
|
||||
self.assertEqual(tuple(res), expected)
|
||||
|
||||
def test_uname_replace(self):
|
||||
res = platform.uname()
|
||||
new = res._replace(
|
||||
system='system', node='node', release='release',
|
||||
version='version', machine='machine')
|
||||
self.assertEqual(new.system, 'system')
|
||||
self.assertEqual(new.node, 'node')
|
||||
self.assertEqual(new.release, 'release')
|
||||
self.assertEqual(new.version, 'version')
|
||||
self.assertEqual(new.machine, 'machine')
|
||||
# processor cannot be replaced
|
||||
self.assertEqual(new.processor, res.processor)
|
||||
|
||||
def test_uname_copy(self):
|
||||
uname = platform.uname()
|
||||
self.assertEqual(copy.copy(uname), uname)
|
||||
self.assertEqual(copy.deepcopy(uname), uname)
|
||||
|
||||
def test_uname_pickle(self):
|
||||
orig = platform.uname()
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
pickled = pickle.dumps(orig, proto)
|
||||
restored = pickle.loads(pickled)
|
||||
self.assertEqual(restored, orig)
|
||||
|
||||
def test_uname_slices(self):
|
||||
res = platform.uname()
|
||||
expected = tuple(res)
|
||||
self.assertEqual(res[:], expected)
|
||||
self.assertEqual(res[:5], expected[:5])
|
||||
|
||||
@unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
|
||||
def test_uname_processor(self):
|
||||
"""
|
||||
|
|
|
@ -204,6 +204,16 @@ class PropertyTests(unittest.TestCase):
|
|||
return 'Second'
|
||||
self.assertEqual(A.__doc__, 'Second')
|
||||
|
||||
def test_property_set_name_incorrect_args(self):
|
||||
p = property()
|
||||
|
||||
for i in (0, 1, 3):
|
||||
with self.assertRaisesRegex(
|
||||
TypeError,
|
||||
fr'^__set_name__\(\) takes 2 positional arguments but {i} were given$'
|
||||
):
|
||||
p.__set_name__(*([0] * i))
|
||||
|
||||
|
||||
# Issue 5890: subclasses of property do not preserve method __doc__ strings
|
||||
class PropertySub(property):
|
||||
|
@ -299,6 +309,46 @@ class PropertySubclassTests(unittest.TestCase):
|
|||
self.assertEqual(Foo.spam.__doc__, "a new docstring")
|
||||
|
||||
|
||||
class _PropertyUnreachableAttribute:
|
||||
msg_format = None
|
||||
obj = None
|
||||
cls = None
|
||||
|
||||
def _format_exc_msg(self, msg):
|
||||
return self.msg_format.format(msg)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.obj = cls.cls()
|
||||
|
||||
def test_get_property(self):
|
||||
with self.assertRaisesRegex(AttributeError, self._format_exc_msg("unreadable attribute")):
|
||||
self.obj.foo
|
||||
|
||||
def test_set_property(self):
|
||||
with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't set attribute")):
|
||||
self.obj.foo = None
|
||||
|
||||
def test_del_property(self):
|
||||
with self.assertRaisesRegex(AttributeError, self._format_exc_msg("can't delete attribute")):
|
||||
del self.obj.foo
|
||||
|
||||
|
||||
class PropertyUnreachableAttributeWithName(_PropertyUnreachableAttribute, unittest.TestCase):
|
||||
msg_format = "^{} 'foo'$"
|
||||
|
||||
class cls:
|
||||
foo = property()
|
||||
|
||||
|
||||
class PropertyUnreachableAttributeNoName(_PropertyUnreachableAttribute, unittest.TestCase):
|
||||
msg_format = "^{}$"
|
||||
|
||||
class cls:
|
||||
pass
|
||||
|
||||
cls.foo = property()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -542,6 +542,34 @@ class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
|
|||
raises(0, 42, 0)
|
||||
raises(0, 42, 3.14159)
|
||||
|
||||
def test_randrange_argument_handling(self):
|
||||
randrange = self.gen.randrange
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
randrange(10.0, 20, 2)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
randrange(10, 20.0, 2)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
randrange(10, 20, 1.0)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
randrange(10, 20, 2.0)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(ValueError):
|
||||
randrange(10.5)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(ValueError):
|
||||
randrange(10, 20.5)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(ValueError):
|
||||
randrange(10, 20, 1.5)
|
||||
|
||||
def test_randrange_step(self):
|
||||
# bpo-42772: When stop is None, the step argument was being ignored.
|
||||
randrange = self.gen.randrange
|
||||
with self.assertRaises(TypeError):
|
||||
randrange(1000, step=100)
|
||||
with self.assertRaises(TypeError):
|
||||
randrange(1000, None, step=100)
|
||||
|
||||
def test_randbelow_logic(self, _log=log, int=int):
|
||||
# check bitcount transition points: 2**i and 2**(i+1)-1
|
||||
# show that: k = int(1.001 + _log(n, 2))
|
||||
|
|
|
@ -165,6 +165,17 @@ class LMTPGeneralTests(GeneralTests, unittest.TestCase):
|
|||
|
||||
client = smtplib.LMTP
|
||||
|
||||
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), "test requires Unix domain socket")
|
||||
def testUnixDomainSocketTimeoutDefault(self):
|
||||
local_host = '/some/local/lmtp/delivery/program'
|
||||
mock_socket.reply_with(b"220 Hello world")
|
||||
try:
|
||||
client = self.client(local_host, self.port)
|
||||
finally:
|
||||
mock_socket.setdefaulttimeout(None)
|
||||
self.assertIsNone(client.sock.gettimeout())
|
||||
client.close()
|
||||
|
||||
def testTimeoutZero(self):
|
||||
super().testTimeoutZero()
|
||||
local_host = '/some/local/lmtp/delivery/program'
|
||||
|
|
|
@ -1121,9 +1121,11 @@ class GeneralModuleTests(unittest.TestCase):
|
|||
s_good_values = [0, 1, 2, 0xffff]
|
||||
l_good_values = s_good_values + [0xffffffff]
|
||||
l_bad_values = [-1, -2, 1<<32, 1<<1000]
|
||||
s_bad_values = l_bad_values + [_testcapi.INT_MIN - 1,
|
||||
_testcapi.INT_MAX + 1]
|
||||
s_deprecated_values = [1<<16, _testcapi.INT_MAX]
|
||||
s_bad_values = (
|
||||
l_bad_values +
|
||||
[_testcapi.INT_MIN-1, _testcapi.INT_MAX+1] +
|
||||
[1 << 16, _testcapi.INT_MAX]
|
||||
)
|
||||
for k in s_good_values:
|
||||
socket.ntohs(k)
|
||||
socket.htons(k)
|
||||
|
@ -1136,9 +1138,6 @@ class GeneralModuleTests(unittest.TestCase):
|
|||
for k in l_bad_values:
|
||||
self.assertRaises(OverflowError, socket.ntohl, k)
|
||||
self.assertRaises(OverflowError, socket.htonl, k)
|
||||
for k in s_deprecated_values:
|
||||
self.assertWarns(DeprecationWarning, socket.ntohs, k)
|
||||
self.assertWarns(DeprecationWarning, socket.htons, k)
|
||||
|
||||
def testGetServBy(self):
|
||||
eq = self.assertEqual
|
||||
|
|
|
@ -277,6 +277,13 @@ class SocketServerTest(unittest.TestCase):
|
|||
t.join()
|
||||
s.server_close()
|
||||
|
||||
def test_close_immediately(self):
|
||||
class MyServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
server = MyServer((HOST, 0), lambda: None)
|
||||
server.server_close()
|
||||
|
||||
def test_tcpserver_bind_leak(self):
|
||||
# Issue #22435: the server socket wouldn't be closed if bind()/listen()
|
||||
# failed.
|
||||
|
@ -491,6 +498,22 @@ class MiscTestCase(unittest.TestCase):
|
|||
self.assertEqual(server.shutdown_called, 1)
|
||||
server.server_close()
|
||||
|
||||
def test_threads_reaped(self):
|
||||
"""
|
||||
In #37193, users reported a memory leak
|
||||
due to the saving of every request thread. Ensure that
|
||||
not all threads are kept forever.
|
||||
"""
|
||||
class MyServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
server = MyServer((HOST, 0), socketserver.StreamRequestHandler)
|
||||
for n in range(10):
|
||||
with socket.create_connection(server.server_address):
|
||||
server.handle_request()
|
||||
self.assertLess(len(server._threads), 10)
|
||||
server.server_close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -204,6 +204,28 @@ class ProcessTestCase(BaseTestCase):
|
|||
input=b'pear')
|
||||
self.assertIn(b'PEAR', output)
|
||||
|
||||
def test_check_output_input_none(self):
|
||||
"""input=None has a legacy meaning of input='' on check_output."""
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c",
|
||||
"import sys; print('XX' if sys.stdin.read() else '')"],
|
||||
input=None)
|
||||
self.assertNotIn(b'XX', output)
|
||||
|
||||
def test_check_output_input_none_text(self):
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c",
|
||||
"import sys; print('XX' if sys.stdin.read() else '')"],
|
||||
input=None, text=True)
|
||||
self.assertNotIn('XX', output)
|
||||
|
||||
def test_check_output_input_none_universal_newlines(self):
|
||||
output = subprocess.check_output(
|
||||
[sys.executable, "-c",
|
||||
"import sys; print('XX' if sys.stdin.read() else '')"],
|
||||
input=None, universal_newlines=True)
|
||||
self.assertNotIn('XX', output)
|
||||
|
||||
def test_check_output_stdout_arg(self):
|
||||
# check_output() refuses to accept 'stdout' argument
|
||||
with self.assertRaises(ValueError) as c:
|
||||
|
|
|
@ -1329,7 +1329,7 @@ class SizeofTest(unittest.TestCase):
|
|||
def setx(self, value): self.__x = value
|
||||
def delx(self): del self.__x
|
||||
x = property(getx, setx, delx, "")
|
||||
check(x, size('4Pi'))
|
||||
check(x, size('5Pi'))
|
||||
# PyCapsule
|
||||
# XXX
|
||||
# rangeiterator
|
||||
|
|
|
@ -874,6 +874,48 @@ class TraceTestCase(unittest.TestCase):
|
|||
(5, 'line'),
|
||||
(5, 'return')])
|
||||
|
||||
def test_nested_ifs(self):
|
||||
|
||||
def func():
|
||||
a = b = 1
|
||||
if a == 1:
|
||||
if b == 1:
|
||||
x = 4
|
||||
else:
|
||||
y = 6
|
||||
else:
|
||||
z = 8
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(3, 'line'),
|
||||
(4, 'line'),
|
||||
(4, 'return')])
|
||||
|
||||
def test_nested_try_if(self):
|
||||
|
||||
def func():
|
||||
x = "hello"
|
||||
try:
|
||||
3/0
|
||||
except ZeroDivisionError:
|
||||
if x == 'raise':
|
||||
raise ValueError() # line 6
|
||||
f = 7
|
||||
|
||||
self.run_and_compare(func,
|
||||
[(0, 'call'),
|
||||
(1, 'line'),
|
||||
(2, 'line'),
|
||||
(3, 'line'),
|
||||
(3, 'exception'),
|
||||
(4, 'line'),
|
||||
(5, 'line'),
|
||||
(7, 'line'),
|
||||
(7, 'return')])
|
||||
|
||||
|
||||
class SkipLineEventsTraceTestCase(TraceTestCase):
|
||||
"""Repeat the trace tests, but with per-line events skipped"""
|
||||
|
|
|
@ -138,10 +138,14 @@ class TclTest(unittest.TestCase):
|
|||
|
||||
def get_integers(self):
|
||||
integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63)
|
||||
# bignum was added in Tcl 8.5, but its support is able only since 8.5.8
|
||||
if (get_tk_patchlevel() >= (8, 6, 0, 'final') or
|
||||
(8, 5, 8) <= get_tk_patchlevel() < (8, 6)):
|
||||
integers += (2**63, -2**63-1, 2**1000, -2**1000)
|
||||
# bignum was added in Tcl 8.5, but its support is able only since 8.5.8.
|
||||
# Actually it is determined at compile time, so using get_tk_patchlevel()
|
||||
# is not reliable.
|
||||
# TODO: expose full static version.
|
||||
if tcl_version >= (8, 5):
|
||||
v = get_tk_patchlevel()
|
||||
if v >= (8, 6, 0, 'final') or (8, 5, 8) <= v < (8, 6):
|
||||
integers += (2**63, -2**63-1, 2**1000, -2**1000)
|
||||
return integers
|
||||
|
||||
def test_getint(self):
|
||||
|
@ -445,7 +449,7 @@ class TclTest(unittest.TestCase):
|
|||
else:
|
||||
self.assertEqual(result, str(i))
|
||||
self.assertIsInstance(result, str)
|
||||
if tcl_version < (8, 5): # bignum was added in Tcl 8.5
|
||||
if get_tk_patchlevel() < (8, 5): # bignum was added in Tcl 8.5
|
||||
self.assertRaises(TclError, tcl.call, 'expr', str(2**1000))
|
||||
|
||||
def test_passing_values(self):
|
||||
|
|
|
@ -737,6 +737,16 @@ class TypesTests(unittest.TestCase):
|
|||
with self.assertRaises(ZeroDivisionError):
|
||||
list[int] | list[bt]
|
||||
|
||||
union_ga = (int | list[str], int | collections.abc.Callable[..., str],
|
||||
int | d)
|
||||
# Raise error when isinstance(type, type | genericalias)
|
||||
for type_ in union_ga:
|
||||
with self.subTest(f"check isinstance/issubclass is invalid for {type_}"):
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance(list, type_)
|
||||
with self.assertRaises(TypeError):
|
||||
issubclass(list, type_)
|
||||
|
||||
def test_ellipsis_type(self):
|
||||
self.assertIsInstance(Ellipsis, types.EllipsisType)
|
||||
|
||||
|
|
|
@ -3021,6 +3021,7 @@ class GetUtilitiesTestCase(TestCase):
|
|||
self.assertIs(get_origin(Callable), collections.abc.Callable)
|
||||
self.assertIs(get_origin(list[int]), list)
|
||||
self.assertIs(get_origin(list), None)
|
||||
self.assertIs(get_origin(list | str), types.Union)
|
||||
|
||||
def test_get_args(self):
|
||||
T = TypeVar('T')
|
||||
|
@ -3048,6 +3049,16 @@ class GetUtilitiesTestCase(TestCase):
|
|||
self.assertEqual(get_args(Callable), ())
|
||||
self.assertEqual(get_args(list[int]), (int,))
|
||||
self.assertEqual(get_args(list), ())
|
||||
self.assertEqual(get_args(collections.abc.Callable[[int], str]), ([int], str))
|
||||
self.assertEqual(get_args(collections.abc.Callable[..., str]), (..., str))
|
||||
self.assertEqual(get_args(collections.abc.Callable[[], str]), ([], str))
|
||||
self.assertEqual(get_args(collections.abc.Callable[[int], str]),
|
||||
get_args(Callable[[int], str]))
|
||||
P = ParamSpec('P')
|
||||
self.assertEqual(get_args(Callable[P, int]), (P, int))
|
||||
self.assertEqual(get_args(Callable[Concatenate[int, P], int]),
|
||||
(Concatenate[int, P], int))
|
||||
self.assertEqual(get_args(list | str), (list, str))
|
||||
|
||||
|
||||
class CollectionsAbcTests(BaseTestCase):
|
||||
|
|
|
@ -346,6 +346,31 @@ Now some general starred expressions (all fail).
|
|||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
>>> (*x),y = 1, 2 # doctest:+ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
>>> (((*x))),y = 1, 2 # doctest:+ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
>>> z,(*x),y = 1, 2, 4 # doctest:+ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
>>> z,(*x) = 1, 2 # doctest:+ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
>>> ((*x),y) = 1, 2 # doctest:+ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
SyntaxError: can't use starred expression here
|
||||
|
||||
Some size constraints (all fail.)
|
||||
|
||||
>>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)"
|
||||
|
|
|
@ -1851,9 +1851,17 @@ class MiscTests(unittest.TestCase):
|
|||
('ftp', 'joe', 'password', 'proxy.example.com')),
|
||||
# Test for no trailing '/' case
|
||||
('http://joe:password@proxy.example.com',
|
||||
('http', 'joe', 'password', 'proxy.example.com'))
|
||||
('http', 'joe', 'password', 'proxy.example.com')),
|
||||
# Testcases with '/' character in username, password
|
||||
('http://user/name:password@localhost:22',
|
||||
('http', 'user/name', 'password', 'localhost:22')),
|
||||
('http://username:pass/word@localhost:22',
|
||||
('http', 'username', 'pass/word', 'localhost:22')),
|
||||
('http://user/name:pass/word@localhost:22',
|
||||
('http', 'user/name', 'pass/word', 'localhost:22')),
|
||||
]
|
||||
|
||||
|
||||
for tc, expected in parse_proxy_test_cases:
|
||||
self.assertEqual(_parse_proxy(tc), expected)
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ def _get_default_root(what=None):
|
|||
if not _support_default_root:
|
||||
raise RuntimeError("No master specified and tkinter is "
|
||||
"configured to not support default root")
|
||||
if not _default_root:
|
||||
if _default_root is None:
|
||||
if what:
|
||||
raise RuntimeError(f"Too early to {what}: no default root window")
|
||||
root = Tk()
|
||||
|
@ -300,6 +300,31 @@ def _get_default_root(what=None):
|
|||
return _default_root
|
||||
|
||||
|
||||
def _get_temp_root():
|
||||
global _support_default_root
|
||||
if not _support_default_root:
|
||||
raise RuntimeError("No master specified and tkinter is "
|
||||
"configured to not support default root")
|
||||
root = _default_root
|
||||
if root is None:
|
||||
assert _support_default_root
|
||||
_support_default_root = False
|
||||
root = Tk()
|
||||
_support_default_root = True
|
||||
assert _default_root is None
|
||||
root.withdraw()
|
||||
root._temporary = True
|
||||
return root
|
||||
|
||||
|
||||
def _destroy_temp_root(master):
|
||||
if getattr(master, '_temporary', False):
|
||||
try:
|
||||
master.destroy()
|
||||
except TclError:
|
||||
pass
|
||||
|
||||
|
||||
def _tkerror(err):
|
||||
"""Internal function."""
|
||||
pass
|
||||
|
@ -342,7 +367,7 @@ class Variable:
|
|||
if name is not None and not isinstance(name, str):
|
||||
raise TypeError("name must be a string")
|
||||
global _varnum
|
||||
if not master:
|
||||
if master is None:
|
||||
master = _get_default_root('create variable')
|
||||
self._root = master._root()
|
||||
self._tk = master.tk
|
||||
|
@ -491,15 +516,11 @@ class Variable:
|
|||
self._tk.call("trace", "vinfo", self._name))]
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Comparison for equality (==).
|
||||
|
||||
Note: if the Variable's master matters to behavior
|
||||
also compare self._master == other._master
|
||||
"""
|
||||
if not isinstance(other, Variable):
|
||||
return NotImplemented
|
||||
return self.__class__.__name__ == other.__class__.__name__ \
|
||||
and self._name == other._name
|
||||
return (self._name == other._name
|
||||
and self.__class__.__name__ == other.__class__.__name__
|
||||
and self._tk == other._tk)
|
||||
|
||||
|
||||
class StringVar(Variable):
|
||||
|
@ -808,7 +829,7 @@ class Misc:
|
|||
function which shall be called. Additional parameters
|
||||
are given as parameters to the function call. Return
|
||||
identifier to cancel scheduling with after_cancel."""
|
||||
if not func:
|
||||
if func is None:
|
||||
# I'd rather use time.sleep(ms*0.001)
|
||||
self.tk.call('after', ms)
|
||||
return None
|
||||
|
@ -1542,7 +1563,7 @@ class Misc:
|
|||
def _root(self):
|
||||
"""Internal function."""
|
||||
w = self
|
||||
while w.master: w = w.master
|
||||
while w.master is not None: w = w.master
|
||||
return w
|
||||
_subst_format = ('%#', '%b', '%f', '%h', '%k',
|
||||
'%s', '%t', '%w', '%x', '%y',
|
||||
|
@ -2306,7 +2327,7 @@ class Tk(Misc, Wm):
|
|||
self.tk.createcommand('exit', _exit)
|
||||
self._tclCommands.append('tkerror')
|
||||
self._tclCommands.append('exit')
|
||||
if _support_default_root and not _default_root:
|
||||
if _support_default_root and _default_root is None:
|
||||
_default_root = self
|
||||
self.protocol("WM_DELETE_WINDOW", self.destroy)
|
||||
|
||||
|
@ -2534,7 +2555,7 @@ class BaseWidget(Misc):
|
|||
|
||||
def _setup(self, master, cnf):
|
||||
"""Internal function. Sets up information about children."""
|
||||
if not master:
|
||||
if master is None:
|
||||
master = _get_default_root()
|
||||
self.master = master
|
||||
self.tk = master.tk
|
||||
|
@ -3949,7 +3970,7 @@ class _setit:
|
|||
|
||||
def __call__(self, *args):
|
||||
self.__var.set(self.__value)
|
||||
if self.__callback:
|
||||
if self.__callback is not None:
|
||||
self.__callback(self.__value, *args)
|
||||
|
||||
|
||||
|
@ -3998,7 +4019,7 @@ class Image:
|
|||
|
||||
def __init__(self, imgtype, name=None, cnf={}, master=None, **kw):
|
||||
self.name = None
|
||||
if not master:
|
||||
if master is None:
|
||||
master = _get_default_root('create image')
|
||||
self.tk = getattr(master, 'tk', master)
|
||||
if not name:
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
__all__ = ["Dialog"]
|
||||
|
||||
from tkinter import Frame
|
||||
from tkinter import Frame, _get_temp_root, _destroy_temp_root
|
||||
|
||||
|
||||
class Dialog:
|
||||
|
@ -18,7 +18,7 @@ class Dialog:
|
|||
command = None
|
||||
|
||||
def __init__(self, master=None, **options):
|
||||
if not master:
|
||||
if master is None:
|
||||
master = options.get('parent')
|
||||
self.master = master
|
||||
self.options = options
|
||||
|
@ -37,22 +37,17 @@ class Dialog:
|
|||
|
||||
self._fixoptions()
|
||||
|
||||
# we need a dummy widget to properly process the options
|
||||
# (at least as long as we use Tkinter 1.63)
|
||||
w = Frame(self.master)
|
||||
|
||||
master = self.master
|
||||
if master is None:
|
||||
master = _get_temp_root()
|
||||
try:
|
||||
|
||||
s = w.tk.call(self.command, *w._options(self.options))
|
||||
|
||||
s = self._fixresult(w, s)
|
||||
|
||||
self._test_callback(master) # The function below is replaced for some tests.
|
||||
s = master.tk.call(self.command, *master._options(self.options))
|
||||
s = self._fixresult(master, s)
|
||||
finally:
|
||||
|
||||
try:
|
||||
# get rid of the widget
|
||||
w.destroy()
|
||||
except:
|
||||
pass
|
||||
_destroy_temp_root(master)
|
||||
|
||||
return s
|
||||
|
||||
def _test_callback(self, master):
|
||||
pass
|
||||
|
|
|
@ -108,7 +108,7 @@ __all__ = ["dnd_start", "DndHandler"]
|
|||
|
||||
def dnd_start(source, event):
|
||||
h = DndHandler(source, event)
|
||||
if h.root:
|
||||
if h.root is not None:
|
||||
return h
|
||||
else:
|
||||
return None
|
||||
|
@ -143,7 +143,7 @@ class DndHandler:
|
|||
def __del__(self):
|
||||
root = self.root
|
||||
self.root = None
|
||||
if root:
|
||||
if root is not None:
|
||||
try:
|
||||
del root.__dnd
|
||||
except AttributeError:
|
||||
|
@ -154,25 +154,25 @@ class DndHandler:
|
|||
target_widget = self.initial_widget.winfo_containing(x, y)
|
||||
source = self.source
|
||||
new_target = None
|
||||
while target_widget:
|
||||
while target_widget is not None:
|
||||
try:
|
||||
attr = target_widget.dnd_accept
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
new_target = attr(source, event)
|
||||
if new_target:
|
||||
if new_target is not None:
|
||||
break
|
||||
target_widget = target_widget.master
|
||||
old_target = self.target
|
||||
if old_target is new_target:
|
||||
if old_target:
|
||||
if old_target is not None:
|
||||
old_target.dnd_motion(source, event)
|
||||
else:
|
||||
if old_target:
|
||||
if old_target is not None:
|
||||
self.target = None
|
||||
old_target.dnd_leave(source, event)
|
||||
if new_target:
|
||||
if new_target is not None:
|
||||
new_target.dnd_enter(source, event)
|
||||
self.target = new_target
|
||||
|
||||
|
@ -193,7 +193,7 @@ class DndHandler:
|
|||
self.initial_widget.unbind("<Motion>")
|
||||
widget['cursor'] = self.save_cursor
|
||||
self.target = self.source = self.initial_widget = self.root = None
|
||||
if target:
|
||||
if target is not None:
|
||||
if commit:
|
||||
target.dnd_commit(source, event)
|
||||
else:
|
||||
|
@ -215,9 +215,9 @@ class Icon:
|
|||
if canvas is self.canvas:
|
||||
self.canvas.coords(self.id, x, y)
|
||||
return
|
||||
if self.canvas:
|
||||
if self.canvas is not None:
|
||||
self.detach()
|
||||
if not canvas:
|
||||
if canvas is None:
|
||||
return
|
||||
label = tkinter.Label(canvas, text=self.name,
|
||||
borderwidth=2, relief="raised")
|
||||
|
@ -229,7 +229,7 @@ class Icon:
|
|||
|
||||
def detach(self):
|
||||
canvas = self.canvas
|
||||
if not canvas:
|
||||
if canvas is None:
|
||||
return
|
||||
id = self.id
|
||||
label = self.label
|
||||
|
|
|
@ -17,10 +17,10 @@ BOLD = "bold"
|
|||
ITALIC = "italic"
|
||||
|
||||
|
||||
def nametofont(name):
|
||||
def nametofont(name, root=None):
|
||||
"""Given the name of a tk named font, returns a Font representation.
|
||||
"""
|
||||
return Font(name=name, exists=True)
|
||||
return Font(name=name, exists=True, root=root)
|
||||
|
||||
|
||||
class Font:
|
||||
|
@ -68,7 +68,7 @@ class Font:
|
|||
|
||||
def __init__(self, root=None, font=None, name=None, exists=False,
|
||||
**options):
|
||||
if not root:
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font')
|
||||
tk = getattr(root, 'tk', root)
|
||||
if font:
|
||||
|
@ -107,7 +107,7 @@ class Font:
|
|||
def __eq__(self, other):
|
||||
if not isinstance(other, Font):
|
||||
return NotImplemented
|
||||
return self.name == other.name
|
||||
return self.name == other.name and self._tk == other._tk
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.cget(key)
|
||||
|
@ -183,7 +183,7 @@ class Font:
|
|||
|
||||
def families(root=None, displayof=None):
|
||||
"Get font families (as a tuple)"
|
||||
if not root:
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font.families()')
|
||||
args = ()
|
||||
if displayof:
|
||||
|
@ -193,7 +193,7 @@ def families(root=None, displayof=None):
|
|||
|
||||
def names(root=None):
|
||||
"Get names of defined fonts (as a tuple)"
|
||||
if not root:
|
||||
if root is None:
|
||||
root = tkinter._get_default_root('use font.names()')
|
||||
return root.tk.splitlist(root.tk.call("font", "names"))
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ askstring -- get a string from the user
|
|||
"""
|
||||
|
||||
from tkinter import *
|
||||
from tkinter import messagebox, _get_default_root
|
||||
from tkinter import _get_temp_root, _destroy_temp_root
|
||||
from tkinter import messagebox
|
||||
|
||||
|
||||
class SimpleDialog:
|
||||
|
@ -55,36 +56,8 @@ class SimpleDialog:
|
|||
b.config(relief=RIDGE, borderwidth=8)
|
||||
b.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
|
||||
self._set_transient(master)
|
||||
|
||||
def _set_transient(self, master, relx=0.5, rely=0.3):
|
||||
widget = self.root
|
||||
widget.withdraw() # Remain invisible while we figure out the geometry
|
||||
widget.transient(master)
|
||||
widget.update_idletasks() # Actualize geometry information
|
||||
if master.winfo_ismapped():
|
||||
m_width = master.winfo_width()
|
||||
m_height = master.winfo_height()
|
||||
m_x = master.winfo_rootx()
|
||||
m_y = master.winfo_rooty()
|
||||
else:
|
||||
m_width = master.winfo_screenwidth()
|
||||
m_height = master.winfo_screenheight()
|
||||
m_x = m_y = 0
|
||||
w_width = widget.winfo_reqwidth()
|
||||
w_height = widget.winfo_reqheight()
|
||||
x = m_x + (m_width - w_width) * relx
|
||||
y = m_y + (m_height - w_height) * rely
|
||||
if x+w_width > master.winfo_screenwidth():
|
||||
x = master.winfo_screenwidth() - w_width
|
||||
elif x < 0:
|
||||
x = 0
|
||||
if y+w_height > master.winfo_screenheight():
|
||||
y = master.winfo_screenheight() - w_height
|
||||
elif y < 0:
|
||||
y = 0
|
||||
widget.geometry("+%d+%d" % (x, y))
|
||||
widget.deiconify() # Become visible at the desired location
|
||||
self.root.transient(master)
|
||||
_place_window(self.root, master)
|
||||
|
||||
def go(self):
|
||||
self.root.wait_visibility()
|
||||
|
@ -127,8 +100,8 @@ class Dialog(Toplevel):
|
|||
title -- the dialog title
|
||||
'''
|
||||
master = parent
|
||||
if not master:
|
||||
master = _get_default_root('create dialog window')
|
||||
if master is None:
|
||||
master = _get_temp_root()
|
||||
|
||||
Toplevel.__init__(self, master)
|
||||
|
||||
|
@ -152,16 +125,12 @@ class Dialog(Toplevel):
|
|||
|
||||
self.buttonbox()
|
||||
|
||||
if not self.initial_focus:
|
||||
if self.initial_focus is None:
|
||||
self.initial_focus = self
|
||||
|
||||
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
||||
|
||||
if parent is not None:
|
||||
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
|
||||
parent.winfo_rooty()+50))
|
||||
|
||||
self.deiconify() # become visible now
|
||||
_place_window(self, parent)
|
||||
|
||||
self.initial_focus.focus_set()
|
||||
|
||||
|
@ -174,6 +143,7 @@ class Dialog(Toplevel):
|
|||
'''Destroy the window'''
|
||||
self.initial_focus = None
|
||||
Toplevel.destroy(self)
|
||||
_destroy_temp_root(self.master)
|
||||
|
||||
#
|
||||
# construction hooks
|
||||
|
@ -251,6 +221,37 @@ class Dialog(Toplevel):
|
|||
pass # override
|
||||
|
||||
|
||||
# Place a toplevel window at the center of parent or screen
|
||||
# It is a Python implementation of ::tk::PlaceWindow.
|
||||
def _place_window(w, parent=None):
|
||||
w.wm_withdraw() # Remain invisible while we figure out the geometry
|
||||
w.update_idletasks() # Actualize geometry information
|
||||
|
||||
minwidth = w.winfo_reqwidth()
|
||||
minheight = w.winfo_reqheight()
|
||||
maxwidth = w.winfo_vrootwidth()
|
||||
maxheight = w.winfo_vrootheight()
|
||||
if parent is not None and parent.winfo_ismapped():
|
||||
x = parent.winfo_rootx() + (parent.winfo_width() - minwidth) // 2
|
||||
y = parent.winfo_rooty() + (parent.winfo_height() - minheight) // 2
|
||||
vrootx = w.winfo_vrootx()
|
||||
vrooty = w.winfo_vrooty()
|
||||
x = min(x, vrootx + maxwidth - minwidth)
|
||||
x = max(x, vrootx)
|
||||
y = min(y, vrooty + maxheight - minheight)
|
||||
y = max(y, vrooty)
|
||||
if w._windowingsystem == 'aqua':
|
||||
# Avoid the native menu bar which sits on top of everything.
|
||||
y = max(y, 22)
|
||||
else:
|
||||
x = (w.winfo_screenwidth() - minwidth) // 2
|
||||
y = (w.winfo_screenheight() - minheight) // 2
|
||||
|
||||
w.wm_maxsize(maxwidth, maxheight)
|
||||
w.wm_geometry('+%d+%d' % (x, y))
|
||||
w.wm_deiconify() # Become visible at the desired location
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# convenience dialogues
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import unittest
|
||||
import tkinter
|
||||
from test.support import requires, run_unittest, swap_attr
|
||||
from tkinter.test.support import AbstractDefaultRootTest
|
||||
from tkinter.commondialog import Dialog
|
||||
from tkinter.colorchooser import askcolor
|
||||
|
||||
requires('gui')
|
||||
|
||||
|
||||
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
||||
|
||||
def test_askcolor(self):
|
||||
def test_callback(dialog, master):
|
||||
nonlocal ismapped
|
||||
master.update()
|
||||
ismapped = master.winfo_ismapped()
|
||||
raise ZeroDivisionError
|
||||
|
||||
with swap_attr(Dialog, '_test_callback', test_callback):
|
||||
ismapped = None
|
||||
self.assertRaises(ZeroDivisionError, askcolor)
|
||||
#askcolor()
|
||||
self.assertEqual(ismapped, False)
|
||||
|
||||
root = tkinter.Tk()
|
||||
ismapped = None
|
||||
self.assertRaises(ZeroDivisionError, askcolor)
|
||||
self.assertEqual(ismapped, True)
|
||||
root.destroy()
|
||||
|
||||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, askcolor)
|
||||
|
||||
|
||||
tests_gui = (DefaultRootTest,)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_unittest(*tests_gui)
|
|
@ -63,15 +63,22 @@ class FontTest(AbstractTkTest, unittest.TestCase):
|
|||
self.assertEqual(self.font.name, fontname)
|
||||
self.assertEqual(str(self.font), fontname)
|
||||
|
||||
def test_eq(self):
|
||||
def test_equality(self):
|
||||
font1 = font.Font(root=self.root, name=fontname, exists=True)
|
||||
font2 = font.Font(root=self.root, name=fontname, exists=True)
|
||||
self.assertIsNot(font1, font2)
|
||||
self.assertEqual(font1, font2)
|
||||
self.assertNotEqual(font1, font1.copy())
|
||||
|
||||
self.assertNotEqual(font1, 0)
|
||||
self.assertEqual(font1, ALWAYS_EQ)
|
||||
|
||||
root2 = tkinter.Tk()
|
||||
self.addCleanup(root2.destroy)
|
||||
font3 = font.Font(root=root2, name=fontname, exists=True)
|
||||
self.assertEqual(str(font1), str(font3))
|
||||
self.assertNotEqual(font1, font3)
|
||||
|
||||
def test_measure(self):
|
||||
self.assertIsInstance(self.font.measure('abc'), int)
|
||||
|
||||
|
@ -101,6 +108,11 @@ class FontTest(AbstractTkTest, unittest.TestCase):
|
|||
self.assertTrue(name)
|
||||
self.assertIn(fontname, names)
|
||||
|
||||
def test_nametofont(self):
|
||||
testfont = font.nametofont(fontname, root=self.root)
|
||||
self.assertIsInstance(testfont, font.Font)
|
||||
self.assertEqual(testfont.name, fontname)
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(
|
||||
repr(self.font), f'<tkinter.font.Font object {fontname!r}>'
|
||||
|
@ -136,6 +148,16 @@ class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
|||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, font.names)
|
||||
|
||||
def test_nametofont(self):
|
||||
self.assertRaises(RuntimeError, font.nametofont, fontname)
|
||||
root = tkinter.Tk()
|
||||
testfont = font.nametofont(fontname)
|
||||
self.assertIsInstance(testfont, font.Font)
|
||||
self.assertEqual(testfont.name, fontname)
|
||||
root.destroy()
|
||||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, font.nametofont, fontname)
|
||||
|
||||
|
||||
tests_gui = (FontTest, DefaultRootTest)
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import unittest
|
||||
import tkinter
|
||||
from test.support import requires, run_unittest, swap_attr
|
||||
from tkinter.test.support import AbstractDefaultRootTest
|
||||
from tkinter.commondialog import Dialog
|
||||
from tkinter.messagebox import showinfo
|
||||
|
||||
requires('gui')
|
||||
|
||||
|
||||
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
||||
|
||||
def test_showinfo(self):
|
||||
def test_callback(dialog, master):
|
||||
nonlocal ismapped
|
||||
master.update()
|
||||
ismapped = master.winfo_ismapped()
|
||||
raise ZeroDivisionError
|
||||
|
||||
with swap_attr(Dialog, '_test_callback', test_callback):
|
||||
ismapped = None
|
||||
self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information")
|
||||
self.assertEqual(ismapped, False)
|
||||
|
||||
root = tkinter.Tk()
|
||||
ismapped = None
|
||||
self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information")
|
||||
self.assertEqual(ismapped, True)
|
||||
root.destroy()
|
||||
|
||||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, showinfo, "Spam", "Egg Information")
|
||||
|
||||
|
||||
tests_gui = (DefaultRootTest,)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_unittest(*tests_gui)
|
|
@ -10,13 +10,25 @@ requires('gui')
|
|||
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
||||
|
||||
def test_askinteger(self):
|
||||
self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
|
||||
root = tkinter.Tk()
|
||||
with swap_attr(Dialog, 'wait_window', lambda self, w: w.destroy()):
|
||||
@staticmethod
|
||||
def mock_wait_window(w):
|
||||
nonlocal ismapped
|
||||
ismapped = w.master.winfo_ismapped()
|
||||
w.destroy()
|
||||
|
||||
with swap_attr(Dialog, 'wait_window', mock_wait_window):
|
||||
ismapped = None
|
||||
askinteger("Go To Line", "Line number")
|
||||
root.destroy()
|
||||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
|
||||
self.assertEqual(ismapped, False)
|
||||
|
||||
root = tkinter.Tk()
|
||||
ismapped = None
|
||||
askinteger("Go To Line", "Line number")
|
||||
self.assertEqual(ismapped, True)
|
||||
root.destroy()
|
||||
|
||||
tkinter.NoDefaultRoot()
|
||||
self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
|
||||
|
||||
|
||||
tests_gui = (DefaultRootTest,)
|
||||
|
|
|
@ -58,22 +58,32 @@ class TestVariable(TestBase):
|
|||
del v2
|
||||
self.assertFalse(self.info_exists("name"))
|
||||
|
||||
def test___eq__(self):
|
||||
def test_equality(self):
|
||||
# values doesn't matter, only class and name are checked
|
||||
v1 = Variable(self.root, name="abc")
|
||||
v2 = Variable(self.root, name="abc")
|
||||
self.assertIsNot(v1, v2)
|
||||
self.assertEqual(v1, v2)
|
||||
|
||||
v3 = StringVar(self.root, name="abc")
|
||||
v3 = Variable(self.root, name="cba")
|
||||
self.assertNotEqual(v1, v3)
|
||||
|
||||
v4 = StringVar(self.root, name="abc")
|
||||
self.assertEqual(str(v1), str(v4))
|
||||
self.assertNotEqual(v1, v4)
|
||||
|
||||
V = type('Variable', (), {})
|
||||
self.assertNotEqual(v1, V())
|
||||
|
||||
self.assertNotEqual(v1, object())
|
||||
self.assertEqual(v1, ALWAYS_EQ)
|
||||
|
||||
root2 = tkinter.Tk()
|
||||
self.addCleanup(root2.destroy)
|
||||
v5 = Variable(root2, name="abc")
|
||||
self.assertEqual(str(v1), str(v5))
|
||||
self.assertNotEqual(v1, v5)
|
||||
|
||||
def test_invalid_name(self):
|
||||
with self.assertRaises(TypeError):
|
||||
Variable(self.root, name=123)
|
||||
|
|
|
@ -22,7 +22,7 @@ def float_round(x):
|
|||
class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
|
||||
_conv_pad_pixels = noconv
|
||||
|
||||
def test_class(self):
|
||||
def test_configure_class(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['class'],
|
||||
widget.__class__.__name__.title())
|
||||
|
@ -31,7 +31,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
|
|||
widget2 = self.create(class_='Foo')
|
||||
self.assertEqual(widget2['class'], 'Foo')
|
||||
|
||||
def test_colormap(self):
|
||||
def test_configure_colormap(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['colormap'], '')
|
||||
self.checkInvalidParam(widget, 'colormap', 'new',
|
||||
|
@ -39,7 +39,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
|
|||
widget2 = self.create(colormap='new')
|
||||
self.assertEqual(widget2['colormap'], 'new')
|
||||
|
||||
def test_container(self):
|
||||
def test_configure_container(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['container'], 0 if self.wantobjects else '0')
|
||||
self.checkInvalidParam(widget, 'container', 1,
|
||||
|
@ -47,7 +47,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
|
|||
widget2 = self.create(container=True)
|
||||
self.assertEqual(widget2['container'], 1 if self.wantobjects else '1')
|
||||
|
||||
def test_visual(self):
|
||||
def test_configure_visual(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['visual'], '')
|
||||
self.checkInvalidParam(widget, 'visual', 'default',
|
||||
|
@ -69,13 +69,13 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Toplevel(self.root, **kwargs)
|
||||
|
||||
def test_menu(self):
|
||||
def test_configure_menu(self):
|
||||
widget = self.create()
|
||||
menu = tkinter.Menu(self.root)
|
||||
self.checkParam(widget, 'menu', menu, eq=widget_eq)
|
||||
self.checkParam(widget, 'menu', '')
|
||||
|
||||
def test_screen(self):
|
||||
def test_configure_screen(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['screen'], '')
|
||||
try:
|
||||
|
@ -87,7 +87,7 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
|
|||
widget2 = self.create(screen=display)
|
||||
self.assertEqual(widget2['screen'], display)
|
||||
|
||||
def test_use(self):
|
||||
def test_configure_use(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['use'], '')
|
||||
parent = self.create(container=True)
|
||||
|
@ -124,14 +124,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.LabelFrame(self.root, **kwargs)
|
||||
|
||||
def test_labelanchor(self):
|
||||
def test_configure_labelanchor(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'labelanchor',
|
||||
'e', 'en', 'es', 'n', 'ne', 'nw',
|
||||
's', 'se', 'sw', 'w', 'wn', 'ws')
|
||||
self.checkInvalidParam(widget, 'labelanchor', 'center')
|
||||
|
||||
def test_labelwidget(self):
|
||||
def test_configure_labelwidget(self):
|
||||
widget = self.create()
|
||||
label = tkinter.Label(self.root, text='Mupp', name='foo')
|
||||
self.checkParam(widget, 'labelwidget', label, expected='.foo')
|
||||
|
@ -141,7 +141,7 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
|
|||
class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
|
||||
_conv_pixels = noconv
|
||||
|
||||
def test_highlightthickness(self):
|
||||
def test_configure_highlightthickness(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'highlightthickness',
|
||||
0, 1.3, 2.6, 6, -2, '10p')
|
||||
|
@ -179,7 +179,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Button(self.root, **kwargs)
|
||||
|
||||
def test_default(self):
|
||||
def test_configure_default(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal')
|
||||
|
||||
|
@ -204,11 +204,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
return tkinter.Checkbutton(self.root, **kwargs)
|
||||
|
||||
|
||||
def test_offvalue(self):
|
||||
def test_configure_offvalue(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
|
||||
|
||||
def test_onvalue(self):
|
||||
def test_configure_onvalue(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
|
||||
|
||||
|
@ -231,7 +231,7 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Radiobutton(self.root, **kwargs)
|
||||
|
||||
def test_value(self):
|
||||
def test_configure_value(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
|
||||
|
||||
|
@ -254,18 +254,19 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Menubutton(self.root, **kwargs)
|
||||
|
||||
def test_direction(self):
|
||||
def test_configure_direction(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'direction',
|
||||
'above', 'below', 'flush', 'left', 'right')
|
||||
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str)
|
||||
|
||||
test_highlightthickness = StandardOptionsTests.test_highlightthickness
|
||||
test_configure_highlightthickness = \
|
||||
StandardOptionsTests.test_configure_highlightthickness
|
||||
|
||||
def test_image(self):
|
||||
def test_configure_image(self):
|
||||
widget = self.create()
|
||||
image = tkinter.PhotoImage(master=self.root, name='image1')
|
||||
self.checkParam(widget, 'image', image, conv=str)
|
||||
|
@ -279,23 +280,23 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
if errmsg is not None:
|
||||
self.assertEqual(str(cm.exception), errmsg)
|
||||
|
||||
def test_menu(self):
|
||||
def test_configure_menu(self):
|
||||
widget = self.create()
|
||||
menu = tkinter.Menu(widget, name='menu')
|
||||
self.checkParam(widget, 'menu', menu, eq=widget_eq)
|
||||
menu.destroy()
|
||||
|
||||
def test_padx(self):
|
||||
def test_configure_padx(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m')
|
||||
self.checkParam(widget, 'padx', -2, expected=0)
|
||||
|
||||
def test_pady(self):
|
||||
def test_configure_pady(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m')
|
||||
self.checkParam(widget, 'pady', -2, expected=0)
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str)
|
||||
|
||||
|
@ -328,18 +329,18 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Entry(self.root, **kwargs)
|
||||
|
||||
def test_disabledbackground(self):
|
||||
def test_configure_disabledbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'disabledbackground')
|
||||
|
||||
def test_insertborderwidth(self):
|
||||
def test_configure_insertborderwidth(self):
|
||||
widget = self.create(insertwidth=100)
|
||||
self.checkPixelsParam(widget, 'insertborderwidth',
|
||||
0, 1.3, 2.6, 6, -2, '10p')
|
||||
# insertborderwidth is bounded above by a half of insertwidth.
|
||||
self.checkParam(widget, 'insertborderwidth', 60, expected=100//2)
|
||||
|
||||
def test_insertwidth(self):
|
||||
def test_configure_insertwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
|
||||
self.checkParam(widget, 'insertwidth', 0.1, expected=2)
|
||||
|
@ -349,32 +350,32 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
|
|||
else:
|
||||
self.checkParam(widget, 'insertwidth', 0.9, expected=1)
|
||||
|
||||
def test_invalidcommand(self):
|
||||
def test_configure_invalidcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'invalidcommand')
|
||||
self.checkCommandParam(widget, 'invcmd')
|
||||
|
||||
def test_readonlybackground(self):
|
||||
def test_configure_readonlybackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'readonlybackground')
|
||||
|
||||
def test_show(self):
|
||||
def test_configure_show(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'show', '*')
|
||||
self.checkParam(widget, 'show', '')
|
||||
self.checkParam(widget, 'show', ' ')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'state',
|
||||
'disabled', 'normal', 'readonly')
|
||||
|
||||
def test_validate(self):
|
||||
def test_configure_validate(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'validate',
|
||||
'all', 'key', 'focus', 'focusin', 'focusout', 'none')
|
||||
|
||||
def test_validatecommand(self):
|
||||
def test_configure_validatecommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'validatecommand')
|
||||
self.checkCommandParam(widget, 'vcmd')
|
||||
|
@ -427,25 +428,25 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Spinbox(self.root, **kwargs)
|
||||
|
||||
test_show = None
|
||||
test_configure_show = None
|
||||
|
||||
def test_buttonbackground(self):
|
||||
def test_configure_buttonbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'buttonbackground')
|
||||
|
||||
def test_buttoncursor(self):
|
||||
def test_configure_buttoncursor(self):
|
||||
widget = self.create()
|
||||
self.checkCursorParam(widget, 'buttoncursor')
|
||||
|
||||
def test_buttondownrelief(self):
|
||||
def test_configure_buttondownrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'buttondownrelief')
|
||||
|
||||
def test_buttonuprelief(self):
|
||||
def test_configure_buttonuprelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'buttonuprelief')
|
||||
|
||||
def test_format(self):
|
||||
def test_configure_format(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'format', '%2f')
|
||||
self.checkParam(widget, 'format', '%2.2f')
|
||||
|
@ -460,25 +461,25 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self.checkParam(widget, 'format', '%09.200f')
|
||||
self.checkInvalidParam(widget, 'format', '%d')
|
||||
|
||||
def test_from(self):
|
||||
def test_configure_from(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'to', 100.0)
|
||||
self.checkFloatParam(widget, 'from', -10, 10.2, 11.7)
|
||||
self.checkInvalidParam(widget, 'from', 200,
|
||||
errmsg='-to value must be greater than -from value')
|
||||
|
||||
def test_increment(self):
|
||||
def test_configure_increment(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0)
|
||||
|
||||
def test_to(self):
|
||||
def test_configure_to(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'from', -100.0)
|
||||
self.checkFloatParam(widget, 'to', -10, 10.2, 11.7)
|
||||
self.checkInvalidParam(widget, 'to', -200,
|
||||
errmsg='-to value must be greater than -from value')
|
||||
|
||||
def test_values(self):
|
||||
def test_configure_values(self):
|
||||
# XXX
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['values'], '')
|
||||
|
@ -489,7 +490,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
expected='42 3.14 {} {any string}')
|
||||
self.checkParam(widget, 'values', '')
|
||||
|
||||
def test_wrap(self):
|
||||
def test_configure_wrap(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'wrap')
|
||||
|
||||
|
@ -555,17 +556,17 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Text(self.root, **kwargs)
|
||||
|
||||
def test_autoseparators(self):
|
||||
def test_configure_autoseparators(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'autoseparators')
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_blockcursor(self):
|
||||
def test_configure_blockcursor(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'blockcursor')
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_endline(self):
|
||||
def test_configure_endline(self):
|
||||
widget = self.create()
|
||||
text = '\n'.join('Line %d' for i in range(100))
|
||||
widget.insert('end', text)
|
||||
|
@ -578,50 +579,50 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.checkInvalidParam(widget, 'endline', 10,
|
||||
errmsg='-startline must be less than or equal to -endline')
|
||||
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c')
|
||||
self.checkParam(widget, 'height', -100, expected=1)
|
||||
self.checkParam(widget, 'height', 0, expected=1)
|
||||
|
||||
def test_maxundo(self):
|
||||
def test_configure_maxundo(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_inactiveselectbackground(self):
|
||||
def test_configure_inactiveselectbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'inactiveselectbackground')
|
||||
|
||||
@requires_tcl(8, 6)
|
||||
def test_insertunfocussed(self):
|
||||
def test_configure_insertunfocussed(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'insertunfocussed',
|
||||
'hollow', 'none', 'solid')
|
||||
|
||||
def test_selectborderwidth(self):
|
||||
def test_configure_selectborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'selectborderwidth',
|
||||
1.3, 2.6, -2, '10p', conv=noconv,
|
||||
keep_orig=tcl_version >= (8, 5))
|
||||
|
||||
def test_spacing1(self):
|
||||
def test_configure_spacing1(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c')
|
||||
self.checkParam(widget, 'spacing1', -5, expected=0)
|
||||
|
||||
def test_spacing2(self):
|
||||
def test_configure_spacing2(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c')
|
||||
self.checkParam(widget, 'spacing2', -1, expected=0)
|
||||
|
||||
def test_spacing3(self):
|
||||
def test_configure_spacing3(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
|
||||
self.checkParam(widget, 'spacing3', -10, expected=0)
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_startline(self):
|
||||
def test_configure_startline(self):
|
||||
widget = self.create()
|
||||
text = '\n'.join('Line %d' for i in range(100))
|
||||
widget.insert('end', text)
|
||||
|
@ -634,14 +635,14 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.checkInvalidParam(widget, 'startline', 70,
|
||||
errmsg='-startline must be less than or equal to -endline')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
if tcl_version < (8, 5):
|
||||
self.checkParams(widget, 'state', 'disabled', 'normal')
|
||||
else:
|
||||
self.checkEnumParam(widget, 'state', 'disabled', 'normal')
|
||||
|
||||
def test_tabs(self):
|
||||
def test_configure_tabs(self):
|
||||
widget = self.create()
|
||||
if get_tk_patchlevel() < (8, 5, 11):
|
||||
self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'),
|
||||
|
@ -657,21 +658,21 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
|
|||
keep_orig=tcl_version >= (8, 5))
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_tabstyle(self):
|
||||
def test_configure_tabstyle(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
|
||||
|
||||
def test_undo(self):
|
||||
def test_configure_undo(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'undo')
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'width', 402)
|
||||
self.checkParam(widget, 'width', -402, expected=1)
|
||||
self.checkParam(widget, 'width', 0, expected=1)
|
||||
|
||||
def test_wrap(self):
|
||||
def test_configure_wrap(self):
|
||||
widget = self.create()
|
||||
if tcl_version < (8, 5):
|
||||
self.checkParams(widget, 'wrap', 'char', 'none', 'word')
|
||||
|
@ -709,16 +710,16 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Canvas(self.root, **kwargs)
|
||||
|
||||
def test_closeenough(self):
|
||||
def test_configure_closeenough(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3,
|
||||
conv=float)
|
||||
|
||||
def test_confine(self):
|
||||
def test_configure_confine(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'confine')
|
||||
|
||||
def test_offset(self):
|
||||
def test_configure_offset(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['offset'], '0,0')
|
||||
self.checkParams(widget, 'offset',
|
||||
|
@ -727,7 +728,7 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.checkParam(widget, 'offset', '#5,6')
|
||||
self.checkInvalidParam(widget, 'offset', 'spam')
|
||||
|
||||
def test_scrollregion(self):
|
||||
def test_configure_scrollregion(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'scrollregion', '0 0 200 150')
|
||||
self.checkParam(widget, 'scrollregion', (0, 0, 200, 150),
|
||||
|
@ -739,17 +740,17 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200))
|
||||
self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0))
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'state', 'disabled', 'normal',
|
||||
errmsg='bad state value "{}": must be normal or disabled')
|
||||
|
||||
def test_xscrollincrement(self):
|
||||
def test_configure_xscrollincrement(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'xscrollincrement',
|
||||
40, 0, 41.2, 43.6, -40, '0.5i')
|
||||
|
||||
def test_yscrollincrement(self):
|
||||
def test_configure_yscrollincrement(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'yscrollincrement',
|
||||
10, 0, 11.2, 13.6, -10, '0.1i')
|
||||
|
@ -794,26 +795,26 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Listbox(self.root, **kwargs)
|
||||
|
||||
def test_activestyle(self):
|
||||
def test_configure_activestyle(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'activestyle',
|
||||
'dotbox', 'none', 'underline')
|
||||
|
||||
test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_justify)
|
||||
test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify)
|
||||
|
||||
def test_listvariable(self):
|
||||
def test_configure_listvariable(self):
|
||||
widget = self.create()
|
||||
var = tkinter.DoubleVar(self.root)
|
||||
self.checkVariableParam(widget, 'listvariable', var)
|
||||
|
||||
def test_selectmode(self):
|
||||
def test_configure_selectmode(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'selectmode', 'single')
|
||||
self.checkParam(widget, 'selectmode', 'browse')
|
||||
self.checkParam(widget, 'selectmode', 'multiple')
|
||||
self.checkParam(widget, 'selectmode', 'extended')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'state', 'disabled', 'normal')
|
||||
|
||||
|
@ -928,53 +929,53 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Scale(self.root, **kwargs)
|
||||
|
||||
def test_bigincrement(self):
|
||||
def test_configure_bigincrement(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5)
|
||||
|
||||
def test_digits(self):
|
||||
def test_configure_digits(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'digits', 5, 0)
|
||||
|
||||
def test_from(self):
|
||||
def test_configure_from(self):
|
||||
widget = self.create()
|
||||
conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round
|
||||
self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
|
||||
|
||||
def test_label(self):
|
||||
def test_configure_label(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'label', 'any string')
|
||||
self.checkParam(widget, 'label', '')
|
||||
|
||||
def test_length(self):
|
||||
def test_configure_length(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
|
||||
|
||||
def test_resolution(self):
|
||||
def test_configure_resolution(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2)
|
||||
|
||||
def test_showvalue(self):
|
||||
def test_configure_showvalue(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'showvalue')
|
||||
|
||||
def test_sliderlength(self):
|
||||
def test_configure_sliderlength(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'sliderlength',
|
||||
10, 11.2, 15.6, -3, '3m')
|
||||
|
||||
def test_sliderrelief(self):
|
||||
def test_configure_sliderrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'sliderrelief')
|
||||
|
||||
def test_tickinterval(self):
|
||||
def test_configure_tickinterval(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0,
|
||||
conv=float_round)
|
||||
self.checkParam(widget, 'tickinterval', -2, expected=2,
|
||||
conv=float_round)
|
||||
|
||||
def test_to(self):
|
||||
def test_configure_to(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10,
|
||||
conv=float_round)
|
||||
|
@ -998,15 +999,15 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Scrollbar(self.root, **kwargs)
|
||||
|
||||
def test_activerelief(self):
|
||||
def test_configure_activerelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'activerelief')
|
||||
|
||||
def test_elementborderwidth(self):
|
||||
def test_configure_elementborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m')
|
||||
|
||||
def test_orient(self):
|
||||
def test_configure_orient(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal',
|
||||
errmsg='bad orientation "{}": must be vertical or horizontal')
|
||||
|
@ -1047,63 +1048,63 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.PanedWindow(self.root, **kwargs)
|
||||
|
||||
def test_handlepad(self):
|
||||
def test_configure_handlepad(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m')
|
||||
|
||||
def test_handlesize(self):
|
||||
def test_configure_handlesize(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
|
||||
conv=noconv)
|
||||
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
|
||||
conv=noconv)
|
||||
|
||||
def test_opaqueresize(self):
|
||||
def test_configure_opaqueresize(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'opaqueresize')
|
||||
|
||||
@requires_tcl(8, 6, 5)
|
||||
def test_proxybackground(self):
|
||||
def test_configure_proxybackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'proxybackground')
|
||||
|
||||
@requires_tcl(8, 6, 5)
|
||||
def test_proxyborderwidth(self):
|
||||
def test_configure_proxyborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'proxyborderwidth',
|
||||
0, 1.3, 2.9, 6, -2, '10p',
|
||||
conv=noconv)
|
||||
|
||||
@requires_tcl(8, 6, 5)
|
||||
def test_proxyrelief(self):
|
||||
def test_configure_proxyrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'proxyrelief')
|
||||
|
||||
def test_sashcursor(self):
|
||||
def test_configure_sashcursor(self):
|
||||
widget = self.create()
|
||||
self.checkCursorParam(widget, 'sashcursor')
|
||||
|
||||
def test_sashpad(self):
|
||||
def test_configure_sashpad(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m')
|
||||
|
||||
def test_sashrelief(self):
|
||||
def test_configure_sashrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'sashrelief')
|
||||
|
||||
def test_sashwidth(self):
|
||||
def test_configure_sashwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
|
||||
conv=noconv)
|
||||
|
||||
def test_showhandle(self):
|
||||
def test_configure_showhandle(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'showhandle')
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
|
||||
conv=noconv)
|
||||
|
@ -1222,23 +1223,23 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Menu(self.root, **kwargs)
|
||||
|
||||
def test_postcommand(self):
|
||||
def test_configure_postcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'postcommand')
|
||||
|
||||
def test_tearoff(self):
|
||||
def test_configure_tearoff(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'tearoff')
|
||||
|
||||
def test_tearoffcommand(self):
|
||||
def test_configure_tearoffcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'tearoffcommand')
|
||||
|
||||
def test_title(self):
|
||||
def test_configure_title(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'title', 'any string')
|
||||
|
||||
def test_type(self):
|
||||
def test_configure_type(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'type',
|
||||
'normal', 'tearoff', 'menubar')
|
||||
|
@ -1291,7 +1292,7 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return tkinter.Message(self.root, **kwargs)
|
||||
|
||||
def test_aspect(self):
|
||||
def test_configure_aspect(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'aspect', 250, 0, -300)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ requires('gui')
|
|||
|
||||
class StandardTtkOptionsTests(StandardOptionsTests):
|
||||
|
||||
def test_class(self):
|
||||
def test_configure_class(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['class'], '')
|
||||
errmsg='attempt to change read-only option'
|
||||
|
@ -26,7 +26,7 @@ class StandardTtkOptionsTests(StandardOptionsTests):
|
|||
widget2 = self.create(class_='Foo')
|
||||
self.assertEqual(widget2['class'], 'Foo')
|
||||
|
||||
def test_padding(self):
|
||||
def test_configure_padding(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'padding', 0, expected=('0',))
|
||||
self.checkParam(widget, 'padding', 5, expected=('5',))
|
||||
|
@ -38,7 +38,7 @@ class StandardTtkOptionsTests(StandardOptionsTests):
|
|||
self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p'))
|
||||
self.checkParam(widget, 'padding', (), expected='')
|
||||
|
||||
def test_style(self):
|
||||
def test_configure_style(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(widget['style'], '')
|
||||
errmsg = 'Layout Foo not found'
|
||||
|
@ -139,14 +139,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.LabelFrame(self.root, **kwargs)
|
||||
|
||||
def test_labelanchor(self):
|
||||
def test_configure_labelanchor(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'labelanchor',
|
||||
'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws',
|
||||
errmsg='Bad label anchor specification {}')
|
||||
self.checkInvalidParam(widget, 'labelanchor', 'center')
|
||||
|
||||
def test_labelwidget(self):
|
||||
def test_configure_labelwidget(self):
|
||||
widget = self.create()
|
||||
label = ttk.Label(self.root, text='Mupp', name='foo')
|
||||
self.checkParam(widget, 'labelwidget', label, expected='.foo')
|
||||
|
@ -168,17 +168,17 @@ class AbstractLabelTest(AbstractWidgetTest):
|
|||
self.checkInvalidParam(widget, name, 'spam',
|
||||
errmsg='image "spam" doesn\'t exist')
|
||||
|
||||
def test_compound(self):
|
||||
def test_configure_compound(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'compound',
|
||||
'none', 'text', 'image', 'center',
|
||||
'top', 'bottom', 'left', 'right')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'state', 'active', 'disabled', 'normal')
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'width', 402, -402, 0)
|
||||
|
||||
|
@ -197,7 +197,7 @@ class LabelTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Label(self.root, **kwargs)
|
||||
|
||||
def test_font(self):
|
||||
def test_configure_font(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'font',
|
||||
'-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
|
||||
|
@ -215,7 +215,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Button(self.root, **kwargs)
|
||||
|
||||
def test_default(self):
|
||||
def test_configure_default(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled')
|
||||
|
||||
|
@ -240,11 +240,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Checkbutton(self.root, **kwargs)
|
||||
|
||||
def test_offvalue(self):
|
||||
def test_configure_offvalue(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
|
||||
|
||||
def test_onvalue(self):
|
||||
def test_configure_onvalue(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
|
||||
|
||||
|
@ -292,27 +292,27 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Entry(self.root, **kwargs)
|
||||
|
||||
def test_invalidcommand(self):
|
||||
def test_configure_invalidcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'invalidcommand')
|
||||
|
||||
def test_show(self):
|
||||
def test_configure_show(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'show', '*')
|
||||
self.checkParam(widget, 'show', '')
|
||||
self.checkParam(widget, 'show', ' ')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'state',
|
||||
'disabled', 'normal', 'readonly')
|
||||
|
||||
def test_validate(self):
|
||||
def test_configure_validate(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'validate',
|
||||
'all', 'key', 'focus', 'focusin', 'focusout', 'none')
|
||||
|
||||
def test_validatecommand(self):
|
||||
def test_configure_validatecommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'validatecommand')
|
||||
|
||||
|
@ -429,7 +429,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Combobox(self.root, **kwargs)
|
||||
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i')
|
||||
|
||||
|
@ -459,7 +459,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
|
|||
self.assertTrue(success)
|
||||
|
||||
|
||||
def test_postcommand(self):
|
||||
def test_configure_postcommand(self):
|
||||
success = []
|
||||
|
||||
self.combo['postcommand'] = lambda: success.append(True)
|
||||
|
@ -475,7 +475,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
|
|||
self.assertEqual(len(success), 1)
|
||||
|
||||
|
||||
def test_values(self):
|
||||
def test_configure_values(self):
|
||||
def check_get_current(getval, currval):
|
||||
self.assertEqual(self.combo.get(), getval)
|
||||
self.assertEqual(self.combo.current(), currval)
|
||||
|
@ -551,7 +551,7 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.PanedWindow(self.root, **kwargs)
|
||||
|
||||
def test_orient(self):
|
||||
def test_configure_orient(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(str(widget['orient']), 'vertical')
|
||||
errmsg='attempt to change read-only option'
|
||||
|
@ -684,11 +684,11 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Radiobutton(self.root, **kwargs)
|
||||
|
||||
def test_value(self):
|
||||
def test_configure_value(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
|
||||
|
||||
def test_invoke(self):
|
||||
def test_configure_invoke(self):
|
||||
success = []
|
||||
def cb_test():
|
||||
success.append(1)
|
||||
|
@ -739,7 +739,7 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
|
|||
self.checkEnumParam(widget, 'direction',
|
||||
'above', 'below', 'left', 'right', 'flush')
|
||||
|
||||
def test_menu(self):
|
||||
def test_configure_menu(self):
|
||||
widget = self.create()
|
||||
menu = tkinter.Menu(widget, name='menu')
|
||||
self.checkParam(widget, 'menu', menu, conv=str)
|
||||
|
@ -764,19 +764,19 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Scale(self.root, **kwargs)
|
||||
|
||||
def test_from(self):
|
||||
def test_configure_from(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False)
|
||||
|
||||
def test_length(self):
|
||||
def test_configure_length(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
|
||||
|
||||
def test_to(self):
|
||||
def test_configure_to(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False)
|
||||
|
||||
def test_value(self):
|
||||
def test_configure_value(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False)
|
||||
|
||||
|
@ -866,23 +866,23 @@ class ProgressbarTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Progressbar(self.root, **kwargs)
|
||||
|
||||
def test_length(self):
|
||||
def test_configure_length(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i')
|
||||
|
||||
def test_maximum(self):
|
||||
def test_configure_maximum(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False)
|
||||
|
||||
def test_mode(self):
|
||||
def test_configure_mode(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate')
|
||||
|
||||
def test_phase(self):
|
||||
def test_configure_phase(self):
|
||||
# XXX
|
||||
pass
|
||||
|
||||
def test_value(self):
|
||||
def test_configure_value(self):
|
||||
widget = self.create()
|
||||
self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10,
|
||||
conv=False)
|
||||
|
@ -1071,7 +1071,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc')
|
||||
|
||||
|
||||
def test_tabs(self):
|
||||
def test_configure_tabs(self):
|
||||
self.assertEqual(len(self.nb.tabs()), 2)
|
||||
|
||||
self.nb.forget(self.child1)
|
||||
|
@ -1147,7 +1147,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
|
||||
self.spin.update_idletasks()
|
||||
|
||||
def test_command(self):
|
||||
def test_configure_command(self):
|
||||
success = []
|
||||
|
||||
self.spin['command'] = lambda: success.append(True)
|
||||
|
@ -1167,7 +1167,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self.spin.update()
|
||||
self.assertEqual(len(success), 2)
|
||||
|
||||
def test_to(self):
|
||||
def test_configure_to(self):
|
||||
self.spin['from'] = 0
|
||||
self.spin['to'] = 5
|
||||
self.spin.set(4)
|
||||
|
@ -1179,7 +1179,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self._click_increment_arrow() # 5
|
||||
self.assertEqual(self.spin.get(), '5')
|
||||
|
||||
def test_from(self):
|
||||
def test_configure_from(self):
|
||||
self.spin['from'] = 1
|
||||
self.spin['to'] = 10
|
||||
self.spin.set(2)
|
||||
|
@ -1189,7 +1189,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self._click_decrement_arrow() # 1
|
||||
self.assertEqual(self.spin.get(), '1')
|
||||
|
||||
def test_increment(self):
|
||||
def test_configure_increment(self):
|
||||
self.spin['from'] = 0
|
||||
self.spin['to'] = 10
|
||||
self.spin['increment'] = 4
|
||||
|
@ -1203,7 +1203,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self._click_decrement_arrow() # 3
|
||||
self.assertEqual(self.spin.get(), '3')
|
||||
|
||||
def test_format(self):
|
||||
def test_configure_format(self):
|
||||
self.spin.set(1)
|
||||
self.spin['format'] = '%10.3f'
|
||||
self.spin.update()
|
||||
|
@ -1220,7 +1220,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self.assertTrue('.' not in value)
|
||||
self.assertEqual(len(value), 1)
|
||||
|
||||
def test_wrap(self):
|
||||
def test_configure_wrap(self):
|
||||
self.spin['to'] = 10
|
||||
self.spin['from'] = 1
|
||||
self.spin.set(1)
|
||||
|
@ -1239,7 +1239,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
|
|||
self._click_decrement_arrow()
|
||||
self.assertEqual(self.spin.get(), '1')
|
||||
|
||||
def test_values(self):
|
||||
def test_configure_values(self):
|
||||
self.assertEqual(self.spin['values'],
|
||||
() if tcl_version < (8, 5) else '')
|
||||
self.checkParam(self.spin, 'values', 'mon tue wed thur',
|
||||
|
@ -1299,14 +1299,14 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
|
|||
def create(self, **kwargs):
|
||||
return ttk.Treeview(self.root, **kwargs)
|
||||
|
||||
def test_columns(self):
|
||||
def test_configure_columns(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'columns', 'a b c',
|
||||
expected=('a', 'b', 'c'))
|
||||
self.checkParam(widget, 'columns', ('a', 'b', 'c'))
|
||||
self.checkParam(widget, 'columns', '')
|
||||
|
||||
def test_displaycolumns(self):
|
||||
def test_configure_displaycolumns(self):
|
||||
widget = self.create()
|
||||
widget['columns'] = ('a', 'b', 'c')
|
||||
self.checkParam(widget, 'displaycolumns', 'b a c',
|
||||
|
@ -1322,17 +1322,17 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.checkInvalidParam(widget, 'displaycolumns', (1, -2),
|
||||
errmsg='Column index -2 out of bounds')
|
||||
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False)
|
||||
self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv)
|
||||
|
||||
def test_selectmode(self):
|
||||
def test_configure_selectmode(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'selectmode',
|
||||
'none', 'browse', 'extended')
|
||||
|
||||
def test_show(self):
|
||||
def test_configure_show(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'show', 'tree headings',
|
||||
expected=('tree', 'headings'))
|
||||
|
|
|
@ -242,31 +242,31 @@ class StandardOptionsTests:
|
|||
'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand',
|
||||
)
|
||||
|
||||
def test_activebackground(self):
|
||||
def test_configure_activebackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'activebackground')
|
||||
|
||||
def test_activeborderwidth(self):
|
||||
def test_configure_activeborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'activeborderwidth',
|
||||
0, 1.3, 2.9, 6, -2, '10p')
|
||||
|
||||
def test_activeforeground(self):
|
||||
def test_configure_activeforeground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'activeforeground')
|
||||
|
||||
def test_anchor(self):
|
||||
def test_configure_anchor(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'anchor',
|
||||
'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
|
||||
|
||||
def test_background(self):
|
||||
def test_configure_background(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'background')
|
||||
if 'bg' in self.OPTIONS:
|
||||
self.checkColorParam(widget, 'bg')
|
||||
|
||||
def test_bitmap(self):
|
||||
def test_configure_bitmap(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'bitmap', 'questhead')
|
||||
self.checkParam(widget, 'bitmap', 'gray50')
|
||||
|
@ -279,88 +279,88 @@ class StandardOptionsTests:
|
|||
self.checkInvalidParam(widget, 'bitmap', 'spam',
|
||||
errmsg='bitmap "spam" not defined')
|
||||
|
||||
def test_borderwidth(self):
|
||||
def test_configure_borderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'borderwidth',
|
||||
0, 1.3, 2.6, 6, -2, '10p')
|
||||
if 'bd' in self.OPTIONS:
|
||||
self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p')
|
||||
|
||||
def test_compound(self):
|
||||
def test_configure_compound(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'compound',
|
||||
'bottom', 'center', 'left', 'none', 'right', 'top')
|
||||
|
||||
def test_cursor(self):
|
||||
def test_configure_cursor(self):
|
||||
widget = self.create()
|
||||
self.checkCursorParam(widget, 'cursor')
|
||||
|
||||
def test_disabledforeground(self):
|
||||
def test_configure_disabledforeground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'disabledforeground')
|
||||
|
||||
def test_exportselection(self):
|
||||
def test_configure_exportselection(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'exportselection')
|
||||
|
||||
def test_font(self):
|
||||
def test_configure_font(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'font',
|
||||
'-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
|
||||
self.checkInvalidParam(widget, 'font', '',
|
||||
errmsg='font "" doesn\'t exist')
|
||||
|
||||
def test_foreground(self):
|
||||
def test_configure_foreground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'foreground')
|
||||
if 'fg' in self.OPTIONS:
|
||||
self.checkColorParam(widget, 'fg')
|
||||
|
||||
def test_highlightbackground(self):
|
||||
def test_configure_highlightbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'highlightbackground')
|
||||
|
||||
def test_highlightcolor(self):
|
||||
def test_configure_highlightcolor(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'highlightcolor')
|
||||
|
||||
def test_highlightthickness(self):
|
||||
def test_configure_highlightthickness(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'highlightthickness',
|
||||
0, 1.3, 2.6, 6, '10p')
|
||||
self.checkParam(widget, 'highlightthickness', -2, expected=0,
|
||||
conv=self._conv_pixels)
|
||||
|
||||
def test_image(self):
|
||||
def test_configure_image(self):
|
||||
widget = self.create()
|
||||
self.checkImageParam(widget, 'image')
|
||||
|
||||
def test_insertbackground(self):
|
||||
def test_configure_insertbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'insertbackground')
|
||||
|
||||
def test_insertborderwidth(self):
|
||||
def test_configure_insertborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'insertborderwidth',
|
||||
0, 1.3, 2.6, 6, -2, '10p')
|
||||
|
||||
def test_insertofftime(self):
|
||||
def test_configure_insertofftime(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'insertofftime', 100)
|
||||
|
||||
def test_insertontime(self):
|
||||
def test_configure_insertontime(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'insertontime', 100)
|
||||
|
||||
def test_insertwidth(self):
|
||||
def test_configure_insertwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p')
|
||||
|
||||
def test_jump(self):
|
||||
def test_configure_jump(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'jump')
|
||||
|
||||
def test_justify(self):
|
||||
def test_configure_justify(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'justify', 'left', 'right', 'center',
|
||||
errmsg='bad justification "{}": must be '
|
||||
|
@ -369,154 +369,155 @@ class StandardOptionsTests:
|
|||
errmsg='ambiguous justification "": must be '
|
||||
'left, right, or center')
|
||||
|
||||
def test_orient(self):
|
||||
def test_configure_orient(self):
|
||||
widget = self.create()
|
||||
self.assertEqual(str(widget['orient']), self.default_orient)
|
||||
self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical')
|
||||
|
||||
def test_padx(self):
|
||||
def test_configure_padx(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m',
|
||||
conv=self._conv_pad_pixels)
|
||||
|
||||
def test_pady(self):
|
||||
def test_configure_pady(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m',
|
||||
conv=self._conv_pad_pixels)
|
||||
|
||||
def test_relief(self):
|
||||
def test_configure_relief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'relief')
|
||||
|
||||
def test_repeatdelay(self):
|
||||
def test_configure_repeatdelay(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'repeatdelay', -500, 500)
|
||||
|
||||
def test_repeatinterval(self):
|
||||
def test_configure_repeatinterval(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'repeatinterval', -500, 500)
|
||||
|
||||
def test_selectbackground(self):
|
||||
def test_configure_selectbackground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'selectbackground')
|
||||
|
||||
def test_selectborderwidth(self):
|
||||
def test_configure_selectborderwidth(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p')
|
||||
|
||||
def test_selectforeground(self):
|
||||
def test_configure_selectforeground(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'selectforeground')
|
||||
|
||||
def test_setgrid(self):
|
||||
def test_configure_setgrid(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'setgrid')
|
||||
|
||||
def test_state(self):
|
||||
def test_configure_state(self):
|
||||
widget = self.create()
|
||||
self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal')
|
||||
|
||||
def test_takefocus(self):
|
||||
def test_configure_takefocus(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'takefocus', '0', '1', '')
|
||||
|
||||
def test_text(self):
|
||||
def test_configure_text(self):
|
||||
widget = self.create()
|
||||
self.checkParams(widget, 'text', '', 'any string')
|
||||
|
||||
def test_textvariable(self):
|
||||
def test_configure_textvariable(self):
|
||||
widget = self.create()
|
||||
var = tkinter.StringVar(self.root)
|
||||
self.checkVariableParam(widget, 'textvariable', var)
|
||||
|
||||
def test_troughcolor(self):
|
||||
def test_configure_troughcolor(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'troughcolor')
|
||||
|
||||
def test_underline(self):
|
||||
def test_configure_underline(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'underline', 0, 1, 10)
|
||||
|
||||
def test_wraplength(self):
|
||||
def test_configure_wraplength(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'wraplength', 100)
|
||||
|
||||
def test_xscrollcommand(self):
|
||||
def test_configure_xscrollcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'xscrollcommand')
|
||||
|
||||
def test_yscrollcommand(self):
|
||||
def test_configure_yscrollcommand(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'yscrollcommand')
|
||||
|
||||
# non-standard but common options
|
||||
|
||||
def test_command(self):
|
||||
def test_configure_command(self):
|
||||
widget = self.create()
|
||||
self.checkCommandParam(widget, 'command')
|
||||
|
||||
def test_indicatoron(self):
|
||||
def test_configure_indicatoron(self):
|
||||
widget = self.create()
|
||||
self.checkBooleanParam(widget, 'indicatoron')
|
||||
|
||||
def test_offrelief(self):
|
||||
def test_configure_offrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'offrelief')
|
||||
|
||||
def test_overrelief(self):
|
||||
def test_configure_overrelief(self):
|
||||
widget = self.create()
|
||||
self.checkReliefParam(widget, 'overrelief')
|
||||
|
||||
def test_selectcolor(self):
|
||||
def test_configure_selectcolor(self):
|
||||
widget = self.create()
|
||||
self.checkColorParam(widget, 'selectcolor')
|
||||
|
||||
def test_selectimage(self):
|
||||
def test_configure_selectimage(self):
|
||||
widget = self.create()
|
||||
self.checkImageParam(widget, 'selectimage')
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_tristateimage(self):
|
||||
def test_configure_tristateimage(self):
|
||||
widget = self.create()
|
||||
self.checkImageParam(widget, 'tristateimage')
|
||||
|
||||
@requires_tcl(8, 5)
|
||||
def test_tristatevalue(self):
|
||||
def test_configure_tristatevalue(self):
|
||||
widget = self.create()
|
||||
self.checkParam(widget, 'tristatevalue', 'unknowable')
|
||||
|
||||
def test_variable(self):
|
||||
def test_configure_variable(self):
|
||||
widget = self.create()
|
||||
var = tkinter.DoubleVar(self.root)
|
||||
self.checkVariableParam(widget, 'variable', var)
|
||||
|
||||
|
||||
class IntegerSizeTests:
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'height', 100, -100, 0)
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkIntegerParam(widget, 'width', 402, -402, 0)
|
||||
|
||||
|
||||
class PixelSizeTests:
|
||||
def test_height(self):
|
||||
def test_configure_height(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c')
|
||||
|
||||
def test_width(self):
|
||||
def test_configure_width(self):
|
||||
widget = self.create()
|
||||
self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i')
|
||||
|
||||
|
||||
def add_standard_options(*source_classes):
|
||||
# This decorator adds test_xxx methods from source classes for every xxx
|
||||
# option in the OPTIONS class attribute if they are not defined explicitly.
|
||||
# This decorator adds test_configure_xxx methods from source classes for
|
||||
# every xxx option in the OPTIONS class attribute if they are not defined
|
||||
# explicitly.
|
||||
def decorator(cls):
|
||||
for option in cls.OPTIONS:
|
||||
methodname = 'test_' + option
|
||||
methodname = 'test_configure_' + option
|
||||
if not hasattr(cls, methodname):
|
||||
for source_class in source_classes:
|
||||
if hasattr(source_class, methodname):
|
||||
|
|
|
@ -386,7 +386,7 @@ class TixWidget(tkinter.Widget):
|
|||
self.tk.call(name, 'configure', '-' + option, value)
|
||||
# These are missing from Tkinter
|
||||
def image_create(self, imgtype, cnf={}, master=None, **kw):
|
||||
if not master:
|
||||
if master is None:
|
||||
master = self
|
||||
if kw and cnf: cnf = _cnfmerge((cnf, kw))
|
||||
elif kw: cnf = kw
|
||||
|
@ -467,7 +467,7 @@ class DisplayStyle:
|
|||
(multiple) Display Items"""
|
||||
|
||||
def __init__(self, itemtype, cnf={}, *, master=None, **kw):
|
||||
if not master:
|
||||
if master is None:
|
||||
if 'refwindow' in kw:
|
||||
master = kw['refwindow']
|
||||
elif 'refwindow' in cnf:
|
||||
|
@ -862,7 +862,7 @@ class HList(TixWidget, XView, YView):
|
|||
return self.tk.call(self._w, 'add', entry, *self._options(cnf, kw))
|
||||
|
||||
def add_child(self, parent=None, cnf={}, **kw):
|
||||
if not parent:
|
||||
if parent is None:
|
||||
parent = ''
|
||||
return self.tk.call(
|
||||
self._w, 'addchild', parent, *self._options(cnf, kw))
|
||||
|
|
|
@ -569,7 +569,7 @@ class Widget(tkinter.Widget):
|
|||
matches statespec. statespec is expected to be a sequence."""
|
||||
ret = self.tk.getboolean(
|
||||
self.tk.call(self._w, "instate", ' '.join(statespec)))
|
||||
if ret and callback:
|
||||
if ret and callback is not None:
|
||||
return callback(*args, **kw)
|
||||
|
||||
return ret
|
||||
|
|
|
@ -544,8 +544,9 @@ def TypeAlias(self, parameters):
|
|||
|
||||
@_SpecialForm
|
||||
def Concatenate(self, parameters):
|
||||
"""Used in conjunction with ParamSpec and Callable to represent a higher
|
||||
order function which adds, removes or transforms parameters of a Callable.
|
||||
"""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
|
||||
higher order function which adds, removes or transforms parameters of a
|
||||
callable.
|
||||
|
||||
For example::
|
||||
|
||||
|
@ -735,11 +736,11 @@ class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
|
|||
|
||||
Parameter specification variables exist primarily for the benefit of static
|
||||
type checkers. They are used to forward the parameter types of one
|
||||
Callable to another Callable, a pattern commonly found in higher order
|
||||
functions and decorators. They are only valid when used in Concatenate, or
|
||||
as the first argument to Callable, or as parameters for user-defined Generics.
|
||||
See class Generic for more information on generic types. An example for
|
||||
annotating a decorator::
|
||||
callable to another callable, a pattern commonly found in higher order
|
||||
functions and decorators. They are only valid when used in ``Concatenate``,
|
||||
or s the first argument to ``Callable``, or as parameters for user-defined
|
||||
Generics. See class Generic for more information on generic types. An
|
||||
example for annotating a decorator::
|
||||
|
||||
T = TypeVar('T')
|
||||
P = ParamSpec('P')
|
||||
|
@ -1249,7 +1250,7 @@ def _no_init(self, *args, **kwargs):
|
|||
raise TypeError('Protocols cannot be instantiated')
|
||||
|
||||
|
||||
def _allow_reckless_class_cheks():
|
||||
def _allow_reckless_class_checks():
|
||||
"""Allow instance and class checks for special stdlib modules.
|
||||
|
||||
The abc and functools modules indiscriminately call isinstance() and
|
||||
|
@ -1338,12 +1339,12 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
|
|||
|
||||
# First, perform various sanity checks.
|
||||
if not getattr(cls, '_is_runtime_protocol', False):
|
||||
if _allow_reckless_class_cheks():
|
||||
if _allow_reckless_class_checks():
|
||||
return NotImplemented
|
||||
raise TypeError("Instance and class checks can only be used with"
|
||||
" @runtime_checkable protocols")
|
||||
if not _is_callable_members_only(cls):
|
||||
if _allow_reckless_class_cheks():
|
||||
if _allow_reckless_class_checks():
|
||||
return NotImplemented
|
||||
raise TypeError("Protocols with non-method members"
|
||||
" don't support issubclass()")
|
||||
|
@ -1668,6 +1669,8 @@ def get_origin(tp):
|
|||
return tp.__origin__
|
||||
if tp is Generic:
|
||||
return Generic
|
||||
if isinstance(tp, types.Union):
|
||||
return types.Union
|
||||
return None
|
||||
|
||||
|
||||
|
@ -1684,12 +1687,14 @@ def get_args(tp):
|
|||
"""
|
||||
if isinstance(tp, _AnnotatedAlias):
|
||||
return (tp.__origin__,) + tp.__metadata__
|
||||
if isinstance(tp, _GenericAlias):
|
||||
if isinstance(tp, (_GenericAlias, GenericAlias)):
|
||||
res = tp.__args__
|
||||
if tp.__origin__ is collections.abc.Callable and res[0] is not Ellipsis:
|
||||
if (tp.__origin__ is collections.abc.Callable
|
||||
and not (res[0] is Ellipsis
|
||||
or isinstance(res[0], (ParamSpec, _ConcatenateGenericAlias)))):
|
||||
res = (list(res[:-1]), res[-1])
|
||||
return res
|
||||
if isinstance(tp, GenericAlias):
|
||||
if isinstance(tp, types.Union):
|
||||
return tp.__args__
|
||||
return ()
|
||||
|
||||
|
|
|
@ -773,7 +773,11 @@ def _parse_proxy(proxy):
|
|||
raise ValueError("proxy URL with no authority: %r" % proxy)
|
||||
# We have an authority, so for RFC 3986-compliant URLs (by ss 3.
|
||||
# and 3.3.), path is empty or starts with '/'
|
||||
end = r_scheme.find("/", 2)
|
||||
if '@' in r_scheme:
|
||||
host_separator = r_scheme.find('@')
|
||||
end = r_scheme.find("/", host_separator)
|
||||
else:
|
||||
end = r_scheme.find("/", 2)
|
||||
if end == -1:
|
||||
end = None
|
||||
authority = r_scheme[2:end]
|
||||
|
|
|
@ -242,15 +242,12 @@ def library_recipes():
|
|||
|
||||
result.extend([
|
||||
dict(
|
||||
name="OpenSSL 1.1.1g",
|
||||
url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
|
||||
checksum='76766e98997660138cdaf13a187bd234',
|
||||
name="OpenSSL 1.1.1i",
|
||||
url="https://www.openssl.org/source/openssl-1.1.1i.tar.gz",
|
||||
checksum='08987c3cf125202e2b0840035efb392c',
|
||||
buildrecipe=build_universal_openssl,
|
||||
configure=None,
|
||||
install=None,
|
||||
patches=[
|
||||
"openssl-mac-arm64.patch",
|
||||
],
|
||||
),
|
||||
])
|
||||
|
||||
|
@ -263,10 +260,10 @@ def library_recipes():
|
|||
tk_patches = ['tk868_on_10_8_10_9.patch']
|
||||
|
||||
else:
|
||||
tcl_tk_ver='8.6.10'
|
||||
tcl_checksum='97c55573f8520bcab74e21bfd8d0aadc'
|
||||
tcl_tk_ver='8.6.11'
|
||||
tcl_checksum='8a4c004f48984a03a7747e9ba06e4da4'
|
||||
|
||||
tk_checksum='602a47ad9ecac7bf655ada729d140a94'
|
||||
tk_checksum='c7ee71a2d05bba78dfffd76528dc17c6'
|
||||
tk_patches = [ ]
|
||||
|
||||
|
||||
|
@ -357,9 +354,9 @@ def library_recipes():
|
|||
),
|
||||
),
|
||||
dict(
|
||||
name="SQLite 3.33.0",
|
||||
url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz",
|
||||
checksum='842a8a100d7b01b09e543deb2b7951dd',
|
||||
name="SQLite 3.34.0",
|
||||
url="https://sqlite.org/2020/sqlite-autoconf-3340000.tar.gz",
|
||||
checksum='7f33c9db7b713957fcb9271fe9049fef',
|
||||
extra_cflags=('-Os '
|
||||
'-DSQLITE_ENABLE_FTS5 '
|
||||
'-DSQLITE_ENABLE_FTS4 '
|
||||
|
@ -1138,7 +1135,6 @@ def buildPythonDocs():
|
|||
if not os.path.exists(htmlDir):
|
||||
# Create virtual environment for docs builds with blurb and sphinx
|
||||
runCommand('make venv')
|
||||
runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
|
||||
runCommand('make html PYTHON=venv/bin/python')
|
||||
os.rename(htmlDir, docdir)
|
||||
os.chdir(curDir)
|
||||
|
@ -1615,7 +1611,7 @@ def buildDMG():
|
|||
if os.path.exists(outdir):
|
||||
shutil.rmtree(outdir)
|
||||
|
||||
# We used to use the deployment target as the last characters of the
|
||||
# We used to use the deployment target as the last characters of the
|
||||
# installer file name. With the introduction of weaklinked installer
|
||||
# variants, we may have two variants with the same file name, i.e.
|
||||
# both ending in '10.9'. To avoid this, we now use the major/minor
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
diff -ur openssl-1.1.1g-orig/Configurations/10-main.conf openssl-1.1.1g/Configurations/10-main.conf
|
||||
--- openssl-1.1.1g-orig/Configurations/10-main.conf 2020-04-21 14:22:39.000000000 +0200
|
||||
+++ openssl-1.1.1g/Configurations/10-main.conf 2020-07-26 12:21:32.000000000 +0200
|
||||
@@ -1557,6 +1557,14 @@
|
||||
bn_ops => "SIXTY_FOUR_BIT_LONG",
|
||||
perlasm_scheme => "macosx",
|
||||
},
|
||||
+ "darwin64-arm64-cc" => {
|
||||
+ inherit_from => [ "darwin-common", asm("aarch64_asm") ],
|
||||
+ CFLAGS => add("-Wall"),
|
||||
+ cflags => add("-arch arm64"),
|
||||
+ lib_cppflags => add("-DL_ENDIAN"),
|
||||
+ bn_ops => "SIXTY_FOUR_BIT_LONG",
|
||||
+ perlasm_scheme => "ios64",
|
||||
+ },
|
||||
|
||||
##### GNU Hurd
|
||||
"hurd-x86" => {
|
||||
diff -ur openssl-1.1.1g-orig/config openssl-1.1.1g/config
|
||||
--- openssl-1.1.1g-orig/config 2020-04-21 14:22:39.000000000 +0200
|
||||
+++ openssl-1.1.1g/config 2020-07-26 12:21:59.000000000 +0200
|
||||
@@ -255,6 +255,9 @@
|
||||
;;
|
||||
x86_64)
|
||||
echo "x86_64-apple-darwin${VERSION}"
|
||||
+ ;;
|
||||
+ arm64)
|
||||
+ echo "arm64-apple-darwin${VERSION}"
|
||||
;;
|
||||
*)
|
||||
echo "i686-apple-darwin${VERSION}"
|
||||
@@ -497,6 +500,9 @@
|
||||
else
|
||||
OUT="darwin64-x86_64-cc"
|
||||
fi ;;
|
||||
+ x86_64-apple-darwin*)
|
||||
+ OUT="darwin64-arm64-cc"
|
||||
+ ;;
|
||||
armv6+7-*-iphoneos)
|
||||
__CNF_CFLAGS="$__CNF_CFLAGS -arch armv6 -arch armv7"
|
||||
__CNF_CXXFLAGS="$__CNF_CXXFLAGS -arch armv6 -arch armv7"
|
|
@ -20,7 +20,7 @@ fi
|
|||
# Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH.
|
||||
BSH="`basename "${theShell}"`"
|
||||
case "${BSH}" in
|
||||
bash|ksh|sh|*csh|zsh)
|
||||
bash|ksh|sh|*csh|zsh|fish)
|
||||
if [ `id -ur` = 0 ]; then
|
||||
P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'`
|
||||
else
|
||||
|
@ -76,6 +76,22 @@ bash)
|
|||
PR="${HOME}/.bash_profile"
|
||||
fi
|
||||
;;
|
||||
fish)
|
||||
CONFIG_DIR="${HOME}/.config/fish"
|
||||
RC="${CONFIG_DIR}/config.fish"
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
if [ -f "${RC}" ]; then
|
||||
cp -fp "${RC}" "${RC}.pysave"
|
||||
fi
|
||||
echo "" >> "${RC}"
|
||||
echo "# Setting PATH for Python ${PYVER}" >> "${RC}"
|
||||
echo "# The original version is saved in ${RC}.pysave" >> "${RC}"
|
||||
echo "set -x PATH \"${PYTHON_ROOT}/bin\" \"\$PATH\"" >> "${RC}"
|
||||
if [ `id -ur` = 0 ]; then
|
||||
chown "${USER}" "${RC}"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
zsh)
|
||||
PR="${HOME}/.zprofile"
|
||||
;;
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>IDLE</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>%version%, © 2001-2020 Python Software Foundation</string>
|
||||
<string>%version%, © 2001-2021 Python Software Foundation</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>IDLE.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>Python Launcher</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>%VERSION%, © 2001-2020 Python Software Foundation</string>
|
||||
<string>%VERSION%, © 2001-2021 Python Software Foundation</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>PythonLauncher.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue