Skip to content

Commit

Permalink
Added arg bound_Generic to various functions that work with types. It…
Browse files Browse the repository at this point in the history
… can be provided to host values for unbound TypeVars contained in the types accepted by these functions. This enables for pytypes functionality like descibed in github.com/agronholm/typeguard/issues/21
  • Loading branch information
Stewori committed Oct 26, 2017
1 parent 27b6bc1 commit c1c207b
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 99 deletions.
24 changes: 17 additions & 7 deletions README.rst
Expand Up @@ -396,34 +396,44 @@ This function works like ``check_argument_types``, but applies to the return val
Because it is impossible for pytypes to automatically figure out the value to be returned in a function, it must be explicitly provided as the ``value``-parameter.


is_of_type(obj, cls)
~~~~~~~~~~~~~~~~~~~~
is_of_type(obj, cls, bound_Generic=None)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Works like ``isinstance``, but supports PEP 484 style types from typing module.

If ``cls`` contains unbound ``TypeVar`s and ``bound_Generic`` is provided, this function attempts to
retrieve corresponding values for the unbound ``TypeVar``s from ``bound_Generic``.


is_subtype(subtype, supertype)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
is_subtype(subtype, supertype, bound_Generic=None)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Works like ``issubclass``, but supports PEP 484 style types from typing module.

If ``subclass`` or ``superclass`` contains unbound ``TypeVar``s and ``bound_Generic`` is
provided, this function attempts to retrieve corresponding values for the
unbound ``TypeVar``s from ``bound_Generic``.


deep_type(obj, depth=None, max_sample=None)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tries to construct a type for a given value. In contrast to ``type(...)``, ``deep_type`` does its best to fit structured types from ``typing`` as close as possible to the given value.
Tries to construct a type for a given value. In contrast to ``type(...)``, ``deep_type`` does its
best to fit structured types from ``typing`` as close as possible to the given value.
E.g. ``deep_type((1, 2, 'a'))`` will return ``Tuple[int, int, str]`` rather than just ``tuple``.
Supports various types from ``typing``, but not yet all.
Also detects nesting up to given depth (uses ``pytypes.default_typecheck_depth`` if no value is given).
If a value for ``max_sample`` is given, this number of elements is probed from lists, sets and dictionaries to determine the element type. By default, all elements are probed. If there are fewer elements than ``max_sample``, all existing elements are probed.


type_str(tp)
~~~~~~~~~~~~
type_str(tp, assumed_globals=None, update_assumed_globals=None, implicit_globals=None, bound_Generic=None)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Generates a nicely readable string representation of the given type.
The returned representation is workable as a source code string and would reconstruct the given type if handed to eval, provided that globals/locals are configured appropriately (e.g. assumes that various types from ``typing`` have been imported).
Used as type-formatting backend of ptypes' code generator abilities in modules ``typelogger`` and ``stubfile_2_converter``.
If ``tp`` contains unbound ``TypeVar``s and ``bound_Generic`` is provided, this function attempts to
retrieve corresponding values for the unbound ``TypeVar``s from ``bound_Generic``.


dump_cache(path=default_typelogger_path, python2=False, suffix=None)
Expand Down
25 changes: 20 additions & 5 deletions pytypes/__init__.py
Expand Up @@ -528,13 +528,28 @@ def _GenericMeta__new__351(cls, *args, **kwds):

# Monkeypatch Generic to circumvent type erasure:
# (Only applies to legacy versions of typing.
# Existance of '_generic_new' is suitable to detect whether this monkeypatch is required.)
# Existence of '_generic_new' is suitable to detect whether this
# monkeypatch is required, i.e. in typing-3.5.2.2.)
if not hasattr(typing, '_generic_new'):
_Generic__new__ = typing.Generic.__new__

# This former approach has issues if self.__orig_class__is needed in __init__:
# _Generic__new__ = typing.Generic.__new__
# def __Generic__new__(cls, *args, **kwds):
# res = _Generic__new__(cls, *args, **kwds)
# res.__orig_class__ = cls
# return res

def __Generic__new__(cls, *args, **kwds):
res = _Generic__new__(cls, *args, **kwds)
res.__orig_class__ = cls
return res
# this is based on Generic.__new__ from typing-3.5.2.2
if cls.__origin__ is None:
obj = cls.__next_in_mro__.__new__(cls)
obj.__orig_class__ = cls
else:
origin = typing._gorg(cls)
obj = cls.__next_in_mro__.__new__(origin)
obj.__orig_class__ = cls
obj.__init__(*args, **kwds)
return obj
typing.Generic.__new__ = __Generic__new__


Expand Down

0 comments on commit c1c207b

Please sign in to comment.