aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/shibokenmodule
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/shibokenmodule')
-rw-r--r--sources/shiboken2/shibokenmodule/CMakeLists.txt1
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py75
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py66
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py19
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py25
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py25
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py17
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py515
8 files changed, 580 insertions, 163 deletions
diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt
index 057a995f8..bbf2677e4 100644
--- a/sources/shiboken2/shibokenmodule/CMakeLists.txt
+++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt
@@ -30,7 +30,6 @@ if(WIN32)
endif()
target_link_libraries(shibokenmodule PUBLIC libshiboken)
-add_dependencies(shibokenmodule shiboken2)
create_generator_target(shibokenmodule)
install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}/shiboken2)
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py
index bd827f1ee..384273d92 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py
@@ -56,7 +56,7 @@ used literally as strings like "signature", "existence", etc.
"""
from textwrap import dedent
-from shibokensupport.signature import inspect
+from shibokensupport.signature import inspect, typing
from shibokensupport.signature.mapping import ellipsis
from shibokensupport.signature.lib.tool import SimpleNamespace
@@ -162,6 +162,35 @@ def define_nameless_parameter():
NamelessParameter = define_nameless_parameter()
+"""
+Note on the "Optional" feature:
+
+When an annotation has a default value that is None, then the
+type has to be wrapped into "typing.Optional".
+
+Note that only the None value creates an Optional expression,
+because the None leaves the domain of the variable.
+Defaults like integer values are ignored: They stay in the domain.
+
+That information would be lost when we use the "..." convention.
+
+Note that the typing module has the remarkable expansion
+
+ Optional[T] is Variant[T, NoneType]
+
+We want to avoid that when generating the .pyi file.
+This is done by a regex in generate_pyi.py .
+The following would work in Python 3, but this is a version-dependent
+hack that also won't work in Python 2 and would be _very_ complex.
+"""
+# import sys
+# if sys.version_info[0] == 3:
+# class hugo(list):pass
+# typing._normalize_alias["hugo"] = "Optional"
+# Optional = typing._alias(hugo, typing.T, inst=False)
+# else:
+# Optional = typing.Optional
+
def make_signature_nameless(signature):
"""
@@ -174,6 +203,13 @@ def make_signature_nameless(signature):
signature.parameters[key].__class__ = NamelessParameter
+_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY
+_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD
+_VAR_POSITIONAL = inspect._VAR_POSITIONAL
+_KEYWORD_ONLY = inspect._KEYWORD_ONLY
+_VAR_KEYWORD = inspect._VAR_KEYWORD
+_empty = inspect._empty
+
def create_signature(props, key):
if not props:
# empty signatures string
@@ -204,26 +240,39 @@ def create_signature(props, key):
elif sig_kind == "classmethod":
varnames = ("klass",) + varnames
else:
- raise SystemError("Methods must be function, method, staticmethod or "
- "classmethod")
+ raise SystemError("Methods must be function, method, staticmethod"
+ " or classmethod")
# calculate the modifications
defaults = props["defaults"][:]
if not layout.defaults:
defaults = ()
- if layout.ellipsis:
- defaults = (ellipsis,) * len(defaults)
annotations = props["annotations"].copy()
if not layout.return_annotation and "return" in annotations:
del annotations["return"]
- # attach parameters to a fake function and build a signature
- argstr = ", ".join(varnames)
- fakefunc = eval("lambda {}: None".format(argstr))
- fakefunc.__name__ = props["name"]
- fakefunc.__defaults__ = defaults
- fakefunc.__kwdefaults__ = props["kwdefaults"]
- fakefunc.__annotations__ = annotations
- sig = inspect._signature_from_function(inspect.Signature, fakefunc)
+ # Build a signature.
+ kind = inspect._POSITIONAL_OR_KEYWORD
+ params = []
+ for idx, name in enumerate(varnames):
+ if name.startswith("**"):
+ kind = _VAR_KEYWORD
+ elif name.startswith("*"):
+ kind = _VAR_POSITIONAL
+ ann = annotations.get(name, _empty)
+ name = name.lstrip("*")
+ defpos = idx - len(varnames) + len(defaults)
+ default = defaults[defpos] if defpos >= 0 else _empty
+ if default is None:
+ ann = typing.Optional[ann]
+ if default is not _empty and layout.ellipsis:
+ default = ellipsis
+ param = inspect.Parameter(name, kind, annotation=ann, default=default)
+ params.append(param)
+ if kind == _VAR_POSITIONAL:
+ kind = _KEYWORD_ONLY
+ sig = inspect.Signature(params,
+ return_annotation=annotations.get('return', _empty),
+ __validate_parameters__=False)
# the special case of nameless parameters
if not layout.parameter_names:
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
index e6f6dc379..a5e3247b1 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
@@ -52,6 +52,11 @@ by producing a lot of clarity.
import sys
from shibokensupport.signature import inspect
from shibokensupport.signature import get_signature
+try:
+ from PySide2.QtCore import Qt
+ EnumType = type(Qt.Key)
+except ImportError:
+ EnumType = None
class ExactEnumerator(object):
@@ -66,6 +71,13 @@ class ExactEnumerator(object):
def __init__(self, formatter, result_type=dict):
self.fmt = formatter
self.result_type = result_type
+ self.fmt.level = 0
+ self.fmt.after_enum = self.after_enum
+ self._after_enum = False
+
+ def after_enum(self):
+ ret = self._after_enum
+ self._after_enum = False
def module(self, mod_name):
__import__(mod_name)
@@ -75,21 +87,19 @@ class ExactEnumerator(object):
functions = inspect.getmembers(module, inspect.isroutine)
ret = self.result_type()
self.fmt.class_name = None
- for func_name, func in functions:
- ret.update(self.function(func_name, func))
for class_name, klass in members:
ret.update(self.klass(class_name, klass))
+ if isinstance(klass, EnumType):
+ self.enum(klass)
+ for func_name, func in functions:
+ ret.update(self.function(func_name, func))
return ret
def klass(self, class_name, klass):
- if not "Shiboken" in repr(klass.mro()):
- # don't look into any foreign classes!
- ret = self.result_type()
- return ret
bases_list = []
for base in klass.__bases__:
name = base.__name__
- if name == "object":
+ if name in ("object", "type"):
pass
else:
modname = base.__module__
@@ -97,30 +107,54 @@ class ExactEnumerator(object):
bases_list.append(name)
class_str = "{}({})".format(class_name, ", ".join(bases_list))
with self.fmt.klass(class_name, class_str):
- ret = self.function("__init__", klass)
+ ret = self.result_type()
# class_members = inspect.getmembers(klass)
# gives us also the inherited things.
class_members = sorted(list(klass.__dict__.items()))
subclasses = []
+ functions = []
for thing_name, thing in class_members:
if inspect.isclass(thing):
subclass_name = ".".join((class_name, thing_name))
subclasses.append((subclass_name, thing))
- else:
+ elif inspect.isroutine(thing):
func_name = thing_name.split(".")[0] # remove ".overload"
- ret.update(self.function(func_name, thing))
+ functions.append((func_name, thing))
+ self.fmt.level += 1
for subclass_name, subclass in subclasses:
ret.update(self.klass(subclass_name, subclass))
- return ret
+ if isinstance(subclass, EnumType):
+ self.enum(subclass)
+ ret = self.function("__init__", klass)
+ for func_name, func in functions:
+ func_kind = get_signature(func, "__func_kind__")
+ modifier = func_kind if func_kind in (
+ "staticmethod", "classmethod") else None
+ ret.update(self.function(func_name, func, modifier))
+ self.fmt.level -= 1
+ return ret
- def function(self, func_name, func):
+ def function(self, func_name, func, modifier=None):
+ self.fmt.level += 1
ret = self.result_type()
signature = getattr(func, '__signature__', None)
if signature is not None:
- with self.fmt.function(func_name, signature) as key:
+ with self.fmt.function(func_name, signature, modifier) as key:
ret[key] = signature
+ self.fmt.level -= 1
return ret
+ def enum(self, subclass):
+ if not hasattr(self.fmt, "enum"):
+ # this is an optional feature
+ return
+ class_name = subclass.__name__
+ for enum_name, value in subclass.__dict__.items():
+ if type(type(value)) is EnumType:
+ with self.fmt.enum(class_name, enum_name, int(value)):
+ pass
+ self._after_enum = True
+
def stringify(signature):
if isinstance(signature, list):
@@ -142,7 +176,7 @@ class SimplifyingEnumerator(ExactEnumerator):
is desired.
"""
- def function(self, func_name, func):
+ def function(self, func_name, func, modifier=None):
ret = self.result_type()
signature = get_signature(func, 'existence')
sig = stringify(signature) if signature is not None else None
@@ -159,11 +193,11 @@ class HintingEnumerator(ExactEnumerator):
hinting stubs. Only default values are replaced by "...".
"""
- def function(self, func_name, func):
+ def function(self, func_name, func, modifier=None):
ret = self.result_type()
signature = get_signature(func, 'hintingstub')
if signature is not None:
- with self.fmt.function(func_name, signature) as key:
+ with self.fmt.function(func_name, signature, modifier) as key:
ret[key] = signature
return ret
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py
index b34bfb404..3b0825049 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py
@@ -43,6 +43,8 @@ from __future__ import print_function, absolute_import
tool.py
Some useful stuff, see below.
+On the function with_metaclass see the answer from Martijn Pieters on
+https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass
"""
from textwrap import dedent
@@ -132,4 +134,21 @@ def build_brace_pattern(level, separators=""):
indent = idx * " ", **locals())
return pattern.replace("C", "{").replace("D", "}")
+
+# Copied from the six module:
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ # This requires a bit of explanation: the basic idea is to make a dummy
+ # metaclass for one level of class instantiation that replaces itself with
+ # the actual metaclass.
+ class metaclass(type):
+
+ def __new__(cls, name, this_bases, d):
+ return meta(name, bases, d)
+
+ @classmethod
+ def __prepare__(cls, name, this_bases):
+ return meta.__prepare__(name, bases)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
# eof
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
index 6c76483a0..8eff19d77 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
@@ -85,20 +85,6 @@ def formatannotation(annotation, base_module=None):
return annotation.__module__ + '.' + annotation.__qualname__
return repr(annotation)
-# patching __repr__ to disable the __repr__ of typing.TypeVar:
-"""
- def __repr__(self):
- if self.__covariant__:
- prefix = '+'
- elif self.__contravariant__:
- prefix = '-'
- else:
- prefix = '~'
- return prefix + self.__name__
-"""
-def _typevar__repr__(self):
- return "typing." + self.__name__
-
# Note also that during the tests we have a different encoding that would
# break the Python license decorated files without an encoding line.
@@ -151,12 +137,18 @@ def list_modules(message):
print(" {:23}".format(name), repr(module)[:70])
+orig_typing = True
if sys.version_info >= (3,):
import typing
import inspect
inspect.formatannotation = formatannotation
else:
- from shibokensupport import typing27 as typing
+ if "typing" not in sys.modules:
+ orig_typing = False
+ from shibokensupport import typing27 as typing
+ sys.modules["typing"] = typing
+ else:
+ import typing
import inspect
namespace = inspect.__dict__
from shibokensupport import backport_inspect as inspect
@@ -165,7 +157,6 @@ else:
inspect.__doc__ += _doc
# force inspect to find all attributes. See "heuristic" in pydoc.py!
inspect.__all__ = list(x for x in dir(inspect) if not x.startswith("_"))
-typing.TypeVar.__repr__ = _typevar__repr__
# Fix the module names in typing if possible. This is important since
# the typing names should be I/O compatible, so that typing.Dict
@@ -196,7 +187,7 @@ def move_into_pyside_package():
put_into_package(PySide2.support.signature, parser)
put_into_package(PySide2.support.signature.lib, enum_sig)
- put_into_package(PySide2.support.signature, typing)
+ put_into_package(None if orig_typing else PySide2.support.signature, typing)
put_into_package(PySide2.support.signature, inspect)
from shibokensupport.signature import mapping
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
index b8097719a..163aac851 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
@@ -55,6 +55,7 @@ import os
from shibokensupport.signature import typing
from shibokensupport.signature.typing import TypeVar, Generic
+from shibokensupport.signature.lib.tool import with_metaclass
class ellipsis(object):
def __repr__(self):
@@ -67,27 +68,15 @@ ModelIndexList = typing.List[int]
QImageCleanupFunction = typing.Callable
StringList = typing.List[str]
+# unfortunately, typing.Optional[t] expands to typing.Union[t, NoneType]
+# Until we can force it to create Optional[t] again, we use this.
+NoneType = type(None)
+
_S = TypeVar("_S")
# Building our own Char type, which is much nicer than
# Char = typing.Union[str, int] # how do I model the limitation to 1 char?
-# Copied from the six module:
-def with_metaclass(meta, *bases):
- """Create a base class with a metaclass."""
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(type):
-
- def __new__(cls, name, this_bases, d):
- return meta(name, bases, d)
-
- @classmethod
- def __prepare__(cls, name, this_bases):
- return meta.__prepare__(name, bases)
- return type.__new__(metaclass, 'temporary_class', (), {})
-
class _CharMeta(type):
def __repr__(self):
return '%s.%s' % (self.__module__, self.__name__)
@@ -225,7 +214,7 @@ class Reloader(object):
self.sys_module_count = len(sys.modules)
g = globals()
# PYSIDE-1009: Try to recognize unknown modules in errorhandler.py
- candidates = list(mod_name for mod_name in sys.modules
+ candidates = list(mod_name for mod_name in sys.modules.copy()
if self.module_valid(sys.modules[mod_name]))
for mod_name in candidates:
# 'top' is PySide2 when we do 'import PySide.QtCore'
@@ -316,6 +305,7 @@ type_map.update({
"zero(int)": 0,
"zero(object)": None,
"zero(str)": "",
+ "...": "...",
})
@@ -345,6 +335,7 @@ def init_sample():
"Foo.HANDLE": int,
"HANDLE": int,
"Null": None,
+ "nullptr": None,
"ObjectType.Identifier": Missing("sample.ObjectType.Identifier"),
"OddBool": bool,
"PStr": str,
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py
index 6109bceee..0081a07ba 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py
@@ -106,7 +106,8 @@ def _parse_line(line):
$
"""
ret = SimpleNamespace(**re.match(line_re, line, re.VERBOSE).groupdict())
- argstr = ret.arglist
+ # PYSIDE-1095: Handle arbitrary default expressions
+ argstr = ret.arglist.replace("->", ".deref.")
arglist = _parse_arglist(argstr)
args = []
for arg in arglist:
@@ -116,7 +117,7 @@ def _parse_line(line):
print("KEYWORD", ret)
name = name + "_"
if "=" in ann:
- ann, default = ann.split("=")
+ ann, default = ann.split("=", 1)
tup = name, ann, default
else:
tup = name, ann
@@ -167,9 +168,11 @@ def try_to_guess(thing, valtype):
def _resolve_value(thing, valtype, line):
if thing in ("0", "None") and valtype:
- if valtype.startswith("PySide2."):
+ if valtype.startswith("PySide2.") or valtype.startswith("typing."):
return None
- name = type_map[valtype].__name__
+ mapped = type_map[valtype]
+ # typing.Any: '_SpecialForm' object has no attribute '__name__'
+ name = mapped.__name__ if hasattr(mapped, "__name__") else str(mapped)
thing = "zero({})".format(name)
if thing in type_map:
return type_map[thing]
@@ -275,9 +278,9 @@ def calculate_props(line):
for idx, tup in enumerate(arglist):
name, ann = tup[:2]
if ann == "...":
- name = "*args"
- # copy the fields back :()
- ann = 'NULL' # maps to None
+ name = "*args" if name.startswith("arg_") else "*" + name
+ # copy the pathed fields back
+ ann = 'nullptr' # maps to None
tup = name, ann
arglist[idx] = tup
annotations[name] = _resolve_type(ann, line, 0)
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py
index 786a84ecb..44d78c433 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py
@@ -86,6 +86,10 @@ PSF LICENSE AGREEMENT FOR PYTHON 3.7.0
to be bound by the terms and conditions of this License Agreement.
"""
+# This is the typing module for Python 2.7
+# https://github.com/python/typing
+# 2019-08-22
+
from __future__ import absolute_import, unicode_literals
import abc
@@ -108,8 +112,11 @@ __all__ = [
'Any',
'Callable',
'ClassVar',
+ 'Final',
'Generic',
+ 'Literal',
'Optional',
+ 'Protocol',
'Tuple',
'Type',
'TypeVar',
@@ -141,6 +148,7 @@ __all__ = [
'SupportsAbs',
'SupportsComplex',
'SupportsFloat',
+ 'SupportsIndex',
'SupportsInt',
# Concrete collection types.
@@ -152,17 +160,20 @@ __all__ = [
'Set',
'FrozenSet',
'NamedTuple', # Not really a type.
+ 'TypedDict', # Not really a type.
'Generator',
# One-off things.
'AnyStr',
'cast',
+ 'final',
'get_type_hints',
'NewType',
'no_type_check',
'no_type_check_decorator',
'NoReturn',
'overload',
+ 'runtime_checkable',
'Text',
'TYPE_CHECKING',
]
@@ -447,7 +458,7 @@ def _type_check(arg, msg):
if (
type(arg).__name__ in ('_Union', '_Optional') and
not getattr(arg, '__origin__', None) or
- isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol)
+ isinstance(arg, TypingMeta) and arg._gorg in (Generic, Protocol)
):
raise TypeError("Plain %s is not valid as type argument" % arg)
return arg
@@ -466,7 +477,7 @@ def _type_repr(obj):
return _qualname(obj)
return '%s.%s' % (obj.__module__, _qualname(obj))
if obj is Ellipsis:
- return('...')
+ return '...'
if isinstance(obj, types.FunctionType):
return obj.__name__
return repr(obj)
@@ -537,6 +548,157 @@ class _ClassVar(_FinalTypingBase):
ClassVar = _ClassVar(_root=True)
+class _FinalMeta(TypingMeta):
+ """Metaclass for _Final"""
+
+ def __new__(cls, name, bases, namespace):
+ cls.assert_no_subclassing(bases)
+ self = super(_FinalMeta, cls).__new__(cls, name, bases, namespace)
+ return self
+
+
+class _Final(_FinalTypingBase):
+ """A special typing construct to indicate that a name
+ cannot be re-assigned or overridden in a subclass.
+ For example:
+
+ MAX_SIZE: Final = 9000
+ MAX_SIZE += 1 # Error reported by type checker
+
+ class Connection:
+ TIMEOUT: Final[int] = 10
+ class FastConnector(Connection):
+ TIMEOUT = 1 # Error reported by type checker
+
+ There is no runtime checking of these properties.
+ """
+
+ __metaclass__ = _FinalMeta
+ __slots__ = ('__type__',)
+
+ def __init__(self, tp=None, **kwds):
+ self.__type__ = tp
+
+ def __getitem__(self, item):
+ cls = type(self)
+ if self.__type__ is None:
+ return cls(_type_check(item,
+ '{} accepts only single type.'.format(cls.__name__[1:])),
+ _root=True)
+ raise TypeError('{} cannot be further subscripted'
+ .format(cls.__name__[1:]))
+
+ def _eval_type(self, globalns, localns):
+ new_tp = _eval_type(self.__type__, globalns, localns)
+ if new_tp == self.__type__:
+ return self
+ return type(self)(new_tp, _root=True)
+
+ def __repr__(self):
+ r = super(_Final, self).__repr__()
+ if self.__type__ is not None:
+ r += '[{}]'.format(_type_repr(self.__type__))
+ return r
+
+ def __hash__(self):
+ return hash((type(self).__name__, self.__type__))
+
+ def __eq__(self, other):
+ if not isinstance(other, _Final):
+ return NotImplemented
+ if self.__type__ is not None:
+ return self.__type__ == other.__type__
+ return self is other
+
+
+Final = _Final(_root=True)
+
+
+def final(f):
+ """This decorator can be used to indicate to type checkers that
+ the decorated method cannot be overridden, and decorated class
+ cannot be subclassed. For example:
+
+ class Base:
+ @final
+ def done(self) -> None:
+ ...
+ class Sub(Base):
+ def done(self) -> None: # Error reported by type checker
+ ...
+ @final
+ class Leaf:
+ ...
+ class Other(Leaf): # Error reported by type checker
+ ...
+
+ There is no runtime checking of these properties.
+ """
+ return f
+
+
+class _LiteralMeta(TypingMeta):
+ """Metaclass for _Literal"""
+
+ def __new__(cls, name, bases, namespace):
+ cls.assert_no_subclassing(bases)
+ self = super(_LiteralMeta, cls).__new__(cls, name, bases, namespace)
+ return self
+
+
+class _Literal(_FinalTypingBase):
+ """A type that can be used to indicate to type checkers that the
+ corresponding value has a value literally equivalent to the
+ provided parameter. For example:
+
+ var: Literal[4] = 4
+
+ The type checker understands that 'var' is literally equal to the
+ value 4 and no other value.
+
+ Literal[...] cannot be subclassed. There is no runtime checking
+ verifying that the parameter is actually a value instead of a type.
+ """
+
+ __metaclass__ = _LiteralMeta
+ __slots__ = ('__values__',)
+
+ def __init__(self, values=None, **kwds):
+ self.__values__ = values
+
+ def __getitem__(self, item):
+ cls = type(self)
+ if self.__values__ is None:
+ if not isinstance(item, tuple):
+ item = (item,)
+ return cls(values=item,
+ _root=True)
+ raise TypeError('{} cannot be further subscripted'
+ .format(cls.__name__[1:]))
+
+ def _eval_type(self, globalns, localns):
+ return self
+
+ def __repr__(self):
+ r = super(_Literal, self).__repr__()
+ if self.__values__ is not None:
+ r += '[{}]'.format(', '.join(map(_type_repr, self.__values__)))
+ return r
+
+ def __hash__(self):
+ return hash((type(self).__name__, self.__values__))
+
+ def __eq__(self, other):
+ if not isinstance(other, _Literal):
+ return NotImplemented
+ if self.__values__ is not None:
+ return self.__values__ == other.__values__
+ return self is other
+
+
+Literal = _Literal(_root=True)
+
+
class AnyMeta(TypingMeta):
"""Metaclass for Any."""
@@ -1122,10 +1284,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
if base is Generic:
raise TypeError("Cannot inherit from plain Generic")
if (isinstance(base, GenericMeta) and
- base.__origin__ is Generic):
+ base.__origin__ in (Generic, Protocol)):
if gvars is not None:
raise TypeError(
- "Cannot inherit from Generic[...] multiple types.")
+ "Cannot inherit from Generic[...] or"
+ " Protocol[...] multiple times.")
gvars = base.__parameters__
if gvars is None:
gvars = tvars
@@ -1135,8 +1298,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
if not tvarset <= gvarset:
raise TypeError(
"Some type variables (%s) "
- "are not listed in Generic[%s]" %
+ "are not listed in %s[%s]" %
(", ".join(str(t) for t in tvars if t not in gvarset),
+ "Generic" if any(b.__origin__ is Generic
+ for b in bases) else "Protocol",
", ".join(str(g) for g in gvars)))
tvars = gvars
@@ -1285,25 +1450,21 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
"Parameter list to %s[...] cannot be empty" % _qualname(self))
msg = "Parameters to generic types must be types."
params = tuple(_type_check(p, msg) for p in params)
- if self is Generic:
+ if self in (Generic, Protocol):
# Generic can only be subscripted with unique type variables.
if not all(isinstance(p, TypeVar) for p in params):
raise TypeError(
- "Parameters to Generic[...] must all be type variables")
+ "Parameters to %s[...] must all be type variables" % self.__name__)
if len(set(params)) != len(params):
raise TypeError(
- "Parameters to Generic[...] must all be unique")
+ "Parameters to %s[...] must all be unique" % self.__name__)
tvars = params
args = params
elif self in (Tuple, Callable):
tvars = _type_vars(params)
args = params
- elif self is _Protocol:
- # _Protocol is internal, don't check anything.
- tvars = params
- args = params
- elif self.__origin__ in (Generic, _Protocol):
- # Can't subscript Generic[...] or _Protocol[...].
+ elif self.__origin__ in (Generic, Protocol):
+ # Can't subscript Generic[...] or Protocol[...].
raise TypeError("Cannot subscript already-subscripted %s" %
repr(self))
else:
@@ -1343,7 +1504,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
# latter, we must extend __instancecheck__ too. For simplicity
# we just skip the cache check -- instance checks for generic
# classes are supposed to be rare anyways.
- if not isinstance(instance, type):
+ if hasattr(instance, "__class__"):
return issubclass(instance.__class__, self)
return False
@@ -1690,85 +1851,175 @@ def overload(func):
return _overload_dummy
+_PROTO_WHITELIST = ['Callable', 'Iterable', 'Iterator',
+ 'Hashable', 'Sized', 'Container', 'Collection',
+ 'Reversible', 'ContextManager']
+
+
class _ProtocolMeta(GenericMeta):
- """Internal metaclass for _Protocol.
+ """Internal metaclass for Protocol.
- This exists so _Protocol classes can be generic without deriving
+ This exists so Protocol classes can be generic without deriving
from Generic.
"""
+ def __init__(cls, *args, **kwargs):
+ super(_ProtocolMeta, cls).__init__(*args, **kwargs)
+ if not cls.__dict__.get('_is_protocol', None):
+ cls._is_protocol = any(b is Protocol or
+ isinstance(b, _ProtocolMeta) and
+ b.__origin__ is Protocol
+ for b in cls.__bases__)
+ if cls._is_protocol:
+ for base in cls.__mro__[1:]:
+ if not (base in (object, Generic) or
+ base.__module__ == '_abcoll' and
+ base.__name__ in _PROTO_WHITELIST or
+ isinstance(base, TypingMeta) and base._is_protocol or
+ isinstance(base, GenericMeta) and base.__origin__ is Generic):
+ raise TypeError('Protocols can only inherit from other protocols,'
+ ' got %r' % base)
+ cls._callable_members_only = all(callable(getattr(cls, attr))
+ for attr in cls._get_protocol_attrs())
+
+ def _no_init(self, *args, **kwargs):
+ if type(self)._is_protocol:
+ raise TypeError('Protocols cannot be instantiated')
+ cls.__init__ = _no_init
+
+ def _proto_hook(cls, other):
+ if not cls.__dict__.get('_is_protocol', None):
+ return NotImplemented
+ if not isinstance(other, type):
+ # Similar error as for issubclass(1, int)
+ # (also not a chance for old-style classes)
+ raise TypeError('issubclass() arg 1 must be a new-style class')
+ for attr in cls._get_protocol_attrs():
+ for base in other.__mro__:
+ if attr in base.__dict__:
+ if base.__dict__[attr] is None:
+ return NotImplemented
+ break
+ else:
+ return NotImplemented
+ return True
+ if '__subclasshook__' not in cls.__dict__:
+ cls.__subclasshook__ = classmethod(_proto_hook)
- def __instancecheck__(self, obj):
- if _Protocol not in self.__bases__:
- return super(_ProtocolMeta, self).__instancecheck__(obj)
- raise TypeError("Protocols cannot be used with isinstance().")
+ def __instancecheck__(self, instance):
+ # We need this method for situations where attributes are assigned in __init__
+ if isinstance(instance, type):
+ # This looks like a fundamental limitation of Python 2.
+ # It cannot support runtime protocol metaclasses, On Python 2 classes
+ # cannot be correctly inspected as instances of protocols.
+ return False
+ if ((not getattr(self, '_is_protocol', False) or
+ self._callable_members_only) and
+ issubclass(instance.__class__, self)):
+ return True
+ if self._is_protocol:
+ if all(hasattr(instance, attr) and
+ (not callable(getattr(self, attr)) or
+ getattr(instance, attr) is not None)
+ for attr in self._get_protocol_attrs()):
+ return True
+ return super(GenericMeta, self).__instancecheck__(instance)
def __subclasscheck__(self, cls):
- if not self._is_protocol:
- # No structural checks since this isn't a protocol.
- return NotImplemented
+ if (self.__dict__.get('_is_protocol', None) and
+ not self.__dict__.get('_is_runtime_protocol', None)):
+ if (sys._getframe(1).f_globals['__name__'] in ['abc', 'functools'] or
+ # This is needed because we remove subclasses from unions on Python 2.
+ sys._getframe(2).f_globals['__name__'] == 'typing'):
+ return False
+ raise TypeError("Instance and class checks can only be used with"
+ " @runtime_checkable protocols")
+ if (self.__dict__.get('_is_runtime_protocol', None) and
+ not self._callable_members_only):
+ if sys._getframe(1).f_globals['__name__'] in ['abc', 'functools']:
+ return super(GenericMeta, self).__subclasscheck__(cls)
+ raise TypeError("Protocols with non-method members"
+ " don't support issubclass()")
+ return super(_ProtocolMeta, self).__subclasscheck__(cls)
- if self is _Protocol:
- # Every class is a subclass of the empty protocol.
- return True
+ def _get_protocol_attrs(self):
+ attrs = set()
+ for base in self.__mro__[:-1]: # without object
+ if base.__name__ in ('Protocol', 'Generic'):
+ continue
+ annotations = getattr(base, '__annotations__', {})
+ for attr in list(base.__dict__.keys()) + list(annotations.keys()):
+ if (not attr.startswith('_abc_') and attr not in (
+ '__abstractmethods__', '__annotations__', '__weakref__',
+ '_is_protocol', '_is_runtime_protocol', '__dict__',
+ '__args__', '__slots__', '_get_protocol_attrs',
+ '__next_in_mro__', '__parameters__', '__origin__',
+ '__orig_bases__', '__extra__', '__tree_hash__',
+ '__doc__', '__subclasshook__', '__init__', '__new__',
+ '__module__', '_MutableMapping__marker',
+ '__metaclass__', '_gorg', '_callable_members_only')):
+ attrs.add(attr)
+ return attrs
- # Find all attributes defined in the protocol.
- attrs = self._get_protocol_attrs()
- for attr in attrs:
- if not any(attr in d.__dict__ for d in cls.__mro__):
- return False
- return True
+class Protocol(object):
+ """Base class for protocol classes. Protocol classes are defined as::
- def _get_protocol_attrs(self):
- # Get all Protocol base classes.
- protocol_bases = []
- for c in self.__mro__:
- if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol':
- protocol_bases.append(c)
+ class Proto(Protocol):
+ def meth(self):
+ # type: () -> int
+ pass
- # Get attributes included in protocol.
- attrs = set()
- for base in protocol_bases:
- for attr in base.__dict__.keys():
- # Include attributes not defined in any non-protocol bases.
- for c in self.__mro__:
- if (c is not base and attr in c.__dict__ and
- not getattr(c, '_is_protocol', False)):
- break
- else:
- if (not attr.startswith('_abc_') and
- attr != '__abstractmethods__' and
- attr != '_is_protocol' and
- attr != '_gorg' and
- attr != '__dict__' and
- attr != '__args__' and
- attr != '__slots__' and
- attr != '_get_protocol_attrs' and
- attr != '__next_in_mro__' and
- attr != '__parameters__' and
- attr != '__origin__' and
- attr != '__orig_bases__' and
- attr != '__extra__' and
- attr != '__tree_hash__' and
- attr != '__module__'):
- attrs.add(attr)
+ Such classes are primarily used with static type checkers that recognize
+ structural subtyping (static duck-typing), for example::
- return attrs
+ class C:
+ def meth(self):
+ # type: () -> int
+ return 0
+
+ def func(x):
+ # type: (Proto) -> int
+ return x.meth()
+
+ func(C()) # Passes static type check
+ See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable
+ act as simple-minded runtime protocols that checks only the presence of
+ given attributes, ignoring their type signatures.
-class _Protocol(object):
- """Internal base class for protocol classes.
+ Protocol classes can be generic, they are defined as::
- This implements a simple-minded structural issubclass check
- (similar but more general than the one-offs in collections.abc
- such as Hashable).
+ class GenProto(Protocol[T]):
+ def meth(self):
+ # type: () -> T
+ pass
"""
__metaclass__ = _ProtocolMeta
__slots__ = ()
-
_is_protocol = True
+ def __new__(cls, *args, **kwds):
+ if cls._gorg is Protocol:
+ raise TypeError("Type Protocol cannot be instantiated; "
+ "it can be used only as a base class")
+ return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
+
+
+def runtime_checkable(cls):
+ """Mark a protocol class as a runtime protocol, so that it
+ can be used with isinstance() and issubclass(). Raise TypeError
+ if applied to a non-protocol class.
+
+ This allows a simple-minded structural check very similar to the
+ one-offs in collections.abc such as Hashable.
+ """
+ if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
+ raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+ ' got %r' % cls)
+ cls._is_runtime_protocol = True
+ return cls
+
# Various ABCs mimicking those in collections.abc.
# A few are simply re-exported for completeness.
@@ -1786,7 +2037,8 @@ class Iterator(Iterable[T_co]):
__extra__ = collections_abc.Iterator
-class SupportsInt(_Protocol):
+@runtime_checkable
+class SupportsInt(Protocol):
__slots__ = ()
@abstractmethod
@@ -1794,7 +2046,8 @@ class SupportsInt(_Protocol):
pass
-class SupportsFloat(_Protocol):
+@runtime_checkable
+class SupportsFloat(Protocol):
__slots__ = ()
@abstractmethod
@@ -1802,7 +2055,8 @@ class SupportsFloat(_Protocol):
pass
-class SupportsComplex(_Protocol):
+@runtime_checkable
+class SupportsComplex(Protocol):
__slots__ = ()
@abstractmethod
@@ -1810,7 +2064,17 @@ class SupportsComplex(_Protocol):
pass
-class SupportsAbs(_Protocol[T_co]):
+@runtime_checkable
+class SupportsIndex(Protocol):
+ __slots__ = ()
+
+ @abstractmethod
+ def __index__(self):
+ pass
+
+
+@runtime_checkable
+class SupportsAbs(Protocol[T_co]):
__slots__ = ()
@abstractmethod
@@ -1823,7 +2087,8 @@ if hasattr(collections_abc, 'Reversible'):
__slots__ = ()
__extra__ = collections_abc.Reversible
else:
- class Reversible(_Protocol[T_co]):
+ @runtime_checkable
+ class Reversible(Protocol[T_co]):
__slots__ = ()
@abstractmethod
@@ -1996,21 +2261,6 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]):
return _generic_new(collections.defaultdict, cls, *args, **kwds)
-############################
-# Insertion by CT 2019-02-21
-#
-class OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]):
- __slots__ = ()
- __extra__ = collections.OrderedDict
-
- def __new__(cls, *args, **kwds):
- if cls._gorg is OrderedDict:
- return collections.OrderedDict(*args, **kwds)
- return _generic_new(collections.OrderedDict, cls, *args, **kwds)
-#
-############################
-
-
class Counter(collections.Counter, Dict[T, int]):
__slots__ = ()
__extra__ = collections.Counter
@@ -2100,6 +2350,87 @@ def NamedTuple(typename, fields):
return cls
+def _check_fails(cls, other):
+ try:
+ if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', 'typing']:
+ # Typed dicts are only for static structural subtyping.
+ raise TypeError('TypedDict does not support instance and class checks')
+ except (AttributeError, ValueError):
+ pass
+ return False
+
+
+def _dict_new(cls, *args, **kwargs):
+ return dict(*args, **kwargs)
+
+
+def _typeddict_new(cls, _typename, _fields=None, **kwargs):
+ total = kwargs.pop('total', True)
+ if _fields is None:
+ _fields = kwargs
+ elif kwargs:
+ raise TypeError("TypedDict takes either a dict or keyword arguments,"
+ " but not both")
+
+ ns = {'__annotations__': dict(_fields), '__total__': total}
+ try:
+ # Setting correct module is necessary to make typed dict classes pickleable.
+ ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ pass
+
+ return _TypedDictMeta(_typename, (), ns)
+
+
+class _TypedDictMeta(type):
+ def __new__(cls, name, bases, ns, total=True):
+ # Create new typed dict class object.
+ # This method is called directly when TypedDict is subclassed,
+ # or via _typeddict_new when TypedDict is instantiated. This way
+ # TypedDict supports all three syntaxes described in its docstring.
+ # Subclasses and instances of TypedDict return actual dictionaries
+ # via _dict_new.
+ ns['__new__'] = _typeddict_new if name == b'TypedDict' else _dict_new
+ tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns)
+
+ anns = ns.get('__annotations__', {})
+ msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+ anns = {n: _type_check(tp, msg) for n, tp in anns.items()}
+ for base in bases:
+ anns.update(base.__dict__.get('__annotations__', {}))
+ tp_dict.__annotations__ = anns
+ if not hasattr(tp_dict, '__total__'):
+ tp_dict.__total__ = total
+ return tp_dict
+
+ __instancecheck__ = __subclasscheck__ = _check_fails
+
+
+TypedDict = _TypedDictMeta(b'TypedDict', (dict,), {})
+TypedDict.__module__ = __name__
+TypedDict.__doc__ = \
+ """A simple typed name space. At runtime it is equivalent to a plain dict.
+
+ TypedDict creates a dictionary type that expects all of its
+ instances to have a certain set of keys, with each key
+ associated with a value of a consistent type. This expectation
+ is not checked at runtime but is only enforced by type checkers.
+ Usage::
+
+ Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+ a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
+ b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
+
+ assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+ The type info could be accessed via Point2D.__annotations__. TypedDict
+ supports an additional equivalent form::
+
+ Point2D = TypedDict('Point2D', x=int, y=int, label=str)
+ """
+
+
def NewType(name, tp):
"""NewType creates simple unique types with almost zero
runtime overhead. NewType(name, tp) is considered a subtype of tp