Compare commits

..

1 Commits

Author SHA1 Message Date
Ethan Furman 630d2e14c5 Revert "[Enum] EnumMeta.__prepare__ now accepts **kwds (#23917)"
This reverts commit 6ec0adefad.
2020-12-24 10:05:17 -08:00
221 changed files with 5509 additions and 9098 deletions

View File

@ -1,19 +0,0 @@
{
"__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
}
]
}
]
}

View File

@ -28,7 +28,7 @@ jobs:
- name: Check for source changes - name: Check for source changes
id: check id: check
run: | run: |
if [ -z "$GITHUB_BASE_REF" ]; then if [ -z "GITHUB_BASE_REF" ]; then
echo '::set-output name=run_tests::true' echo '::set-output name=run_tests::true'
else else
git fetch origin $GITHUB_BASE_REF --depth=1 git fetch origin $GITHUB_BASE_REF --depth=1
@ -99,8 +99,6 @@ jobs:
if: needs.check_source.outputs.run_tests == 'true' if: needs.check_source.outputs.run_tests == 'true'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Register MSVC problem matcher
run: echo "::add-matcher::.github/problem-matchers/msvc.json"
- name: Build CPython - name: Build CPython
run: .\PCbuild\build.bat -e -p x64 run: .\PCbuild\build.bat -e -p x64
- name: Display build info - name: Display build info

View File

@ -4,7 +4,7 @@ Copyright
Python and this documentation is: Python and this documentation is:
Copyright © 2001-2021 Python Software Foundation. All rights reserved. Copyright © 2001-2020 Python Software Foundation. All rights reserved.
Copyright © 2000 BeOpen.com. All rights reserved. Copyright © 2000 BeOpen.com. All rights reserved.

View File

@ -934,42 +934,32 @@ here is a pure Python equivalent:
if doc is None and fget is not None: if doc is None and fget is not None:
doc = fget.__doc__ doc = fget.__doc__
self.__doc__ = doc self.__doc__ = doc
self._name = ''
def __set_name__(self, owner, name):
self._name = name
def __get__(self, obj, objtype=None): def __get__(self, obj, objtype=None):
if obj is None: if obj is None:
return self return self
if self.fget is None: if self.fget is None:
raise AttributeError(f'unreadable attribute {self._name}') raise AttributeError("unreadable attribute")
return self.fget(obj) return self.fget(obj)
def __set__(self, obj, value): def __set__(self, obj, value):
if self.fset is None: if self.fset is None:
raise AttributeError(f"can't set attribute {self._name}") raise AttributeError("can't set attribute")
self.fset(obj, value) self.fset(obj, value)
def __delete__(self, obj): def __delete__(self, obj):
if self.fdel is None: if self.fdel is None:
raise AttributeError(f"can't delete attribute {self._name}") raise AttributeError("can't delete attribute")
self.fdel(obj) self.fdel(obj)
def getter(self, fget): def getter(self, fget):
prop = type(self)(fget, self.fset, self.fdel, self.__doc__) return type(self)(fget, self.fset, self.fdel, self.__doc__)
prop._name = self._name
return prop
def setter(self, fset): def setter(self, fset):
prop = type(self)(self.fget, fset, self.fdel, self.__doc__) return type(self)(self.fget, fset, self.fdel, self.__doc__)
prop._name = self._name
return prop
def deleter(self, fdel): def deleter(self, fdel):
prop = type(self)(self.fget, self.fset, fdel, self.__doc__) return type(self)(self.fget, self.fset, fdel, self.__doc__)
prop._name = self._name
return prop
.. testcode:: .. testcode::
:hide: :hide:

View File

@ -112,15 +112,14 @@ The module :mod:`curses` defines the following functions:
.. function:: color_content(color_number) .. function:: color_content(color_number)
Return the intensity of the red, green, and blue (RGB) components in the color Return the intensity of the red, green, and blue (RGB) components in the color
*color_number*, which must be between ``0`` and ``COLORS - 1``. Return a 3-tuple, *color_number*, which must be between ``0`` and :const:`COLORS`. Return a 3-tuple,
containing the R,G,B values for the given color, which will be between containing the R,G,B values for the given color, which will be between
``0`` (no component) and ``1000`` (maximum amount of component). ``0`` (no component) and ``1000`` (maximum amount of component).
.. function:: color_pair(pair_number) .. function:: color_pair(color_number)
Return the attribute value for displaying text in the specified color pair. Return the attribute value for displaying text in the specified color. This
Only the first 256 color pairs are supported. This
attribute value can be combined with :const:`A_STANDOUT`, :const:`A_REVERSE`, attribute value can be combined with :const:`A_STANDOUT`, :const:`A_REVERSE`,
and the other :const:`A_\*` attributes. :func:`pair_number` is the counterpart and the other :const:`A_\*` attributes. :func:`pair_number` is the counterpart
to this function. to this function.
@ -288,7 +287,7 @@ The module :mod:`curses` defines the following functions:
Change the definition of a color, taking the number of the color to be changed 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 followed by three RGB values (for the amounts of red, green, and blue
components). The value of *color_number* must be between ``0`` and components). The value of *color_number* must be between ``0`` and
`COLORS - 1`. Each of *r*, *g*, *b*, must be a value between ``0`` and :const:`COLORS`. 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 ``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 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``. most terminals; it is active only if :func:`can_change_color` returns ``True``.
@ -301,8 +300,7 @@ The module :mod:`curses` defines the following functions:
color number. The value of *pair_number* must be between ``1`` and 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 ``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 be changed). The value of *fg* and *bg* arguments must be between ``0`` and
``COLORS - 1``, or, after calling :func:`use_default_colors`, ``-1``. :const:`COLORS`. If the color-pair was previously initialized, the screen is
If the color-pair was previously initialized, the screen is
refreshed and all occurrences of that color-pair are changed to the new refreshed and all occurrences of that color-pair are changed to the new
definition. definition.
@ -452,7 +450,7 @@ The module :mod:`curses` defines the following functions:
.. function:: pair_content(pair_number) .. function:: pair_content(pair_number)
Return a tuple ``(fg, bg)`` containing the colors for the requested color pair. Return a tuple ``(fg, bg)`` containing the colors for the requested color pair.
The value of *pair_number* must be between ``0`` and ``COLOR_PAIRS - 1``. The value of *pair_number* must be between ``1`` and ``COLOR_PAIRS - 1``.
.. function:: pair_number(attr) .. function:: pair_number(attr)

View File

@ -289,7 +289,7 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1.
A :class:`ParameterizedMIMEHeader` class that handles the A :class:`ParameterizedMIMEHeader` class that handles the
:mailheader:`Content-Disposition` header. :mailheader:`Content-Disposition` header.
.. attribute:: content_disposition .. attribute:: content-disposition
``inline`` and ``attachment`` are the only valid values in common use. ``inline`` and ``attachment`` are the only valid values in common use.

View File

@ -62,26 +62,16 @@ The :mod:`functools` module defines the following functions:
Example:: Example::
class DataSet: class DataSet:
def __init__(self, sequence_of_numbers): def __init__(self, sequence_of_numbers):
self._data = tuple(sequence_of_numbers) self._data = sequence_of_numbers
@cached_property @cached_property
def stdev(self): def stdev(self):
return statistics.stdev(self._data) return statistics.stdev(self._data)
The mechanics of :func:`cached_property` are somewhat different from @cached_property
:func:`property`. A regular property blocks attribute writes unless a def variance(self):
setter is defined. In contrast, a *cached_property* allows writes. return statistics.variance(self._data)
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` Note, this decorator interferes with the operation of :pep:`412`
key-sharing dictionaries. This means that instance dictionaries key-sharing dictionaries. This means that instance dictionaries

View File

@ -115,9 +115,8 @@ Every distribution includes some metadata, which you can extract using the
>>> wheel_metadata = metadata('wheel') # doctest: +SKIP >>> wheel_metadata = metadata('wheel') # doctest: +SKIP
The keys of the returned data structure, a ``PackageMetadata``, The keys of the returned data structure [#f1]_ name the metadata keywords, and
name the metadata keywords, and their values are returned unparsed from the distribution metadata::
the values are returned unparsed from the distribution metadata::
>>> wheel_metadata['Requires-Python'] # doctest: +SKIP >>> wheel_metadata['Requires-Python'] # doctest: +SKIP
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
@ -207,9 +206,9 @@ Thus, an alternative way to get the version number is through the
There are all kinds of additional metadata available on the ``Distribution`` There are all kinds of additional metadata available on the ``Distribution``
instance:: instance::
>>> dist.metadata['Requires-Python'] # doctest: +SKIP >>> d.metadata['Requires-Python'] # doctest: +SKIP
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
>>> dist.metadata['License'] # doctest: +SKIP >>> d.metadata['License'] # doctest: +SKIP
'MIT' 'MIT'
The full set of available metadata is not described here. See :pep:`566` The full set of available metadata is not described here. See :pep:`566`
@ -260,3 +259,9 @@ a custom finder, return instances of this derived ``Distribution`` in the
.. rubric:: Footnotes .. 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.

View File

@ -786,18 +786,6 @@ which incur interpreter overhead.
def dotproduct(vec1, vec2): def dotproduct(vec1, vec2):
return sum(map(operator.mul, 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): def flatten(list_of_lists):
"Flatten one level of nesting" "Flatten one level of nesting"
return chain.from_iterable(list_of_lists) return chain.from_iterable(list_of_lists)

View File

@ -665,14 +665,14 @@ The ``errors`` module has the following attributes:
.. data:: codes .. data:: codes
A dictionary mapping string descriptions to their error codes. A dictionary mapping numeric error codes to their string descriptions.
.. versionadded:: 3.2 .. versionadded:: 3.2
.. data:: messages .. data:: messages
A dictionary mapping numeric error codes to their string descriptions. A dictionary mapping string descriptions to their error codes.
.. versionadded:: 3.2 .. versionadded:: 3.2

View File

@ -135,15 +135,6 @@ Functions for integers
values. Formerly it used a style like ``int(random()*n)`` which could produce values. Formerly it used a style like ``int(random()*n)`` which could produce
slightly uneven distributions. 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) .. function:: randint(a, b)
Return a random integer *N* such that ``a <= N <= b``. Alias for Return a random integer *N* such that ``a <= N <= b``. Alias for

View File

@ -907,9 +907,11 @@ 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; 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. otherwise, it performs a 2-byte swap operation.
.. versionchanged:: 3.10 .. deprecated:: 3.7
Raises :exc:`OverflowError` if *x* does not fit in a 16-bit unsigned In case *x* does not fit in 16-bit unsigned integer, but does fit in a
integer. 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.
.. function:: htonl(x) .. function:: htonl(x)
@ -925,9 +927,11 @@ 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; 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. otherwise, it performs a 2-byte swap operation.
.. versionchanged:: 3.10 .. deprecated:: 3.7
Raises :exc:`OverflowError` if *x* does not fit in a 16-bit unsigned In case *x* does not fit in 16-bit unsigned integer, but does fit in a
integer. 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.
.. function:: inet_aton(ip_string) .. function:: inet_aton(ip_string)

View File

@ -546,7 +546,7 @@ Connection Objects
con.close() con.close()
.. method:: backup(target, *, pages=-1, progress=None, name="main", sleep=0.250) .. method:: backup(target, *, pages=0, progress=None, name="main", sleep=0.250)
This method makes a backup of a SQLite database even while it's being accessed 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 by other clients, or concurrently by the same connection. The copy will be

View File

@ -198,7 +198,7 @@ However, for reading convenience, most of the examples show sorted sequences.
.. versionadded:: 3.6 .. versionadded:: 3.6
.. versionchanged:: 3.10 .. versionchanged:: 3.8
Added support for *weights*. Added support for *weights*.
.. function:: median(data) .. function:: median(data)

View File

@ -4959,11 +4959,6 @@ All parameterized generics implement special read-only attributes.
(~T,) (~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:: .. seealso::
* :pep:`585` -- "Type Hinting Generics In Standard Collections" * :pep:`585` -- "Type Hinting Generics In Standard Collections"

View File

@ -1187,9 +1187,8 @@ calls these functions.
The arguments shown above are merely some common ones. The arguments shown above are merely some common ones.
The full function signature is largely the same as that of :func:`run` - The full function signature is largely the same as that of :func:`run` -
most arguments are passed directly through to that interface. most arguments are passed directly through to that interface.
One API deviation from :func:`run` behavior exists: passing ``input=None`` However, explicitly passing ``input=None`` to inherit the parent's
will behave the same as ``input=b''`` (or ``input=''``, depending on other standard input file handle is not supported.
arguments) rather than using the parent's standard input file handle.
By default, this function will return the data as encoded bytes. The actual 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 encoding of the output data may depend on the command being invoked, so the

View File

@ -160,8 +160,6 @@ Examining Symbol Tables
Return ``True`` if the symbol is annotated. Return ``True`` if the symbol is annotated.
.. versionadded:: 3.6
.. method:: is_free() .. method:: is_free()
Return ``True`` if the symbol is referenced in its block, but not assigned Return ``True`` if the symbol is referenced in its block, but not assigned

View File

@ -91,9 +91,6 @@ The different font weights and slants are:
Return the names of defined fonts. Return the names of defined fonts.
.. function:: nametofont(name, root=None) .. function:: nametofont(name)
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.

View File

@ -18,8 +18,7 @@
-------------- --------------
This module provides runtime support for type hints as specified by This module provides runtime support for type hints as specified by
:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, :pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, and :pep:`613`.
:pep:`612` and :pep:`613`.
The most fundamental support consists of the types :data:`Any`, :data:`Union`, The most fundamental support consists of the types :data:`Any`, :data:`Union`,
:data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and :data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and
:class:`Generic`. For full specification please see :pep:`484`. For :class:`Generic`. For full specification please see :pep:`484`. For
@ -172,22 +171,6 @@ It is possible to declare the return type of a callable without specifying
the call signature by substituting a literal ellipsis the call signature by substituting a literal ellipsis
for the list of arguments in the type hint: ``Callable[..., ReturnType]``. 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:
Generics Generics
@ -333,43 +316,6 @@ User defined generic type aliases are also supported. Examples::
.. versionchanged:: 3.7 .. versionchanged:: 3.7
:class:`Generic` no longer has a custom metaclass. :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 A user-defined generic class can have ABCs as base classes without a metaclass
conflict. Generic metaclasses are not supported. The outcome of parameterizing conflict. Generic metaclasses are not supported. The outcome of parameterizing
generics is cached, and most types in the typing module are hashable and generics is cached, and most types in the typing module are hashable and
@ -656,80 +602,10 @@ These can be used as types in annotations using ``[]``, each having a unique syn
``Callable[..., Any]``, and in turn to ``Callable[..., Any]``, and in turn to
:class:`collections.abc.Callable`. :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 .. deprecated:: 3.9
:class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and
:ref:`types-genericalias`. :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]) .. class:: Type(Generic[CT_co])
A variable annotated with ``C`` may accept a value of type ``C``. In A variable annotated with ``C`` may accept a value of type ``C``. In
@ -1000,84 +876,6 @@ 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, for the type variable must be a subclass of the boundary type,
see :pep:`484`. 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 .. data:: AnyStr
``AnyStr`` is a type variable defined as ``AnyStr`` is a type variable defined as

View File

@ -100,7 +100,7 @@ PSF LICENSE AGREEMENT FOR PYTHON |release|
analyze, test, perform and/or display publicly, prepare derivative works, analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python |release| alone or in any derivative distribute, and otherwise use Python |release| alone or in any derivative
version, provided, however, that PSF's License Agreement and PSF's notice of version, provided, however, that PSF's License Agreement and PSF's notice of
copyright, i.e., "Copyright © 2001-2021 Python Software Foundation; All Rights copyright, i.e., "Copyright © 2001-2020 Python Software Foundation; All Rights
Reserved" are retained in Python |release| alone or in any derivative version Reserved" are retained in Python |release| alone or in any derivative version
prepared by Licensee. prepared by Licensee.

View File

@ -168,7 +168,6 @@ library/ipaddress,,::,2001:db00::0/24
library/ipaddress,,:db00,2001:db00::0/ffff:ff00:: library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
library/ipaddress,,::,2001:db00::0/ffff:ff00:: library/ipaddress,,::,2001:db00::0/ffff:ff00::
library/itertools,,:step,elements from seq[start:stop:step] 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/itertools,,:stop,elements from seq[start:stop:step]
library/logging.handlers,,:port,host:port library/logging.handlers,,:port,host:port
library/mmap,,:i2,obj[i1:i2] library/mmap,,:i2,obj[i1:i2]

1 c-api/arg :ref PyArg_ParseTuple(args, "O|O:ref", &object, &callback)
168 library/ipaddress :db00 2001:db00::0/ffff:ff00::
169 library/ipaddress :: 2001:db00::0/ffff:ff00::
170 library/itertools :step elements from seq[start:stop:step]
library/itertools :: kernel = tuple(kernel)[::-1]
171 library/itertools :stop elements from seq[start:stop:step]
172 library/logging.handlers :port host:port
173 library/mmap :i2 obj[i1:i2]

View File

@ -144,28 +144,6 @@ See :pep:`604` for more details.
(Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) (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 Other Language Changes
====================== ======================
@ -426,11 +404,9 @@ Optimizations
average. average.
(Contributed by Victor Stinner in :issue:`41006`.) (Contributed by Victor Stinner in :issue:`41006`.)
* The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism. It * The ``LOAD_ATTR`` instruction now uses new "per opcode cache" mechanism.
is about 36% faster now. This makes optimized ``LOAD_ATTR`` instructions the It is about 36% faster now. (Contributed by Pablo Galindo and Yury Selivanov
current most performance attribute access method (faster than slots). in :issue:`42093`, based on ideas implemented originally in PyPy and MicroPython.)
(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 * When building Python with ``--enable-optimizations`` now
``-fno-semantic-interposition`` is added to both the compile and link line. ``-fno-semantic-interposition`` is added to both the compile and link line.
@ -561,12 +537,6 @@ Changes in the Python API
silently in Python 3.9. silently in Python 3.9.
(Contributed by Ken Jin in :issue:`42195`.) (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 CPython bytecode changes
======================== ========================
@ -588,10 +558,6 @@ Build Changes
* The :mod:`atexit` module must now always be built as a built-in module. * The :mod:`atexit` module must now always be built as a built-in module.
(Contributed by Victor Stinner in :issue:`42639`.) (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 C API Changes
============= =============

View File

@ -1482,37 +1482,4 @@ and to match the behavior of static type checkers specified in the PEP.
File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set' 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`.)

View File

@ -580,23 +580,18 @@ star_targets[expr_ty]:
| a=star_target !',' { a } | a=star_target !',' { a }
| a=star_target b=(',' c=star_target { c })* [','] { | a=star_target b=(',' c=star_target { c })* [','] {
_Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) } _Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, a, b)), Store, EXTRA) }
star_targets_list_seq[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_target+ [','] { a } star_targets_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): star_target[expr_ty] (memo):
| '*' a=(!'*' star_target) { | '*' a=(!'*' star_target) {
_Py_Starred(CHECK(expr_ty, _PyPegen_set_expr_context(p, a, Store)), Store, EXTRA) } _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=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) } | a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) }
| star_atom | star_atom
star_atom[expr_ty]: star_atom[expr_ty]:
| a=NAME { _PyPegen_set_expr_context(p, a, Store) } | a=NAME { _PyPegen_set_expr_context(p, a, Store) }
| '(' a=target_with_star_atom ')' { _PyPegen_set_expr_context(p, a, Store) } | '(' a=star_target ')' { _PyPegen_set_expr_context(p, a, Store) }
| '(' a=[star_targets_tuple_seq] ')' { _Py_Tuple(a, Store, EXTRA) } | '(' a=[star_targets_seq] ')' { _Py_Tuple(a, Store, EXTRA) }
| '[' a=[star_targets_list_seq] ']' { _Py_List(a, Store, EXTRA) } | '[' a=[star_targets_seq] ']' { _Py_List(a, Store, EXTRA) }
single_target[expr_ty]: single_target[expr_ty]:
| single_subscript_attribute_target | single_subscript_attribute_target

View File

@ -63,7 +63,7 @@ PyVectorcall_Function(PyObject *callable)
{ {
PyTypeObject *tp; PyTypeObject *tp;
Py_ssize_t offset; Py_ssize_t offset;
vectorcallfunc ptr; vectorcallfunc *ptr;
assert(callable != NULL); assert(callable != NULL);
tp = Py_TYPE(callable); tp = Py_TYPE(callable);
@ -73,8 +73,8 @@ PyVectorcall_Function(PyObject *callable)
assert(PyCallable_Check(callable)); assert(PyCallable_Check(callable));
offset = tp->tp_vectorcall_offset; offset = tp->tp_vectorcall_offset;
assert(offset > 0); assert(offset > 0);
memcpy(&ptr, (char *) callable + offset, sizeof(ptr)); ptr = (vectorcallfunc *)(((char *)callable) + offset);
return ptr; return *ptr;
} }
/* Call the callable object 'callable' with the "vectorcall" calling /* Call the callable object 'callable' with the "vectorcall" calling

View File

@ -35,13 +35,12 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
_PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*. _PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*.
*/ */
typedef struct _Py_Identifier { typedef struct _Py_Identifier {
struct _Py_Identifier *next;
const char* string; const char* string;
// Index in PyInterpreterState.unicode.ids.array. It is process-wide PyObject *object;
// unique and must be initialized to -1.
Py_ssize_t index;
} _Py_Identifier; } _Py_Identifier;
#define _Py_static_string_init(value) { .string = value, .index = -1 } #define _Py_static_string_init(value) { .next = NULL, .string = value, .object = NULL }
#define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value) #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) #define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)

View File

@ -64,11 +64,6 @@ struct _Py_bytes_state {
PyBytesObject *characters[256]; PyBytesObject *characters[256];
}; };
struct _Py_unicode_ids {
Py_ssize_t size;
PyObject **array;
};
struct _Py_unicode_state { struct _Py_unicode_state {
// The empty Unicode object is a singleton to improve performance. // The empty Unicode object is a singleton to improve performance.
PyObject *empty_string; PyObject *empty_string;
@ -76,19 +71,6 @@ struct _Py_unicode_state {
shared as well. */ shared as well. */
PyObject *latin1[256]; PyObject *latin1[256];
struct _Py_unicode_fs_codec fs_codec; 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 { struct _Py_float_state {
@ -191,27 +173,6 @@ 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 */ /* interpreter state */
#define _PY_NSMALLPOSINTS 257 #define _PY_NSMALLPOSINTS 257
@ -316,7 +277,6 @@ struct _is {
struct _Py_exc_state exc_state; struct _Py_exc_state exc_state;
struct ast_state ast; struct ast_state ast;
struct type_cache type_cache;
}; };
extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp); extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp);

View File

@ -27,9 +27,6 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
return ((type->tp_flags & feature) != 0); return ((type->tp_flags & feature) != 0);
} }
extern void _PyType_InitCache(PyInterpreterState *interp);
/* Inline functions trading binary compatibility for speed: /* Inline functions trading binary compatibility for speed:
_PyObject_Init() is the fast version of PyObject_Init(), and _PyObject_Init() is the fast version of PyObject_Init(), and
_PyObject_InitVar() is the fast version of PyObject_InitVar(). _PyObject_InitVar() is the fast version of PyObject_InitVar().

View File

@ -76,7 +76,7 @@ extern void _PyExc_Fini(PyThreadState *tstate);
extern void _PyImport_Fini(void); extern void _PyImport_Fini(void);
extern void _PyImport_Fini2(void); extern void _PyImport_Fini2(void);
extern void _PyGC_Fini(PyThreadState *tstate); extern void _PyGC_Fini(PyThreadState *tstate);
extern void _PyType_Fini(PyThreadState *tstate); extern void _PyType_Fini(void);
extern void _Py_HashRandomization_Fini(void); extern void _Py_HashRandomization_Fini(void);
extern void _PyUnicode_Fini(PyThreadState *tstate); extern void _PyUnicode_Fini(PyThreadState *tstate);
extern void _PyUnicode_ClearInterned(PyThreadState *tstate); extern void _PyUnicode_ClearInterned(PyThreadState *tstate);

View File

@ -49,11 +49,6 @@ typedef struct _Py_AuditHookEntry {
void *userData; void *userData;
} _Py_AuditHookEntry; } _Py_AuditHookEntry;
struct _Py_unicode_runtime_ids {
PyThread_type_lock lock;
Py_ssize_t next_index;
};
/* Full Python runtime state */ /* Full Python runtime state */
typedef struct pyruntimestate { typedef struct pyruntimestate {
@ -111,8 +106,6 @@ typedef struct pyruntimestate {
void *open_code_userdata; void *open_code_userdata;
_Py_AuditHookEntry *audit_hook_head; _Py_AuditHookEntry *audit_hook_head;
struct _Py_unicode_runtime_ids unicode_ids;
// XXX Consolidate globals found via the check-c-globals script. // XXX Consolidate globals found via the check-c-globals script.
} _PyRuntimeState; } _PyRuntimeState;

View File

@ -20,10 +20,10 @@
#define PY_MINOR_VERSION 10 #define PY_MINOR_VERSION 10
#define PY_MICRO_VERSION 0 #define PY_MICRO_VERSION 0
#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA
#define PY_RELEASE_SERIAL 4 #define PY_RELEASE_SERIAL 3
/* Version as a string */ /* Version as a string */
#define PY_VERSION "3.10.0a4+" #define PY_VERSION "3.10.0a3+"
/*--end constants--*/ /*--end constants--*/
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.

View File

@ -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, distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright, 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, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation; 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee. prepared by Licensee.

View File

@ -63,10 +63,7 @@ def literal_eval(node_or_string):
if isinstance(node_or_string, Expression): if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body node_or_string = node_or_string.body
def _raise_malformed_node(node): def _raise_malformed_node(node):
msg = "malformed node or string" raise ValueError(f'malformed node or string: {node!r}')
if lno := getattr(node, 'lineno', None):
msg += f' on line {lno}'
raise ValueError(msg + f': {node!r}')
def _convert_num(node): def _convert_num(node):
if not isinstance(node, Constant) or type(node.value) not in (int, float, complex): if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
_raise_malformed_node(node) _raise_malformed_node(node)

View File

@ -344,7 +344,7 @@ def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
global _a85chars, _a85chars2 global _a85chars, _a85chars2
# Delay the initialization of tables to not waste memory # Delay the initialization of tables to not waste memory
# if the function is never called # if the function is never called
if _a85chars2 is None: if _a85chars is None:
_a85chars = [bytes((i,)) for i in range(33, 118)] _a85chars = [bytes((i,)) for i in range(33, 118)]
_a85chars2 = [(a + b) for a in _a85chars for b in _a85chars] _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
@ -452,7 +452,7 @@ def b85encode(b, pad=False):
global _b85chars, _b85chars2 global _b85chars, _b85chars2
# Delay the initialization of tables to not waste memory # Delay the initialization of tables to not waste memory
# if the function is never called # if the function is never called
if _b85chars2 is None: if _b85chars is None:
_b85chars = [bytes((i,)) for i in _b85alphabet] _b85chars = [bytes((i,)) for i in _b85alphabet]
_b85chars2 = [(a + b) for a in _b85chars for b in _b85chars] _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
return _85encode(b, _b85chars, _b85chars2, pad) return _85encode(b, _b85chars, _b85chars2, pad)

View File

@ -178,7 +178,7 @@ class EnumMeta(type):
Metaclass for Enum Metaclass for Enum
""" """
@classmethod @classmethod
def __prepare__(metacls, cls, bases, **kwds): def __prepare__(metacls, cls, bases):
# check that previous enum members do not exist # check that previous enum members do not exist
metacls._check_for_existing_members(cls, bases) metacls._check_for_existing_members(cls, bases)
# create the namespace dict # create the namespace dict
@ -235,10 +235,10 @@ class EnumMeta(type):
# create our new Enum type # create our new Enum type
if bases: if bases:
bases = (_NoInitSubclass, ) + bases bases = (_NoInitSubclass, ) + bases
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) enum_class = type.__new__(metacls, cls, bases, classdict)
enum_class.__bases__ = enum_class.__bases__[1:] #or (object, ) enum_class.__bases__ = enum_class.__bases__[1:] #or (object, )
else: else:
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) enum_class = type.__new__(metacls, cls, bases, classdict)
old_init_subclass = getattr(enum_class, '__init_subclass__', None) old_init_subclass = getattr(enum_class, '__init_subclass__', None)
# and restore the new one (if there was one) # and restore the new one (if there was one)
if new_init_subclass is not None: if new_init_subclass is not None:

View File

@ -3,9 +3,6 @@ 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 bpo-42508: Keep IDLE running on macOS. Remove obsolete workaround
that prevented running files with shortcuts when using new universal2 that prevented running files with shortcuts when using new universal2
installers built on macOS 11. installers built on macOS 11.

View File

@ -2316,15 +2316,7 @@ display when Code Context is turned on for an editor window.
Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines Shell Preferences: Auto-Squeeze Min. Lines is the minimum number of lines
of output to automatically "squeeze". 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.
''',
} }

View File

@ -28,8 +28,8 @@ variables:
(There are a few more, but they are rarely useful.) (There are a few more, but they are rarely useful.)
The extension class must not directly bind Window Manager (e.g. X) events. The extension class must not directly bind Window Manager (e.g. X) events.
Rather, it must define one or more virtual events, e.g. <<z-in>>, and Rather, it must define one or more virtual events, e.g. <<zoom-height>>, and
corresponding methods, e.g. z_in_event(). The virtual events will be corresponding methods, e.g. zoom_height_event(). The virtual events will be
bound to the corresponding methods, and Window Manager events can then be bound 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 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 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 implement. (They are also not required to create keybindings, but in that
case there must be empty bindings in cofig-extensions.def) case there must be empty bindings in cofig-extensions.def)
Here is a partial example from zzdummy.py: Here is a complete example:
class ZzDummy: class ZoomHeight:
menudefs = [ menudefs = [
('format', [ ('edit', [
('Z in', '<<z-in>>'), None, # Separator
('Z out', '<<z-out>>'), ('_Zoom Height', '<<zoom-height>>'),
] ) ])
] ]
def __init__(self, editwin): def __init__(self, editwin):
self.editwin = editwin self.editwin = editwin
def z_in_event(self, event=None): def zoom_height_event(self, event):
"...Do what you want here..." "...Do what you want here..."
The final piece of the puzzle is the file "config-extensions.def", which is The final piece of the puzzle is the file "config-extensions.def", which is

View File

@ -1,152 +0,0 @@
"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)

View File

@ -1,73 +1,42 @@
"""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 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: class ZzDummy:
"""Prepend or remove initial text from selected lines."""
# Extend the format menu. ## menudefs = [
menudefs = [ ## ('format', [
('format', [ ## ('Z in', '<<z-in>>'),
('Z in', '<<z-in>>'), ## ('Z out', '<<z-out>>'),
('Z out', '<<z-out>>'), ## ] )
] ) ## ]
]
def __init__(self, editwin): def __init__(self, editwin):
"Initialize the settings for this extension."
self.editwin = editwin
self.text = editwin.text self.text = editwin.text
self.formatter = editwin.fregion z_in = False
@classmethod @classmethod
def reload(cls): def reload(cls):
"Load class variables from config."
cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text') cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
@format_selection def z_in_event(self, event):
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) text = self.text
return line[zlength:] text.undo_block_start()
for line in range(1, text.index('end')):
text.insert('%d.0', ztext)
text.undo_block_stop()
return "break"
def z_out_event(self, event): pass
ZzDummy.reload() ZzDummy.reload()
##if __name__ == "__main__":
if __name__ == "__main__": ## import unittest
import unittest ## unittest.main('idlelib.idle_test.test_zzdummy',
unittest.main('idlelib.idle_test.test_zzdummy', verbosity=2, exit=False) ## verbosity=2, exit=False)

View File

@ -1,3 +1,4 @@
import io
import os import os
import re import re
import abc import abc
@ -17,7 +18,6 @@ from contextlib import suppress
from importlib import import_module from importlib import import_module
from importlib.abc import MetaPathFinder from importlib.abc import MetaPathFinder
from itertools import starmap from itertools import starmap
from typing import Any, List, Optional, Protocol, TypeVar, Union
__all__ = [ __all__ = [
@ -31,7 +31,7 @@ __all__ = [
'metadata', 'metadata',
'requires', 'requires',
'version', 'version',
] ]
class PackageNotFoundError(ModuleNotFoundError): class PackageNotFoundError(ModuleNotFoundError):
@ -43,7 +43,7 @@ class PackageNotFoundError(ModuleNotFoundError):
@property @property
def name(self): def name(self):
(name,) = self.args name, = self.args
return name return name
@ -60,7 +60,7 @@ class EntryPoint(
r'(?P<module>[\w.]+)\s*' r'(?P<module>[\w.]+)\s*'
r'(:\s*(?P<attr>[\w.]+))?\s*' r'(:\s*(?P<attr>[\w.]+))?\s*'
r'(?P<extras>\[.*\])?\s*$' r'(?P<extras>\[.*\])?\s*$'
) )
""" """
A regular expression describing the syntax for an entry point, A regular expression describing the syntax for an entry point,
which might look like: which might look like:
@ -77,8 +77,6 @@ class EntryPoint(
following the attr, and following any extras. following the attr, and following any extras.
""" """
dist: Optional['Distribution'] = None
def load(self): def load(self):
"""Load the entry point from its definition. If only a module """Load the entry point from its definition. If only a module
is indicated by the value, return that module. Otherwise, is indicated by the value, return that module. Otherwise,
@ -106,27 +104,23 @@ class EntryPoint(
@classmethod @classmethod
def _from_config(cls, config): def _from_config(cls, config):
return ( return [
cls(name, value, group) cls(name, value, group)
for group in config.sections() for group in config.sections()
for name, value in config.items(group) for name, value in config.items(group)
) ]
@classmethod @classmethod
def _from_text(cls, text): def _from_text(cls, text):
config = ConfigParser(delimiters='=') config = ConfigParser(delimiters='=')
# case sensitive: https://stackoverflow.com/q/1611799/812183 # case sensitive: https://stackoverflow.com/q/1611799/812183
config.optionxform = str config.optionxform = str
config.read_string(text) try:
return cls._from_config(config) config.read_string(text)
except AttributeError: # pragma: nocover
@classmethod # Python 2 has no read_string
def _from_text_for(cls, text, dist): config.readfp(io.StringIO(text))
return (ep._for(dist) for ep in cls._from_text(text)) return EntryPoint._from_config(config)
def _for(self, dist):
self.dist = dist
return self
def __iter__(self): def __iter__(self):
""" """
@ -138,7 +132,7 @@ class EntryPoint(
return ( return (
self.__class__, self.__class__,
(self.name, self.value, self.group), (self.name, self.value, self.group),
) )
class PackagePath(pathlib.PurePosixPath): class PackagePath(pathlib.PurePosixPath):
@ -165,25 +159,6 @@ class FileHash:
return '<FileHash mode: {} value: {}>'.format(self.mode, self.value) 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: class Distribution:
"""A Python distribution package.""" """A Python distribution package."""
@ -235,8 +210,9 @@ class Distribution:
raise ValueError("cannot accept context and kwargs") raise ValueError("cannot accept context and kwargs")
context = context or DistributionFinder.Context(**kwargs) context = context or DistributionFinder.Context(**kwargs)
return itertools.chain.from_iterable( return itertools.chain.from_iterable(
resolver(context) for resolver in cls._discover_resolvers() resolver(context)
) for resolver in cls._discover_resolvers()
)
@staticmethod @staticmethod
def at(path): def at(path):
@ -251,24 +227,24 @@ class Distribution:
def _discover_resolvers(): def _discover_resolvers():
"""Search the meta_path for resolvers.""" """Search the meta_path for resolvers."""
declared = ( 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) return filter(None, declared)
@classmethod @classmethod
def _local(cls, root='.'): def _local(cls, root='.'):
from pep517 import build, meta from pep517 import build, meta
system = build.compat_system(root) system = build.compat_system(root)
builder = functools.partial( builder = functools.partial(
meta.build, meta.build,
source_dir=root, source_dir=root,
system=system, system=system,
) )
return PathDistribution(zipfile.Path(meta.build_as_zip(builder))) return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
@property @property
def metadata(self) -> PackageMetadata: def metadata(self):
"""Return the parsed metadata for this Distribution. """Return the parsed metadata for this Distribution.
The returned object will have keys that name the various bits of The returned object will have keys that name the various bits of
@ -281,14 +257,9 @@ class Distribution:
# effect is to just end up using the PathDistribution's self._path # effect is to just end up using the PathDistribution's self._path
# (which points to the egg-info file) attribute unchanged. # (which points to the egg-info file) attribute unchanged.
or self.read_text('') or self.read_text('')
) )
return email.message_from_string(text) return email.message_from_string(text)
@property
def name(self):
"""Return the 'Name' metadata for the distribution package."""
return self.metadata['Name']
@property @property
def version(self): def version(self):
"""Return the 'Version' metadata for the distribution package.""" """Return the 'Version' metadata for the distribution package."""
@ -296,7 +267,7 @@ class Distribution:
@property @property
def entry_points(self): def entry_points(self):
return list(EntryPoint._from_text_for(self.read_text('entry_points.txt'), self)) return EntryPoint._from_text(self.read_text('entry_points.txt'))
@property @property
def files(self): def files(self):
@ -353,10 +324,9 @@ class Distribution:
section_pairs = cls._read_sections(source.splitlines()) section_pairs = cls._read_sections(source.splitlines())
sections = { sections = {
section: list(map(operator.itemgetter('line'), results)) section: list(map(operator.itemgetter('line'), results))
for section, results in itertools.groupby( for section, results in
section_pairs, operator.itemgetter('section') itertools.groupby(section_pairs, operator.itemgetter('section'))
) }
}
return cls._convert_egg_info_reqs_to_simple_reqs(sections) return cls._convert_egg_info_reqs_to_simple_reqs(sections)
@staticmethod @staticmethod
@ -380,7 +350,6 @@ class Distribution:
requirement. This method converts the former to the requirement. This method converts the former to the
latter. See _test_deps_from_requires_text for an example. latter. See _test_deps_from_requires_text for an example.
""" """
def make_condition(name): def make_condition(name):
return name and 'extra == "{name}"'.format(name=name) return name and 'extra == "{name}"'.format(name=name)
@ -469,69 +438,48 @@ class FastPath:
names = zip_path.root.namelist() names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath self.joinpath = zip_path.joinpath
return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) 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'))
def search(self, name): def search(self, name):
return ( for child in self.children():
self.joinpath(child) n_low = child.lower()
for child in self.children() if (n_low in name.exact_matches
if name.matches(child, self.base) 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)
class Prepared: class Prepared:
""" """
A prepared search for metadata on a possibly-named package. A prepared search for metadata on a possibly-named package.
""" """
normalized = ''
normalized = None prefix = ''
suffixes = '.dist-info', '.egg-info' suffixes = '.dist-info', '.egg-info'
exact_matches = [''][:0] exact_matches = [''][:0]
versionless_egg_name = ''
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
if name is None: if name is None:
return return
self.normalized = self.normalize(name) self.normalized = name.lower().replace('-', '_')
self.exact_matches = [self.normalized + suffix for suffix in self.suffixes] self.prefix = self.normalized + '-'
self.exact_matches = [
@staticmethod self.normalized + suffix for suffix in self.suffixes]
def normalize(name): self.versionless_egg_name = self.normalized + '.egg'
"""
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): class MetadataPathFinder(DistributionFinder):
@ -552,8 +500,9 @@ class MetadataPathFinder(DistributionFinder):
def _search_paths(cls, name, paths): def _search_paths(cls, name, paths):
"""Find metadata directories in paths heuristically.""" """Find metadata directories in paths heuristically."""
return itertools.chain.from_iterable( 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): class PathDistribution(Distribution):
@ -566,15 +515,9 @@ class PathDistribution(Distribution):
self._path = path self._path = path
def read_text(self, filename): def read_text(self, filename):
with suppress( with suppress(FileNotFoundError, IsADirectoryError, KeyError,
FileNotFoundError, NotADirectoryError, PermissionError):
IsADirectoryError,
KeyError,
NotADirectoryError,
PermissionError,
):
return self._path.joinpath(filename).read_text(encoding='utf-8') return self._path.joinpath(filename).read_text(encoding='utf-8')
read_text.__doc__ = Distribution.read_text.__doc__ read_text.__doc__ = Distribution.read_text.__doc__
def locate_file(self, path): def locate_file(self, path):
@ -598,11 +541,11 @@ def distributions(**kwargs):
return Distribution.discover(**kwargs) return Distribution.discover(**kwargs)
def metadata(distribution_name) -> PackageMetadata: def metadata(distribution_name):
"""Get the metadata for the named package. """Get the metadata for the named package.
:param distribution_name: The name of the distribution package to query. :param distribution_name: The name of the distribution package to query.
:return: A PackageMetadata containing the parsed metadata. :return: An email.Message containing the parsed metadata.
""" """
return Distribution.from_name(distribution_name).metadata return Distribution.from_name(distribution_name).metadata
@ -622,11 +565,15 @@ def entry_points():
:return: EntryPoint objects for all installed packages. :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') by_group = operator.attrgetter('group')
ordered = sorted(eps, key=by_group) ordered = sorted(eps, key=by_group)
grouped = itertools.groupby(ordered, 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): def files(distribution_name):

View File

@ -174,7 +174,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
The file is read and scanned in chunks of chunksize bytes. The file is read and scanned in chunks of chunksize bytes.
""" """
if not executable: if executable is None:
try: try:
ver = os.confstr('CS_GNU_LIBC_VERSION') ver = os.confstr('CS_GNU_LIBC_VERSION')
# parse 'glibc 2.28' as ('glibc', '2.28') # parse 'glibc 2.28' as ('glibc', '2.28')
@ -769,7 +769,7 @@ class uname_result(
): ):
""" """
A uname_result that's largely compatible with a A uname_result that's largely compatible with a
simple namedtuple except that 'processor' is simple namedtuple except that 'platform' is
resolved late and cached to avoid calling "uname" resolved late and cached to avoid calling "uname"
except when needed. except when needed.
""" """
@ -784,25 +784,12 @@ class uname_result(
(self.processor,) (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): def __getitem__(self, key):
return tuple(self)[key] return tuple(iter(self))[key]
def __len__(self): def __len__(self):
return len(tuple(iter(self))) return len(tuple(iter(self)))
def __reduce__(self):
return uname_result, tuple(self)[:len(self._fields)]
_uname_cache = None _uname_cache = None

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Autogenerated by Sphinx on Mon Jan 4 17:25:50 2021 # Autogenerated by Sphinx on Mon Dec 7 19:34:00 2020
topics = {'assert': 'The "assert" statement\n' topics = {'assert': 'The "assert" statement\n'
'**********************\n' '**********************\n'
'\n' '\n'
@ -461,12 +461,13 @@ topics = {'assert': 'The "assert" statement\n'
'\n' '\n'
' async_for_stmt ::= "async" for_stmt\n' ' async_for_stmt ::= "async" for_stmt\n'
'\n' '\n'
'An *asynchronous iterable* provides an "__aiter__" method that\n' 'An *asynchronous iterable* is able to call asynchronous code in '
'directly returns an *asynchronous iterator*, which can call\n' 'its\n'
'asynchronous code in its "__anext__" method.\n' '*iter* implementation, and *asynchronous iterator* can call\n'
'asynchronous code in its *next* method.\n'
'\n' '\n'
'The "async for" statement allows convenient iteration over\n' 'The "async for" statement allows convenient iteration over\n'
'asynchronous iterables.\n' 'asynchronous iterators.\n'
'\n' '\n'
'The following code:\n' 'The following code:\n'
'\n' '\n'
@ -2382,9 +2383,8 @@ topics = {'assert': 'The "assert" statement\n'
'compatible\n' 'compatible\n'
'with an exception if it is the class or a base class of the ' 'with an exception if it is the class or a base class of the '
'exception\n' 'exception\n'
'object, or a tuple containing an item that is the class or a ' 'object or a tuple containing an item compatible with the '
'base\n' 'exception.\n'
'class of the exception object.\n'
'\n' '\n'
'If no except clause matches the exception, the search for an ' 'If no except clause matches the exception, the search for an '
'exception\n' 'exception\n'
@ -2451,32 +2451,11 @@ topics = {'assert': 'The "assert" statement\n'
'(see\n' '(see\n'
'section The standard type hierarchy) identifying the point in ' 'section The standard type hierarchy) identifying the point in '
'the\n' 'the\n'
'program where the exception occurred. The details about the ' 'program where the exception occurred. "sys.exc_info()" values '
'exception\n' 'are\n'
'accessed via "sys.exc_info()" are restored to their previous ' 'restored to their previous values (before the call) when '
'values\n' 'returning\n'
'when leaving an exception handler:\n' 'from a function that handled an exception.\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' '\n'
'The optional "else" clause is executed if the control flow ' 'The optional "else" clause is executed if the control flow '
'leaves the\n' 'leaves the\n'
@ -3006,12 +2985,13 @@ topics = {'assert': 'The "assert" statement\n'
'\n' '\n'
' async_for_stmt ::= "async" for_stmt\n' ' async_for_stmt ::= "async" for_stmt\n'
'\n' '\n'
'An *asynchronous iterable* provides an "__aiter__" method that\n' 'An *asynchronous iterable* is able to call asynchronous code in '
'directly returns an *asynchronous iterator*, which can call\n' 'its\n'
'asynchronous code in its "__anext__" method.\n' '*iter* implementation, and *asynchronous iterator* can call\n'
'asynchronous code in its *next* method.\n'
'\n' '\n'
'The "async for" statement allows convenient iteration over\n' 'The "async for" statement allows convenient iteration over\n'
'asynchronous iterables.\n' 'asynchronous iterators.\n'
'\n' '\n'
'The following code:\n' 'The following code:\n'
'\n' '\n'
@ -5544,51 +5524,44 @@ topics = {'assert': 'The "assert" statement\n'
' | | formats the result in either fixed-point ' ' | | formats the result in either fixed-point '
'format or in |\n' 'format or in |\n'
' | | scientific notation, depending on its ' ' | | scientific notation, depending on its '
'magnitude. A |\n' 'magnitude. The |\n'
' | | precision of "0" is treated as equivalent ' ' | | precise rules are as follows: suppose that '
'to a precision |\n' 'the result |\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\'" ' ' | | 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' 'and precision |\n'
' | | "p-1". In both cases insignificant ' ' | | "p-1-exp". Otherwise, the number is '
'trailing zeros are |\n' 'formatted with |\n'
' | | removed from the significand, and the ' ' | | presentation type "\'e\'" and precision '
'decimal point is |\n' '"p-1". In both cases |\n'
' | | also removed if there are no remaining ' ' | | insignificant trailing zeros are removed '
'digits following |\n' 'from the |\n'
' | | it, unless the "\'#\'" option is used. ' ' | | significand, and the decimal point is also '
'With no precision |\n' 'removed if |\n'
' | | given, uses a precision of "6" significant ' ' | | there are no remaining digits following '
'digits for |\n' 'it, unless the |\n'
' | | "float". For "Decimal", the coefficient of ' ' | | "\'#\'" option is used. Positive and '
'the result is |\n' 'negative infinity, |\n'
' | | formed from the coefficient digits of the ' ' | | positive and negative zero, and nans, are '
'value; |\n' 'formatted as |\n'
' | | scientific notation is used for values ' ' | | "inf", "-inf", "0", "-0" and "nan" '
'smaller than "1e-6" |\n' 'respectively, |\n'
' | | in absolute value and values where the ' ' | | regardless of the precision. A precision '
'place value of the |\n' 'of "0" is |\n'
' | | least significant digit is larger than 1, ' ' | | treated as equivalent to a precision of '
'and fixed-point |\n' '"1". With no |\n'
' | | notation is used otherwise. Positive and ' ' | | precision given, uses a precision of "6" '
'negative |\n' 'significant |\n'
' | | infinity, positive and negative zero, and ' ' | | digits for "float", and shows all '
'nans, are |\n' 'coefficient digits for |\n'
' | | formatted as "inf", "-inf", "0", "-0" and ' ' | | '
'"nan" |\n' '"Decimal". '
' | | respectively, regardless of the ' '|\n'
'precision. |\n'
' ' ' '
'+-----------+------------------------------------------------------------+\n' '+-----------+------------------------------------------------------------+\n'
' | "\'G\'" | General format. Same as "\'g\'" except ' ' | "\'G\'" | General format. Same as "\'g\'" except '
@ -5613,24 +5586,19 @@ topics = {'assert': 'The "assert" statement\n'
'percent sign. |\n' 'percent sign. |\n'
' ' ' '
'+-----------+------------------------------------------------------------+\n' '+-----------+------------------------------------------------------------+\n'
' | None | For "float" this is the same as "\'g\'", ' ' | None | Similar to "\'g\'", except that '
'except that when |\n' 'fixed-point notation, when |\n'
' | | fixed-point notation is used to format the ' ' | | used, has at least one digit past the '
'result, it |\n' 'decimal point. The |\n'
' | | always includes at least one digit past ' ' | | default precision is as high as needed to '
'the decimal point. |\n' 'represent the |\n'
' | | The precision used is as large as needed ' ' | | particular value. The overall effect is to '
'to represent the |\n' 'match the |\n'
' | | given value faithfully. For "Decimal", ' ' | | output of "str()" as altered by the other '
'this is the same |\n' 'format |\n'
' | | as either "\'g\'" or "\'G\'" depending on ' ' | | '
'the value of |\n' 'modifiers. '
' | | "context.capitals" for the current decimal ' '|\n'
'context. The |\n'
' | | overall effect is to match the output of '
'"str()" as |\n'
' | | altered by the other format '
'modifiers. |\n'
' ' ' '
'+-----------+------------------------------------------------------------+\n' '+-----------+------------------------------------------------------------+\n'
'\n' '\n'
@ -6004,10 +5972,8 @@ topics = {'assert': 'The "assert" statement\n'
'\n' '\n'
'Names listed in a "global" statement must not be defined as ' 'Names listed in a "global" statement must not be defined as '
'formal\n' 'formal\n'
'parameters, or as targets in "with" statements or "except" ' 'parameters or in a "for" loop control target, "class" definition,\n'
'clauses, or\n' 'function definition, "import" statement, or variable annotation.\n'
'in a "for" target list, "class" definition, function definition,\n'
'"import" statement, or variable annotation.\n'
'\n' '\n'
'**CPython implementation detail:** The current implementation does ' '**CPython implementation detail:** The current implementation does '
'not\n' 'not\n'
@ -7959,7 +7925,7 @@ topics = {'assert': 'The "assert" statement\n'
'immediate\n' 'immediate\n'
' subclasses. This method returns a list of all those ' ' subclasses. This method returns a list of all those '
'references\n' 'references\n'
' still alive. The list is in definition order. Example:\n' ' still alive. Example:\n'
'\n' '\n'
' >>> int.__subclasses__()\n' ' >>> int.__subclasses__()\n'
" [<class 'bool'>]\n" " [<class 'bool'>]\n"
@ -11258,8 +11224,7 @@ topics = {'assert': 'The "assert" statement\n'
'object is “compatible” with the exception. An object is compatible\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 ' 'with an exception if it is the class or a base class of the '
'exception\n' 'exception\n'
'object, or a tuple containing an item that is the class or a base\n' 'object or a tuple containing an item compatible with the exception.\n'
'class of the exception object.\n'
'\n' '\n'
'If no except clause matches the exception, the search for an ' 'If no except clause matches the exception, the search for an '
'exception\n' 'exception\n'
@ -11314,31 +11279,9 @@ topics = {'assert': 'The "assert" statement\n'
'the\n' 'the\n'
'exception class, the exception instance and a traceback object (see\n' 'exception class, the exception instance and a traceback object (see\n'
'section The standard type hierarchy) identifying the point in the\n' 'section The standard type hierarchy) identifying the point in the\n'
'program where the exception occurred. The details about the ' 'program where the exception occurred. "sys.exc_info()" values are\n'
'exception\n' 'restored to their previous values (before the call) when returning\n'
'accessed via "sys.exc_info()" are restored to their previous values\n' 'from a function that handled an exception.\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' '\n'
'The optional "else" clause is executed if the control flow leaves ' 'The optional "else" clause is executed if the control flow leaves '
'the\n' 'the\n'
@ -11502,6 +11445,7 @@ topics = {'assert': 'The "assert" statement\n'
' There are two types of integers:\n' ' There are two types of integers:\n'
'\n' '\n'
' Integers ("int")\n' ' Integers ("int")\n'
'\n'
' These represent numbers in an unlimited range, subject to\n' ' These represent numbers in an unlimited range, subject to\n'
' available (virtual) memory only. For the purpose of ' ' available (virtual) memory only. For the purpose of '
'shift\n' 'shift\n'

View File

@ -51,7 +51,6 @@ 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 math import tau as TWOPI, floor as _floor, isfinite as _isfinite
from os import urandom as _urandom from os import urandom as _urandom
from _collections_abc import Set as _Set, Sequence as _Sequence 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 itertools import accumulate as _accumulate, repeat as _repeat
from bisect import bisect as _bisect from bisect import bisect as _bisect
import os as _os import os as _os
@ -96,7 +95,6 @@ LOG4 = _log(4.0)
SG_MAGICCONST = 1.0 + _log(4.5) SG_MAGICCONST = 1.0 + _log(4.5)
BPF = 53 # Number of bits in a float BPF = 53 # Number of bits in a float
RECIP_BPF = 2 ** -BPF RECIP_BPF = 2 ** -BPF
_ONE = 1
class Random(_random.Random): class Random(_random.Random):
@ -289,7 +287,7 @@ class Random(_random.Random):
## -------------------- integer methods ------------------- ## -------------------- integer methods -------------------
def randrange(self, start, stop=None, step=_ONE): def randrange(self, start, stop=None, step=1):
"""Choose a random item from range(start, stop[, step]). """Choose a random item from range(start, stop[, step]).
This fixes the problem with randint() which includes the This fixes the problem with randint() which includes the
@ -299,72 +297,38 @@ class Random(_random.Random):
# This code is a bit messy to make it fast for the # This code is a bit messy to make it fast for the
# common case while still doing adequate error checking. # common case while still doing adequate error checking.
try: istart = int(start)
istart = _index(start) if istart != start:
except TypeError: raise ValueError("non-integer arg 1 for randrange()")
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: 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: if istart > 0:
return self._randbelow(istart) return self._randbelow(istart)
raise ValueError("empty range for randrange()") raise ValueError("empty range for randrange()")
# stop argument supplied. # stop argument supplied.
try: istop = int(stop)
istop = _index(stop) if istop != stop:
except TypeError: raise ValueError("non-integer stop for randrange()")
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 width = istop - istart
if istep == 1: if step == 1 and width > 0:
if width > 0: return istart + self._randbelow(width)
return istart + self._randbelow(width) if step == 1:
raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width)) raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
# Non-unit step argument supplied. # Non-unit step argument supplied.
istep = int(step)
if istep != step:
raise ValueError("non-integer step for randrange()")
if istep > 0: if istep > 0:
n = (width + istep - 1) // istep n = (width + istep - 1) // istep
elif istep < 0: elif istep < 0:
n = (width + istep + 1) // istep n = (width + istep + 1) // istep
else: else:
raise ValueError("zero step for randrange()") raise ValueError("zero step for randrange()")
if n <= 0: if n <= 0:
raise ValueError("empty range for randrange()") raise ValueError("empty range for randrange()")
return istart + istep * self._randbelow(n) return istart + istep * self._randbelow(n)
def randint(self, a, b): def randint(self, a, b):

View File

@ -1082,8 +1082,7 @@ class LMTP(SMTP):
# Handle Unix-domain sockets. # Handle Unix-domain sockets.
try: try:
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: self.sock.settimeout(self.timeout)
self.sock.settimeout(self.timeout)
self.file = None self.file = None
self.sock.connect(host) self.sock.connect(host)
except OSError: except OSError:

View File

@ -628,39 +628,6 @@ if hasattr(os, "fork"):
self.collect_children(blocking=self.block_on_close) 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: class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread.""" """Mix-in class to handle each request in a new thread."""
@ -669,9 +636,9 @@ class ThreadingMixIn:
daemon_threads = False daemon_threads = False
# If true, server_close() waits until all non-daemonic threads terminate. # If true, server_close() waits until all non-daemonic threads terminate.
block_on_close = True block_on_close = True
# Threads object # For non-daemonic threads, list of threading.Threading objects
# used by server_close() to wait for all threads completion. # used by server_close() to wait for all threads completion.
_threads = _NoThreads() _threads = None
def process_request_thread(self, request, client_address): def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread. """Same as in BaseServer but as a thread.
@ -688,17 +655,23 @@ class ThreadingMixIn:
def process_request(self, request, client_address): def process_request(self, request, client_address):
"""Start a new thread to process the request.""" """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, t = threading.Thread(target = self.process_request_thread,
args = (request, client_address)) args = (request, client_address))
t.daemon = self.daemon_threads t.daemon = self.daemon_threads
self._threads.append(t) if not t.daemon and self.block_on_close:
if self._threads is None:
self._threads = []
self._threads.append(t)
t.start() t.start()
def server_close(self): def server_close(self):
super().server_close() super().server_close()
self._threads.join() if self.block_on_close:
threads = self._threads
self._threads = None
if threads:
for thread in threads:
thread.join()
if hasattr(os, "fork"): if hasattr(os, "fork"):

View File

@ -260,14 +260,6 @@ class TraceCallbackTests(unittest.TestCase):
cur.execute(queries[0]) cur.execute(queries[0])
con2.execute("create table bar(x)") con2.execute("create table bar(x)")
cur.execute(queries[1]) 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) self.assertEqual(traced_statements, queries)

View File

@ -420,11 +420,7 @@ def check_output(*popenargs, timeout=None, **kwargs):
if 'input' in kwargs and kwargs['input'] is None: if 'input' in kwargs and kwargs['input'] is None:
# Explicitly passing input=None was previously equivalent to passing an # Explicitly passing input=None was previously equivalent to passing an
# empty string. That is maintained here for backwards compatibility. # empty string. That is maintained here for backwards compatibility.
if kwargs.get('universal_newlines') or kwargs.get('text'): kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b''
empty = ''
else:
empty = b''
kwargs['input'] = empty
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
**kwargs).stdout **kwargs).stdout

View File

@ -14,6 +14,6 @@ the user build or load random bytecodes anyway. Otherwise, this is a
import types import types
co = types.CodeType(0, 0, 0, 0, 0, 0, b'\x04\x00\x71\x00', co = types.CodeType(0, 0, 0, 0, 0, b'\x04\x71\x00\x00',
(), (), (), '', '', 1, b'') (), (), (), '', '', 1, b'')
exec(co) exec(co)

View File

@ -107,9 +107,6 @@ class MockSocket:
def close(self): def close(self):
pass pass
def connect(self, host):
pass
def socket(family=None, type=None, proto=None): def socket(family=None, type=None, proto=None):
return MockSocket(family) return MockSocket(family)
@ -155,12 +152,8 @@ error = socket_module.error
# Constants # Constants
_GLOBAL_DEFAULT_TIMEOUT = socket_module._GLOBAL_DEFAULT_TIMEOUT
AF_INET = socket_module.AF_INET AF_INET = socket_module.AF_INET
AF_INET6 = socket_module.AF_INET6 AF_INET6 = socket_module.AF_INET6
SOCK_STREAM = socket_module.SOCK_STREAM SOCK_STREAM = socket_module.SOCK_STREAM
SOL_SOCKET = None SOL_SOCKET = None
SO_REUSEADDR = None SO_REUSEADDR = None
if hasattr(socket_module, 'AF_UNIX'):
AF_UNIX = socket_module.AF_UNIX

View File

@ -69,10 +69,6 @@ def count_opcode(code, pickle):
return n return n
def identity(x):
return x
class UnseekableIO(io.BytesIO): class UnseekableIO(io.BytesIO):
def peek(self, *args): def peek(self, *args):
raise NotImplementedError raise NotImplementedError
@ -142,12 +138,11 @@ class E(C):
def __getinitargs__(self): def __getinitargs__(self):
return () return ()
# Simple mutable object. class H(object):
class Object:
pass pass
# Hashable immutable key object containing unheshable mutable data. # Hashable mutable key
class K: class K(object):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
@ -162,6 +157,10 @@ __main__.D = D
D.__module__ = "__main__" D.__module__ = "__main__"
__main__.E = E __main__.E = E
E.__module__ = "__main__" E.__module__ = "__main__"
__main__.H = H
H.__module__ = "__main__"
__main__.K = K
K.__module__ = "__main__"
class myint(int): class myint(int):
def __init__(self, x): def __init__(self, x):
@ -1497,182 +1496,54 @@ class AbstractPickleTests(unittest.TestCase):
got = filelike.getvalue() got = filelike.getvalue()
self.assertEqual(expected, got) self.assertEqual(expected, got)
def _test_recursive_list(self, cls, aslist=identity, minprotocol=0): def test_recursive_list(self):
# List containing itself. l = []
l = cls()
l.append(l) l.append(l)
for proto in range(minprotocol, pickle.HIGHEST_PROTOCOL + 1): for proto in protocols:
s = self.dumps(l, proto) s = self.dumps(l, proto)
x = self.loads(s) x = self.loads(s)
self.assertIsInstance(x, cls) self.assertIsInstance(x, list)
y = aslist(x)
self.assertEqual(len(y), 1)
self.assertIs(y[0], x)
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 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.assertEqual(len(x), 1)
self.assertIsInstance(x[0], cls) self.assertIs(x[0], x)
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): def test_recursive_tuple_and_list(self):
self._test_recursive_tuple_and_list(list) t = ([],)
t[0].append(t)
def test_recursive_tuple_and_list_subclass(self): for proto in protocols:
self._test_recursive_tuple_and_list(MyList, minprotocol=2) s = self.dumps(t, proto)
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) x = self.loads(s)
self.assertIsInstance(x, cls) self.assertIsInstance(x, tuple)
y = asdict(x) self.assertEqual(len(x), 1)
self.assertEqual(list(y.keys()), [1]) self.assertIsInstance(x[0], list)
self.assertIs(y[1], x) self.assertEqual(len(x[0]), 1)
self.assertIs(x[0][0], x)
def test_recursive_dict(self): def test_recursive_dict(self):
self._test_recursive_dict(dict) d = {}
d[1] = d
def test_recursive_dict_subclass(self): for proto in protocols:
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) s = self.dumps(d, proto)
x = self.loads(s) x = self.loads(s)
self.assertIsInstance(x, cls) self.assertIsInstance(x, dict)
y = asdict(x) self.assertEqual(list(x.keys()), [1])
self.assertEqual(len(y.keys()), 1) self.assertIs(x[1], x)
self.assertIsInstance(list(y.keys())[0], K)
self.assertIs(list(y.keys())[0].value, x)
def test_recursive_dict_key(self): def test_recursive_dict_key(self):
self._test_recursive_dict_key(dict) d = {}
k = K(d)
def test_recursive_dict_subclass_key(self): d[k] = 1
self._test_recursive_dict_key(MyDict, minprotocol=2) for proto in protocols:
s = self.dumps(d, proto)
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) x = self.loads(s)
self.assertIsInstance(x, tuple) self.assertIsInstance(x, dict)
self.assertEqual(len(x), 1) self.assertEqual(len(x.keys()), 1)
self.assertIsInstance(x[0], cls) self.assertIsInstance(list(x.keys())[0], K)
y = asdict(x[0]) self.assertIs(list(x.keys())[0].value, x)
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): def test_recursive_set(self):
# Set containing an immutable object containing the original set.
y = set() y = set()
y.add(K(y)) k = K(y)
y.add(k)
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): for proto in range(4, pickle.HIGHEST_PROTOCOL + 1):
s = self.dumps(y, proto) s = self.dumps(y, proto)
x = self.loads(s) x = self.loads(s)
@ -1681,31 +1552,52 @@ class AbstractPickleTests(unittest.TestCase):
self.assertIsInstance(list(x)[0], K) self.assertIsInstance(list(x)[0], K)
self.assertIs(list(x)[0].value, x) self.assertIs(list(x)[0].value, x)
# Immutable object containing a set containing the original object. def test_recursive_list_subclass(self):
y, = y y = MyList()
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): y.append(y)
for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
s = self.dumps(y, proto) s = self.dumps(y, proto)
x = self.loads(s) x = self.loads(s)
self.assertIsInstance(x, K) self.assertIsInstance(x, MyList)
self.assertIsInstance(x.value, set) self.assertEqual(len(x), 1)
self.assertEqual(len(x.value), 1) self.assertIs(x[0], x)
self.assertIs(list(x.value)[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)
def test_recursive_inst(self): def test_recursive_inst(self):
# Mutable object containing itself. i = C()
i = Object()
i.attr = i i.attr = i
for proto in protocols: for proto in protocols:
s = self.dumps(i, proto) s = self.dumps(i, proto)
x = self.loads(s) x = self.loads(s)
self.assertIsInstance(x, Object) self.assertIsInstance(x, C)
self.assertEqual(dir(x), dir(i)) self.assertEqual(dir(x), dir(i))
self.assertIs(x.attr, x) self.assertIs(x.attr, x)
def test_recursive_multi(self): def test_recursive_multi(self):
l = [] l = []
d = {1:l} d = {1:l}
i = Object() i = C()
i.attr = d i.attr = d
l.append(i) l.append(i)
for proto in protocols: for proto in protocols:
@ -1715,94 +1607,49 @@ class AbstractPickleTests(unittest.TestCase):
self.assertEqual(len(x), 1) self.assertEqual(len(x), 1)
self.assertEqual(dir(x[0]), dir(i)) self.assertEqual(dir(x[0]), dir(i))
self.assertEqual(list(x[0].attr.keys()), [1]) self.assertEqual(list(x[0].attr.keys()), [1])
self.assertIs(x[0].attr[1], x) self.assertTrue(x[0].attr[1] is x)
def _test_recursive_collection_and_inst(self, factory): def check_recursive_collection_and_inst(self, factory):
# Mutable object containing a collection containing the original h = H()
# object. y = factory([h])
o = Object() h.attr = y
o.attr = factory([o])
t = type(o.attr)
for proto in protocols:
s = self.dumps(o, proto)
x = self.loads(s)
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], Object)
self.assertIs(list(x)[0].attr, x)
def test_recursive_list_and_inst(self):
self._test_recursive_collection_and_inst(list)
def test_recursive_tuple_and_inst(self):
self._test_recursive_collection_and_inst(tuple)
def test_recursive_dict_and_inst(self):
self._test_recursive_collection_and_inst(dict.fromkeys)
def test_recursive_set_and_inst(self):
self._test_recursive_collection_and_inst(set)
def test_recursive_frozenset_and_inst(self):
self._test_recursive_collection_and_inst(frozenset)
def test_recursive_list_subclass_and_inst(self):
self._test_recursive_collection_and_inst(MyList)
def test_recursive_tuple_subclass_and_inst(self):
self._test_recursive_collection_and_inst(MyTuple)
def test_recursive_dict_subclass_and_inst(self):
self._test_recursive_collection_and_inst(MyDict.fromkeys)
def test_recursive_set_subclass_and_inst(self):
self._test_recursive_collection_and_inst(MySet)
def test_recursive_frozenset_subclass_and_inst(self):
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: for proto in protocols:
s = self.dumps(y, proto) s = self.dumps(y, proto)
x = self.loads(s) x = self.loads(s)
self.assertIsInstance(x, REX_state) self.assertIsInstance(x, type(y))
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.assertEqual(len(x), 1)
self.assertIsInstance(x[0], REX_state) self.assertIsInstance(list(x)[0], H)
self.assertIs(x[0].state, x) self.assertIs(list(x)[0].attr, x)
# Mutable object containing a tuple containing the object. def test_recursive_list_and_inst(self):
t, = t self.check_recursive_collection_and_inst(list)
for proto in protocols:
s = self.dumps(t, proto) def test_recursive_tuple_and_inst(self):
x = self.loads(s) self.check_recursive_collection_and_inst(tuple)
self.assertIsInstance(x, REX_state)
self.assertIsInstance(x.state, tuple) def test_recursive_dict_and_inst(self):
self.assertEqual(len(x.state), 1) self.check_recursive_collection_and_inst(dict.fromkeys)
self.assertIs(x.state[0], x)
def test_recursive_set_and_inst(self):
self.check_recursive_collection_and_inst(set)
def test_recursive_frozenset_and_inst(self):
self.check_recursive_collection_and_inst(frozenset)
def test_recursive_list_subclass_and_inst(self):
self.check_recursive_collection_and_inst(MyList)
def test_recursive_tuple_subclass_and_inst(self):
self.check_recursive_collection_and_inst(MyTuple)
def test_recursive_dict_subclass_and_inst(self):
self.check_recursive_collection_and_inst(MyDict.fromkeys)
def test_recursive_set_subclass_and_inst(self):
self.check_recursive_collection_and_inst(MySet)
def test_recursive_frozenset_subclass_and_inst(self):
self.check_recursive_collection_and_inst(MyFrozenSet)
def test_unicode(self): def test_unicode(self):
endcases = ['', '<\\u>', '<\\\u1234>', '<\n>', endcases = ['', '<\\u>', '<\\\u1234>', '<\n>',
@ -3215,19 +3062,6 @@ class REX_seven(object):
def __reduce__(self): def __reduce__(self):
return type(self), (), None, None, iter(self.table.items()) 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 # Test classes for newobj

View File

@ -1,7 +1,6 @@
"""Tests for the asdl parser in Parser/asdl.py""" """Tests for the asdl parser in Parser/asdl.py"""
import importlib.machinery import importlib.machinery
import importlib.util
import os import os
from os.path import dirname from os.path import dirname
import sys import sys
@ -27,10 +26,7 @@ class TestAsdlParser(unittest.TestCase):
sys.path.insert(0, parser_dir) sys.path.insert(0, parser_dir)
loader = importlib.machinery.SourceFileLoader( loader = importlib.machinery.SourceFileLoader(
'asdl', os.path.join(parser_dir, 'asdl.py')) 'asdl', os.path.join(parser_dir, 'asdl.py'))
spec = importlib.util.spec_from_loader('asdl', loader) cls.asdl = loader.load_module()
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.mod = cls.asdl.parse(os.path.join(parser_dir, 'Python.asdl'))
cls.assertTrue(cls.asdl.check(cls.mod), 'Module validation failed') cls.assertTrue(cls.asdl.check(cls.mod), 'Module validation failed')

View File

@ -1011,18 +1011,6 @@ Module(
self.assertEqual(ast.literal_eval(" \t -1"), -1) self.assertEqual(ast.literal_eval(" \t -1"), -1)
self.assertRaises(IndentationError, ast.literal_eval, "\n -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): def test_bad_integer(self):
# issue13436: Bad error message with invalid numeric values # issue13436: Bad error message with invalid numeric values
body = [ast.ImportFrom(module='time', body = [ast.ImportFrom(module='time',

View File

@ -1621,6 +1621,48 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(self.iter_error(z1, ValueError), t) self.assertEqual(self.iter_error(z1, ValueError), t)
self.assertEqual(self.iter_error(z2, 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): def test_zip_bad_iterable(self):
exception = TypeError() exception = TypeError()

View File

@ -4,7 +4,8 @@
# This script doesn't actually display anything very coherent. but it # This script doesn't actually display anything very coherent. but it
# does call (nearly) every method and function. # does call (nearly) every method and function.
# #
# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr() # Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
# init_color()
# Only called, not tested: getmouse(), ungetmouse() # Only called, not tested: getmouse(), ungetmouse()
# #
@ -12,7 +13,6 @@ import os
import string import string
import sys import sys
import tempfile import tempfile
import functools
import unittest import unittest
from test.support import requires, verbose, SaveSignals from test.support import requires, verbose, SaveSignals
@ -37,17 +37,7 @@ def requires_curses_func(name):
return unittest.skipUnless(hasattr(curses, name), return unittest.skipUnless(hasattr(curses, name),
'requires curses.%s' % 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') term = os.environ.get('TERM')
SHORT_MAX = 0x7fff
# If newterm was supported we could use it instead of initscr and not exit # If newterm was supported we could use it instead of initscr and not exit
@unittest.skipIf(not term or term == 'unknown', @unittest.skipIf(not term or term == 'unknown',
@ -58,59 +48,37 @@ class TestCurses(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
if verbose: if not sys.__stdout__.isatty():
print(f'TERM={term}', file=sys.stderr, flush=True) # 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()
# testing setupterm() inside initscr/endwin # testing setupterm() inside initscr/endwin
# causes terminal breakage # causes terminal breakage
stdout_fd = sys.__stdout__.fileno() curses.setupterm(fd=fd)
curses.setupterm(fd=stdout_fd)
@classmethod
def tearDownClass(cls):
if cls.tmp:
cls.tmp.close()
del cls.tmp
def setUp(self): 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 = SaveSignals()
self.save_signals.save() self.save_signals.save()
self.addCleanup(self.save_signals.restore) if verbose:
if verbose and self.output is not None:
# just to make the test output a little more readable # just to make the test output a little more readable
sys.stderr.flush() print()
sys.stdout.flush()
print(file=self.output, flush=True)
self.stdscr = curses.initscr() self.stdscr = curses.initscr()
if self.isatty: curses.savetty()
curses.savetty()
self.addCleanup(curses.endwin) def tearDown(self):
self.addCleanup(curses.resetty) curses.resetty()
curses.endwin()
self.save_signals.restore()
def test_window_funcs(self): def test_window_funcs(self):
"Test the methods of windows" "Test the methods of windows"
@ -128,7 +96,7 @@ class TestCurses(unittest.TestCase):
for meth in [stdscr.clear, stdscr.clrtobot, for meth in [stdscr.clear, stdscr.clrtobot,
stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch, stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
stdscr.deleteln, stdscr.erase, stdscr.getbegyx, stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
stdscr.getbkgd, stdscr.getmaxyx, stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
stdscr.getparyx, stdscr.getyx, stdscr.inch, stdscr.getparyx, stdscr.getyx, stdscr.inch,
stdscr.insertln, stdscr.instr, stdscr.is_wintouched, stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
win.noutrefresh, stdscr.redrawwin, stdscr.refresh, win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
@ -239,11 +207,6 @@ class TestCurses(unittest.TestCase):
if hasattr(stdscr, 'enclose'): if hasattr(stdscr, 'enclose'):
stdscr.enclose(10, 10) 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, -400)
self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400) self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
self.assertRaises(ValueError, stdscr.instr, -2) self.assertRaises(ValueError, stdscr.instr, -2)
@ -262,20 +225,17 @@ class TestCurses(unittest.TestCase):
def test_module_funcs(self): def test_module_funcs(self):
"Test module-level functions" "Test module-level functions"
for func in [curses.baudrate, curses.beep, curses.can_change_color, for func in [curses.baudrate, curses.beep, curses.can_change_color,
curses.doupdate, curses.flash, curses.flushinp, curses.cbreak, curses.def_prog_mode, curses.doupdate,
curses.flash, curses.flushinp,
curses.has_colors, curses.has_ic, curses.has_il, curses.has_colors, curses.has_ic, curses.has_il,
curses.isendwin, curses.killchar, curses.longname, curses.isendwin, curses.killchar, curses.longname,
curses.noecho, curses.nonl, curses.noqiflush, curses.nocbreak, curses.noecho, curses.nonl,
curses.termattrs, curses.termname, curses.erasechar, curses.noqiflush, curses.noraw,
curses.reset_prog_mode, curses.termattrs,
curses.termname, curses.erasechar,
curses.has_extended_color_support]: curses.has_extended_color_support]:
with self.subTest(func=func.__qualname__): with self.subTest(func=func.__qualname__):
func() 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'): if hasattr(curses, 'filter'):
curses.filter() curses.filter()
if hasattr(curses, 'getsyx'): if hasattr(curses, 'getsyx'):
@ -287,9 +247,13 @@ class TestCurses(unittest.TestCase):
curses.delay_output(1) curses.delay_output(1)
curses.echo() ; curses.echo(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.halfdelay(1)
if self.isatty: curses.intrflush(1)
curses.intrflush(1)
curses.meta(1) curses.meta(1)
curses.napms(100) curses.napms(100)
curses.newpad(50,50) curses.newpad(50,50)
@ -298,8 +262,7 @@ class TestCurses(unittest.TestCase):
curses.nl() ; curses.nl(1) curses.nl() ; curses.nl(1)
curses.putp(b'abc') curses.putp(b'abc')
curses.qiflush() curses.qiflush()
if self.isatty: curses.raw() ; curses.raw(1)
curses.raw() ; curses.raw(1)
curses.set_escdelay(25) curses.set_escdelay(25)
self.assertEqual(curses.get_escdelay(), 25) self.assertEqual(curses.get_escdelay(), 25)
curses.set_tabsize(4) curses.set_tabsize(4)
@ -318,127 +281,31 @@ class TestCurses(unittest.TestCase):
curses.use_env(1) curses.use_env(1)
# Functions only available on a few platforms # 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(): if not curses.has_colors():
self.skipTest('requires colors support') self.skipTest('requires colors support')
curses.start_color() curses.start_color()
if verbose: curses.init_pair(2, 1,1)
print(f'COLORS = {curses.COLORS}', file=sys.stderr) curses.color_content(1)
print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr) curses.color_pair(2)
curses.pair_content(curses.COLOR_PAIRS - 1)
curses.pair_number(0)
@requires_colors if hasattr(curses, 'use_default_colors'):
def test_color_content(self): curses.use_default_colors()
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
curses.color_content(0)
maxcolor = curses.COLORS - 1
curses.color_content(maxcolor)
for color in self.bad_colors(): self.assertRaises(ValueError, curses.color_content, -1)
self.assertRaises(ValueError, curses.color_content, color) self.assertRaises(ValueError, curses.color_content, curses.COLORS + 1)
self.assertRaises(ValueError, curses.color_content, -2**31 - 1)
@requires_colors self.assertRaises(ValueError, curses.color_content, 2**31)
def test_init_color(self): self.assertRaises(ValueError, curses.color_content, -2**63 - 1)
if not curses.can_change_color: self.assertRaises(ValueError, curses.color_content, 2**63 - 1)
self.skipTest('cannot change color') self.assertRaises(ValueError, curses.pair_content, -1)
self.assertRaises(ValueError, curses.pair_content, curses.COLOR_PAIRS)
old = curses.color_content(0) self.assertRaises(ValueError, curses.pair_content, -2**31 - 1)
try: self.assertRaises(ValueError, curses.pair_content, 2**31)
curses.init_color(0, *old) self.assertRaises(ValueError, curses.pair_content, -2**63 - 1)
except curses.error: self.assertRaises(ValueError, curses.pair_content, 2**63 - 1)
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') @requires_curses_func('keyname')
def test_keyname(self): def test_keyname(self):
@ -506,6 +373,7 @@ class TestCurses(unittest.TestCase):
@requires_curses_func('resizeterm') @requires_curses_func('resizeterm')
def test_resizeterm(self): def test_resizeterm(self):
stdscr = self.stdscr
lines, cols = curses.LINES, curses.COLS lines, cols = curses.LINES, curses.COLS
new_lines = lines - 1 new_lines = lines - 1
new_cols = cols + 1 new_cols = cols + 1
@ -609,8 +477,6 @@ class MiscTests(unittest.TestCase):
@requires_curses_func('ncurses_version') @requires_curses_func('ncurses_version')
def test_ncurses_version(self): def test_ncurses_version(self):
v = curses.ncurses_version v = curses.ncurses_version
if verbose:
print(f'ncurses_version = {curses.ncurses_version}', flush=True)
self.assertIsInstance(v[:], tuple) self.assertIsInstance(v[:], tuple)
self.assertEqual(len(v), 3) self.assertEqual(len(v), 3)
self.assertIsInstance(v[0], int) self.assertIsInstance(v[0], int)

View File

@ -2119,7 +2119,7 @@ class TestEnum(unittest.TestCase):
one = '1' one = '1'
two = b'2', 'ascii', 9 two = b'2', 'ascii', 9
def test_init_subclass_calling(self): def test_init_subclass(self):
class MyEnum(Enum): class MyEnum(Enum):
def __init_subclass__(cls, **kwds): def __init_subclass__(cls, **kwds):
super(MyEnum, cls).__init_subclass__(**kwds) super(MyEnum, cls).__init_subclass__(**kwds)
@ -2155,16 +2155,6 @@ class TestEnum(unittest.TestCase):
self.assertFalse(NeverEnum.__dict__.get('_test1', False)) self.assertFalse(NeverEnum.__dict__.get('_test1', False))
self.assertFalse(NeverEnum.__dict__.get('_test2', 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( @unittest.skipUnless(
sys.version_info[:2] == (3, 9), sys.version_info[:2] == (3, 9),

View File

@ -332,59 +332,6 @@ non-important content
self.assertEqual(binop.left.col_offset, 4) self.assertEqual(binop.left.col_offset, 4)
self.assertEqual(binop.right.col_offset, 7) 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 test_docstring(self):
def f(): def f():
f'''Not a docstring''' f'''Not a docstring'''

View File

@ -27,7 +27,8 @@ import functools
py_functools = import_helper.import_fresh_module('functools', py_functools = import_helper.import_fresh_module('functools',
blocked=['_functools']) blocked=['_functools'])
c_functools = import_helper.import_fresh_module('functools') c_functools = import_helper.import_fresh_module('functools',
fresh=['_functools'])
decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal']) decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal'])

View File

@ -7,7 +7,6 @@ import textwrap
import contextlib import contextlib
from test.support.os_helper import FS_NONASCII from test.support.os_helper import FS_NONASCII
from typing import Dict, Union
@contextlib.contextmanager @contextlib.contextmanager
@ -72,13 +71,8 @@ class OnSysPath(Fixtures):
self.fixtures.enter_context(self.add_sys_path(self.site_dir)) 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): class DistInfoPkg(OnSysPath, SiteDir):
files: FilesDef = { files = {
"distinfo_pkg-1.0.0.dist-info": { "distinfo_pkg-1.0.0.dist-info": {
"METADATA": """ "METADATA": """
Name: distinfo-pkg Name: distinfo-pkg
@ -92,55 +86,19 @@ class DistInfoPkg(OnSysPath, SiteDir):
[entries] [entries]
main = mod:main main = mod:main
ns:sub = mod:main ns:sub = mod:main
""", """
}, },
"mod.py": """ "mod.py": """
def main(): def main():
print("hello world") print("hello world")
""", """,
} }
def setUp(self): def setUp(self):
super(DistInfoPkg, self).setUp() super(DistInfoPkg, self).setUp()
build_files(DistInfoPkg.files, self.site_dir) 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): class DistInfoPkgOffPath(SiteDir):
def setUp(self): def setUp(self):
super(DistInfoPkgOffPath, self).setUp() super(DistInfoPkgOffPath, self).setUp()
@ -148,7 +106,7 @@ class DistInfoPkgOffPath(SiteDir):
class EggInfoPkg(OnSysPath, SiteDir): class EggInfoPkg(OnSysPath, SiteDir):
files: FilesDef = { files = {
"egginfo_pkg.egg-info": { "egginfo_pkg.egg-info": {
"PKG-INFO": """ "PKG-INFO": """
Name: egginfo-pkg Name: egginfo-pkg
@ -171,13 +129,13 @@ class EggInfoPkg(OnSysPath, SiteDir):
[test] [test]
pytest pytest
""", """,
"top_level.txt": "mod\n", "top_level.txt": "mod\n"
}, },
"mod.py": """ "mod.py": """
def main(): def main():
print("hello world") print("hello world")
""", """,
} }
def setUp(self): def setUp(self):
super(EggInfoPkg, self).setUp() super(EggInfoPkg, self).setUp()
@ -185,7 +143,7 @@ class EggInfoPkg(OnSysPath, SiteDir):
class EggInfoFile(OnSysPath, SiteDir): class EggInfoFile(OnSysPath, SiteDir):
files: FilesDef = { files = {
"egginfo_file.egg-info": """ "egginfo_file.egg-info": """
Metadata-Version: 1.0 Metadata-Version: 1.0
Name: egginfo_file Name: egginfo_file
@ -198,7 +156,7 @@ class EggInfoFile(OnSysPath, SiteDir):
Description: UNKNOWN Description: UNKNOWN
Platform: UNKNOWN Platform: UNKNOWN
""", """,
} }
def setUp(self): def setUp(self):
super(EggInfoFile, self).setUp() super(EggInfoFile, self).setUp()
@ -206,12 +164,12 @@ class EggInfoFile(OnSysPath, SiteDir):
class LocalPackage: class LocalPackage:
files: FilesDef = { files = {
"setup.py": """ "setup.py": """
import setuptools import setuptools
setuptools.setup(name="local-pkg", version="2.0.1") setuptools.setup(name="local-pkg", version="2.0.1")
""", """,
} }
def setUp(self): def setUp(self):
self.fixtures = contextlib.ExitStack() self.fixtures = contextlib.ExitStack()
@ -256,7 +214,8 @@ def build_files(file_defs, prefix=pathlib.Path()):
class FileBuilder: class FileBuilder:
def unicode_filename(self): 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): def DALS(str):

View File

@ -1,3 +1,5 @@
# coding: utf-8
import re import re
import json import json
import pickle import pickle
@ -12,14 +14,10 @@ except ImportError:
from . import fixtures from . import fixtures
from importlib.metadata import ( from importlib.metadata import (
Distribution, Distribution, EntryPoint,
EntryPoint, PackageNotFoundError, distributions,
PackageNotFoundError, entry_points, metadata, version,
distributions, )
entry_points,
metadata,
version,
)
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
@ -72,11 +70,12 @@ class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):
name='ep', name='ep',
value='importlib.metadata', value='importlib.metadata',
group='grp', group='grp',
) )
assert ep.load() is importlib.metadata assert ep.load() is importlib.metadata
class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): class NameNormalizationTests(
fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
@staticmethod @staticmethod
def pkg_with_dashes(site_dir): def pkg_with_dashes(site_dir):
""" """
@ -145,15 +144,11 @@ class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
metadata_dir.mkdir() metadata_dir.mkdir()
metadata = metadata_dir / 'METADATA' metadata = metadata_dir / 'METADATA'
with metadata.open('w', encoding='utf-8') as fp: with metadata.open('w', encoding='utf-8') as fp:
fp.write( fp.write(textwrap.dedent("""
textwrap.dedent(
"""
Name: portend Name: portend
pôrˈtend pôrˈtend
""" """).lstrip())
).lstrip()
)
return 'portend' return 'portend'
def test_metadata_loads(self): def test_metadata_loads(self):
@ -167,12 +162,24 @@ class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
assert meta.get_payload() == 'pôrˈtend\n' 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): def test_package_discovery(self):
dists = list(distributions()) dists = list(distributions())
assert all(isinstance(dist, Distribution) for dist in dists) assert all(
assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists) isinstance(dist, Distribution)
assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) 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): def test_invalid_usage(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
@ -258,21 +265,10 @@ class TestEntryPoints(unittest.TestCase):
def test_attr(self): def test_attr(self):
assert self.ep.attr is None 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( 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): def test_unicode_dir_on_sys_path(self):
""" """
Ensure a Unicode subdirectory of a directory on sys.path Ensure a Unicode subdirectory of a directory on sys.path
@ -281,5 +277,5 @@ class FileSystem(
fixtures.build_files( fixtures.build_files(
{self.unicode_filename(): {}}, {self.unicode_filename(): {}},
prefix=self.site_dir, prefix=self.site_dir,
) )
list(distributions()) list(distributions())

View File

@ -2,26 +2,20 @@ import re
import textwrap import textwrap
import unittest import unittest
from collections.abc import Iterator
from . import fixtures from . import fixtures
from importlib.metadata import ( from importlib.metadata import (
Distribution, Distribution, PackageNotFoundError, distribution,
PackageNotFoundError, entry_points, files, metadata, requires, version,
distribution, )
entry_points,
files,
metadata,
requires,
version,
)
class APITests( class APITests(
fixtures.EggInfoPkg, fixtures.EggInfoPkg,
fixtures.DistInfoPkg, fixtures.DistInfoPkg,
fixtures.DistInfoPkgWithDot, fixtures.EggInfoFile,
fixtures.EggInfoFile, unittest.TestCase):
unittest.TestCase,
):
version_pattern = r'\d+\.\d+(\.\d)?' version_pattern = r'\d+\.\d+(\.\d)?'
@ -39,28 +33,16 @@ class APITests(
with self.assertRaises(PackageNotFoundError): with self.assertRaises(PackageNotFoundError):
distribution('does-not-exist') 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): def test_for_top_level(self):
self.assertEqual( 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): def test_read_text(self):
top_level = [ top_level = [
path for path in files('egginfo-pkg') if path.name == 'top_level.txt' path for path in files('egginfo-pkg')
][0] if path.name == 'top_level.txt'
][0]
self.assertEqual(top_level.read_text(), 'mod\n') self.assertEqual(top_level.read_text(), 'mod\n')
def test_entry_points(self): def test_entry_points(self):
@ -69,13 +51,6 @@ class APITests(
self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.value, 'mod:main')
self.assertEqual(ep.extras, []) 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): def test_metadata_for_this_package(self):
md = metadata('egginfo-pkg') md = metadata('egginfo-pkg')
assert md['author'] == 'Steven Ma' assert md['author'] == 'Steven Ma'
@ -100,8 +75,13 @@ class APITests(
def test_file_hash_repr(self): def test_file_hash_repr(self):
assertRegex = self.assertRegex assertRegex = self.assertRegex
util = [p for p in files('distinfo-pkg') if p.name == 'mod.py'][0] util = [
assertRegex(repr(util.hash), '<FileHash mode: sha256 value: .*>') 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): def test_files_dist_info(self):
self._test_files(files('distinfo-pkg')) self._test_files(files('distinfo-pkg'))
@ -119,7 +99,10 @@ class APITests(
def test_requires_egg_info(self): def test_requires_egg_info(self):
deps = requires('egginfo-pkg') deps = requires('egginfo-pkg')
assert len(deps) == 2 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): def test_requires_dist_info(self):
deps = requires('distinfo-pkg') deps = requires('distinfo-pkg')
@ -129,8 +112,7 @@ class APITests(
assert "pytest; extra == 'test'" in deps assert "pytest; extra == 'test'" in deps
def test_more_complex_deps_requires_text(self): def test_more_complex_deps_requires_text(self):
requires = textwrap.dedent( requires = textwrap.dedent("""
"""
dep1 dep1
dep2 dep2
@ -142,8 +124,7 @@ class APITests(
[extra2:python_version < "3"] [extra2:python_version < "3"]
dep5 dep5
""" """)
)
deps = sorted(Distribution._deps_from_requires_text(requires)) deps = sorted(Distribution._deps_from_requires_text(requires))
expected = [ expected = [
'dep1', 'dep1',
@ -151,7 +132,7 @@ class APITests(
'dep3; python_version < "3"', 'dep3; python_version < "3"',
'dep4; extra == "extra1"', 'dep4; extra == "extra1"',
'dep5; (python_version < "3") and extra == "extra2"', 'dep5; (python_version < "3") and extra == "extra2"',
] ]
# It's important that the environment marker expression be # It's important that the environment marker expression be
# wrapped in parentheses to avoid the following 'and' binding more # wrapped in parentheses to avoid the following 'and' binding more
# tightly than some other part of the environment expression. # tightly than some other part of the environment expression.
@ -159,27 +140,17 @@ class APITests(
assert deps == expected 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): class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
def test_find_distributions_specified_path(self): def test_find_distributions_specified_path(self):
dists = Distribution.discover(path=[str(self.site_dir)]) 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): 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_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info'
dist = Distribution.at(dist_info_path) dist = Distribution.at(dist_info_path)
assert dist.version == '1.0.0' assert dist.version == '1.0.0'

View File

@ -3,12 +3,8 @@ import unittest
from contextlib import ExitStack from contextlib import ExitStack
from importlib.metadata import ( from importlib.metadata import (
PackageNotFoundError, distribution, entry_points, files, PackageNotFoundError,
distribution, version, distributions,
distributions,
entry_points,
files,
version,
) )
from importlib import resources from importlib import resources

View File

@ -82,7 +82,7 @@ class NetworkedNNTPTestsMixin:
desc = self.server.description(self.GROUP_NAME) desc = self.server.description(self.GROUP_NAME)
_check_desc(desc) _check_desc(desc)
# Another sanity check # Another sanity check
self.assertIn(self.DESC, desc) self.assertIn("Python", desc)
# With a pattern # With a pattern
desc = self.server.description(self.GROUP_PAT) desc = self.server.description(self.GROUP_PAT)
_check_desc(desc) _check_desc(desc)
@ -309,7 +309,6 @@ class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase):
NNTP_HOST = 'news.trigofacile.com' NNTP_HOST = 'news.trigofacile.com'
GROUP_NAME = 'fr.comp.lang.python' GROUP_NAME = 'fr.comp.lang.python'
GROUP_PAT = 'fr.comp.lang.*' GROUP_PAT = 'fr.comp.lang.*'
DESC = 'Python'
NNTP_CLASS = NNTP NNTP_CLASS = NNTP
@ -344,11 +343,8 @@ class NetworkedNNTP_SSLTests(NetworkedNNTPTests):
# 400 connections per day are accepted from each IP address." # 400 connections per day are accepted from each IP address."
NNTP_HOST = 'nntp.aioe.org' NNTP_HOST = 'nntp.aioe.org'
# bpo-42794: aioe.test is one of the official groups on this server GROUP_NAME = 'comp.lang.python'
# used for testing: https://news.aioe.org/manual/aioe-hierarchy/ GROUP_PAT = 'comp.lang.*'
GROUP_NAME = 'aioe.test'
GROUP_PAT = 'aioe.*'
DESC = 'test'
NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None) NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None)

View File

@ -1,51 +0,0 @@
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)

View File

@ -1,6 +1,4 @@
import os import os
import copy
import pickle
import platform import platform
import subprocess import subprocess
import sys import sys
@ -236,38 +234,6 @@ class PlatformTest(unittest.TestCase):
) )
self.assertEqual(tuple(res), expected) 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") @unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
def test_uname_processor(self): def test_uname_processor(self):
""" """

View File

@ -204,16 +204,6 @@ class PropertyTests(unittest.TestCase):
return 'Second' return 'Second'
self.assertEqual(A.__doc__, '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 # Issue 5890: subclasses of property do not preserve method __doc__ strings
class PropertySub(property): class PropertySub(property):
@ -309,46 +299,6 @@ class PropertySubclassTests(unittest.TestCase):
self.assertEqual(Foo.spam.__doc__, "a new docstring") 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -542,34 +542,6 @@ class SystemRandom_TestBasicOps(TestBasicOps, unittest.TestCase):
raises(0, 42, 0) raises(0, 42, 0)
raises(0, 42, 3.14159) 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): def test_randbelow_logic(self, _log=log, int=int):
# check bitcount transition points: 2**i and 2**(i+1)-1 # check bitcount transition points: 2**i and 2**(i+1)-1
# show that: k = int(1.001 + _log(n, 2)) # show that: k = int(1.001 + _log(n, 2))

View File

@ -165,17 +165,6 @@ class LMTPGeneralTests(GeneralTests, unittest.TestCase):
client = smtplib.LMTP 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): def testTimeoutZero(self):
super().testTimeoutZero() super().testTimeoutZero()
local_host = '/some/local/lmtp/delivery/program' local_host = '/some/local/lmtp/delivery/program'

View File

@ -1121,11 +1121,9 @@ class GeneralModuleTests(unittest.TestCase):
s_good_values = [0, 1, 2, 0xffff] s_good_values = [0, 1, 2, 0xffff]
l_good_values = s_good_values + [0xffffffff] l_good_values = s_good_values + [0xffffffff]
l_bad_values = [-1, -2, 1<<32, 1<<1000] l_bad_values = [-1, -2, 1<<32, 1<<1000]
s_bad_values = ( s_bad_values = l_bad_values + [_testcapi.INT_MIN - 1,
l_bad_values + _testcapi.INT_MAX + 1]
[_testcapi.INT_MIN-1, _testcapi.INT_MAX+1] + s_deprecated_values = [1<<16, _testcapi.INT_MAX]
[1 << 16, _testcapi.INT_MAX]
)
for k in s_good_values: for k in s_good_values:
socket.ntohs(k) socket.ntohs(k)
socket.htons(k) socket.htons(k)
@ -1138,6 +1136,9 @@ class GeneralModuleTests(unittest.TestCase):
for k in l_bad_values: for k in l_bad_values:
self.assertRaises(OverflowError, socket.ntohl, k) self.assertRaises(OverflowError, socket.ntohl, k)
self.assertRaises(OverflowError, socket.htonl, 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): def testGetServBy(self):
eq = self.assertEqual eq = self.assertEqual

View File

@ -277,13 +277,6 @@ class SocketServerTest(unittest.TestCase):
t.join() t.join()
s.server_close() 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): def test_tcpserver_bind_leak(self):
# Issue #22435: the server socket wouldn't be closed if bind()/listen() # Issue #22435: the server socket wouldn't be closed if bind()/listen()
# failed. # failed.
@ -498,22 +491,6 @@ class MiscTestCase(unittest.TestCase):
self.assertEqual(server.shutdown_called, 1) self.assertEqual(server.shutdown_called, 1)
server.server_close() 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -204,28 +204,6 @@ class ProcessTestCase(BaseTestCase):
input=b'pear') input=b'pear')
self.assertIn(b'PEAR', output) 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): def test_check_output_stdout_arg(self):
# check_output() refuses to accept 'stdout' argument # check_output() refuses to accept 'stdout' argument
with self.assertRaises(ValueError) as c: with self.assertRaises(ValueError) as c:

View File

@ -1329,7 +1329,7 @@ class SizeofTest(unittest.TestCase):
def setx(self, value): self.__x = value def setx(self, value): self.__x = value
def delx(self): del self.__x def delx(self): del self.__x
x = property(getx, setx, delx, "") x = property(getx, setx, delx, "")
check(x, size('5Pi')) check(x, size('4Pi'))
# PyCapsule # PyCapsule
# XXX # XXX
# rangeiterator # rangeiterator

View File

@ -874,48 +874,6 @@ class TraceTestCase(unittest.TestCase):
(5, 'line'), (5, 'line'),
(5, 'return')]) (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): class SkipLineEventsTraceTestCase(TraceTestCase):
"""Repeat the trace tests, but with per-line events skipped""" """Repeat the trace tests, but with per-line events skipped"""

View File

@ -138,14 +138,10 @@ class TclTest(unittest.TestCase):
def get_integers(self): def get_integers(self):
integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63) 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. # 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() if (get_tk_patchlevel() >= (8, 6, 0, 'final') or
# is not reliable. (8, 5, 8) <= get_tk_patchlevel() < (8, 6)):
# TODO: expose full static version. integers += (2**63, -2**63-1, 2**1000, -2**1000)
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 return integers
def test_getint(self): def test_getint(self):
@ -449,7 +445,7 @@ class TclTest(unittest.TestCase):
else: else:
self.assertEqual(result, str(i)) self.assertEqual(result, str(i))
self.assertIsInstance(result, str) self.assertIsInstance(result, str)
if get_tk_patchlevel() < (8, 5): # bignum was added in Tcl 8.5 if tcl_version < (8, 5): # bignum was added in Tcl 8.5
self.assertRaises(TclError, tcl.call, 'expr', str(2**1000)) self.assertRaises(TclError, tcl.call, 'expr', str(2**1000))
def test_passing_values(self): def test_passing_values(self):

View File

@ -737,16 +737,6 @@ class TypesTests(unittest.TestCase):
with self.assertRaises(ZeroDivisionError): with self.assertRaises(ZeroDivisionError):
list[int] | list[bt] 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): def test_ellipsis_type(self):
self.assertIsInstance(Ellipsis, types.EllipsisType) self.assertIsInstance(Ellipsis, types.EllipsisType)

View File

@ -3021,7 +3021,6 @@ class GetUtilitiesTestCase(TestCase):
self.assertIs(get_origin(Callable), collections.abc.Callable) self.assertIs(get_origin(Callable), collections.abc.Callable)
self.assertIs(get_origin(list[int]), list) self.assertIs(get_origin(list[int]), list)
self.assertIs(get_origin(list), None) self.assertIs(get_origin(list), None)
self.assertIs(get_origin(list | str), types.Union)
def test_get_args(self): def test_get_args(self):
T = TypeVar('T') T = TypeVar('T')
@ -3049,16 +3048,6 @@ class GetUtilitiesTestCase(TestCase):
self.assertEqual(get_args(Callable), ()) self.assertEqual(get_args(Callable), ())
self.assertEqual(get_args(list[int]), (int,)) self.assertEqual(get_args(list[int]), (int,))
self.assertEqual(get_args(list), ()) 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): class CollectionsAbcTests(BaseTestCase):

View File

@ -346,31 +346,6 @@ Now some general starred expressions (all fail).
... ...
SyntaxError: can't use starred expression here 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.) Some size constraints (all fail.)
>>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)" >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)"

View File

@ -1851,17 +1851,9 @@ class MiscTests(unittest.TestCase):
('ftp', 'joe', 'password', 'proxy.example.com')), ('ftp', 'joe', 'password', 'proxy.example.com')),
# Test for no trailing '/' case # Test for no trailing '/' case
('http://joe:password@proxy.example.com', ('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: for tc, expected in parse_proxy_test_cases:
self.assertEqual(_parse_proxy(tc), expected) self.assertEqual(_parse_proxy(tc), expected)

View File

@ -292,7 +292,7 @@ def _get_default_root(what=None):
if not _support_default_root: if not _support_default_root:
raise RuntimeError("No master specified and tkinter is " raise RuntimeError("No master specified and tkinter is "
"configured to not support default root") "configured to not support default root")
if _default_root is None: if not _default_root:
if what: if what:
raise RuntimeError(f"Too early to {what}: no default root window") raise RuntimeError(f"Too early to {what}: no default root window")
root = Tk() root = Tk()
@ -300,31 +300,6 @@ def _get_default_root(what=None):
return _default_root 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): def _tkerror(err):
"""Internal function.""" """Internal function."""
pass pass
@ -367,7 +342,7 @@ class Variable:
if name is not None and not isinstance(name, str): if name is not None and not isinstance(name, str):
raise TypeError("name must be a string") raise TypeError("name must be a string")
global _varnum global _varnum
if master is None: if not master:
master = _get_default_root('create variable') master = _get_default_root('create variable')
self._root = master._root() self._root = master._root()
self._tk = master.tk self._tk = master.tk
@ -516,11 +491,15 @@ class Variable:
self._tk.call("trace", "vinfo", self._name))] self._tk.call("trace", "vinfo", self._name))]
def __eq__(self, other): 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): if not isinstance(other, Variable):
return NotImplemented return NotImplemented
return (self._name == other._name return self.__class__.__name__ == other.__class__.__name__ \
and self.__class__.__name__ == other.__class__.__name__ and self._name == other._name
and self._tk == other._tk)
class StringVar(Variable): class StringVar(Variable):
@ -829,7 +808,7 @@ class Misc:
function which shall be called. Additional parameters function which shall be called. Additional parameters
are given as parameters to the function call. Return are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel.""" identifier to cancel scheduling with after_cancel."""
if func is None: if not func:
# I'd rather use time.sleep(ms*0.001) # I'd rather use time.sleep(ms*0.001)
self.tk.call('after', ms) self.tk.call('after', ms)
return None return None
@ -1563,7 +1542,7 @@ class Misc:
def _root(self): def _root(self):
"""Internal function.""" """Internal function."""
w = self w = self
while w.master is not None: w = w.master while w.master: w = w.master
return w return w
_subst_format = ('%#', '%b', '%f', '%h', '%k', _subst_format = ('%#', '%b', '%f', '%h', '%k',
'%s', '%t', '%w', '%x', '%y', '%s', '%t', '%w', '%x', '%y',
@ -2327,7 +2306,7 @@ class Tk(Misc, Wm):
self.tk.createcommand('exit', _exit) self.tk.createcommand('exit', _exit)
self._tclCommands.append('tkerror') self._tclCommands.append('tkerror')
self._tclCommands.append('exit') self._tclCommands.append('exit')
if _support_default_root and _default_root is None: if _support_default_root and not _default_root:
_default_root = self _default_root = self
self.protocol("WM_DELETE_WINDOW", self.destroy) self.protocol("WM_DELETE_WINDOW", self.destroy)
@ -2555,7 +2534,7 @@ class BaseWidget(Misc):
def _setup(self, master, cnf): def _setup(self, master, cnf):
"""Internal function. Sets up information about children.""" """Internal function. Sets up information about children."""
if master is None: if not master:
master = _get_default_root() master = _get_default_root()
self.master = master self.master = master
self.tk = master.tk self.tk = master.tk
@ -3970,7 +3949,7 @@ class _setit:
def __call__(self, *args): def __call__(self, *args):
self.__var.set(self.__value) self.__var.set(self.__value)
if self.__callback is not None: if self.__callback:
self.__callback(self.__value, *args) self.__callback(self.__value, *args)
@ -4019,7 +3998,7 @@ class Image:
def __init__(self, imgtype, name=None, cnf={}, master=None, **kw): def __init__(self, imgtype, name=None, cnf={}, master=None, **kw):
self.name = None self.name = None
if master is None: if not master:
master = _get_default_root('create image') master = _get_default_root('create image')
self.tk = getattr(master, 'tk', master) self.tk = getattr(master, 'tk', master)
if not name: if not name:

View File

@ -10,7 +10,7 @@
__all__ = ["Dialog"] __all__ = ["Dialog"]
from tkinter import Frame, _get_temp_root, _destroy_temp_root from tkinter import Frame
class Dialog: class Dialog:
@ -18,7 +18,7 @@ class Dialog:
command = None command = None
def __init__(self, master=None, **options): def __init__(self, master=None, **options):
if master is None: if not master:
master = options.get('parent') master = options.get('parent')
self.master = master self.master = master
self.options = options self.options = options
@ -37,17 +37,22 @@ class Dialog:
self._fixoptions() self._fixoptions()
master = self.master # we need a dummy widget to properly process the options
if master is None: # (at least as long as we use Tkinter 1.63)
master = _get_temp_root() w = Frame(self.master)
try: try:
self._test_callback(master) # The function below is replaced for some tests.
s = master.tk.call(self.command, *master._options(self.options)) s = w.tk.call(self.command, *w._options(self.options))
s = self._fixresult(master, s)
s = self._fixresult(w, s)
finally: finally:
_destroy_temp_root(master)
try:
# get rid of the widget
w.destroy()
except:
pass
return s return s
def _test_callback(self, master):
pass

View File

@ -108,7 +108,7 @@ __all__ = ["dnd_start", "DndHandler"]
def dnd_start(source, event): def dnd_start(source, event):
h = DndHandler(source, event) h = DndHandler(source, event)
if h.root is not None: if h.root:
return h return h
else: else:
return None return None
@ -143,7 +143,7 @@ class DndHandler:
def __del__(self): def __del__(self):
root = self.root root = self.root
self.root = None self.root = None
if root is not None: if root:
try: try:
del root.__dnd del root.__dnd
except AttributeError: except AttributeError:
@ -154,25 +154,25 @@ class DndHandler:
target_widget = self.initial_widget.winfo_containing(x, y) target_widget = self.initial_widget.winfo_containing(x, y)
source = self.source source = self.source
new_target = None new_target = None
while target_widget is not None: while target_widget:
try: try:
attr = target_widget.dnd_accept attr = target_widget.dnd_accept
except AttributeError: except AttributeError:
pass pass
else: else:
new_target = attr(source, event) new_target = attr(source, event)
if new_target is not None: if new_target:
break break
target_widget = target_widget.master target_widget = target_widget.master
old_target = self.target old_target = self.target
if old_target is new_target: if old_target is new_target:
if old_target is not None: if old_target:
old_target.dnd_motion(source, event) old_target.dnd_motion(source, event)
else: else:
if old_target is not None: if old_target:
self.target = None self.target = None
old_target.dnd_leave(source, event) old_target.dnd_leave(source, event)
if new_target is not None: if new_target:
new_target.dnd_enter(source, event) new_target.dnd_enter(source, event)
self.target = new_target self.target = new_target
@ -193,7 +193,7 @@ class DndHandler:
self.initial_widget.unbind("<Motion>") self.initial_widget.unbind("<Motion>")
widget['cursor'] = self.save_cursor widget['cursor'] = self.save_cursor
self.target = self.source = self.initial_widget = self.root = None self.target = self.source = self.initial_widget = self.root = None
if target is not None: if target:
if commit: if commit:
target.dnd_commit(source, event) target.dnd_commit(source, event)
else: else:
@ -215,9 +215,9 @@ class Icon:
if canvas is self.canvas: if canvas is self.canvas:
self.canvas.coords(self.id, x, y) self.canvas.coords(self.id, x, y)
return return
if self.canvas is not None: if self.canvas:
self.detach() self.detach()
if canvas is None: if not canvas:
return return
label = tkinter.Label(canvas, text=self.name, label = tkinter.Label(canvas, text=self.name,
borderwidth=2, relief="raised") borderwidth=2, relief="raised")
@ -229,7 +229,7 @@ class Icon:
def detach(self): def detach(self):
canvas = self.canvas canvas = self.canvas
if canvas is None: if not canvas:
return return
id = self.id id = self.id
label = self.label label = self.label

View File

@ -17,10 +17,10 @@ BOLD = "bold"
ITALIC = "italic" ITALIC = "italic"
def nametofont(name, root=None): def nametofont(name):
"""Given the name of a tk named font, returns a Font representation. """Given the name of a tk named font, returns a Font representation.
""" """
return Font(name=name, exists=True, root=root) return Font(name=name, exists=True)
class Font: class Font:
@ -68,7 +68,7 @@ class Font:
def __init__(self, root=None, font=None, name=None, exists=False, def __init__(self, root=None, font=None, name=None, exists=False,
**options): **options):
if root is None: if not root:
root = tkinter._get_default_root('use font') root = tkinter._get_default_root('use font')
tk = getattr(root, 'tk', root) tk = getattr(root, 'tk', root)
if font: if font:
@ -107,7 +107,7 @@ class Font:
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Font): if not isinstance(other, Font):
return NotImplemented return NotImplemented
return self.name == other.name and self._tk == other._tk return self.name == other.name
def __getitem__(self, key): def __getitem__(self, key):
return self.cget(key) return self.cget(key)
@ -183,7 +183,7 @@ class Font:
def families(root=None, displayof=None): def families(root=None, displayof=None):
"Get font families (as a tuple)" "Get font families (as a tuple)"
if root is None: if not root:
root = tkinter._get_default_root('use font.families()') root = tkinter._get_default_root('use font.families()')
args = () args = ()
if displayof: if displayof:
@ -193,7 +193,7 @@ def families(root=None, displayof=None):
def names(root=None): def names(root=None):
"Get names of defined fonts (as a tuple)" "Get names of defined fonts (as a tuple)"
if root is None: if not root:
root = tkinter._get_default_root('use font.names()') root = tkinter._get_default_root('use font.names()')
return root.tk.splitlist(root.tk.call("font", "names")) return root.tk.splitlist(root.tk.call("font", "names"))

View File

@ -24,8 +24,7 @@ askstring -- get a string from the user
""" """
from tkinter import * from tkinter import *
from tkinter import _get_temp_root, _destroy_temp_root from tkinter import messagebox, _get_default_root
from tkinter import messagebox
class SimpleDialog: class SimpleDialog:
@ -56,8 +55,36 @@ class SimpleDialog:
b.config(relief=RIDGE, borderwidth=8) b.config(relief=RIDGE, borderwidth=8)
b.pack(side=LEFT, fill=BOTH, expand=1) b.pack(side=LEFT, fill=BOTH, expand=1)
self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window) self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
self.root.transient(master) self._set_transient(master)
_place_window(self.root, 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
def go(self): def go(self):
self.root.wait_visibility() self.root.wait_visibility()
@ -100,8 +127,8 @@ class Dialog(Toplevel):
title -- the dialog title title -- the dialog title
''' '''
master = parent master = parent
if master is None: if not master:
master = _get_temp_root() master = _get_default_root('create dialog window')
Toplevel.__init__(self, master) Toplevel.__init__(self, master)
@ -125,12 +152,16 @@ class Dialog(Toplevel):
self.buttonbox() self.buttonbox()
if self.initial_focus is None: if not self.initial_focus:
self.initial_focus = self self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel)
_place_window(self, parent) if parent is not None:
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.deiconify() # become visible now
self.initial_focus.focus_set() self.initial_focus.focus_set()
@ -143,7 +174,6 @@ class Dialog(Toplevel):
'''Destroy the window''' '''Destroy the window'''
self.initial_focus = None self.initial_focus = None
Toplevel.destroy(self) Toplevel.destroy(self)
_destroy_temp_root(self.master)
# #
# construction hooks # construction hooks
@ -221,37 +251,6 @@ class Dialog(Toplevel):
pass # override 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 # convenience dialogues

View File

@ -1,39 +0,0 @@
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)

View File

@ -63,22 +63,15 @@ class FontTest(AbstractTkTest, unittest.TestCase):
self.assertEqual(self.font.name, fontname) self.assertEqual(self.font.name, fontname)
self.assertEqual(str(self.font), fontname) self.assertEqual(str(self.font), fontname)
def test_equality(self): def test_eq(self):
font1 = font.Font(root=self.root, name=fontname, exists=True) font1 = font.Font(root=self.root, name=fontname, exists=True)
font2 = font.Font(root=self.root, name=fontname, exists=True) font2 = font.Font(root=self.root, name=fontname, exists=True)
self.assertIsNot(font1, font2) self.assertIsNot(font1, font2)
self.assertEqual(font1, font2) self.assertEqual(font1, font2)
self.assertNotEqual(font1, font1.copy()) self.assertNotEqual(font1, font1.copy())
self.assertNotEqual(font1, 0) self.assertNotEqual(font1, 0)
self.assertEqual(font1, ALWAYS_EQ) 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): def test_measure(self):
self.assertIsInstance(self.font.measure('abc'), int) self.assertIsInstance(self.font.measure('abc'), int)
@ -108,11 +101,6 @@ class FontTest(AbstractTkTest, unittest.TestCase):
self.assertTrue(name) self.assertTrue(name)
self.assertIn(fontname, names) 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): def test_repr(self):
self.assertEqual( self.assertEqual(
repr(self.font), f'<tkinter.font.Font object {fontname!r}>' repr(self.font), f'<tkinter.font.Font object {fontname!r}>'
@ -148,16 +136,6 @@ class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
tkinter.NoDefaultRoot() tkinter.NoDefaultRoot()
self.assertRaises(RuntimeError, font.names) 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) tests_gui = (FontTest, DefaultRootTest)

View File

@ -1,38 +0,0 @@
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)

View File

@ -10,25 +10,13 @@ requires('gui')
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
def test_askinteger(self): def test_askinteger(self):
@staticmethod self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
def mock_wait_window(w): root = tkinter.Tk()
nonlocal ismapped with swap_attr(Dialog, 'wait_window', lambda self, w: w.destroy()):
ismapped = w.master.winfo_ismapped()
w.destroy()
with swap_attr(Dialog, 'wait_window', mock_wait_window):
ismapped = None
askinteger("Go To Line", "Line number") askinteger("Go To Line", "Line number")
self.assertEqual(ismapped, False) root.destroy()
tkinter.NoDefaultRoot()
root = tkinter.Tk() self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
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,) tests_gui = (DefaultRootTest,)

View File

@ -58,32 +58,22 @@ class TestVariable(TestBase):
del v2 del v2
self.assertFalse(self.info_exists("name")) self.assertFalse(self.info_exists("name"))
def test_equality(self): def test___eq__(self):
# values doesn't matter, only class and name are checked # values doesn't matter, only class and name are checked
v1 = Variable(self.root, name="abc") v1 = Variable(self.root, name="abc")
v2 = Variable(self.root, name="abc") v2 = Variable(self.root, name="abc")
self.assertIsNot(v1, v2) self.assertIsNot(v1, v2)
self.assertEqual(v1, v2) self.assertEqual(v1, v2)
v3 = Variable(self.root, name="cba") v3 = StringVar(self.root, name="abc")
self.assertNotEqual(v1, v3) self.assertNotEqual(v1, v3)
v4 = StringVar(self.root, name="abc")
self.assertEqual(str(v1), str(v4))
self.assertNotEqual(v1, v4)
V = type('Variable', (), {}) V = type('Variable', (), {})
self.assertNotEqual(v1, V()) self.assertNotEqual(v1, V())
self.assertNotEqual(v1, object()) self.assertNotEqual(v1, object())
self.assertEqual(v1, ALWAYS_EQ) 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): def test_invalid_name(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
Variable(self.root, name=123) Variable(self.root, name=123)

View File

@ -22,7 +22,7 @@ def float_round(x):
class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
_conv_pad_pixels = noconv _conv_pad_pixels = noconv
def test_configure_class(self): def test_class(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['class'], self.assertEqual(widget['class'],
widget.__class__.__name__.title()) widget.__class__.__name__.title())
@ -31,7 +31,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
widget2 = self.create(class_='Foo') widget2 = self.create(class_='Foo')
self.assertEqual(widget2['class'], 'Foo') self.assertEqual(widget2['class'], 'Foo')
def test_configure_colormap(self): def test_colormap(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['colormap'], '') self.assertEqual(widget['colormap'], '')
self.checkInvalidParam(widget, 'colormap', 'new', self.checkInvalidParam(widget, 'colormap', 'new',
@ -39,7 +39,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
widget2 = self.create(colormap='new') widget2 = self.create(colormap='new')
self.assertEqual(widget2['colormap'], 'new') self.assertEqual(widget2['colormap'], 'new')
def test_configure_container(self): def test_container(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['container'], 0 if self.wantobjects else '0') self.assertEqual(widget['container'], 0 if self.wantobjects else '0')
self.checkInvalidParam(widget, 'container', 1, self.checkInvalidParam(widget, 'container', 1,
@ -47,7 +47,7 @@ class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
widget2 = self.create(container=True) widget2 = self.create(container=True)
self.assertEqual(widget2['container'], 1 if self.wantobjects else '1') self.assertEqual(widget2['container'], 1 if self.wantobjects else '1')
def test_configure_visual(self): def test_visual(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['visual'], '') self.assertEqual(widget['visual'], '')
self.checkInvalidParam(widget, 'visual', 'default', self.checkInvalidParam(widget, 'visual', 'default',
@ -69,13 +69,13 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Toplevel(self.root, **kwargs) return tkinter.Toplevel(self.root, **kwargs)
def test_configure_menu(self): def test_menu(self):
widget = self.create() widget = self.create()
menu = tkinter.Menu(self.root) menu = tkinter.Menu(self.root)
self.checkParam(widget, 'menu', menu, eq=widget_eq) self.checkParam(widget, 'menu', menu, eq=widget_eq)
self.checkParam(widget, 'menu', '') self.checkParam(widget, 'menu', '')
def test_configure_screen(self): def test_screen(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['screen'], '') self.assertEqual(widget['screen'], '')
try: try:
@ -87,7 +87,7 @@ class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
widget2 = self.create(screen=display) widget2 = self.create(screen=display)
self.assertEqual(widget2['screen'], display) self.assertEqual(widget2['screen'], display)
def test_configure_use(self): def test_use(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['use'], '') self.assertEqual(widget['use'], '')
parent = self.create(container=True) parent = self.create(container=True)
@ -124,14 +124,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.LabelFrame(self.root, **kwargs) return tkinter.LabelFrame(self.root, **kwargs)
def test_configure_labelanchor(self): def test_labelanchor(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'labelanchor', self.checkEnumParam(widget, 'labelanchor',
'e', 'en', 'es', 'n', 'ne', 'nw', 'e', 'en', 'es', 'n', 'ne', 'nw',
's', 'se', 'sw', 'w', 'wn', 'ws') 's', 'se', 'sw', 'w', 'wn', 'ws')
self.checkInvalidParam(widget, 'labelanchor', 'center') self.checkInvalidParam(widget, 'labelanchor', 'center')
def test_configure_labelwidget(self): def test_labelwidget(self):
widget = self.create() widget = self.create()
label = tkinter.Label(self.root, text='Mupp', name='foo') label = tkinter.Label(self.root, text='Mupp', name='foo')
self.checkParam(widget, 'labelwidget', label, expected='.foo') self.checkParam(widget, 'labelwidget', label, expected='.foo')
@ -141,7 +141,7 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
_conv_pixels = noconv _conv_pixels = noconv
def test_configure_highlightthickness(self): def test_highlightthickness(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'highlightthickness', self.checkPixelsParam(widget, 'highlightthickness',
0, 1.3, 2.6, 6, -2, '10p') 0, 1.3, 2.6, 6, -2, '10p')
@ -179,7 +179,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Button(self.root, **kwargs) return tkinter.Button(self.root, **kwargs)
def test_configure_default(self): def test_default(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal') self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal')
@ -204,11 +204,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
return tkinter.Checkbutton(self.root, **kwargs) return tkinter.Checkbutton(self.root, **kwargs)
def test_configure_offvalue(self): def test_offvalue(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
def test_configure_onvalue(self): def test_onvalue(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
@ -231,7 +231,7 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Radiobutton(self.root, **kwargs) return tkinter.Radiobutton(self.root, **kwargs)
def test_configure_value(self): def test_value(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'value', 1, 2.3, '', 'any string') self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
@ -254,19 +254,18 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Menubutton(self.root, **kwargs) return tkinter.Menubutton(self.root, **kwargs)
def test_configure_direction(self): def test_direction(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'direction', self.checkEnumParam(widget, 'direction',
'above', 'below', 'flush', 'left', 'right') 'above', 'below', 'flush', 'left', 'right')
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str)
test_configure_highlightthickness = \ test_highlightthickness = StandardOptionsTests.test_highlightthickness
StandardOptionsTests.test_configure_highlightthickness
def test_configure_image(self): def test_image(self):
widget = self.create() widget = self.create()
image = tkinter.PhotoImage(master=self.root, name='image1') image = tkinter.PhotoImage(master=self.root, name='image1')
self.checkParam(widget, 'image', image, conv=str) self.checkParam(widget, 'image', image, conv=str)
@ -280,23 +279,23 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
if errmsg is not None: if errmsg is not None:
self.assertEqual(str(cm.exception), errmsg) self.assertEqual(str(cm.exception), errmsg)
def test_configure_menu(self): def test_menu(self):
widget = self.create() widget = self.create()
menu = tkinter.Menu(widget, name='menu') menu = tkinter.Menu(widget, name='menu')
self.checkParam(widget, 'menu', menu, eq=widget_eq) self.checkParam(widget, 'menu', menu, eq=widget_eq)
menu.destroy() menu.destroy()
def test_configure_padx(self): def test_padx(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m')
self.checkParam(widget, 'padx', -2, expected=0) self.checkParam(widget, 'padx', -2, expected=0)
def test_configure_pady(self): def test_pady(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m')
self.checkParam(widget, 'pady', -2, expected=0) self.checkParam(widget, 'pady', -2, expected=0)
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str)
@ -329,18 +328,18 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Entry(self.root, **kwargs) return tkinter.Entry(self.root, **kwargs)
def test_configure_disabledbackground(self): def test_disabledbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'disabledbackground') self.checkColorParam(widget, 'disabledbackground')
def test_configure_insertborderwidth(self): def test_insertborderwidth(self):
widget = self.create(insertwidth=100) widget = self.create(insertwidth=100)
self.checkPixelsParam(widget, 'insertborderwidth', self.checkPixelsParam(widget, 'insertborderwidth',
0, 1.3, 2.6, 6, -2, '10p') 0, 1.3, 2.6, 6, -2, '10p')
# insertborderwidth is bounded above by a half of insertwidth. # insertborderwidth is bounded above by a half of insertwidth.
self.checkParam(widget, 'insertborderwidth', 60, expected=100//2) self.checkParam(widget, 'insertborderwidth', 60, expected=100//2)
def test_configure_insertwidth(self): def test_insertwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
self.checkParam(widget, 'insertwidth', 0.1, expected=2) self.checkParam(widget, 'insertwidth', 0.1, expected=2)
@ -350,32 +349,32 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
else: else:
self.checkParam(widget, 'insertwidth', 0.9, expected=1) self.checkParam(widget, 'insertwidth', 0.9, expected=1)
def test_configure_invalidcommand(self): def test_invalidcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'invalidcommand') self.checkCommandParam(widget, 'invalidcommand')
self.checkCommandParam(widget, 'invcmd') self.checkCommandParam(widget, 'invcmd')
def test_configure_readonlybackground(self): def test_readonlybackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'readonlybackground') self.checkColorParam(widget, 'readonlybackground')
def test_configure_show(self): def test_show(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'show', '*') self.checkParam(widget, 'show', '*')
self.checkParam(widget, 'show', '') self.checkParam(widget, 'show', '')
self.checkParam(widget, 'show', ' ') self.checkParam(widget, 'show', ' ')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'state', self.checkEnumParam(widget, 'state',
'disabled', 'normal', 'readonly') 'disabled', 'normal', 'readonly')
def test_configure_validate(self): def test_validate(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'validate', self.checkEnumParam(widget, 'validate',
'all', 'key', 'focus', 'focusin', 'focusout', 'none') 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
def test_configure_validatecommand(self): def test_validatecommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'validatecommand') self.checkCommandParam(widget, 'validatecommand')
self.checkCommandParam(widget, 'vcmd') self.checkCommandParam(widget, 'vcmd')
@ -428,25 +427,25 @@ class SpinboxTest(EntryTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Spinbox(self.root, **kwargs) return tkinter.Spinbox(self.root, **kwargs)
test_configure_show = None test_show = None
def test_configure_buttonbackground(self): def test_buttonbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'buttonbackground') self.checkColorParam(widget, 'buttonbackground')
def test_configure_buttoncursor(self): def test_buttoncursor(self):
widget = self.create() widget = self.create()
self.checkCursorParam(widget, 'buttoncursor') self.checkCursorParam(widget, 'buttoncursor')
def test_configure_buttondownrelief(self): def test_buttondownrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'buttondownrelief') self.checkReliefParam(widget, 'buttondownrelief')
def test_configure_buttonuprelief(self): def test_buttonuprelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'buttonuprelief') self.checkReliefParam(widget, 'buttonuprelief')
def test_configure_format(self): def test_format(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'format', '%2f') self.checkParam(widget, 'format', '%2f')
self.checkParam(widget, 'format', '%2.2f') self.checkParam(widget, 'format', '%2.2f')
@ -461,25 +460,25 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self.checkParam(widget, 'format', '%09.200f') self.checkParam(widget, 'format', '%09.200f')
self.checkInvalidParam(widget, 'format', '%d') self.checkInvalidParam(widget, 'format', '%d')
def test_configure_from(self): def test_from(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'to', 100.0) self.checkParam(widget, 'to', 100.0)
self.checkFloatParam(widget, 'from', -10, 10.2, 11.7) self.checkFloatParam(widget, 'from', -10, 10.2, 11.7)
self.checkInvalidParam(widget, 'from', 200, self.checkInvalidParam(widget, 'from', 200,
errmsg='-to value must be greater than -from value') errmsg='-to value must be greater than -from value')
def test_configure_increment(self): def test_increment(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0) self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0)
def test_configure_to(self): def test_to(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'from', -100.0) self.checkParam(widget, 'from', -100.0)
self.checkFloatParam(widget, 'to', -10, 10.2, 11.7) self.checkFloatParam(widget, 'to', -10, 10.2, 11.7)
self.checkInvalidParam(widget, 'to', -200, self.checkInvalidParam(widget, 'to', -200,
errmsg='-to value must be greater than -from value') errmsg='-to value must be greater than -from value')
def test_configure_values(self): def test_values(self):
# XXX # XXX
widget = self.create() widget = self.create()
self.assertEqual(widget['values'], '') self.assertEqual(widget['values'], '')
@ -490,7 +489,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
expected='42 3.14 {} {any string}') expected='42 3.14 {} {any string}')
self.checkParam(widget, 'values', '') self.checkParam(widget, 'values', '')
def test_configure_wrap(self): def test_wrap(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'wrap') self.checkBooleanParam(widget, 'wrap')
@ -556,17 +555,17 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Text(self.root, **kwargs) return tkinter.Text(self.root, **kwargs)
def test_configure_autoseparators(self): def test_autoseparators(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'autoseparators') self.checkBooleanParam(widget, 'autoseparators')
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_blockcursor(self): def test_blockcursor(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'blockcursor') self.checkBooleanParam(widget, 'blockcursor')
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_endline(self): def test_endline(self):
widget = self.create() widget = self.create()
text = '\n'.join('Line %d' for i in range(100)) text = '\n'.join('Line %d' for i in range(100))
widget.insert('end', text) widget.insert('end', text)
@ -579,50 +578,50 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
self.checkInvalidParam(widget, 'endline', 10, self.checkInvalidParam(widget, 'endline', 10,
errmsg='-startline must be less than or equal to -endline') errmsg='-startline must be less than or equal to -endline')
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c')
self.checkParam(widget, 'height', -100, expected=1) self.checkParam(widget, 'height', -100, expected=1)
self.checkParam(widget, 'height', 0, expected=1) self.checkParam(widget, 'height', 0, expected=1)
def test_configure_maxundo(self): def test_maxundo(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_inactiveselectbackground(self): def test_inactiveselectbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'inactiveselectbackground') self.checkColorParam(widget, 'inactiveselectbackground')
@requires_tcl(8, 6) @requires_tcl(8, 6)
def test_configure_insertunfocussed(self): def test_insertunfocussed(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'insertunfocussed', self.checkEnumParam(widget, 'insertunfocussed',
'hollow', 'none', 'solid') 'hollow', 'none', 'solid')
def test_configure_selectborderwidth(self): def test_selectborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'selectborderwidth', self.checkPixelsParam(widget, 'selectborderwidth',
1.3, 2.6, -2, '10p', conv=noconv, 1.3, 2.6, -2, '10p', conv=noconv,
keep_orig=tcl_version >= (8, 5)) keep_orig=tcl_version >= (8, 5))
def test_configure_spacing1(self): def test_spacing1(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c') self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c')
self.checkParam(widget, 'spacing1', -5, expected=0) self.checkParam(widget, 'spacing1', -5, expected=0)
def test_configure_spacing2(self): def test_spacing2(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c') self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c')
self.checkParam(widget, 'spacing2', -1, expected=0) self.checkParam(widget, 'spacing2', -1, expected=0)
def test_configure_spacing3(self): def test_spacing3(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
self.checkParam(widget, 'spacing3', -10, expected=0) self.checkParam(widget, 'spacing3', -10, expected=0)
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_startline(self): def test_startline(self):
widget = self.create() widget = self.create()
text = '\n'.join('Line %d' for i in range(100)) text = '\n'.join('Line %d' for i in range(100))
widget.insert('end', text) widget.insert('end', text)
@ -635,14 +634,14 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
self.checkInvalidParam(widget, 'startline', 70, self.checkInvalidParam(widget, 'startline', 70,
errmsg='-startline must be less than or equal to -endline') errmsg='-startline must be less than or equal to -endline')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
if tcl_version < (8, 5): if tcl_version < (8, 5):
self.checkParams(widget, 'state', 'disabled', 'normal') self.checkParams(widget, 'state', 'disabled', 'normal')
else: else:
self.checkEnumParam(widget, 'state', 'disabled', 'normal') self.checkEnumParam(widget, 'state', 'disabled', 'normal')
def test_configure_tabs(self): def test_tabs(self):
widget = self.create() widget = self.create()
if get_tk_patchlevel() < (8, 5, 11): if get_tk_patchlevel() < (8, 5, 11):
self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'), self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'),
@ -658,21 +657,21 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
keep_orig=tcl_version >= (8, 5)) keep_orig=tcl_version >= (8, 5))
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_tabstyle(self): def test_tabstyle(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
def test_configure_undo(self): def test_undo(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'undo') self.checkBooleanParam(widget, 'undo')
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'width', 402) self.checkIntegerParam(widget, 'width', 402)
self.checkParam(widget, 'width', -402, expected=1) self.checkParam(widget, 'width', -402, expected=1)
self.checkParam(widget, 'width', 0, expected=1) self.checkParam(widget, 'width', 0, expected=1)
def test_configure_wrap(self): def test_wrap(self):
widget = self.create() widget = self.create()
if tcl_version < (8, 5): if tcl_version < (8, 5):
self.checkParams(widget, 'wrap', 'char', 'none', 'word') self.checkParams(widget, 'wrap', 'char', 'none', 'word')
@ -710,16 +709,16 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Canvas(self.root, **kwargs) return tkinter.Canvas(self.root, **kwargs)
def test_configure_closeenough(self): def test_closeenough(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3, self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3,
conv=float) conv=float)
def test_configure_confine(self): def test_confine(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'confine') self.checkBooleanParam(widget, 'confine')
def test_configure_offset(self): def test_offset(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['offset'], '0,0') self.assertEqual(widget['offset'], '0,0')
self.checkParams(widget, 'offset', self.checkParams(widget, 'offset',
@ -728,7 +727,7 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
self.checkParam(widget, 'offset', '#5,6') self.checkParam(widget, 'offset', '#5,6')
self.checkInvalidParam(widget, 'offset', 'spam') self.checkInvalidParam(widget, 'offset', 'spam')
def test_configure_scrollregion(self): def test_scrollregion(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'scrollregion', '0 0 200 150') self.checkParam(widget, 'scrollregion', '0 0 200 150')
self.checkParam(widget, 'scrollregion', (0, 0, 200, 150), self.checkParam(widget, 'scrollregion', (0, 0, 200, 150),
@ -740,17 +739,17 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase):
self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200)) self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200))
self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0)) self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0))
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'state', 'disabled', 'normal', self.checkEnumParam(widget, 'state', 'disabled', 'normal',
errmsg='bad state value "{}": must be normal or disabled') errmsg='bad state value "{}": must be normal or disabled')
def test_configure_xscrollincrement(self): def test_xscrollincrement(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'xscrollincrement', self.checkPixelsParam(widget, 'xscrollincrement',
40, 0, 41.2, 43.6, -40, '0.5i') 40, 0, 41.2, 43.6, -40, '0.5i')
def test_configure_yscrollincrement(self): def test_yscrollincrement(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'yscrollincrement', self.checkPixelsParam(widget, 'yscrollincrement',
10, 0, 11.2, 13.6, -10, '0.1i') 10, 0, 11.2, 13.6, -10, '0.1i')
@ -795,26 +794,26 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Listbox(self.root, **kwargs) return tkinter.Listbox(self.root, **kwargs)
def test_configure_activestyle(self): def test_activestyle(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'activestyle', self.checkEnumParam(widget, 'activestyle',
'dotbox', 'none', 'underline') 'dotbox', 'none', 'underline')
test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify) test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_justify)
def test_configure_listvariable(self): def test_listvariable(self):
widget = self.create() widget = self.create()
var = tkinter.DoubleVar(self.root) var = tkinter.DoubleVar(self.root)
self.checkVariableParam(widget, 'listvariable', var) self.checkVariableParam(widget, 'listvariable', var)
def test_configure_selectmode(self): def test_selectmode(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'selectmode', 'single') self.checkParam(widget, 'selectmode', 'single')
self.checkParam(widget, 'selectmode', 'browse') self.checkParam(widget, 'selectmode', 'browse')
self.checkParam(widget, 'selectmode', 'multiple') self.checkParam(widget, 'selectmode', 'multiple')
self.checkParam(widget, 'selectmode', 'extended') self.checkParam(widget, 'selectmode', 'extended')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'state', 'disabled', 'normal') self.checkEnumParam(widget, 'state', 'disabled', 'normal')
@ -929,53 +928,53 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Scale(self.root, **kwargs) return tkinter.Scale(self.root, **kwargs)
def test_configure_bigincrement(self): def test_bigincrement(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5) self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5)
def test_configure_digits(self): def test_digits(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'digits', 5, 0) self.checkIntegerParam(widget, 'digits', 5, 0)
def test_configure_from(self): def test_from(self):
widget = self.create() widget = self.create()
conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round
self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
def test_configure_label(self): def test_label(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'label', 'any string') self.checkParam(widget, 'label', 'any string')
self.checkParam(widget, 'label', '') self.checkParam(widget, 'label', '')
def test_configure_length(self): def test_length(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
def test_configure_resolution(self): def test_resolution(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2) self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2)
def test_configure_showvalue(self): def test_showvalue(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'showvalue') self.checkBooleanParam(widget, 'showvalue')
def test_configure_sliderlength(self): def test_sliderlength(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'sliderlength', self.checkPixelsParam(widget, 'sliderlength',
10, 11.2, 15.6, -3, '3m') 10, 11.2, 15.6, -3, '3m')
def test_configure_sliderrelief(self): def test_sliderrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'sliderrelief') self.checkReliefParam(widget, 'sliderrelief')
def test_configure_tickinterval(self): def test_tickinterval(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0, self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0,
conv=float_round) conv=float_round)
self.checkParam(widget, 'tickinterval', -2, expected=2, self.checkParam(widget, 'tickinterval', -2, expected=2,
conv=float_round) conv=float_round)
def test_configure_to(self): def test_to(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10,
conv=float_round) conv=float_round)
@ -999,15 +998,15 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Scrollbar(self.root, **kwargs) return tkinter.Scrollbar(self.root, **kwargs)
def test_configure_activerelief(self): def test_activerelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'activerelief') self.checkReliefParam(widget, 'activerelief')
def test_configure_elementborderwidth(self): def test_elementborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m') self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m')
def test_configure_orient(self): def test_orient(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal', self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal',
errmsg='bad orientation "{}": must be vertical or horizontal') errmsg='bad orientation "{}": must be vertical or horizontal')
@ -1048,63 +1047,63 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.PanedWindow(self.root, **kwargs) return tkinter.PanedWindow(self.root, **kwargs)
def test_configure_handlepad(self): def test_handlepad(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m') self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m')
def test_configure_handlesize(self): def test_handlesize(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
conv=noconv) conv=noconv)
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
conv=noconv) conv=noconv)
def test_configure_opaqueresize(self): def test_opaqueresize(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'opaqueresize') self.checkBooleanParam(widget, 'opaqueresize')
@requires_tcl(8, 6, 5) @requires_tcl(8, 6, 5)
def test_configure_proxybackground(self): def test_proxybackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'proxybackground') self.checkColorParam(widget, 'proxybackground')
@requires_tcl(8, 6, 5) @requires_tcl(8, 6, 5)
def test_configure_proxyborderwidth(self): def test_proxyborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'proxyborderwidth', self.checkPixelsParam(widget, 'proxyborderwidth',
0, 1.3, 2.9, 6, -2, '10p', 0, 1.3, 2.9, 6, -2, '10p',
conv=noconv) conv=noconv)
@requires_tcl(8, 6, 5) @requires_tcl(8, 6, 5)
def test_configure_proxyrelief(self): def test_proxyrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'proxyrelief') self.checkReliefParam(widget, 'proxyrelief')
def test_configure_sashcursor(self): def test_sashcursor(self):
widget = self.create() widget = self.create()
self.checkCursorParam(widget, 'sashcursor') self.checkCursorParam(widget, 'sashcursor')
def test_configure_sashpad(self): def test_sashpad(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m') self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m')
def test_configure_sashrelief(self): def test_sashrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'sashrelief') self.checkReliefParam(widget, 'sashrelief')
def test_configure_sashwidth(self): def test_sashwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
conv=noconv) conv=noconv)
def test_configure_showhandle(self): def test_showhandle(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'showhandle') self.checkBooleanParam(widget, 'showhandle')
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
conv=noconv) conv=noconv)
@ -1223,23 +1222,23 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Menu(self.root, **kwargs) return tkinter.Menu(self.root, **kwargs)
def test_configure_postcommand(self): def test_postcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'postcommand') self.checkCommandParam(widget, 'postcommand')
def test_configure_tearoff(self): def test_tearoff(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'tearoff') self.checkBooleanParam(widget, 'tearoff')
def test_configure_tearoffcommand(self): def test_tearoffcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'tearoffcommand') self.checkCommandParam(widget, 'tearoffcommand')
def test_configure_title(self): def test_title(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'title', 'any string') self.checkParam(widget, 'title', 'any string')
def test_configure_type(self): def test_type(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'type', self.checkEnumParam(widget, 'type',
'normal', 'tearoff', 'menubar') 'normal', 'tearoff', 'menubar')
@ -1292,7 +1291,7 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Message(self.root, **kwargs) return tkinter.Message(self.root, **kwargs)
def test_configure_aspect(self): def test_aspect(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'aspect', 250, 0, -300) self.checkIntegerParam(widget, 'aspect', 250, 0, -300)

View File

@ -16,7 +16,7 @@ requires('gui')
class StandardTtkOptionsTests(StandardOptionsTests): class StandardTtkOptionsTests(StandardOptionsTests):
def test_configure_class(self): def test_class(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['class'], '') self.assertEqual(widget['class'], '')
errmsg='attempt to change read-only option' errmsg='attempt to change read-only option'
@ -26,7 +26,7 @@ class StandardTtkOptionsTests(StandardOptionsTests):
widget2 = self.create(class_='Foo') widget2 = self.create(class_='Foo')
self.assertEqual(widget2['class'], 'Foo') self.assertEqual(widget2['class'], 'Foo')
def test_configure_padding(self): def test_padding(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'padding', 0, expected=('0',)) self.checkParam(widget, 'padding', 0, expected=('0',))
self.checkParam(widget, 'padding', 5, expected=('5',)) 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', ('5p', '6p', '7p', '8p'))
self.checkParam(widget, 'padding', (), expected='') self.checkParam(widget, 'padding', (), expected='')
def test_configure_style(self): def test_style(self):
widget = self.create() widget = self.create()
self.assertEqual(widget['style'], '') self.assertEqual(widget['style'], '')
errmsg = 'Layout Foo not found' errmsg = 'Layout Foo not found'
@ -139,14 +139,14 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.LabelFrame(self.root, **kwargs) return ttk.LabelFrame(self.root, **kwargs)
def test_configure_labelanchor(self): def test_labelanchor(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'labelanchor', self.checkEnumParam(widget, 'labelanchor',
'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws', 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws',
errmsg='Bad label anchor specification {}') errmsg='Bad label anchor specification {}')
self.checkInvalidParam(widget, 'labelanchor', 'center') self.checkInvalidParam(widget, 'labelanchor', 'center')
def test_configure_labelwidget(self): def test_labelwidget(self):
widget = self.create() widget = self.create()
label = ttk.Label(self.root, text='Mupp', name='foo') label = ttk.Label(self.root, text='Mupp', name='foo')
self.checkParam(widget, 'labelwidget', label, expected='.foo') self.checkParam(widget, 'labelwidget', label, expected='.foo')
@ -168,17 +168,17 @@ class AbstractLabelTest(AbstractWidgetTest):
self.checkInvalidParam(widget, name, 'spam', self.checkInvalidParam(widget, name, 'spam',
errmsg='image "spam" doesn\'t exist') errmsg='image "spam" doesn\'t exist')
def test_configure_compound(self): def test_compound(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'compound', self.checkEnumParam(widget, 'compound',
'none', 'text', 'image', 'center', 'none', 'text', 'image', 'center',
'top', 'bottom', 'left', 'right') 'top', 'bottom', 'left', 'right')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'state', 'active', 'disabled', 'normal') self.checkParams(widget, 'state', 'active', 'disabled', 'normal')
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'width', 402, -402, 0) self.checkParams(widget, 'width', 402, -402, 0)
@ -197,7 +197,7 @@ class LabelTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Label(self.root, **kwargs) return ttk.Label(self.root, **kwargs)
def test_configure_font(self): def test_font(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'font', self.checkParam(widget, 'font',
'-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
@ -215,7 +215,7 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Button(self.root, **kwargs) return ttk.Button(self.root, **kwargs)
def test_configure_default(self): def test_default(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled') self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled')
@ -240,11 +240,11 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Checkbutton(self.root, **kwargs) return ttk.Checkbutton(self.root, **kwargs)
def test_configure_offvalue(self): def test_offvalue(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
def test_configure_onvalue(self): def test_onvalue(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
@ -292,27 +292,27 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Entry(self.root, **kwargs) return ttk.Entry(self.root, **kwargs)
def test_configure_invalidcommand(self): def test_invalidcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'invalidcommand') self.checkCommandParam(widget, 'invalidcommand')
def test_configure_show(self): def test_show(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'show', '*') self.checkParam(widget, 'show', '*')
self.checkParam(widget, 'show', '') self.checkParam(widget, 'show', '')
self.checkParam(widget, 'show', ' ') self.checkParam(widget, 'show', ' ')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'state', self.checkParams(widget, 'state',
'disabled', 'normal', 'readonly') 'disabled', 'normal', 'readonly')
def test_configure_validate(self): def test_validate(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'validate', self.checkEnumParam(widget, 'validate',
'all', 'key', 'focus', 'focusin', 'focusout', 'none') 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
def test_configure_validatecommand(self): def test_validatecommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'validatecommand') self.checkCommandParam(widget, 'validatecommand')
@ -429,7 +429,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Combobox(self.root, **kwargs) return ttk.Combobox(self.root, **kwargs)
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i')
@ -459,7 +459,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
self.assertTrue(success) self.assertTrue(success)
def test_configure_postcommand(self): def test_postcommand(self):
success = [] success = []
self.combo['postcommand'] = lambda: success.append(True) self.combo['postcommand'] = lambda: success.append(True)
@ -475,7 +475,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
self.assertEqual(len(success), 1) self.assertEqual(len(success), 1)
def test_configure_values(self): def test_values(self):
def check_get_current(getval, currval): def check_get_current(getval, currval):
self.assertEqual(self.combo.get(), getval) self.assertEqual(self.combo.get(), getval)
self.assertEqual(self.combo.current(), currval) self.assertEqual(self.combo.current(), currval)
@ -551,7 +551,7 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.PanedWindow(self.root, **kwargs) return ttk.PanedWindow(self.root, **kwargs)
def test_configure_orient(self): def test_orient(self):
widget = self.create() widget = self.create()
self.assertEqual(str(widget['orient']), 'vertical') self.assertEqual(str(widget['orient']), 'vertical')
errmsg='attempt to change read-only option' errmsg='attempt to change read-only option'
@ -684,11 +684,11 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Radiobutton(self.root, **kwargs) return ttk.Radiobutton(self.root, **kwargs)
def test_configure_value(self): def test_value(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'value', 1, 2.3, '', 'any string') self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
def test_configure_invoke(self): def test_invoke(self):
success = [] success = []
def cb_test(): def cb_test():
success.append(1) success.append(1)
@ -739,7 +739,7 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
self.checkEnumParam(widget, 'direction', self.checkEnumParam(widget, 'direction',
'above', 'below', 'left', 'right', 'flush') 'above', 'below', 'left', 'right', 'flush')
def test_configure_menu(self): def test_menu(self):
widget = self.create() widget = self.create()
menu = tkinter.Menu(widget, name='menu') menu = tkinter.Menu(widget, name='menu')
self.checkParam(widget, 'menu', menu, conv=str) self.checkParam(widget, 'menu', menu, conv=str)
@ -764,19 +764,19 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Scale(self.root, **kwargs) return ttk.Scale(self.root, **kwargs)
def test_configure_from(self): def test_from(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False)
def test_configure_length(self): def test_length(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
def test_configure_to(self): def test_to(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False)
def test_configure_value(self): def test_value(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) 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): def create(self, **kwargs):
return ttk.Progressbar(self.root, **kwargs) return ttk.Progressbar(self.root, **kwargs)
def test_configure_length(self): def test_length(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i')
def test_configure_maximum(self): def test_maximum(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False)
def test_configure_mode(self): def test_mode(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate')
def test_configure_phase(self): def test_phase(self):
# XXX # XXX
pass pass
def test_configure_value(self): def test_value(self):
widget = self.create() widget = self.create()
self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10,
conv=False) conv=False)
@ -1071,7 +1071,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc')
def test_configure_tabs(self): def test_tabs(self):
self.assertEqual(len(self.nb.tabs()), 2) self.assertEqual(len(self.nb.tabs()), 2)
self.nb.forget(self.child1) 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.event_generate('<ButtonRelease-1>', x=x, y=y)
self.spin.update_idletasks() self.spin.update_idletasks()
def test_configure_command(self): def test_command(self):
success = [] success = []
self.spin['command'] = lambda: success.append(True) self.spin['command'] = lambda: success.append(True)
@ -1167,7 +1167,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self.spin.update() self.spin.update()
self.assertEqual(len(success), 2) self.assertEqual(len(success), 2)
def test_configure_to(self): def test_to(self):
self.spin['from'] = 0 self.spin['from'] = 0
self.spin['to'] = 5 self.spin['to'] = 5
self.spin.set(4) self.spin.set(4)
@ -1179,7 +1179,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self._click_increment_arrow() # 5 self._click_increment_arrow() # 5
self.assertEqual(self.spin.get(), '5') self.assertEqual(self.spin.get(), '5')
def test_configure_from(self): def test_from(self):
self.spin['from'] = 1 self.spin['from'] = 1
self.spin['to'] = 10 self.spin['to'] = 10
self.spin.set(2) self.spin.set(2)
@ -1189,7 +1189,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self._click_decrement_arrow() # 1 self._click_decrement_arrow() # 1
self.assertEqual(self.spin.get(), '1') self.assertEqual(self.spin.get(), '1')
def test_configure_increment(self): def test_increment(self):
self.spin['from'] = 0 self.spin['from'] = 0
self.spin['to'] = 10 self.spin['to'] = 10
self.spin['increment'] = 4 self.spin['increment'] = 4
@ -1203,7 +1203,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self._click_decrement_arrow() # 3 self._click_decrement_arrow() # 3
self.assertEqual(self.spin.get(), '3') self.assertEqual(self.spin.get(), '3')
def test_configure_format(self): def test_format(self):
self.spin.set(1) self.spin.set(1)
self.spin['format'] = '%10.3f' self.spin['format'] = '%10.3f'
self.spin.update() self.spin.update()
@ -1220,7 +1220,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self.assertTrue('.' not in value) self.assertTrue('.' not in value)
self.assertEqual(len(value), 1) self.assertEqual(len(value), 1)
def test_configure_wrap(self): def test_wrap(self):
self.spin['to'] = 10 self.spin['to'] = 10
self.spin['from'] = 1 self.spin['from'] = 1
self.spin.set(1) self.spin.set(1)
@ -1239,7 +1239,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
self._click_decrement_arrow() self._click_decrement_arrow()
self.assertEqual(self.spin.get(), '1') self.assertEqual(self.spin.get(), '1')
def test_configure_values(self): def test_values(self):
self.assertEqual(self.spin['values'], self.assertEqual(self.spin['values'],
() if tcl_version < (8, 5) else '') () if tcl_version < (8, 5) else '')
self.checkParam(self.spin, 'values', 'mon tue wed thur', self.checkParam(self.spin, 'values', 'mon tue wed thur',
@ -1299,14 +1299,14 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Treeview(self.root, **kwargs) return ttk.Treeview(self.root, **kwargs)
def test_configure_columns(self): def test_columns(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'columns', 'a b c', self.checkParam(widget, 'columns', 'a b c',
expected=('a', 'b', 'c')) expected=('a', 'b', 'c'))
self.checkParam(widget, 'columns', ('a', 'b', 'c')) self.checkParam(widget, 'columns', ('a', 'b', 'c'))
self.checkParam(widget, 'columns', '') self.checkParam(widget, 'columns', '')
def test_configure_displaycolumns(self): def test_displaycolumns(self):
widget = self.create() widget = self.create()
widget['columns'] = ('a', 'b', 'c') widget['columns'] = ('a', 'b', 'c')
self.checkParam(widget, 'displaycolumns', 'b a c', self.checkParam(widget, 'displaycolumns', 'b a c',
@ -1322,17 +1322,17 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
self.checkInvalidParam(widget, 'displaycolumns', (1, -2), self.checkInvalidParam(widget, 'displaycolumns', (1, -2),
errmsg='Column index -2 out of bounds') errmsg='Column index -2 out of bounds')
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False)
self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv)
def test_configure_selectmode(self): def test_selectmode(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'selectmode', self.checkEnumParam(widget, 'selectmode',
'none', 'browse', 'extended') 'none', 'browse', 'extended')
def test_configure_show(self): def test_show(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'show', 'tree headings', self.checkParam(widget, 'show', 'tree headings',
expected=('tree', 'headings')) expected=('tree', 'headings'))

View File

@ -242,31 +242,31 @@ class StandardOptionsTests:
'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand', 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand',
) )
def test_configure_activebackground(self): def test_activebackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'activebackground') self.checkColorParam(widget, 'activebackground')
def test_configure_activeborderwidth(self): def test_activeborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'activeborderwidth', self.checkPixelsParam(widget, 'activeborderwidth',
0, 1.3, 2.9, 6, -2, '10p') 0, 1.3, 2.9, 6, -2, '10p')
def test_configure_activeforeground(self): def test_activeforeground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'activeforeground') self.checkColorParam(widget, 'activeforeground')
def test_configure_anchor(self): def test_anchor(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'anchor', self.checkEnumParam(widget, 'anchor',
'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
def test_configure_background(self): def test_background(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'background') self.checkColorParam(widget, 'background')
if 'bg' in self.OPTIONS: if 'bg' in self.OPTIONS:
self.checkColorParam(widget, 'bg') self.checkColorParam(widget, 'bg')
def test_configure_bitmap(self): def test_bitmap(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'questhead')
self.checkParam(widget, 'bitmap', 'gray50') self.checkParam(widget, 'bitmap', 'gray50')
@ -279,88 +279,88 @@ class StandardOptionsTests:
self.checkInvalidParam(widget, 'bitmap', 'spam', self.checkInvalidParam(widget, 'bitmap', 'spam',
errmsg='bitmap "spam" not defined') errmsg='bitmap "spam" not defined')
def test_configure_borderwidth(self): def test_borderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'borderwidth', self.checkPixelsParam(widget, 'borderwidth',
0, 1.3, 2.6, 6, -2, '10p') 0, 1.3, 2.6, 6, -2, '10p')
if 'bd' in self.OPTIONS: if 'bd' in self.OPTIONS:
self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p') self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p')
def test_configure_compound(self): def test_compound(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'compound', self.checkEnumParam(widget, 'compound',
'bottom', 'center', 'left', 'none', 'right', 'top') 'bottom', 'center', 'left', 'none', 'right', 'top')
def test_configure_cursor(self): def test_cursor(self):
widget = self.create() widget = self.create()
self.checkCursorParam(widget, 'cursor') self.checkCursorParam(widget, 'cursor')
def test_configure_disabledforeground(self): def test_disabledforeground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'disabledforeground') self.checkColorParam(widget, 'disabledforeground')
def test_configure_exportselection(self): def test_exportselection(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'exportselection') self.checkBooleanParam(widget, 'exportselection')
def test_configure_font(self): def test_font(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'font', self.checkParam(widget, 'font',
'-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
self.checkInvalidParam(widget, 'font', '', self.checkInvalidParam(widget, 'font', '',
errmsg='font "" doesn\'t exist') errmsg='font "" doesn\'t exist')
def test_configure_foreground(self): def test_foreground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'foreground') self.checkColorParam(widget, 'foreground')
if 'fg' in self.OPTIONS: if 'fg' in self.OPTIONS:
self.checkColorParam(widget, 'fg') self.checkColorParam(widget, 'fg')
def test_configure_highlightbackground(self): def test_highlightbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'highlightbackground') self.checkColorParam(widget, 'highlightbackground')
def test_configure_highlightcolor(self): def test_highlightcolor(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'highlightcolor') self.checkColorParam(widget, 'highlightcolor')
def test_configure_highlightthickness(self): def test_highlightthickness(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'highlightthickness', self.checkPixelsParam(widget, 'highlightthickness',
0, 1.3, 2.6, 6, '10p') 0, 1.3, 2.6, 6, '10p')
self.checkParam(widget, 'highlightthickness', -2, expected=0, self.checkParam(widget, 'highlightthickness', -2, expected=0,
conv=self._conv_pixels) conv=self._conv_pixels)
def test_configure_image(self): def test_image(self):
widget = self.create() widget = self.create()
self.checkImageParam(widget, 'image') self.checkImageParam(widget, 'image')
def test_configure_insertbackground(self): def test_insertbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'insertbackground') self.checkColorParam(widget, 'insertbackground')
def test_configure_insertborderwidth(self): def test_insertborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'insertborderwidth', self.checkPixelsParam(widget, 'insertborderwidth',
0, 1.3, 2.6, 6, -2, '10p') 0, 1.3, 2.6, 6, -2, '10p')
def test_configure_insertofftime(self): def test_insertofftime(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'insertofftime', 100) self.checkIntegerParam(widget, 'insertofftime', 100)
def test_configure_insertontime(self): def test_insertontime(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'insertontime', 100) self.checkIntegerParam(widget, 'insertontime', 100)
def test_configure_insertwidth(self): def test_insertwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p')
def test_configure_jump(self): def test_jump(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'jump') self.checkBooleanParam(widget, 'jump')
def test_configure_justify(self): def test_justify(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'justify', 'left', 'right', 'center', self.checkEnumParam(widget, 'justify', 'left', 'right', 'center',
errmsg='bad justification "{}": must be ' errmsg='bad justification "{}": must be '
@ -369,155 +369,154 @@ class StandardOptionsTests:
errmsg='ambiguous justification "": must be ' errmsg='ambiguous justification "": must be '
'left, right, or center') 'left, right, or center')
def test_configure_orient(self): def test_orient(self):
widget = self.create() widget = self.create()
self.assertEqual(str(widget['orient']), self.default_orient) self.assertEqual(str(widget['orient']), self.default_orient)
self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical')
def test_configure_padx(self): def test_padx(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m', self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m',
conv=self._conv_pad_pixels) conv=self._conv_pad_pixels)
def test_configure_pady(self): def test_pady(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m', self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m',
conv=self._conv_pad_pixels) conv=self._conv_pad_pixels)
def test_configure_relief(self): def test_relief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'relief') self.checkReliefParam(widget, 'relief')
def test_configure_repeatdelay(self): def test_repeatdelay(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'repeatdelay', -500, 500) self.checkIntegerParam(widget, 'repeatdelay', -500, 500)
def test_configure_repeatinterval(self): def test_repeatinterval(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'repeatinterval', -500, 500) self.checkIntegerParam(widget, 'repeatinterval', -500, 500)
def test_configure_selectbackground(self): def test_selectbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'selectbackground') self.checkColorParam(widget, 'selectbackground')
def test_configure_selectborderwidth(self): def test_selectborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p')
def test_configure_selectforeground(self): def test_selectforeground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'selectforeground') self.checkColorParam(widget, 'selectforeground')
def test_configure_setgrid(self): def test_setgrid(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'setgrid') self.checkBooleanParam(widget, 'setgrid')
def test_configure_state(self): def test_state(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal') self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal')
def test_configure_takefocus(self): def test_takefocus(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'takefocus', '0', '1', '') self.checkParams(widget, 'takefocus', '0', '1', '')
def test_configure_text(self): def test_text(self):
widget = self.create() widget = self.create()
self.checkParams(widget, 'text', '', 'any string') self.checkParams(widget, 'text', '', 'any string')
def test_configure_textvariable(self): def test_textvariable(self):
widget = self.create() widget = self.create()
var = tkinter.StringVar(self.root) var = tkinter.StringVar(self.root)
self.checkVariableParam(widget, 'textvariable', var) self.checkVariableParam(widget, 'textvariable', var)
def test_configure_troughcolor(self): def test_troughcolor(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'troughcolor') self.checkColorParam(widget, 'troughcolor')
def test_configure_underline(self): def test_underline(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'underline', 0, 1, 10) self.checkIntegerParam(widget, 'underline', 0, 1, 10)
def test_configure_wraplength(self): def test_wraplength(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'wraplength', 100) self.checkPixelsParam(widget, 'wraplength', 100)
def test_configure_xscrollcommand(self): def test_xscrollcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'xscrollcommand') self.checkCommandParam(widget, 'xscrollcommand')
def test_configure_yscrollcommand(self): def test_yscrollcommand(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'yscrollcommand') self.checkCommandParam(widget, 'yscrollcommand')
# non-standard but common options # non-standard but common options
def test_configure_command(self): def test_command(self):
widget = self.create() widget = self.create()
self.checkCommandParam(widget, 'command') self.checkCommandParam(widget, 'command')
def test_configure_indicatoron(self): def test_indicatoron(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'indicatoron') self.checkBooleanParam(widget, 'indicatoron')
def test_configure_offrelief(self): def test_offrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'offrelief') self.checkReliefParam(widget, 'offrelief')
def test_configure_overrelief(self): def test_overrelief(self):
widget = self.create() widget = self.create()
self.checkReliefParam(widget, 'overrelief') self.checkReliefParam(widget, 'overrelief')
def test_configure_selectcolor(self): def test_selectcolor(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'selectcolor') self.checkColorParam(widget, 'selectcolor')
def test_configure_selectimage(self): def test_selectimage(self):
widget = self.create() widget = self.create()
self.checkImageParam(widget, 'selectimage') self.checkImageParam(widget, 'selectimage')
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_tristateimage(self): def test_tristateimage(self):
widget = self.create() widget = self.create()
self.checkImageParam(widget, 'tristateimage') self.checkImageParam(widget, 'tristateimage')
@requires_tcl(8, 5) @requires_tcl(8, 5)
def test_configure_tristatevalue(self): def test_tristatevalue(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'tristatevalue', 'unknowable') self.checkParam(widget, 'tristatevalue', 'unknowable')
def test_configure_variable(self): def test_variable(self):
widget = self.create() widget = self.create()
var = tkinter.DoubleVar(self.root) var = tkinter.DoubleVar(self.root)
self.checkVariableParam(widget, 'variable', var) self.checkVariableParam(widget, 'variable', var)
class IntegerSizeTests: class IntegerSizeTests:
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'height', 100, -100, 0) self.checkIntegerParam(widget, 'height', 100, -100, 0)
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'width', 402, -402, 0) self.checkIntegerParam(widget, 'width', 402, -402, 0)
class PixelSizeTests: class PixelSizeTests:
def test_configure_height(self): def test_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c')
def test_configure_width(self): def test_width(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i')
def add_standard_options(*source_classes): def add_standard_options(*source_classes):
# This decorator adds test_configure_xxx methods from source classes for # This decorator adds test_xxx methods from source classes for every xxx
# every xxx option in the OPTIONS class attribute if they are not defined # option in the OPTIONS class attribute if they are not defined explicitly.
# explicitly.
def decorator(cls): def decorator(cls):
for option in cls.OPTIONS: for option in cls.OPTIONS:
methodname = 'test_configure_' + option methodname = 'test_' + option
if not hasattr(cls, methodname): if not hasattr(cls, methodname):
for source_class in source_classes: for source_class in source_classes:
if hasattr(source_class, methodname): if hasattr(source_class, methodname):

View File

@ -386,7 +386,7 @@ class TixWidget(tkinter.Widget):
self.tk.call(name, 'configure', '-' + option, value) self.tk.call(name, 'configure', '-' + option, value)
# These are missing from Tkinter # These are missing from Tkinter
def image_create(self, imgtype, cnf={}, master=None, **kw): def image_create(self, imgtype, cnf={}, master=None, **kw):
if master is None: if not master:
master = self master = self
if kw and cnf: cnf = _cnfmerge((cnf, kw)) if kw and cnf: cnf = _cnfmerge((cnf, kw))
elif kw: cnf = kw elif kw: cnf = kw
@ -467,7 +467,7 @@ class DisplayStyle:
(multiple) Display Items""" (multiple) Display Items"""
def __init__(self, itemtype, cnf={}, *, master=None, **kw): def __init__(self, itemtype, cnf={}, *, master=None, **kw):
if master is None: if not master:
if 'refwindow' in kw: if 'refwindow' in kw:
master = kw['refwindow'] master = kw['refwindow']
elif 'refwindow' in cnf: 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)) return self.tk.call(self._w, 'add', entry, *self._options(cnf, kw))
def add_child(self, parent=None, cnf={}, **kw): def add_child(self, parent=None, cnf={}, **kw):
if parent is None: if not parent:
parent = '' parent = ''
return self.tk.call( return self.tk.call(
self._w, 'addchild', parent, *self._options(cnf, kw)) self._w, 'addchild', parent, *self._options(cnf, kw))

View File

@ -569,7 +569,7 @@ class Widget(tkinter.Widget):
matches statespec. statespec is expected to be a sequence.""" matches statespec. statespec is expected to be a sequence."""
ret = self.tk.getboolean( ret = self.tk.getboolean(
self.tk.call(self._w, "instate", ' '.join(statespec))) self.tk.call(self._w, "instate", ' '.join(statespec)))
if ret and callback is not None: if ret and callback:
return callback(*args, **kw) return callback(*args, **kw)
return ret return ret

View File

@ -544,9 +544,8 @@ def TypeAlias(self, parameters):
@_SpecialForm @_SpecialForm
def Concatenate(self, parameters): def Concatenate(self, parameters):
"""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a """Used in conjunction with ParamSpec and Callable to represent a higher
higher order function which adds, removes or transforms parameters of a order function which adds, removes or transforms parameters of a Callable.
callable.
For example:: For example::
@ -736,11 +735,11 @@ class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
Parameter specification variables exist primarily for the benefit of static Parameter specification variables exist primarily for the benefit of static
type checkers. They are used to forward the parameter types of one type checkers. They are used to forward the parameter types of one
callable to another callable, a pattern commonly found in higher order Callable to another Callable, a pattern commonly found in higher order
functions and decorators. They are only valid when used in ``Concatenate``, functions and decorators. They are only valid when used in Concatenate, or
or s the first argument to ``Callable``, or as parameters for user-defined as the first argument to Callable, or as parameters for user-defined Generics.
Generics. See class Generic for more information on generic types. An See class Generic for more information on generic types. An example for
example for annotating a decorator:: annotating a decorator::
T = TypeVar('T') T = TypeVar('T')
P = ParamSpec('P') P = ParamSpec('P')
@ -1250,7 +1249,7 @@ def _no_init(self, *args, **kwargs):
raise TypeError('Protocols cannot be instantiated') raise TypeError('Protocols cannot be instantiated')
def _allow_reckless_class_checks(): def _allow_reckless_class_cheks():
"""Allow instance and class checks for special stdlib modules. """Allow instance and class checks for special stdlib modules.
The abc and functools modules indiscriminately call isinstance() and The abc and functools modules indiscriminately call isinstance() and
@ -1339,12 +1338,12 @@ class Protocol(Generic, metaclass=_ProtocolMeta):
# First, perform various sanity checks. # First, perform various sanity checks.
if not getattr(cls, '_is_runtime_protocol', False): if not getattr(cls, '_is_runtime_protocol', False):
if _allow_reckless_class_checks(): if _allow_reckless_class_cheks():
return NotImplemented return NotImplemented
raise TypeError("Instance and class checks can only be used with" raise TypeError("Instance and class checks can only be used with"
" @runtime_checkable protocols") " @runtime_checkable protocols")
if not _is_callable_members_only(cls): if not _is_callable_members_only(cls):
if _allow_reckless_class_checks(): if _allow_reckless_class_cheks():
return NotImplemented return NotImplemented
raise TypeError("Protocols with non-method members" raise TypeError("Protocols with non-method members"
" don't support issubclass()") " don't support issubclass()")
@ -1669,8 +1668,6 @@ def get_origin(tp):
return tp.__origin__ return tp.__origin__
if tp is Generic: if tp is Generic:
return Generic return Generic
if isinstance(tp, types.Union):
return types.Union
return None return None
@ -1687,14 +1684,12 @@ def get_args(tp):
""" """
if isinstance(tp, _AnnotatedAlias): if isinstance(tp, _AnnotatedAlias):
return (tp.__origin__,) + tp.__metadata__ return (tp.__origin__,) + tp.__metadata__
if isinstance(tp, (_GenericAlias, GenericAlias)): if isinstance(tp, _GenericAlias):
res = tp.__args__ res = tp.__args__
if (tp.__origin__ is collections.abc.Callable if tp.__origin__ is collections.abc.Callable and res[0] is not Ellipsis:
and not (res[0] is Ellipsis
or isinstance(res[0], (ParamSpec, _ConcatenateGenericAlias)))):
res = (list(res[:-1]), res[-1]) res = (list(res[:-1]), res[-1])
return res return res
if isinstance(tp, types.Union): if isinstance(tp, GenericAlias):
return tp.__args__ return tp.__args__
return () return ()

View File

@ -773,11 +773,7 @@ def _parse_proxy(proxy):
raise ValueError("proxy URL with no authority: %r" % proxy) raise ValueError("proxy URL with no authority: %r" % proxy)
# We have an authority, so for RFC 3986-compliant URLs (by ss 3. # We have an authority, so for RFC 3986-compliant URLs (by ss 3.
# and 3.3.), path is empty or starts with '/' # and 3.3.), path is empty or starts with '/'
if '@' in r_scheme: end = r_scheme.find("/", 2)
host_separator = r_scheme.find('@')
end = r_scheme.find("/", host_separator)
else:
end = r_scheme.find("/", 2)
if end == -1: if end == -1:
end = None end = None
authority = r_scheme[2:end] authority = r_scheme[2:end]

View File

@ -242,12 +242,15 @@ def library_recipes():
result.extend([ result.extend([
dict( dict(
name="OpenSSL 1.1.1i", name="OpenSSL 1.1.1g",
url="https://www.openssl.org/source/openssl-1.1.1i.tar.gz", url="https://www.openssl.org/source/openssl-1.1.1g.tar.gz",
checksum='08987c3cf125202e2b0840035efb392c', checksum='76766e98997660138cdaf13a187bd234',
buildrecipe=build_universal_openssl, buildrecipe=build_universal_openssl,
configure=None, configure=None,
install=None, install=None,
patches=[
"openssl-mac-arm64.patch",
],
), ),
]) ])
@ -260,10 +263,10 @@ def library_recipes():
tk_patches = ['tk868_on_10_8_10_9.patch'] tk_patches = ['tk868_on_10_8_10_9.patch']
else: else:
tcl_tk_ver='8.6.11' tcl_tk_ver='8.6.10'
tcl_checksum='8a4c004f48984a03a7747e9ba06e4da4' tcl_checksum='97c55573f8520bcab74e21bfd8d0aadc'
tk_checksum='c7ee71a2d05bba78dfffd76528dc17c6' tk_checksum='602a47ad9ecac7bf655ada729d140a94'
tk_patches = [ ] tk_patches = [ ]
@ -354,9 +357,9 @@ def library_recipes():
), ),
), ),
dict( dict(
name="SQLite 3.34.0", name="SQLite 3.33.0",
url="https://sqlite.org/2020/sqlite-autoconf-3340000.tar.gz", url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz",
checksum='7f33c9db7b713957fcb9271fe9049fef', checksum='842a8a100d7b01b09e543deb2b7951dd',
extra_cflags=('-Os ' extra_cflags=('-Os '
'-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS5 '
'-DSQLITE_ENABLE_FTS4 ' '-DSQLITE_ENABLE_FTS4 '
@ -1135,6 +1138,7 @@ def buildPythonDocs():
if not os.path.exists(htmlDir): if not os.path.exists(htmlDir):
# Create virtual environment for docs builds with blurb and sphinx # Create virtual environment for docs builds with blurb and sphinx
runCommand('make venv') runCommand('make venv')
runCommand('venv/bin/python3 -m pip install -U Sphinx==2.3.1')
runCommand('make html PYTHON=venv/bin/python') runCommand('make html PYTHON=venv/bin/python')
os.rename(htmlDir, docdir) os.rename(htmlDir, docdir)
os.chdir(curDir) os.chdir(curDir)
@ -1611,7 +1615,7 @@ def buildDMG():
if os.path.exists(outdir): if os.path.exists(outdir):
shutil.rmtree(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 # installer file name. With the introduction of weaklinked installer
# variants, we may have two variants with the same file name, i.e. # 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 # both ending in '10.9'. To avoid this, we now use the major/minor

View File

@ -0,0 +1,41 @@
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"

View File

@ -20,7 +20,7 @@ fi
# Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH.
BSH="`basename "${theShell}"`" BSH="`basename "${theShell}"`"
case "${BSH}" in case "${BSH}" in
bash|ksh|sh|*csh|zsh|fish) bash|ksh|sh|*csh|zsh)
if [ `id -ur` = 0 ]; then 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'` 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 else
@ -76,22 +76,6 @@ bash)
PR="${HOME}/.bash_profile" PR="${HOME}/.bash_profile"
fi 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) zsh)
PR="${HOME}/.zprofile" PR="${HOME}/.zprofile"
;; ;;

View File

@ -36,7 +36,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>IDLE</string> <string>IDLE</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>%version%, © 2001-2021 Python Software Foundation</string> <string>%version%, © 2001-2020 Python Software Foundation</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>IDLE.icns</string> <string>IDLE.icns</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>

View File

@ -40,7 +40,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>Python Launcher</string> <string>Python Launcher</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>%VERSION%, © 2001-2021 Python Software Foundation</string> <string>%VERSION%, © 2001-2020 Python Software Foundation</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>PythonLauncher.icns</string> <string>PythonLauncher.icns</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>

Some files were not shown because too many files have changed in this diff Show More