mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Merge pull request #1805 from youknowone/various-lib
module inspect, functools, zipfile and test test_unary, test_typechecks
This commit is contained in:
6 files changed
+2982
-168
No files matched your search
Vendored
+194
-47
@@ -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
|
||||
Vendored
+210
-119
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
Vendored
+2450
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user