Typing-toolbox for Python 3 _and_ 2.7 w.r.t. PEP 484.

Overview
Build Status

pytypes

Welcome to the pytypes project

pytypes is a typing toolbox w.r.t. PEP 484 (PEP 526 on the road map, later also 544 if it gets accepted).

Its main features are currently

  • @typechecked decorator for runtime typechecking with support for stubfiles and type comments
  • @override decorator that asserts existence of a type-compatible parent method
  • @annotations decorator to turn type info from stubfiles or from type comments into __annotations__
  • @typelogged decorator observes function and method calls at runtime and generates stubfiles from acquired type info
  • service functions to apply these decorators module wide or even globally, i.e. runtime wide
  • typechecking can alternatively be done in decorator-free manner (friendlier for debuggers)
  • all the above decorators work smoothly with OOP, i.e. with methods, static methods, class methods and properties, even if classes are nested
  • converter for stubfiles to Python 2.7 compliant form
  • lots of utility functions regarding types, e.g. a Python 2.7 compliant and actually functional implementation of get_type_hints
  • full Python 2.7 support for all these features

An additional future goal will be integration with the Java typing system when running on Jython. Along with this, some generator utilities to produce type-safe Java bindings for Python frameworks are planned.

In wider sense, PEP 484-style type annotations can be used to build type safe interfaces to allow also other programming languages to call into Python code (kind of reverse FFI). In this sense the project name refers to 'ctypes', which provides Python-bindings of C.

Python 2.7, 3.5, 3.6

All described features of pytypes were carefully implemented such that they are equally workable on CPython 3.5, 3.6, 2.7 and on Jython 2.7.1 (other interpreters might work as well, but were not yet tested). For Python 2.7, pytypes fully supports type-annotations via type comments. It also supports Python 2.7-style type annotations in Python 3.5-code to allow easier 2.7/3.5 multi-version development.

Why write another runtime typecheck decorator?

There have been earlier approaches for runtime-typechecking. However, most of them predate PEP 484 or lack some crucial features like support of Python 2.7 or support of stubfiles. Also, none of them features a typechecking override decorator. There were separate approaches for override decorators, but these usually don't consider PEP 484 at all. So we decided that it's time for a new runtime typechecking framework, designed to support PEP 484 from the roots, including its extensive features like (Python 2.7-style-)type comments and stub files.

Quick manual

Typechecking

pytypes provides a rich set of utilities for runtime typechecking.

@typechecked decorator

Decorator applicable to functions, methods, properties and classes. Asserts compatibility of runtime argument and return values of all targeted functions and methods w.r.t. PEP 484-style type annotations of these functions and methods. This supports stubfiles and type comments and is thus workable on Python 2.7.

Disabling typechecking

Running Python with the '-O' flag, which also disables assert statements, turns off typechecking completely. Alternatively, one can modify the flag pytypes.checking_enabled.

Note that this must be done right after import of pytypes, because it affects the way how @typechecked decorator works. For modules that were imported with this flag disabled, typechecking cannot be turned on later on within the same runtime.

Usage Python 2

from pytypes import typechecked

@typechecked
def some_function(a, b, c):
    # type: (int, str, List[Union[str, float]]) -> int
    return a+len(b)+len(c)

Usage Python 3

from pytypes import typechecked

@typechecked
def some_function(a: int, b: str, c: List[Union[str, float]]) -> int:
    return a+len(b)+len(c)

Overriding methods in type-safe manner

The decorators in this section allow type-safe method overriding.

@override decorator

Decorator applicable to methods only. For a version applicable also to classes or modules use auto_override. Asserts that for the decorated method a parent method exists in its mro. If both the decorated method and its parent method are type annotated, the decorator additionally asserts compatibility of the annotated types. Note that the return type is checked in contravariant manner. A successful check guarantees that the child method can always be used in places that support the parent method's signature. Use pytypes.check_override_at_runtime and pytypes.check_override_at_class_definition_time to control whether checks happen at class definition time or at "actual runtime".

The following rules apply for override checking:

  • a parent method must exist
  • the parent method must have call-compatible signature (e.g. same number of args)
  • arg types of parent method must be more or equal specific than arg types of child
  • return type behaves contravariant - parent method must have less or equal specific return type than child

Usage Example

from pytypes import override

class some_baseclass():
    def some_method1(self, a: int) -> None: ...
    def some_method2(self, a: int) -> None: ...
    def some_method3(self, a: int) -> None: ...
    def some_method4(self) -> int: ...

class some_subclass(some_baseclass):
    @override
    def some_method1(self, a: float) -> None: ...

    @override
    def some_method2(self, a: str) -> None: ...

    @override
    def some_metd3(self, a: int) -> None: ...

    @override
    def some_method4(self) -> float: ...
  • some_method1: override check passes
  • some_method2: override check fails because type is not compatible
  • some_method3: override check fails because of typo in method name
  • some_method4: override check fails because return type must be more or equal specific than parent

@auto_override decorator

Decorator applicable to methods and classes. Works like override decorator on type annotated methods that actually have a type annotated parent method. Has no effect on methods that do not override anything. In contrast to plain override decorator, auto_override can be applied easily on every method in a class or module. In contrast to explicit override decorator, auto_override is not suitable to detect typos in spelling of a child method's name. It is only useful to assert compatibility of type information (note that return type is contravariant). Use pytypes.check_override_at_runtime and pytypes.check_override_at_class_definition_time to control whether checks happen at class definition time or at "actual runtime".

The following rules apply, if a parent method exists:

  • the parent method must have call-compatible signature (e.g. same number of args)
  • arg types of parent method must be more or equal specific than arg types of child
  • return type behaves contravariant - parent method must have less or equal specific return type than child

Compared to ordinary override decorator, the rule “a parent method must exist” is not applied here. If no parent method exists, auto_override silently passes.

Provide info from type comments and stubfiles as __annotations__ for other tools

@annotations decorator

Decorator applicable to functions, methods, properties and classes. Methods with type comment will have type hints parsed from that string and get them attached as __annotations__ attribute. Methods with either a type comment or ordinary type annotations in a stubfile will get that information attached as __annotations__ attribute (also a relevant use case in Python 3). Behavior in case of collision with previously (manually) attached __annotations__ can be controlled using the flags pytypes.annotations_override_typestring and pytypes.annotations_from_typestring.

Type logging

@typelogged decorator

Decorator applicable to functions, methods, properties and classes. It observes function and method calls at runtime and can generate stubfiles from acquired type info.

Disabling typelogging

One can disable typelogging via the flag pytypes.typelogging_enabled.

Note that this must be done right after import of pytypes, because it affects the way how @typelogged decorator works. For modules that were imported with this flag disabled, typelogging cannot be turned on later on within the same runtime.

Usage example with decorator

Assume you run a file ./script.py like this:

from pytypes import typelogged

@typelogged
def logtest(a, b, c=7, *var, **kw): return 7, a, b

@typelogged
class logtest_class(object):
    def logmeth(self, b): return 2*b

    @classmethod
    def logmeth_cls(cls, c): return len(c)

    @staticmethod
    def logmeth_static(c): return len(c)

    @property
    def log_prop(self): return self._log_prop

    @log_prop.setter
    def log_prop(self, val): self._log_prop = val

logtest(3, 2, 5, 6, 7, 3.1, y=3.2, x=9)
logtest(3.5, 7.3, 5, 6, 7, 3.1, y=3.2, x=9)
logtest('abc', 7.3, 5, 6, 7, 3.1, y=2, x=9)
lcs = logtest_class()
lcs.log_prop = (7.8, 'log')
lcs.log_prop
logtest_class.logmeth_cls('hijk')
logtest_class.logmeth_static(range(3))

pytypes.dump_cache()

Usage example with profiler

Alternatively you can use the TypeLogger profiler:

from pytypes import TypeLogger

def logtest(a, b, c=7, *var, **kw): return 7, a, b

class logtest_class(object):
    def logmeth(self, b): return 2*b

    @classmethod
    def logmeth_cls(cls, c): return len(c)

    @staticmethod
    def logmeth_static(c): return len(c)

    @property
    def log_prop(self): return self._log_prop

    @log_prop.setter
    def log_prop(self, val): self._log_prop = val

with TypeLogger():
    logtest(3, 2, 5, 6, 7, 3.1, y=3.2, x=9)
    logtest(3.5, 7.3, 5, 6, 7, 3.1, y=3.2, x=9)
    logtest('abc', 7.3, 5, 6, 7, 3.1, y=2, x=9)
    lcs = logtest_class()
    lcs.log_prop = (7.8, 'log')
    lcs.log_prop
    logtest_class.logmeth_cls('hijk')
    logtest_class.logmeth_static(range(3))

Note that this will produce more stubs, i.e. also for indirectly used modules, because the profiler will handle every function call. To scope a specific module at a time use pytypes.typelogged on that module or its name. This should be called on a module after it is fully loaded. To use it inside the scoped module (e.g. for __main__) apply it right after all classes and functions are defined.

Output

Any of the examples above will create the following file in ./typelogger_output:

script.pyi:

from typing import Tuple, Union

def logtest(a: Union[float, str], b: float, c: int, *var: float, **kw: Union[float, int]) -> Union[Tuple[int, float, float], Tuple[int, str, float]]: ...

class logtest_class(object):
    def logmeth(self, b: int) -> int: ...

    @classmethod
    def logmeth_cls(cls, c: str) -> int: ...

    @staticmethod
    def logmeth_static(c: range) -> int: ...

    @property
    def log_prop(self) -> Tuple[float, str]: ...

    @log_prop.setter
    def log_prop(self, val: Tuple[float, str]) -> None: ...

Use pytypes.dump_cache(python2=True) to produce a Python 2.7 compliant stubfile.

Writing typelog at exit

By default, pytypes performs pytypes.dump_cache() at exit, i.e. writes typelog as a Python 3 style stubfile. Use pytypes.dump_typelog_at_exit to control this behavior. Use pytypes.dump_typelog_at_exit_python2 to write typelog as a Python 2 style stubfile.

Global mode and module wide mode

Note that global mode is experimental.

The pytypes decorators @typechecked, @auto_override, @annotations and @typelogged can be applied module wide by explicitly calling them on a module object or a module name contained in sys.modules. In such a case, the decorator is applied to all functions and classes in that module and recursively to all methods, properties and inner classes too.

Warning: If A decorator is applied to a partly imported module, only functions and classes that were already defined are affected. After the module imported completely, the decorator is applied to the remaining functions and classes. In the meantime, internal code of that module can circumvent the decorator, e.g. can make module-internal calls that are not typechecked.

Global mode via profilers

The pytypes decorators @typechecked and @typelogged have corresponding profiler implementations TypeChecker and TypeLogger. You can conveniently install them globally via enable_global_typechecked_profiler() and enable_global_typelogged_profiler().

Alternatively you can apply them in a with-context:

from pytypes import TypeChecker

def agnt_test(v):
    # type: (str) -> int
    return 67

with TypeChecker():
    agnt_test(12)

One glitch is to consider in case you want to catch TypeCheckError (i.e. ReturnTypeError or InputTypeError as well) and continue execution afterwards. The TypeChecker would be suspended unless you call restore_profiler, e.g.:

from pytypes import TypeChecker, restore_profiler

def agnt_test(v):
    # type: (str) -> int
    return 67

with TypeChecker():
    try:
        agnt_test(12)
    except TypeCheckError:
        restore_profiler()
        # handle error....

Note that the call to restore_profiler must be performed by the thread that raised the error.

Alternatively you can enable pytypes.warning_mode = True to raise warnings rather than errors. (This only helps if you don't use filterwarnings("error") or likewise.)

Global mode via decorators

The pytypes decorators @typechecked, @auto_override, @annotations and @typelogged can be applied globally to all loaded modules and subsequently loaded modules. Modules that were loaded while typechecking or typelogging was disabled will not be affected. Apart from that this will affect every module in the way described above. Note that we recommend to use the profilers explained in the previous section if global typechecking or typelogging is required. Use this feature with care as it is still experimental and can notably slow down your python runtime. In any case, it is intended for debugging and testing phase only.

  • To apply @typechecked globally, use pytypes.set_global_typechecked_decorator
  • To apply @auto_override globally, use pytypes.set_global_auto_override_decorator
  • To apply @annotations globally, use pytypes.set_global_annotations_decorator
  • To apply @typelogged globally, use pytypes.set_global_typelogged_decorator

Warning: If the module that performs the ``pytypes.set_global_xy_decorator``-call is not yet fully imported, the warning regarding module-wide decorators (see above) applies to that module in the same sense. I.e. functions and classes that were not yet defined, will be covered only once the module-import has fully completed.

OOP support

All the above decorators work smoothly with OOP. You can safely apply @typechecked, @annotations and @typelogged on methods, abstract methods, static methods, class methods and properties. @override is – already by semantics – only applicable to methods, @auto_override is additionally applicable to classes and modules.

pytypes also takes care of inner classes and resolves name space properly. Make sure to apply decorators from pytypes on top of @staticmethod, @classmethod, @property or @abstractmethod rather than the other way round. This is because OOP support involves some special treatment internally, so OOP decorators must be visible to pytypes decorators. This also applies to old-style classes.

No @override on __init__

For now, @override cannot be applied to __init__, because __init__ typically extends the list of initialization parameters and usually uses super to explicitly serve a parent's signature. The purpose of @override is to avoid typos and to guarantee that the child method can always be used as a fill in for the parent in terms of signature and type information. Both aspects are hardly relevant for __init__:

  • a typo is unlikely and would show up quickly for various reasons
  • when creating an instance the caller usually knows the exact class to instantiate and thus its signature

For special cases where this might be relevant, @typechecked can be used to catch most errors.

Utilities

Utility functions described in this section can be directly imported from the pytypes module. Only the most important utility functions are listed here.

get_type_hints(func)

Resembles typing.get_type_hints, but is also workable on Python 2.7 and searches stubfiles for type information. Also on Python 3, this takes type comments into account if present.

get_types(func)

Works like get_type_hints, but returns types as a sequence rather than a dictionary. Types are returned in declaration order of the corresponding arguments.

check_argument_types(cllable=None, call_args=None, clss=None, caller_level=0)

This function mimics typeguard syntax and semantics. It can be applied within a function or method to check argument values to comply with type annotations. It behaves similar to @typechecked except that it is not a decorator and does not check the return type. A decorator less way for argument checking yields less interference with some debuggers.

check_return_type(value, cllable=None, clss=None, caller_level=0)

This function works like check_argument_types, but applies to the return value. 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, 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, 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. 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, 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)

Writes cached observations by @typelogged into stubfiles.

Files will be created in the directory provided as 'path'; overwrites existing files without notice. Uses 'pyi2' suffix if 'python2' flag is given else 'pyi'. Resulting files will be Python 2.7 compliant accordingly.

get_Generic_itemtype(sq, simplify=True)

Retrieves the item type from a PEP 484 generic or subclass of such. sq must be a typing.Tuple or (subclass of) typing.Iterable or typing.Container. Consequently this also works with typing.List, typing.Set and typing.Dict. Note that for typing.Dict and mapping types in general, the key type is regarded as item type. For typing.Tuple all contained types are returned as a typing.Union. If simplify == True some effort is taken to eliminate redundancies in such a union.

get_Mapping_key_value(mp)

Retrieves the key and value types from a PEP 484 mapping or subclass of such. mp must be a (subclass of) typing.Mapping.

get_arg_for_TypeVar(typevar, generic)

Retrieves the parameter value of a given TypeVar from a Generic. Returns None if the generic does not contain an appropriate value. Note that the TypeVar is compared by instance and not by name. E.g. using a local TypeVar T would yield different results than using typing.T despite the equal name.

resolve_fw_decl(in_type, module_name=None, globs=None, level=0, search_stack_depth=2)

Resolves forward references in in_type.

globs should be a dictionary containing values for the names that must be resolved in in_type. If globs is not provided, it will be created by __globals__ from the module named module_name, plus __locals__ from the last search_stack_depth stack frames, beginning at the calling function. This is to resolve cases where in_type and/or types it fw-references are defined inside a function.

To prevent walking the stack, set search_stack_depth=0. Ideally provide a proper globs for best efficiency. See util.get_function_perspective_globals for obtaining a globs that can be cached. util.get_function_perspective_globals works like described above.

get_orig_class(obj, default_to__class__=False)

Robust way to access obj.__orig_class__. Compared to a direct access this has the following advantages:

  1. It works around python/typing#658.

  2. It prevents infinite recursion when wrapping a method (obj is self or cls) and either

    • the object's class defines __getattribute__ or
    • the object has no __orig_class__ attribute and the object's class defines __getattr__.

    See discussion at pull request 53.

If default_to__class__ is True it returns obj.__class__ as final fallback. Otherwise, AttributeError is raised in failure case (default behavior).

Python 2.7 compliant stubfiles

Currently pytypes uses the python runtime, i.e. import, eval, dir and inspect to parse stubfiles and type comments. A runtime independent parser for stubfiles is a desired future feature, but is not yet available. This means that conventional PEP 484 stubfiles would not work on Python 2.7. To resolve this gap, pytypes features a converter script that can convert conventional stubfiles into Python 2.7 compliant form. More specifically it converts parameter annotations into type comments and converts ... syntax into pass.

As of this writing it does not yet support stubfiles containing the @overload decorator. Also, it does not yet convert type annotations of attributes and variables.

'pyi2' suffix

pytypes uses the suffix 'pyi2' for Python 2.7 compliant stubfiles, but does not require it. Plain 'pyi' is also an acceptable suffix (as far as pytypes is concerned), because Python 2.7 compliant stubfiles can also be used in Python 3.

The main purpose of 'pyi2' suffix is to avoid name conflicts when conventional stubfiles and Python 2.7 compliant stubfiles coexist for the same module. In that case the pyi2 file will override the pyi file when running on Python 2.7.

stubfile_2_converter

Run stubfile_2_converter.py to leverage pytypes' stubfile converter capabilities:

python3 -m pytypes.stubfile_2_converter [options/flags] [in_file]

Use python3 -m pytypes.stubfile_2_converter -h to see detailed usage.

By default the out file will be created in the same folder as the in file, but with 'pyi2' suffix.

Next steps

Contributors

pytypes was created in 2016/17 by Stefan Richthofer.

Contributors (no specific order, names as provided on github)

License

pytypes is released under Apache 2.0 license. A copy is provided in the file LICENSE.


Copyright 2017, 2018 Stefan Richthofer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at


Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contact

[email protected]

Comments
  • Surprising issubclass result

    Surprising issubclass result

    Why the following returns false?

    import typing
    from pytypes import type_util
    
    type_util._issubclass(typing.List[float], typing.List)
    

    Isn't this the same as type_util._issubclass(typing.List[float], typing.List[typing.Any])? And then float is an instance of typing.Any? But this is false as well:

    type_util._issubclass(typing.List[float], typing.List[typing.Any])
    
    bug 
    opened by mitar 27
  • Add type hints for @typechecked

    Add type hints for @typechecked

    Adds a py.typed marker file to indicate pytypes has type hints (see PEP 561), and adds type hints for @typechecked. These type hints are added in a separate pyi file to help with compatibility for python 3.3 & 3.4, since inline type annotations are a syntax error for those versions.

    ~The fixes in typechecker.py were for issues flagged by the typechecker and shouldn't change the behavior of the code at all. They may not be strictly needed since type hint resolution order is supposed to check *.pyi files before their associated *.py files.~ EDIT: Dropped the type checker fixes. They were more trouble than I expected and probably not needed.

    Fixes #74.

    opened by dbarnett 13
  • Inspect stack frame by frame to improve performance

    Inspect stack frame by frame to improve performance

    Addressing performance issue with the workaround used to address this issue: https://github.com/python/typing/issues/658.

    Led to roughly 10x speedup in our project.

    opened by rover-debug 12
  • Partially-annotated functions with default parameter values result in errors.

    Partially-annotated functions with default parameter values result in errors.

    In this code, the only difference between Foo and Bar is whether or not the parameter a has a type annotation:

    
    from typing import Any
    
    from pytypes import typechecked
    
    
    class Foo:
        def log(self, a: Any, b: Any = None) -> None:
            print(self, a, b)
    
    
    class Bar:
        def log(self, a, b: Any = None) -> None:
            print(self, a, b)
    
    
    if __name__ == '__main__':
        typechecked('__main__')
        Foo().log(1)
        Bar().log(2)
    

    The result is:

    $ python test.py
    <__main__.Foo object at 0x000002871A85E978> 1 None
    Traceback (most recent call last):
      File "test.py", line 20, in <module>
        Bar().log(2)
    pytypes.exceptions.InputTypeError:
      __main__.Bar.log
      called with incompatible types:
    Expected: Tuple[Any, Union[Any, NoneType], NoneType]
    Received: Tuple[int, NoneType]
    

    That is, the type checking is happy with Foo, but with Bar it seems to expect an extra None argument after b.

    The problem goes away when b doesn't have a default value. It doesn't go away when a second argument is added to the call to Bar().log.

    bug 
    opened by sjjessop 12
  • Cannot use typing.Generic with pytypes

    Cannot use typing.Generic with pytypes

    Using pytypes 1.0b5:

    Python 3.7.3rc1 (default, Mar 13 2019, 11:01:15) 
    [GCC 7.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import typing
    >>> import pytypes
    >>> class PCollection(typing.Generic[typing.TypeVar('T')]):
    ...   pass
    ... 
    >>> PCollection()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/google/home/ehudm/virtualenvs/beam-py37/lib/python3.7/site-packages/pytypes/__init__.py", line 567, in __Generic__new__
        if cls.__origin__ is None:
    AttributeError: type object 'PCollection' has no attribute '__origin__'
    
    opened by udim 12
  • Optimize resolve_fw_decl

    Optimize resolve_fw_decl

    This is a huge time-saver. Otherwise globals for resolve_fw_decl are being rebuild again and again our case, which just blows up. It brings 4 minutes loading time (we do some type checking at loading time) to 14 seconds.

    See crazy slow trace here.

    So resolve_fw_decl calls get_function_perspective_globals which then calls getframeinfo 53240 times which calls getmodule 81927 times and this is just slow. In our case cache has just 8 entries (I left it to default 128) at the end for this speedup:

    CacheInfo(hits=1414, misses=8, maxsize=128, currsize=8)
    
    opened by mitar 12
  • _issubclass and bound_typevars argument

    _issubclass and bound_typevars argument

    import typing
    from pytypes import type_util
    
    T = typing.TypeVar('T', covariant=True)
    
    class L(typing.List[T]):
        pass
    
    C = typing.TypeVar('T', bound=L)
    
    type_util._issubclass(L[float], C)  # False
    type_util._issubclass(L[float], C, bound_typevars={})  # True
    

    Why does bound_typevar change result from False to True?

    Also, why code never checks if superclass is in bound_typevars? I think it just relays on catch-all except. I think such approach might hide some bugs?

    question 
    opened by mitar 12
  • Packaging and testing improvements

    Packaging and testing improvements

    This PR adds missing packaging/testing boilerplate. What is still missing is:

    1. Encrypted PyPI password for automated PyPI deployment via Travis
    2. The "long" description that goes to the PyPI page. I feel that both READMEs (of which there should only be one!) are too long for that purpose.

    If you provide this missing information, I will amend my commits so you can merge while maintaining a pretty version history. You can also now enable Travis integration in github's repository settings.

    opened by agronholm 12
  • Python >= 3.6.1 (maybe others too but not 3.6.0 or 3.5): Typecheck agent fails on re.compile()

    Python >= 3.6.1 (maybe others too but not 3.6.0 or 3.5): Typecheck agent fails on re.compile()

    The following code:

    import re
    from pytypes import TypeChecker
    
    with TypeChecker():
        re.compile('a')
    

    fails as follows:

    $ python3 Test.py 
    /usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py:2532: UserWarning: the system profiling hook has changed unexpectedly
      warn('the system profiling hook has changed unexpectedly')
    Traceback (most recent call last):
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/typecomment_parser.py", line 52, in _get_typestrings
        srclines = inspect.getsourcelines(obj)[0]
      File "/usr/lib/python3.6/inspect.py", line 955, in getsourcelines
        lines, lnum = findsource(object)
      File "/usr/lib/python3.6/inspect.py", line 768, in findsource
        file = getsourcefile(object)
      File "/usr/lib/python3.6/inspect.py", line 684, in getsourcefile
        filename = getfile(object)
      File "/usr/lib/python3.6/inspect.py", line 666, in getfile
        'function, traceback, frame, or code object'.format(object))
    TypeError: <slot wrapper '__and__' of 'int' objects> is not a module, class, method, function, traceback, frame, or code object
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "Test.py", line 5, in <module>
        re.compile('a')
      File "/usr/lib/python3.6/re.py", line 233, in compile
        return _compile(pattern, flags)
      File "/usr/lib/python3.6/re.py", line 302, in _compile
        if not (flags & DEBUG):
      File "/usr/lib/python3.6/enum.py", line 803, in __and__
        def __and__(self, other):
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py", line 2563, in __call__
        _check_caller_type(False, caller_level=self._caller_level_shift+1)
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py", line 2416, in _check_caller_type
        cllable, clss = _find_typed_base_method(cllable, clss)
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py", line 2131, in _find_typed_base_method
        if has_type_hints(util._actualfunc(fmeth)):
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py", line 657, in has_type_hints
        return _has_type_hints(func0)
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/type_util.py", line 686, in _has_type_hints
        tpStr = _get_typestrings(func, False)
      File "/usr/local/lib/python3.6/dist-packages/pytypes-1.0b5.post20-py3.6.egg/pytypes/typecomment_parser.py", line 54, in _get_typestrings
        srclines = inspect.getsourcelines(getattr(obj.__class__, obj.__name__))[0]
    AttributeError: type object 'wrapper_descriptor' has no attribute '__and__'
    
    bug fixed 
    opened by jolaf 11
  • Jython: ImportError: No module named pkg_resources

    Jython: ImportError: No module named pkg_resources

    This is probably a Jython issue, but I'd like to have a fallback, since pytypes used to work with Jython apart from this. As far as I understand, this is about some functionality of setuptools. I didn't explicitly run setup.py on Jython, so something might be ill-configured. However, I also didn't do such a thing on CPython, but there it runs out of the box.

    How to fix it?

    opened by Stewori 11
  • List[int] is incompatible with List[Union[float]]

    List[int] is incompatible with List[Union[float]]

    The following code:

    from typing import cast, List
    
    from pytypes import TypeChecker
    
    def f() -> List[float]:
        return cast(List[float], [2])
    
    with TypeChecker():
        f()
        print("OK")
    

    fails as follows:

    $ python3 Test.py
    Traceback (most recent call last):
      File "Test.py", line 9, in <module>
        f()
      File "Test.py", line 6, in f
        return cast(List[float], [2])
    pytypes.exceptions.ReturnTypeError: 
      __main__.f
      returned incompatible type:
    Expected: List[float]
    Received: List[int]
    
    opened by jolaf 9
  • AttributeError: 'NoneType' object has no attribute '__bound__'

    AttributeError: 'NoneType' object has no attribute '__bound__'

    from typing import Set, Generic, TypeVar
    import pytypes
    
    
    T = TypeVar('T')
    class Value(Generic[T]):
        @pytypes.typechecked
        def __init__(self, initial : T):
            ...
        
    Value[Set]( set([1,2,3]) )
    

    error:

    Traceback (most recent call last):
      File "_internal\github_project\main.py", line 227, in <module>
        Value[Set]( set([1,2,3]) )
      File "typing.py", line 729, in __call__
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\typechecker.py", line 858, in checker_tp
        checked_val = _checkfunctype(argSig, check_args,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\typechecker.py", line 688, in _checkfunctype
        result, checked_val = _checkinstance(check_val, argSig,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\typechecker.py", line 581, in _checkinstance
        res, obj2 = _checkinstance(obj[i], prms[0 if elps else i], bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\typechecker.py", line 675, in _checkinstance
        return _isinstance(obj, cls, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 2164, in _isinstance
        return _issubclass(deep_type(obj), cls, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 1992, in _issubclass
        return _issubclass(subclass, superclass, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 2019, in _issubclass
        res = _issubclass_2(subclass, superclass, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 2051, in _issubclass_2
        return _issubclass_Generic(subclass, superclass, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 1582, in _issubclass_Generic
        if not _issubclass(p_self, p_cls, bound_Generic, bound_typevars,
      File "D:\DevelopPPP\projects\InterBox\_internal\python\lib\site-packages\pytypes\type_util.py", line 2015, in _issubclass
        if not subclass.__bound__ is None:
    AttributeError: 'NoneType' object has no attribute '__bound__'
    
    opened by iperov 5
  • Incorrect collections imports in type_util.py

    Incorrect collections imports in type_util.py

    type_util.py references collections.Iterable and collections.Iterator which is incorrect, it should be collections.abc.Iterable and collections.abc.Iterator.

    It causes actual crashes like this:

    Traceback (most recent call last):
    ...
        import pytypes
      File "/home/buildmaster/.local/lib/python3.10/site-packages/pytypes/__init__.py", line 314, in <module>
        from .typechecker import _install_import_hook
      File "/home/buildmaster/.local/lib/python3.10/site-packages/pytypes/typechecker.py", line 35, in <module>
        from .type_util import type_str, has_type_hints, _has_type_hints, is_builtin_type, \
      File "/home/buildmaster/.local/lib/python3.10/site-packages/pytypes/type_util.py", line 2252, in <module>
        class _typechecked_Iterable(collections.Iterable):
    AttributeError: module 'collections' has no attribute 'Iterable'
    
    opened by jolaf 1
  • @typechecked fails to correctly parse functions with  PEP 3102

    @typechecked fails to correctly parse functions with PEP 3102

    example:

    
    from pytypes import typechecked
    
    @typechecked
    def foo(a: int, *, c: str):
        for _ in range(a):
            print(c)
    
    foo(10,c="1")
    
    

    leads to this:

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    Input In [17], in <cell line: 1>()
    ----> 1 foo(10,"1")
    
    File ~/opt/anaconda3/envs/isxcore_github/lib/python3.8/site-packages/pytypes/typechecker.py:882, in _typeinspect_func.<locals>.checker_tp(*args, **kw)
        880         res = func(args[0], *checked_args, **checked_kw)
        881     else:
    --> 882         res = func(*checked_args, **checked_kw)
        884 checked_res = _checkfuncresult(resSig, res, toCheck, \
        885         slf or clsm, parent_class, True, prop_getter, bound_typevars=bound_typevars)
        886 if pytypes.do_logging_in_typechecked:
    
    TypeError: foo() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given
    
    opened by sg-s 0
  • Support Python 3.10

    Support Python 3.10

    Some code needs to be updated now that the deprecated top-level ABCs in collections have been removed.

      File "/path/to/lib/python3.10/site-packages/pytypes/type_util.py", line 2252, in <module>
        class _typechecked_Iterable(collections.Iterable):
    AttributeError: module 'collections' has no attribute 'Iterable'
    
    opened by llchan 2
  • Infer return type of generic functions based on input

    Infer return type of generic functions based on input

    Is it possible to infer the return type based on the input, for generic functions?

    Eg, with this classic example:

    from typing import TypeVar, Sequence
    
    T = TypeVar('T')      # Declare type variable
    
    def first(seq: Sequence[T]) -> T:   # Generic function
        return seq[0]
    

    If I know that seq has type Sequence[int], how can I infer that first would return int in that case?

    opened by betodealmeida 0
Releases(1.0b10)
  • 1.0b10(Dec 19, 2021)

    pytypes 1.0 beta 10

    The 1.0 beta 10 release of pytypes is mainly a bugfix and compatibility release. It brings support for Python 3.7 and 3.8. This support is however not complete, but mostly complete. Some issues still need to be fixed to complete this support, see #40.

    This release consists of minor improvements over release 1.0 beta 9.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b9(Nov 21, 2021)

    pytypes 1.0 beta 9

    The 1.0 beta 9 release of pytypes is mainly a bugfix and compatibility release. It brings support for Python 3.7 and 3.8. This support is however not complete, but mostly complete. Some issues still need to be fixed to complete this support, see #40.

    Note: this release is almost identical to 1.0 beta 6, 7 and 8. It just unpins the version requirement setuptools_scm==3.5.0 because it caused an issue for publishing in conda forge. As a consequence, Python 3.4 is no longer supported. If you need to use pytypes with Python 3.4, stick to release 1.0b8.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b8(Nov 19, 2021)

    pytypes 1.0 beta 8

    The 1.0 beta 8 release of pytypes is mainly a bugfix and compatibility release. It brings support for Python 3.7 and 3.8. This support is however not complete, but mostly complete. Some issues still need to be fixed to complete this support, see #40.

    Note: this release is almost identical to 1.0 beta 6 and 1.0 beta 7. It just fixes an rst syntax issue that prevented publishing 1.0 beta 7 on PyPI.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b7(Nov 19, 2021)

    pytypes 1.0 beta 7

    The 1.0 beta 7 release of pytypes is mainly a bugfix and compatibility release. It brings support for Python 3.7 and 3.8. This support is however not complete, but mostly complete. Some issues still need to be fixed to complete this support, see #40.

    Note: this release is almost identical to 1.0 beta 6. It just fixed an rst syntax issue that prevented publishing 1.0 beta 6 on PyPI.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b6(Nov 19, 2021)

    pytypes 1.0 beta 6

    The 1.0 beta 6 release of pytypes is mainly a bugfix and compatibility release. It brings support for Python 3.7 and 3.8. This support is however not complete, but mostly complete. Some issues still need to be fixed to complete this support, see #40.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b5(Jul 9, 2018)

    pytypes 1.0 beta 5

    The 1.0 beta 5 release of pytypes is mainly a bugfix release. Most notably it fixes some regressions in pytypes 1.0 beta 4.

    Many thanks to all contributors!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b4(May 15, 2018)

    pytypes 1.0 beta 4

    We proudly present the 1.0 beta 4 release of pytypes. Beneath several bugfixes, the most notable new feature is a utility function for explicitly resolving forward references, e.g. of a manually defined type. Another significant improvement is proper support for tuple ellipses. The converter utility stubfile_2_converter alias typestubs was improved significantly. It can now be used to dump type info from a module into a stubfile, supporting type comments and ordinary type annotations as input or output. E.g. it can convert type comments from Python 2 code to an original Python 3 style stubfile.

    Have fun!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b3(Nov 19, 2017)

    pytypes 1.0 beta 3

    The second release of pytypes is mainly a bugfix release. The most notable new feature is better handling of type variables. pytypes 1.0 beta 2 is available on PyPI.

    Enjoy!

    Note: 1.0 beta 3 is the same as 1.0 beta 2. Just release configuration was fixed.

    Source code(tar.gz)
    Source code(zip)
  • 1.0b2(Nov 18, 2017)

    pytypes 1.0 beta 2

    The second release of pytypes is mainly a bugfix release. The most notable new feature is better handling of type variables. pytypes 1.0 beta 2 is available on PyPI.

    Enjoy!

    Source code(tar.gz)
    Source code(zip)
  • 1.0b1(Jul 10, 2017)

    Welcome to pytypes 1.0 beta 1

    This is the first release of pytypes. It is mainly intended to test pytypes' approaches in the real world. Please check to what extend this beta version suites your needs and help us to improve it. You can also access it via PyPI and install it via pip.

    See the quick manual to get an overview of current features and what is planned next.

    Happy typing!

    Source code(tar.gz)
    Source code(zip)
Owner
Stefan Richthofer
Stefan Richthofer
Auto-generate PEP-484 annotations

PyAnnotate: Auto-generate PEP-484 annotations Insert annotations into your source code based on call arguments and return types observed at runtime. F

Dropbox 1.4k Dec 26, 2022
Typical: Fast, simple, & correct data-validation using Python 3 typing.

typical: Python's Typing Toolkit Introduction Typical is a library devoted to runtime analysis, inference, validation, and enforcement of Python types

Sean 170 Dec 26, 2022
A simple stopwatch for measuring code performance with static typing.

A simple stopwatch for measuring code performance. This is a fork from python-stopwatch, which adds static typing and a few other things.

Rafael 2 Feb 18, 2022
pycallgraph is a Python module that creates call graphs for Python programs.

Project Abandoned Many apologies. I've stopped maintaining this project due to personal time constraints. Blog post with more information. I'm happy t

gak 1.7k Jan 1, 2023
Turn your Python and Javascript code into DOT flowcharts

Notes from 2017 This is an older project which I am no longer working on. It was built before ES6 existed and before Python 3 had much usage. While it

Scott Rogowski 3k Jan 9, 2023
Inspects Python source files and provides information about type and location of classes, methods etc

prospector About Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and comple

Python Code Quality Authority 1.7k Dec 31, 2022
Find dead Python code

Vulture - Find dead code Vulture finds unused code in Python programs. This is useful for cleaning up and finding errors in large code bases. If you r

Jendrik Seipp 2.4k Jan 3, 2023
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Kirill Klenov 966 Dec 29, 2022
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

wemake.services 2.1k Jan 5, 2023
The uncompromising Python code formatter

The Uncompromising Code Formatter “Any color you like.” Black is the uncompromising Python code formatter. By using it, you agree to cede control over

Python Software Foundation 30.7k Dec 28, 2022
A Python utility / library to sort imports.

Read Latest Documentation - Browse GitHub Code Repository isort your imports, so you don't have to. isort is a Python utility / library to sort import

Python Code Quality Authority 5.5k Jan 6, 2023
A formatter for Python files

YAPF Introduction Most of the current formatters for Python --- e.g., autopep8, and pep8ify --- are made to remove lint errors from code. This has som

Google 13k Dec 31, 2022
Performant type-checking for python.

Pyre is a performant type checker for Python compliant with PEP 484. Pyre can analyze codebases with millions of lines of code incrementally – providi

Facebook 6.2k Jan 7, 2023
Collection of library stubs for Python, with static types

typeshed About Typeshed contains external type annotations for the Python standard library and Python builtins, as well as third party packages as con

Python 3.3k Jan 2, 2023
A system for Python that generates static type annotations by collecting runtime types

MonkeyType MonkeyType collects runtime types of function arguments and return values, and can automatically generate stub files or even add draft type

Instagram 4.1k Jan 2, 2023
A static type analyzer for Python code

pytype - ? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Google 4k Dec 31, 2022
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 9.4k Jan 7, 2023
A static analysis tool for Python

pyanalyze Pyanalyze is a tool for programmatically detecting common mistakes in Python code, such as references to undefined variables and some catego

Quora 212 Jan 7, 2023
Unbearably fast O(1) runtime type-checking in pure Python.

Look for the bare necessities, the simple bare necessities. Forget about your worries and your strife. — The Jungle Book.

null 1.4k Dec 29, 2022