Merge pull request #1805 from youknowone/various-lib

module inspect, functools, zipfile and test test_unary, test_typechecks
This commit is contained in:
Jeong YunWon authored and GitHub committed 2020-03-21 09:53:32 +09:00
commit 07ebb9fed3
6 files changed
+2982 -168

No files matched your search

+194 -47
View File
@@ -11,12 +11,8 @@
__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial',
'partialmethod', 'singledispatch']
'partialmethod', 'singledispatch', 'singledispatchmethod']
try:
from _functools import reduce
except ImportError:
pass
from abc import get_cache_token
from collections import namedtuple
# import types, weakref # Deferred to single_dispatch()
@@ -226,6 +222,45 @@ except ImportError:
pass
################################################################################
### reduce() sequence to a single item
################################################################################
_initial_missing = object()
def reduce(function, sequence, initial=_initial_missing):
"""
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
"""
it = iter(sequence)
if initial is _initial_missing:
try:
value = next(it)
except StopIteration:
raise TypeError("reduce() of empty sequence with no initial value") from None
else:
value = initial
for element in it:
value = function(value, element)
return value
try:
from _functools import reduce
except ImportError:
pass
################################################################################
### partial() argument application
################################################################################
@@ -238,22 +273,13 @@ class partial:
__slots__ = "func", "args", "keywords", "__dict__", "__weakref__"
def __new__(*args, **keywords):
if not args:
raise TypeError("descriptor '__new__' of partial needs an argument")
if len(args) < 2:
raise TypeError("type 'partial' takes at least one argument")
cls, func, *args = args
def __new__(cls, func, /, *args, **keywords):
if not callable(func):
raise TypeError("the first argument must be callable")
args = tuple(args)
if hasattr(func, "func"):
args = func.args + args
tmpkw = func.keywords.copy()
tmpkw.update(keywords)
keywords = tmpkw
del tmpkw
keywords = {**func.keywords, **keywords}
func = func.func
self = super(partial, cls).__new__(cls)
@@ -263,13 +289,9 @@ class partial:
self.keywords = keywords
return self
def __call__(*args, **keywords):
if not args:
raise TypeError("descriptor '__call__' of partial needs an argument")
self, *args = args
newkeywords = self.keywords.copy()
newkeywords.update(keywords)
return self.func(*self.args, *args, **newkeywords)
def __call__(self, /, *args, **keywords):
keywords = {**self.keywords, **keywords}
return self.func(*self.args, *args, **keywords)
@recursive_repr()
def __repr__(self):
@@ -323,7 +345,23 @@ class partialmethod(object):
callables as instance methods.
"""
def __init__(self, func, *args, **keywords):
def __init__(*args, **keywords):
if len(args) >= 2:
self, func, *args = args
elif not args:
raise TypeError("descriptor '__init__' of partialmethod "
"needs an argument")
elif 'func' in keywords:
func = keywords.pop('func')
self, *args = args
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError("type 'partialmethod' takes at least one argument, "
"got %d" % (len(args)-1))
args = tuple(args)
if not callable(func) and not hasattr(func, "__get__"):
raise TypeError("{!r} is not callable or a descriptor"
.format(func))
@@ -336,12 +374,12 @@ class partialmethod(object):
# it's also more efficient since only one function will be called
self.func = func.func
self.args = func.args + args
self.keywords = func.keywords.copy()
self.keywords.update(keywords)
self.keywords = {**func.keywords, **keywords}
else:
self.func = func
self.args = args
self.keywords = keywords
__init__.__text_signature__ = '($self, func, /, *args, **keywords)'
def __repr__(self):
args = ", ".join(map(repr, self.args))
@@ -355,17 +393,14 @@ class partialmethod(object):
keywords=keywords)
def _make_unbound_method(self):
def _method(*args, **keywords):
call_keywords = self.keywords.copy()
call_keywords.update(keywords)
cls_or_self, *rest = args
call_args = (cls_or_self,) + self.args + tuple(rest)
return self.func(*call_args, **call_keywords)
def _method(cls_or_self, /, *args, **keywords):
keywords = {**self.keywords, **keywords}
return self.func(cls_or_self, *self.args, *args, **keywords)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method._partialmethod = self
return _method
def __get__(self, obj, cls):
def __get__(self, obj, cls=None):
get = getattr(self.func, "__get__", None)
result = None
if get is not None:
@@ -388,6 +423,12 @@ class partialmethod(object):
def __isabstractmethod__(self):
return getattr(self.func, "__isabstractmethod__", False)
# Helper functions
def _unwrap_partial(func):
while isinstance(func, partial):
func = func.func
return func
################################################################################
### LRU Cache function decorator
@@ -413,7 +454,7 @@ class _HashedSeq(list):
def _make_key(args, kwds, typed,
kwd_mark = (object(),),
fasttypes = {int, str, frozenset, type(None)},
fasttypes = {int, str},
tuple=tuple, type=type, len=len):
"""Make a cache key from optionally typed positional and keyword arguments
@@ -458,7 +499,7 @@ def lru_cache(maxsize=128, typed=False):
with f.cache_info(). Clear the cache and statistics with f.cache_clear().
Access the underlying function with f.__wrapped__.
See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
See: http://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
"""
@@ -467,11 +508,18 @@ def lru_cache(maxsize=128, typed=False):
# The internals of the lru_cache are encapsulated for thread safety and
# to allow the implementation to change (including a possible C version).
# Early detection of an erroneous call to @lru_cache without any arguments
# resulting in the inner function being passed to maxsize instead of an
# integer or None.
if maxsize is not None and not isinstance(maxsize, int):
raise TypeError('Expected maxsize to be an integer or None')
if isinstance(maxsize, int):
# Negative maxsize is treated as 0
if maxsize < 0:
maxsize = 0
elif callable(maxsize) and isinstance(typed, bool):
# The user_function was passed in directly via the maxsize argument
user_function, maxsize = maxsize, 128
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
return update_wrapper(wrapper, user_function)
elif maxsize is not None:
raise TypeError(
'Expected first argument to be an integer, a callable, or None')
def decorating_function(user_function):
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
@@ -497,10 +545,10 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
if maxsize == 0:
def wrapper(*args, **kwds):
# No caching -- just a statistics update after a successful call
# No caching -- just a statistics update
nonlocal misses
result = user_function(*args, **kwds)
misses += 1
result = user_function(*args, **kwds)
return result
elif maxsize is None:
@@ -513,9 +561,9 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
if result is not sentinel:
hits += 1
return result
misses += 1
result = user_function(*args, **kwds)
cache[key] = result
misses += 1
return result
else:
@@ -537,6 +585,7 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
link[NEXT] = root
hits += 1
return result
misses += 1
result = user_function(*args, **kwds)
with lock:
if key in cache:
@@ -574,7 +623,6 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
# Use the cache_len bound method instead of the len() function
# which could potentially be wrapped in an lru_cache itself.
full = (cache_len() >= maxsize)
misses += 1
return result
def cache_info():
@@ -807,9 +855,11 @@ def singledispatch(func):
# only import typing if annotation parsing is necessary
from typing import get_type_hints
argname, cls = next(iter(get_type_hints(func).items()))
assert isinstance(cls, type), (
f"Invalid annotation for {argname!r}. {cls!r} is not a class."
)
if not isinstance(cls, type):
raise TypeError(
f"Invalid annotation for {argname!r}. "
f"{cls!r} is not a class."
)
registry[cls] = func
if cache_token is None and hasattr(cls, '__abstractmethods__'):
cache_token = get_cache_token()
@@ -817,8 +867,13 @@ def singledispatch(func):
return func
def wrapper(*args, **kw):
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__)(*args, **kw)
funcname = getattr(func, '__name__', 'singledispatch function')
registry[object] = func
wrapper.register = register
wrapper.dispatch = dispatch
@@ -826,3 +881,95 @@ def singledispatch(func):
wrapper._clear_cache = dispatch_cache.clear
update_wrapper(wrapper, func)
return wrapper
# Descriptor version
class singledispatchmethod:
"""Single-dispatch generic method descriptor.
Supports wrapping existing descriptors and handles non-descriptor
callables as instance methods.
"""
def __init__(self, func):
if not callable(func) and not hasattr(func, "__get__"):
raise TypeError(f"{func!r} is not callable or a descriptor")
self.dispatcher = singledispatch(func)
self.func = func
def register(self, cls, method=None):
"""generic_method.register(cls, func) -> func
Registers a new implementation for the given *cls* on a *generic_method*.
"""
return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls=None):
def _method(*args, **kwargs):
method = self.dispatcher.dispatch(args[0].__class__)
return method.__get__(obj, cls)(*args, **kwargs)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method.register = self.register
update_wrapper(_method, self.func)
return _method
@property
def __isabstractmethod__(self):
return getattr(self.func, '__isabstractmethod__', False)
################################################################################
### cached_property() - computed once per instance, cached as attribute
################################################################################
_NOT_FOUND = object()
class cached_property:
def __init__(self, func):
self.func = func
self.attrname = None
self.__doc__ = func.__doc__
self.lock = RLock()
def __set_name__(self, owner, name):
if self.attrname is None:
self.attrname = name
elif name != self.attrname:
raise TypeError(
"Cannot assign the same cached_property to two different names "
f"({self.attrname!r} and {name!r})."
)
def __get__(self, instance, owner=None):
if instance is None:
return self
if self.attrname is None:
raise TypeError(
"Cannot use cached_property instance without calling __set_name__ on it.")
try:
cache = instance.__dict__
except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
msg = (
f"No '__dict__' attribute on {type(instance).__name__!r} "
f"instance to cache {self.attrname!r} property."
)
raise TypeError(msg) from None
val = cache.get(self.attrname, _NOT_FOUND)
if val is _NOT_FOUND:
with self.lock:
# check if another thread filled cache while we awaited lock
val = cache.get(self.attrname, _NOT_FOUND)
if val is _NOT_FOUND:
val = self.func(instance)
try:
cache[self.attrname] = val
except TypeError:
msg = (
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
f"does not support item assignment for caching {self.attrname!r} property."
)
raise TypeError(msg) from None
return val
+210 -119
View File
@@ -18,7 +18,7 @@ Here are some of the useful functions provided by this module:
getargvalues(), getcallargs() - get info about function arguments
getfullargspec() - same, with support for Python 3 features
formatargspec(), formatargvalues() - format an argument spec
formatargvalues() - format an argument spec
getouterframes(), getinnerframes() - get info about frames
currentframe() - get the current stack frame
stack(), trace() - get info about frames on the stack or in a traceback
@@ -31,7 +31,7 @@ Here are some of the useful functions provided by this module:
__author__ = ('Ka-Ping Yee <ping@lfw.org>',
'Yury Selivanov <yselivanov@sprymix.com>')
import ast
import abc
import dis
import collections.abc
import enum
@@ -110,7 +110,7 @@ def ismethoddescriptor(object):
def isdatadescriptor(object):
"""Return true if the object is a data descriptor.
Data descriptors have both a __get__ and a __set__ attribute. Examples are
Data descriptors have a __set__ or a __delete__ attribute. Examples are
properties (defined in Python) and getsets and members (defined in C).
Typically, data descriptors will also have __name__ and __doc__ attributes
(properties, getsets, and members have both of these attributes), but this
@@ -119,7 +119,7 @@ def isdatadescriptor(object):
# mutual exclusion
return False
tp = type(object)
return hasattr(tp, "__set__") and hasattr(tp, "__get__")
return hasattr(tp, "__set__") or hasattr(tp, "__delete__")
if hasattr(types, 'MemberDescriptorType'):
# CPython and equivalent
@@ -168,30 +168,38 @@ def isfunction(object):
__kwdefaults__ dict of keyword only parameters with defaults"""
return isinstance(object, types.FunctionType)
def isgeneratorfunction(object):
def _has_code_flag(f, flag):
"""Return true if ``f`` is a function (or a method or functools.partial
wrapper wrapping a function) whose code object has the given ``flag``
set in its flags."""
while ismethod(f):
f = f.__func__
f = functools._unwrap_partial(f)
if not isfunction(f):
return False
return bool(f.__code__.co_flags & flag)
def isgeneratorfunction(obj):
"""Return true if the object is a user-defined generator function.
Generator function objects provide the same attributes as functions.
See help(isfunction) for a list of attributes."""
return bool((isfunction(object) or ismethod(object)) and
object.__code__.co_flags & CO_GENERATOR)
return _has_code_flag(obj, CO_GENERATOR)
def iscoroutinefunction(object):
def iscoroutinefunction(obj):
"""Return true if the object is a coroutine function.
Coroutine functions are defined with "async def" syntax.
"""
return bool((isfunction(object) or ismethod(object)) and
object.__code__.co_flags & CO_COROUTINE)
return _has_code_flag(obj, CO_COROUTINE)
def isasyncgenfunction(object):
def isasyncgenfunction(obj):
"""Return true if the object is an asynchronous generator function.
Asynchronous generator functions are defined with "async def"
syntax and have "yield" expressions in their body.
"""
return bool((isfunction(object) or ismethod(object)) and
object.__code__.co_flags & CO_ASYNC_GENERATOR)
return _has_code_flag(obj, CO_ASYNC_GENERATOR)
def isasyncgen(object):
"""Return true if the object is an asynchronous generator."""
@@ -253,18 +261,25 @@ def iscode(object):
"""Return true if the object is a code object.
Code objects provide these attributes:
co_argcount number of arguments (not including * or ** args)
co_code string of raw compiled bytecode
co_consts tuple of constants used in the bytecode
co_filename name of file in which this code object was created
co_firstlineno number of first line in Python source code
co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
co_lnotab encoded mapping of line numbers to bytecode indices
co_name name with which this code object was defined
co_names tuple of names of local variables
co_nlocals number of local variables
co_stacksize virtual machine stack space required
co_varnames tuple of names of arguments and local variables"""
co_argcount number of arguments (not including *, ** args
or keyword only arguments)
co_code string of raw compiled bytecode
co_cellvars tuple of names of cell variables
co_consts tuple of constants used in the bytecode
co_filename name of file in which this code object was created
co_firstlineno number of first line in Python source code
co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
| 16=nested | 32=generator | 64=nofree | 128=coroutine
| 256=iterable_coroutine | 512=async_generator
co_freevars tuple of names of free variables
co_posonlyargcount number of positional only arguments
co_kwonlyargcount number of keyword only arguments (not including ** arg)
co_lnotab encoded mapping of line numbers to bytecode indices
co_name name with which this code object was defined
co_names tuple of names of local variables
co_nlocals number of local variables
co_stacksize virtual machine stack space required
co_varnames tuple of names of arguments and local variables"""
return isinstance(object, types.CodeType)
def isbuiltin(object):
@@ -285,7 +300,27 @@ def isroutine(object):
def isabstract(object):
"""Return true if the object is an abstract base class (ABC)."""
return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT)
if not isinstance(object, type):
return False
if object.__flags__ & TPFLAGS_IS_ABSTRACT:
return True
if not issubclass(type(object), abc.ABCMeta):
return False
if hasattr(object, '__abstractmethods__'):
# It looks like ABCMeta.__new__ has finished running;
# TPFLAGS_IS_ABSTRACT should have been accurate.
return False
# It looks like ABCMeta.__new__ has not finished running yet; we're
# probably in __init_subclass__. We'll look for abstractmethods manually.
for name, value in object.__dict__.items():
if getattr(value, "__isabstractmethod__", False):
return True
for base in object.__bases__:
for name in getattr(base, "__abstractmethods__", ()):
value = getattr(object, name, None)
if getattr(value, "__isabstractmethod__", False):
return True
return False
def getmembers(object, predicate=None):
"""Return all members of an object as (name, value) pairs sorted by name.
@@ -362,7 +397,7 @@ def classify_class_attrs(cls):
mro = getmro(cls)
metamro = getmro(type(cls)) # for attributes stored in the metaclass
metamro = tuple([cls for cls in metamro if cls not in (type, object)])
metamro = tuple(cls for cls in metamro if cls not in (type, object))
class_bases = (cls,) + mro
all_bases = class_bases + metamro
names = dir(cls)
@@ -430,10 +465,10 @@ def classify_class_attrs(cls):
continue
obj = get_obj if get_obj is not None else dict_obj
# Classify the object or its descriptor.
if isinstance(dict_obj, staticmethod):
if isinstance(dict_obj, (staticmethod, types.BuiltinMethodType)):
kind = "static method"
obj = dict_obj
elif isinstance(dict_obj, classmethod):
elif isinstance(dict_obj, (classmethod, types.ClassMethodDescriptorType)):
kind = "class method"
obj = dict_obj
elif isinstance(dict_obj, property):
@@ -478,13 +513,16 @@ def unwrap(func, *, stop=None):
def _is_wrapper(f):
return hasattr(f, '__wrapped__') and not stop(f)
f = func # remember the original func for error reporting
memo = {id(f)} # Memoise by id to tolerate non-hashable objects
# Memoise by id to tolerate non-hashable objects, but store objects to
# ensure they aren't destroyed, which would allow their IDs to be reused.
memo = {id(f): f}
recursion_limit = sys.getrecursionlimit()
while _is_wrapper(func):
func = func.__wrapped__
id_func = id(func)
if id_func in memo:
if (id_func in memo) or (len(memo) >= recursion_limit):
raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
memo.add(id_func)
memo[id_func] = func
return func
# -------------------------------------------------- source code extraction
@@ -550,9 +588,12 @@ def _finddoc(obj):
cls = obj.__objclass__
if getattr(cls, name) is not obj:
return None
if ismemberdescriptor(obj):
slots = getattr(cls, '__slots__', None)
if isinstance(slots, dict) and name in slots:
return slots[name]
else:
return None
for base in cls.__mro__:
try:
doc = getattr(base, name).__doc__
@@ -613,14 +654,14 @@ def cleandoc(doc):
def getfile(object):
"""Work out which source or compiled file an object was defined in."""
if ismodule(object):
if hasattr(object, '__file__'):
if getattr(object, '__file__', None):
return object.__file__
raise TypeError('{!r} is a built-in module'.format(object))
if isclass(object):
if hasattr(object, '__module__'):
object = sys.modules.get(object.__module__)
if hasattr(object, '__file__'):
return object.__file__
module = sys.modules.get(object.__module__)
if getattr(module, '__file__', None):
return module.__file__
raise TypeError('{!r} is a built-in class'.format(object))
if ismethod(object):
object = object.__func__
@@ -632,8 +673,9 @@ def getfile(object):
object = object.f_code
if iscode(object):
return object.co_filename
raise TypeError('{!r} is not a module, class, method, '
'function, traceback, frame, or code object'.format(object))
raise TypeError('module, class, method, function, traceback, frame, or '
'code object was expected, got {}'.format(
type(object).__name__))
def getmodulename(path):
"""Return the module name for a given file, or None."""
@@ -924,7 +966,12 @@ def getsourcelines(object):
object = unwrap(object)
lines, lnum = findsource(object)
if ismodule(object):
if istraceback(object):
object = object.tb_frame
# for module or frame that corresponds to module, return all source lines
if (ismodule(object) or
(isframe(object) and object.f_code.co_name == "<module>")):
return lines, 0
else:
return getblock(lines[lnum:]), lnum + 1
@@ -963,7 +1010,7 @@ def getclasstree(classes, unique=False):
for c in classes:
if c.__bases__:
for parent in c.__bases__:
if not parent in children:
if parent not in children:
children[parent] = []
if c not in children[parent]:
children[parent].append(c)
@@ -985,21 +1032,11 @@ def getargs(co):
'args' is the list of argument names. Keyword-only arguments are
appended. 'varargs' and 'varkw' are the names of the * and **
arguments or None."""
args, varargs, kwonlyargs, varkw = _getfullargs(co)
return Arguments(args + kwonlyargs, varargs, varkw)
def _getfullargs(co):
"""Get information about the arguments accepted by a code object.
Four things are returned: (args, varargs, kwonlyargs, varkw), where
'args' and 'kwonlyargs' are lists of argument names, and 'varargs'
and 'varkw' are the names of the * and ** arguments or None."""
if not iscode(co):
raise TypeError('{!r} is not a code object'.format(co))
nargs = co.co_argcount
names = co.co_varnames
nargs = co.co_argcount
nkwargs = co.co_kwonlyargcount
args = list(names[:nargs])
kwonlyargs = list(names[nargs:nargs+nkwargs])
@@ -1013,8 +1050,7 @@ def _getfullargs(co):
varkw = None
if co.co_flags & CO_VARKEYWORDS:
varkw = co.co_varnames[nargs]
return args, varargs, kwonlyargs, varkw
return Arguments(args + kwonlyargs, varargs, varkw)
ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults')
@@ -1035,15 +1071,17 @@ def getargspec(func):
Alternatively, use getfullargspec() for an API with a similar namedtuple
based interface, but full support for annotations and keyword-only
parameters.
Deprecated since Python 3.5, use `inspect.getfullargspec()`.
"""
warnings.warn("inspect.getargspec() is deprecated, "
warnings.warn("inspect.getargspec() is deprecated since Python 3.0, "
"use inspect.signature() or inspect.getfullargspec()",
DeprecationWarning, stacklevel=2)
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
getfullargspec(func)
if kwonlyargs or ann:
raise ValueError("Function has keyword-only parameters or annotations"
", use getfullargspec() API which can support them")
", use inspect.signature() API which can support them")
return ArgSpec(args, varargs, varkw, defaults)
FullArgSpec = namedtuple('FullArgSpec',
@@ -1065,7 +1103,6 @@ def getfullargspec(func):
- the "self" parameter is always reported, even for bound methods
- wrapper chains defined by __wrapped__ *not* unwrapped automatically
"""
try:
# Re: `skip_bound_arg=False`
#
@@ -1097,6 +1134,7 @@ def getfullargspec(func):
args = []
varargs = None
varkw = None
posonlyargs = []
kwonlyargs = []
defaults = ()
annotations = {}
@@ -1111,7 +1149,9 @@ def getfullargspec(func):
name = param.name
if kind is _POSITIONAL_ONLY:
args.append(name)
posonlyargs.append(name)
if param.default is not param.empty:
defaults += (param.default,)
elif kind is _POSITIONAL_OR_KEYWORD:
args.append(name)
if param.default is not param.empty:
@@ -1136,7 +1176,7 @@ def getfullargspec(func):
# compatibility with 'func.__defaults__'
defaults = None
return FullArgSpec(args, varargs, varkw, defaults,
return FullArgSpec(posonlyargs + args, varargs, varkw, defaults,
kwonlyargs, kwdefaults, annotations)
@@ -1181,7 +1221,19 @@ def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs, kwonlydefaults, annotations). The other five arguments
are the corresponding optional formatting functions that are called to
turn names and values into strings. The last argument is an optional
function to format the sequence of arguments."""
function to format the sequence of arguments.
Deprecated since Python 3.5: use the `signature` function and `Signature`
objects.
"""
from warnings import warn
warn("`formatargspec` is deprecated since Python 3.5. Use `signature` and "
"the `Signature` object directly",
DeprecationWarning,
stacklevel=2)
def formatargandannotation(arg):
result = formatarg(arg)
if arg in annotations:
@@ -1273,14 +1325,12 @@ def _too_many(f_name, args, kwonly, varargs, defcount, given, values):
(f_name, sig, "s" if plural else "", given, kwonly_sig,
"was" if given == 1 and not kwonly_given else "were"))
def getcallargs(*func_and_positional, **named):
def getcallargs(func, /, *positional, **named):
"""Get the mapping of arguments to values.
A dict is returned, with keys the function argument names (including the
names of the * and ** arguments, if any), and values the respective bound
values from 'positional' and 'named'."""
func = func_and_positional[0]
positional = func_and_positional[1:]
spec = getfullargspec(func)
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = spec
f_name = func.__name__
@@ -1350,7 +1400,7 @@ def getclosurevars(func):
func = func.__func__
if not isfunction(func):
raise TypeError("'{!r}' is not a Python function".format(func))
raise TypeError("{!r} is not a Python function".format(func))
code = func.__code__
# Nonlocal references are named in co_freevars and resolved
@@ -1416,7 +1466,6 @@ def getframeinfo(frame, context=1):
except OSError:
lines = index = None
else:
start = max(start, 1)
start = max(0, min(start, len(lines) - context))
lines = lines[start:start+context]
index = lineno - 1 - start
@@ -1594,7 +1643,7 @@ def getgeneratorlocals(generator):
bound values."""
if not isgenerator(generator):
raise TypeError("'{!r}' is not a Python generator".format(generator))
raise TypeError("{!r} is not a Python generator".format(generator))
frame = getattr(generator, "gi_frame", None)
if frame is not None:
@@ -1910,6 +1959,9 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
"""Private helper to parse content of '__text_signature__'
and return a Signature based on it.
"""
# Lazy import ast because it's relatively heavy and
# it's not used for other than this function.
import ast
Parameter = cls._parameter_cls
@@ -1939,11 +1991,11 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
module = sys.modules.get(module_name, None)
if module:
module_dict = module.__dict__
sys_module_dict = sys.modules
sys_module_dict = sys.modules.copy()
def parse_name(node):
assert isinstance(node, ast.arg)
if node.annotation != None:
if node.annotation is not None:
raise ValueError("Annotations are not currently supported")
return node.arg
@@ -1956,14 +2008,8 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
except NameError:
raise RuntimeError()
if isinstance(value, str):
return ast.Str(value)
if isinstance(value, (int, float)):
return ast.Num(value)
if isinstance(value, bytes):
return ast.Bytes(value)
if value in (True, False, None):
return ast.NameConstant(value)
if isinstance(value, (str, int, float, bytes, bool, type(None))):
return ast.Constant(value)
raise RuntimeError()
class RewriteSymbolics(ast.NodeTransformer):
@@ -2063,7 +2109,7 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True):
return _signature_fromstr(cls, func, s, skip_bound_arg)
def _signature_from_function(cls, func):
def _signature_from_function(cls, func, skip_bound_arg=True):
"""Private helper: constructs Signature for the given python function."""
is_duck_function = False
@@ -2075,15 +2121,20 @@ def _signature_from_function(cls, func):
# of pure function:
raise TypeError('{!r} is not a Python function'.format(func))
s = getattr(func, "__text_signature__", None)
if s:
return _signature_fromstr(cls, func, s, skip_bound_arg)
Parameter = cls._parameter_cls
# Parameter information.
func_code = func.__code__
pos_count = func_code.co_argcount
arg_names = func_code.co_varnames
positional = tuple(arg_names[:pos_count])
posonly_count = func_code.co_posonlyargcount
positional = arg_names[:pos_count]
keyword_only_count = func_code.co_kwonlyargcount
keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
keyword_only = arg_names[pos_count:pos_count + keyword_only_count]
annotations = func.__annotations__
defaults = func.__defaults__
kwdefaults = func.__kwdefaults__
@@ -2095,19 +2146,27 @@ def _signature_from_function(cls, func):
parameters = []
# Non-keyword-only parameters w/o defaults.
non_default_count = pos_count - pos_default_count
posonly_left = posonly_count
# Non-keyword-only parameters w/o defaults.
for name in positional[:non_default_count]:
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_POSITIONAL_OR_KEYWORD))
kind=kind))
if posonly_left:
posonly_left -= 1
# ... w/ defaults.
for offset, name in enumerate(positional[non_default_count:]):
kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
kind=_POSITIONAL_OR_KEYWORD,
kind=kind,
default=defaults[offset]))
if posonly_left:
posonly_left -= 1
# *args
if func_code.co_flags & CO_VARARGS:
@@ -2215,16 +2274,23 @@ def _signature_from_callable(obj, *,
sigcls=sigcls)
sig = _signature_get_partial(wrapped_sig, partialmethod, (None,))
first_wrapped_param = tuple(wrapped_sig.parameters.values())[0]
new_params = (first_wrapped_param,) + tuple(sig.parameters.values())
return sig.replace(parameters=new_params)
if first_wrapped_param.kind is Parameter.VAR_POSITIONAL:
# First argument of the wrapped callable is `*args`, as in
# `partialmethod(lambda *args)`.
return sig
else:
sig_params = tuple(sig.parameters.values())
assert (not sig_params or
first_wrapped_param is not sig_params[0])
new_params = (first_wrapped_param,) + sig_params
return sig.replace(parameters=new_params)
if isfunction(obj) or _signature_is_functionlike(obj):
# If it's a pure Python function, or an object that is duck type
# of a Python function (Cython functions, for instance), then:
return _signature_from_function(sigcls, obj)
return _signature_from_function(sigcls, obj,
skip_bound_arg=skip_bound_arg)
if _signature_is_builtin(obj):
return _signature_from_builtin(sigcls, obj,
@@ -2301,7 +2367,7 @@ def _signature_from_callable(obj, *,
if (obj.__init__ is object.__init__ and
obj.__new__ is object.__new__):
# Return a signature of 'object' builtin.
return signature(object)
return sigcls.from_callable(object)
else:
raise ValueError(
'no signature found for builtin type {!r}'.format(obj))
@@ -2357,6 +2423,9 @@ class _ParameterKind(enum.IntEnum):
def __str__(self):
return self._name_
@property
def description(self):
return _PARAM_NAME_MAPPING[self]
_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY
_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD
@@ -2364,6 +2433,14 @@ _VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL
_KEYWORD_ONLY = _ParameterKind.KEYWORD_ONLY
_VAR_KEYWORD = _ParameterKind.VAR_KEYWORD
_PARAM_NAME_MAPPING = {
_POSITIONAL_ONLY: 'positional-only',
_POSITIONAL_OR_KEYWORD: 'positional or keyword',
_VAR_POSITIONAL: 'variadic positional',
_KEYWORD_ONLY: 'keyword-only',
_VAR_KEYWORD: 'variadic keyword'
}
class Parameter:
"""Represents a parameter in a function signature.
@@ -2398,15 +2475,14 @@ class Parameter:
empty = _empty
def __init__(self, name, kind, *, default=_empty, annotation=_empty):
if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
_VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
raise ValueError("invalid value for 'Parameter.kind' attribute")
self._kind = kind
try:
self._kind = _ParameterKind(kind)
except ValueError:
raise ValueError(f'value {kind!r} is not a valid Parameter.kind')
if default is not _empty:
if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
msg = '{} parameters cannot have default values'.format(kind)
if self._kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
msg = '{} parameters cannot have default values'
msg = msg.format(self._kind.description)
raise ValueError(msg)
self._default = default
self._annotation = annotation
@@ -2415,19 +2491,21 @@ class Parameter:
raise ValueError('name is a required attribute for Parameter')
if not isinstance(name, str):
raise TypeError("name must be a str, not a {!r}".format(name))
msg = 'name must be a str, not a {}'.format(type(name).__name__)
raise TypeError(msg)
if name[0] == '.' and name[1:].isdigit():
# These are implicit arguments generated by comprehensions. In
# order to provide a friendlier interface to users, we recast
# their name as "implicitN" and treat them as positional-only.
# See issue 19611.
if kind != _POSITIONAL_OR_KEYWORD:
raise ValueError(
'implicit arguments must be passed in as {}'.format(
_POSITIONAL_OR_KEYWORD
)
if self._kind != _POSITIONAL_OR_KEYWORD:
msg = (
'implicit arguments must be passed as '
'positional or keyword arguments, not {}'
)
msg = msg.format(self._kind.description)
raise ValueError(msg)
self._kind = _POSITIONAL_ONLY
name = 'implicit{}'.format(name[1:])
@@ -2486,11 +2564,14 @@ class Parameter:
# Add annotation and default value
if self._annotation is not _empty:
formatted = '{}:{}'.format(formatted,
formatted = '{}: {}'.format(formatted,
formatannotation(self._annotation))
if self._default is not _empty:
formatted = '{}={}'.format(formatted, repr(self._default))
if self._annotation is not _empty:
formatted = '{} = {}'.format(formatted, repr(self._default))
else:
formatted = '{}={}'.format(formatted, repr(self._default))
if kind == _VAR_POSITIONAL:
formatted = '*' + formatted
@@ -2695,8 +2776,12 @@ class Signature:
name = param.name
if kind < top_kind:
msg = 'wrong parameter order: {!r} before {!r}'
msg = msg.format(top_kind, kind)
msg = (
'wrong parameter order: {} parameter before {} '
'parameter'
)
msg = msg.format(top_kind.description,
kind.description)
raise ValueError(msg)
elif kind > top_kind:
kind_defaults = False
@@ -2729,19 +2814,25 @@ class Signature:
@classmethod
def from_function(cls, func):
"""Constructs Signature for the given python function."""
"""Constructs Signature for the given python function.
warnings.warn("inspect.Signature.from_function() is deprecated, "
"use Signature.from_callable()",
Deprecated since Python 3.5, use `Signature.from_callable()`.
"""
warnings.warn("inspect.Signature.from_function() is deprecated since "
"Python 3.5, use Signature.from_callable()",
DeprecationWarning, stacklevel=2)
return _signature_from_function(cls, func)
@classmethod
def from_builtin(cls, func):
"""Constructs Signature for the given builtin function."""
"""Constructs Signature for the given builtin function.
warnings.warn("inspect.Signature.from_builtin() is deprecated, "
"use Signature.from_callable()",
Deprecated since Python 3.5, use `Signature.from_callable()`.
"""
warnings.warn("inspect.Signature.from_builtin() is deprecated since "
"Python 3.5, use Signature.from_callable()",
DeprecationWarning, stacklevel=2)
return _signature_from_builtin(cls, func)
@@ -2869,7 +2960,7 @@ class Signature:
arguments[param.name] = tuple(values)
break
if param.name in kwargs:
if param.name in kwargs and param.kind != _POSITIONAL_ONLY:
raise TypeError(
'multiple values for argument {arg!r}'.format(
arg=param.name)) from None
@@ -2926,19 +3017,19 @@ class Signature:
return self._bound_arguments_cls(self, arguments)
def bind(*args, **kwargs):
def bind(self, /, *args, **kwargs):
"""Get a BoundArguments object, that maps the passed `args`
and `kwargs` to the function's signature. Raises `TypeError`
if the passed arguments can not be bound.
"""
return args[0]._bind(args[1:], kwargs)
return self._bind(args, kwargs)
def bind_partial(*args, **kwargs):
def bind_partial(self, /, *args, **kwargs):
"""Get a BoundArguments object, that partially maps the
passed `args` and `kwargs` to the function's signature.
Raises `TypeError` if the passed arguments can not be bound.
"""
return args[0]._bind(args[1:], kwargs, partial=True)
return self._bind(args, kwargs, partial=True)
def __reduce__(self):
return (type(self),
@@ -3027,7 +3118,7 @@ def _main():
type(exc).__name__,
exc)
print(msg, file=sys.stderr)
exit(2)
sys.exit(2)
if has_attrs:
parts = attrs.split(".")
@@ -3037,7 +3128,7 @@ def _main():
if module.__name__ in sys.builtin_module_names:
print("Can't get info for builtin modules.", file=sys.stderr)
exit(1)
sys.exit(1)
if args.details:
print('Target: {}'.format(target))
+2 -2
View File
@@ -8,7 +8,7 @@ import os
import os.path
import subprocess
import py_compile
# import zipfile XXX RustPython
import zipfile
from importlib.util import source_from_cache
from test.support import make_legacy_pyc, strip_python_stderr
@@ -137,7 +137,7 @@ def run_python_until_end(*args, **env_vars):
err = strip_python_stderr(err)
return _PythonRunResult(rc, out, err), cmd_line
def _assert_python(expected_success, *args, **env_vars):
def _assert_python(expected_success, /, *args, **env_vars):
res, cmd_line = run_python_until_end(*args, **env_vars)
if (res.rc and expected_success) or (not res.rc and not expected_success):
res.fail(cmd_line)
+73
View File
@@ -0,0 +1,73 @@
"""Unit tests for __instancecheck__ and __subclasscheck__."""
import unittest
class ABC(type):
def __instancecheck__(cls, inst):
"""Implement isinstance(inst, cls)."""
return any(cls.__subclasscheck__(c)
for c in {type(inst), inst.__class__})
def __subclasscheck__(cls, sub):
"""Implement issubclass(sub, cls)."""
candidates = cls.__dict__.get("__subclass__", set()) | {cls}
return any(c in candidates for c in sub.mro())
class Integer(metaclass=ABC):
__subclass__ = {int}
class SubInt(Integer):
pass
class TypeChecksTest(unittest.TestCase):
def testIsSubclassInternal(self):
self.assertEqual(Integer.__subclasscheck__(int), True)
self.assertEqual(Integer.__subclasscheck__(float), False)
def testIsSubclassBuiltin(self):
self.assertEqual(issubclass(int, Integer), True)
self.assertEqual(issubclass(int, (Integer,)), True)
self.assertEqual(issubclass(float, Integer), False)
self.assertEqual(issubclass(float, (Integer,)), False)
def testIsInstanceBuiltin(self):
self.assertEqual(isinstance(42, Integer), True)
self.assertEqual(isinstance(42, (Integer,)), True)
self.assertEqual(isinstance(3.14, Integer), False)
self.assertEqual(isinstance(3.14, (Integer,)), False)
def testIsInstanceActual(self):
self.assertEqual(isinstance(Integer(), Integer), True)
self.assertEqual(isinstance(Integer(), (Integer,)), True)
def testIsSubclassActual(self):
self.assertEqual(issubclass(Integer, Integer), True)
self.assertEqual(issubclass(Integer, (Integer,)), True)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def testSubclassBehavior(self):
self.assertEqual(issubclass(SubInt, Integer), True)
self.assertEqual(issubclass(SubInt, (Integer,)), True)
self.assertEqual(issubclass(SubInt, SubInt), True)
self.assertEqual(issubclass(SubInt, (SubInt,)), True)
self.assertEqual(issubclass(Integer, SubInt), False)
self.assertEqual(issubclass(Integer, (SubInt,)), False)
self.assertEqual(issubclass(int, SubInt), False)
self.assertEqual(issubclass(int, (SubInt,)), False)
self.assertEqual(isinstance(SubInt(), Integer), True)
self.assertEqual(isinstance(SubInt(), (Integer,)), True)
self.assertEqual(isinstance(SubInt(), SubInt), True)
self.assertEqual(isinstance(SubInt(), (SubInt,)), True)
self.assertEqual(isinstance(42, SubInt), False)
self.assertEqual(isinstance(42, (SubInt,)), False)
if __name__ == "__main__":
unittest.main()
+53
View File
@@ -0,0 +1,53 @@
"""Test compiler changes for unary ops (+, -, ~) introduced in Python 2.2"""
import unittest
class UnaryOpTestCase(unittest.TestCase):
def test_negative(self):
self.assertTrue(-2 == 0 - 2)
self.assertEqual(-0, 0)
self.assertEqual(--2, 2)
self.assertTrue(-2 == 0 - 2)
self.assertTrue(-2.0 == 0 - 2.0)
self.assertTrue(-2j == 0 - 2j)
def test_positive(self):
self.assertEqual(+2, 2)
self.assertEqual(+0, 0)
self.assertEqual(++2, 2)
self.assertEqual(+2, 2)
self.assertEqual(+2.0, 2.0)
self.assertEqual(+2j, 2j)
def test_invert(self):
self.assertTrue(-2 == 0 - 2)
self.assertEqual(-0, 0)
self.assertEqual(--2, 2)
self.assertTrue(-2 == 0 - 2)
def test_no_overflow(self):
nines = "9" * 32
self.assertTrue(eval("+" + nines) == 10**32-1)
self.assertTrue(eval("-" + nines) == -(10**32-1))
self.assertTrue(eval("~" + nines) == ~(10**32-1))
def test_negation_of_exponentiation(self):
# Make sure '**' does the right thing; these form a
# regression test for SourceForge bug #456756.
self.assertEqual(-2 ** 3, -8)
self.assertEqual((-2) ** 3, -8)
self.assertEqual(-2 ** 4, -16)
self.assertEqual((-2) ** 4, 16)
def test_bad_types(self):
for op in '+', '-', '~':
self.assertRaises(TypeError, eval, op + "b'a'")
self.assertRaises(TypeError, eval, op + "'a'")
self.assertRaises(TypeError, eval, "~2j")
self.assertRaises(TypeError, eval, "~2.0")
if __name__ == "__main__":
unittest.main()
+2450
View File
File diff suppressed because it is too large. Load diff