Code Monkey home page Code Monkey logo

typeguard's Introduction

Build Status Code Coverage Documentation

This library provides run-time type checking for functions defined with PEP 484 argument (and return) type annotations, and any arbitrary objects. It can be used together with static type checkers as an additional layer of type safety, to catch type violations that could only be detected at run time.

Two principal ways to do type checking are provided:

  1. The check_type function:
    • like isinstance(), but supports arbitrary type annotations (within limits)
    • can be used as a cast() replacement, but with actual checking of the value
  2. Code instrumentation:
    • entire modules, or individual functions (via @typechecked) are recompiled, with type checking code injected into them
    • automatically checks function arguments, return values and assignments to annotated local variables
    • for generator functions (regular and async), checks yield and send values
    • requires the original source code of the instrumented module(s) to be accessible

Two options are provided for code instrumentation:

  1. the @typechecked function:
    • can be applied to functions individually
  2. the import hook (typeguard.install_import_hook()):
    • automatically instruments targeted modules on import
    • no manual code changes required in the target modules
    • requires the import hook to be installed before the targeted modules are imported
    • may clash with other import hooks

See the documentation for further information.

typeguard's People

Contributors

agronholm avatar biolds avatar cjolowicz avatar cotsog avatar dalejung avatar danie-1 avatar deathowl avatar epenet avatar epronovost avatar fefe982 avatar hauntsaninja avatar jacobpbrugh avatar jellezijlstra avatar jtbeach avatar jwilk avatar kelseyfrancis avatar kstauffer avatar mtelka avatar pirate avatar pre-commit-ci[bot] avatar prescod avatar reinhrst avatar rfrowe avatar russok avatar supersergiy avatar tfiers avatar tolker-ku avatar vthemelis avatar wbolster avatar williamlw999-fb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typeguard's Issues

Modify check_type to work without memo

In my project I would like to check arbitrary object against PEP 484 definition.
check_type seems to be what I need, but it requires a memo object which I do not have in my use case.

Custom Error type

Hi, I'd love to be able to catch typeguard validation errors explicitly, as I'm using it for validation of user's input and I'd love to be able to tell them when it's their fault and not try and tell them that my code's TypeError is their fault. I might be able to send a pull request for this, but wanted to run it by you first.

sphinx-autodoc-typehints: NameError: name 'ConcatOperator' is not defined

sphinx-autodoc-typehints build fails due to the use of @typechecked() decorator

from typeguard import typechecked

class ConcatOperator(Operator):
    @strict_type
    def __init__(self, ...):
        super().__init__(name=name)
        pass

    @typechecked()
    def create_operator(self, model_name:str, operator_id:int) -> 'ConcatOperator':
        self.model_name = model_name
        self.operator_id = operator_id
        self.validate()
        return self

    def validate(self):
        pass

Stacktrace

@agronholm Typeguard is a simple yet effective library. But the above issue still persists with the use of your lib for type checking

[FR] hide stacktrace info from typeguard

Is possible replace from

    @wraps(func)
    def wrapper(*args, **kwargs):
        memo = _CallMemo(func, args=args, kwargs=kwargs)
        check_argument_types(memo)
        retval = func(*args, **kwargs)
        check_return_type(retval, memo)
        return retval

to

    @wraps(func)
    def wrapper(*args, **kwargs):
        memo = _CallMemo(func, args=args, kwargs=kwargs)
        try:
            check_argument_types(memo)
        except TypeError as err:
            raise TypeError(err)
        retval = func(*args, **kwargs)
        try:
            check_return_type(retval, memo)
        except TypeError as err:
            raise TypeError(err)
        return retval

for hide stacktrace info from typeguard.

typechecked Tuple type invalid type check

typeguard==2.2.2

Using @typechecked as decorator from a class i'm trying to pass a string, or a Tuple[str].

i defined my type in this way:

DocPath = Optional[Union[str, Tuple[str]]]

and the signature of my function is this:

@typechecked
def path (self, path: Union[DocPath, AnotherTypeWhereICanExtractThePath] = undefined):

but running this method with a tuple as parameter for path, cause TypeError Exception.

  File "/home/step/Workspace/XXX/.venv/lib/python3.7/site-packages/typeguard.py", line 462, in check_argument_types
    raise TypeError(exc) from None
TypeError: type of argument "path" must be one of (str, Tuple, NoneType); got tuple instead

Not sure if is a bug, or i am trying to mix too many types

Support for NamedTuple

Hi. I was just wondering if NamedTuples are supported? Or if there is plans to support NamedTuples in the future. I would like to do this:

Employee = typing.NamedTuple("Employee", [("name", str), ("id", int)] )

@typeguard.typechecked
def foo(bar:Employee):
    pass  

foo(Employee('bob', 1))

Currently, running the above example results in an AttributeError

Here is the output:
https://gist.github.com/ruxi/17d934768aa9404ca99fac2cb1d2a1e2

`# type: ignore` comment is not respected

PEP 484 states:
"A # type: ignore comment on a line by itself is equivalent to adding an inline # type: ignore to each line until the end of the current indented block. At top indentation level this has effect of disabling type checking until the end of file."

Suggestion: check_return_type()

For cases where a developer wishes to avoid the extra frame of the @typechecked decorator but would still like to check the return value, typeguard could provide a check_return_type() function to call directly before returning, similar to check_argument_types().

typing.Collection not checked on Python 3.7

Using this code

import typing

from typeguard import typechecked


@typechecked
def foo(collection: typing.Collection) -> None:
    pass


if __name__ == '__main__':
    foo(True)

On python3.6 I get the following error, as expected:

TypeError: type of argument "collection" must be collections.abc.Collection; got bool instead

But, on Python3.7, nothing happens.

I think what changed was inspect.isclass(typing.Collection) which returns True on python3.6 and False on python3.7, and it skips this check https://github.com/agronholm/typeguard/blob/master/typeguard.py#L382 .

None in Tuple is not checked

Hello there,
While comparing this library with other type checkers such as enforce and typecheck-decorator I found this bug:

from typeguard import typechecked

@typechecked
def method(a: Tuple[str, int] = ('hello', 1)) -> int:
    if a is None:
        return a
    else:
        print(a[0])
        return a[1]

try:
    method(None)
except TypeError:
    print('None for the whole tuple is correctly handled')

try:
    method((None, 0))
    raise Exception('error None not checked inside tuple!')
except TypeError:
    pass

Support common usage of Optional.

It seems, at least in my experience, that a common pattern for Optional arguments is to provide a default value of None and then, optionally, add the Optional[] qualifier. This results in the expected type of the argument being Union[some_type, None]. This can result in a weird TypeError though due to the way Union is handled for type checking. For example:

@typechecked
def func(arg: Optional[List[str]] = None):
    pass

This function if used like this:

func(['value', None])

results in this TypeError:

TypeError: type of argument "arg" must be one of (List, NoneType); got list instead

This doesn't really indicate the actual reason this passed list doesn't match the type requirements of func.

Given this pattern of having the actual expected type, or None indicating it was not passed, seems common I propose special casing the handling of Union[some_type, None]. The actual value of the argument can be checked against None and if it isn't None then the proper type check against some_type is performed. Otherwise if it is None, then no further checking needs to happen.

function compatability check

Awesome project, I think it will be very useful.
One question, is there anything that might be used to check the compatibility between two functions?
I am imagining something like this:


from typing import List, Any, Sequence
from typeguard import functions_compatible


def f1(var1: int) -> List[int]:
    return [var1]


def f2(var1: List[int]) -> Any:
    return var1
    
    
functions_compatible(f1, f2)  # True 
functions_compatible(f2, f1)  # True,  because Any can be int
functions_compatible(f2, f1, strict=True)  # False, because Any is not always int

I am working on a configurable data pipeline that is meant to run for a very long time, but the exact function connections are not always known until import time or later.

Externally defined class functions, unable to typecheck accepting an argument of type of the parent class

This is related to this general issue: python/typing#105

I have classes which are large and the make the situation manageable I have defined the class function in separate files and add these functions to the class on class initialisation.

I want these functions to have arguments the same type as the class of which they are a part, so I can compare two instances of the same class in multiple ways.

According to the discussion in: python/typing#105
this is the best practice method (for static type checking), but unfortunately this does not work for me in Python 3.7.2 with runtime checking, as provided by typeguard. It can work when the functions are all in the same file as the class, but doing this makes those files unmanageable in size.

Inside the external_file.py

from typing import TYPE_CHECKING
from typeguard import typechecked

if TYPE_CHECKING:
    from test_class_file import test_class

@typechecked
def large_function(self, test_class_instance : 'test_class') -> None:
    """Do some comparison on the class instances"""
    
    if test_class_instance.arg1 == self.arg1:
    	print("The values of arg1 matched")
    else:
    	print("The values of arg1 did not match")

Inside the class definition file test_class_file.py

from typeguard import typechecked
from external_file import large_function


class test_class():
    
    @typechecked
    def __init__(self, arg1 : str) -> None:
        """Init which includes externally defined large_function"""
        self.arg1 = arg1

        # Modify the class to add this externally defined function to all instances
        test_class.large_function = large_function    
        
    '''    
    # defined in the same file, this function works, if imported it fails
    # comment out the part in the init which inserts it into the class to test the below
    @typechecked
    def large_function(self, test_class_instance : 'test_class') -> None:
        """Do some comparison on the class instances"""
        
        if test_class_instance.arg1 == self.arg1:
            print("The values of arg1 matched")
        else:
            print("The values of arg1 did not match")
    '''
        
instance = test_class(arg1 = 'some_data')
instance2 = test_class(arg1 = 'some_data')

# Let's compare these class instances
instance.large_function(test_class_instance=instance2)

Occasional error in weakref

I

Exception ignored in: <function _removeHandlerRef at 0x10b2e8620>
Traceback (most recent call last):
  File "/Users/username/.pyenv/versions/3.6.0/lib/python3.6/logging/__init__.py", line 728, in _removeHandlerRef
  File "/Users/username/venv/lib/python3.6/site-packages/typeguard.py", line 589, in __call__
  File "/Users/username/venv/lib/python3.6/site-packages/typeguard.py", line 81, in find_function
  File "/Users/username/venv/lib/python3.6/weakref.py", line 400, in get
TypeError: 'NoneType' object is not callable

Have you tested with the new typing.py yet?

While Python 3.6 is in beta we're changing the way typing.py represents types. I can't make any promises that it'll stay stable until the 4th beta (after all PEP 484 is still provisional) but I figured I should give you a heads up that we're changing things around. You can follow the fun in our repo: https://github.com/python/typing/

Releasing a new version?

The latest released version on PyPI (2.2.0) does not work well with Python 3.7. There seem several unreleased patch commits to be compatible with Python 3.7. Could you release a new version including these patches?

Check all called functions at runtime

When adding type hints to some bigger project with good test coverage, it'd be nice to be able to typecheck anything which is called and annotated rather than doing so selectively.

This would be possible using an import hook or sys.settrace possibly.

I'm aware this is rather "weird" and breaks e.g. debuggers, but I imagine it as a separate "mode" you run your tests under selectively, similar to how people e.g. run tests without coverage locally (for speed), and with coverage on a CI.

I might work on this myself once I start using type annotations in my project, but it might take me a few months since there's more important stuff to do first ๐Ÿ˜‰

Disable type checking on production

Thank you for cool library!

It will be useful to be able to disable type checking via parameter for decorator:

@typechecked(enabled=__DEBUG__)
def some_function(a: int, b: float, c: str, *args: str) -> bool:
    ...

Reasoning: type checking at runtime has huge overhead that have to be avoided on production, but it very useful at dev environment. We should be able to separate this environments.

*args and **kwargs not according to PEP 484

Hi, PEP 484 says *args and **kwargs (e.g., **kwargs: int) should by annotated only by their value types and the type of the resulting dict (e.g., Dict[str, int]) should be deduced.

check_argument_types(), however, required the dict in the annotation which makes it impossible to mypy for static type checking and typeguard at the same time.

Support typechecking of AbstractSet

import typing
import uuid

from pytest import raises
from typeguard import typechecked

@typechecked
def check(v: typing.AbstractSet[uuid.UUID]):
    pass

with raises(TypeError):
    check({str(uuid.uuid4())})
Results (4.85s):
       1 failed
         - /Users/ung/tests/services/customer_test.py:125: Failed: DID NOT RAISE <class 'TypeError'>

Conda packaging

I'm about to open a PR for conda-forge packaging of typeguard. It would be useful if you were a maintainer as well, so you can updates conda packages as soon as there is a new release. Any problems with adding you as maintainer?

@typechecked ignores parameters with default arg


from typeguard import typechecked
import typing


class X:
    pass


@typechecked
def foo(a: typing.Dict[X, int]={}):
    pass


foo(a={X():2})
foo(a={2:2})

This code is supposed to throw an exception for the second foo() call. But no error is being raised.
If you removed the default paramter {} then the check raises an exception.

Bug or feature?

Wrong args on 2.1.2 version

import typing

from typeguard import typecheked

@typecheked
def test(t:typing.Tuple[str, Node]):
    pass

  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 2000, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/sqlalchemy_imageattach/stores/fs.py", line 226, in app
    return _app(environ, start_response)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/sqlalchemy_imageattach/stores/fs.py", line 283, in __call__
    return self.app(environ, start_response)
  File "/Users/ung/dodo-ads/ads/web/wsgi.py", line 119, in __call__
    return self.app(environ, start_response)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/sassutils/wsgi.py", line 162, in __call__
    return self.app(environ, start_response)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1991, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1567, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/ung/dodo-ads/ads/web/oauth.py", line 53, in decorator
    return f(*args, **kwargs)
  File "/Users/ung/dodo-ads/ads/web/admin.py", line 70, in list_channels
    return render_template('admin/list_channels.html', channels=channels)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/jinja2/environment.py", line 989, in render
    return self.environment.handle_exception(exc_info, True)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/jinja2/environment.py", line 754, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "/Users/ung/dodo-ads/ads/web/templates/admin/list_channels.html", line 1, in top-level template code
    {% extends 'admin/layout.html' %}
  File "/Users/ung/dodo-ads/ads/web/templates/admin/layout.html", line 1, in top-level template code
    {% extends 'layout.html' %}
  File "/Users/ung/dodo-ads/ads/web/templates/layout.html", line 97, in top-level template code
    {% block sidebar %}
  File "/Users/ung/dodo-ads/ads/web/templates/layout.html", line 98, in block "sidebar"
    {% for label, link in list_sitemap() if link.has_permission() %}
  File "/Users/ung/dodo-ads/ads/web/sitemap.py", line 62, in list_sitemap
    'admin.targets',
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 457, in wrapper
    check_argument_types(memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 426, in check_argument_types
    check_type(description, value, expected_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 377, in check_type
    check_tuple(argname, value, expected_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 257, in check_tuple
    check_type('{}[{}]'.format(argname, i), element, element_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 373, in check_type
    checker_func(argname, value, expected_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 213, in check_sequence
    check_type('{}[{}]'.format(argname, i), v, value_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 377, in check_type
    check_tuple(argname, value, expected_type, memo)
  File "/Users/ung/.venvs/ads/lib/python3.5/site-packages/typeguard.py", line 240, in check_tuple
    format(argname, qualified_name(value)))
TypeError: type of argument "nodes"[0][0] must be a tuple; got str instead

Issues with PyCharm debugger

The following simple example fail with recent PyCharm versions:

from typeguard import check_argument_types


def run(x: int):
    assert check_argument_types()
    print(x)      # <--- place a breakpoint here and run debug


if __name__ == "__main__":
    run(1)
    # run(1.0)    # uncomment & run normally to make sure it correctly detects incorrect type
  }
}

The error message I get is

C:\ProgramFiles\Anaconda\envs\general_py36\python.exe "C:\Program Files\JetBrains\PyCharm 2017.2\helpers\pydev\pydevd.py" --multiproc --qt-support=auto --client 127.0.0.1 --port 55659 --file W:/tests/PyTests/typeguard_in_debug/check_args_in_debug.py
pydev debugger: process 18164 is connecting

Connected to pydev debugger (build 172.3968.37)
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2017.2\helpers\pydev\pydevd.py", line 1599, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "C:\Program Files\JetBrains\PyCharm 2017.2\helpers\pydev\pydevd.py", line 1026, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "C:\Program Files\JetBrains\PyCharm 2017.2\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "W:/tests/PyTests/typeguard_in_debug/check_args_in_debug.py", line 10, in <module>
    run(1)
  File "W:/tests/PyTests/typeguard_in_debug/check_args_in_debug.py", line 5, in run
    assert check_argument_types()
  File "C:\ProgramFiles\Anaconda\envs\general_py36\lib\site-packages\typeguard.py", line 421, in check_argument_types
    memo = _CallMemo(func, frame)
  File "C:\ProgramFiles\Anaconda\envs\general_py36\lib\site-packages\typeguard.py", line 46, in __init__
    self.func_name = function_name(func)
  File "C:\ProgramFiles\Anaconda\envs\general_py36\lib\site-packages\typeguard.py", line 130, in function_name
    module = func.__module__
AttributeError: 'NoneType' object has no attribute '__module__'

Process finished with exit code 1

I've already reported it on their bug tracker.
Luckily, they've left an option to fallback to the previous debugger mechanism (the PYDEVD_USE_FRAME_EVAL trick).

I'd like to report this here as well, just in case. I've still not looked into the code and I'm not sure whether or not it is possible to modify typeguard code so that it would work with JetBrains' debugger.

Union[str, Sequence[T]] denies strings

Union[str, Sequence[T]] (where T is not str) denies strings:

from typing import Sequence, Union

from typeguard import typechecked


@typechecked
def check_union_sequence(v: Union[str, Sequence[int]]):
    pass


check_union_sequence('test')

The last call should pass without TypeError, but it actually doesn't:

Traceback (most recent call last):
  File "t.py", line 11, in <module>
    check_union_sequence('test')
  File "/Users/dahlia/.virtualenvs/store-service/lib/python3.5/site-packages/typeguard.py", line 450, in wrapper
    check_argument_types(memo)
  File "/Users/dahlia/.virtualenvs/store-service/lib/python3.5/site-packages/typeguard.py", line 419, in check_argument_types
    check_type(description, value, expected_type, memo)
  File "/Users/dahlia/.virtualenvs/store-service/lib/python3.5/site-packages/typeguard.py", line 366, in check_type
    checker_func(argname, value, expected_type, memo)
  File "/Users/dahlia/.virtualenvs/store-service/lib/python3.5/site-packages/typeguard.py", line 204, in check_sequence
    check_type('{}[{}]'.format(argname, i), v, value_type, memo)
  File "/Users/dahlia/.virtualenvs/store-service/lib/python3.5/site-packages/typeguard.py", line 382, in check_type
    format(argname, qualified_name(expected_type), qualified_name(value)))
TypeError: type of argument "v"[0] must be int; got str instead

check_type doesn't work for the empty tuple type

typeguard.check_type('x', (), typing.Tuple[()]) should succeed, but raises a TypeError instead

According to the mypy docs, the empty tuple () has type typing.Tuple[()]. (I couldn't find anything on this in the typing docs, but since Guido reviewed and merged the commit to mypy's docs , Tuple[()] seems like the official way to do it.)

On my machine, check_type('x', (), Tuple[()]) does this:

>>> from typing import Tuple
>>> from typeguard import check_type
>>> check_type('x', (), Tuple[()])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\lubieowoce\.virtualenvs\sumtype-PdEpNsgZ\lib\site-packages\typeguard.py", line 392, in check_type
    checker_func(argname, value, expected_type, memo)
  File "C:\Users\lubieowoce\.virtualenvs\sumtype-PdEpNsgZ\lib\site-packages\typeguard.py", line 256, in check_tuple
    .format(argname, len(tuple_params), len(value)))
TypeError: x has wrong number of elements (expected 1, got 0 instead)

Check for "None" return type

This code passes runtime checks, but should fail:

#!/usr/bin/env python3

from typeguard import typechecked


@typechecked
def main() -> None:
    return 'asdf'


if __name__ == '__main__':
    main()

Main is defined as having no return type according to PEP 484:

https://www.python.org/dev/peps/pep-0484/#using-none

Runtime type checking does work if you define main like this:

@typechecked
def main() -> type(None):
    return 'asdf'

int values should be accepted for float parameters ?

PEP 484 says here:

when an argument is annotated as having type float, an argument of type int is acceptable

Typeguard 2.1.3 reports this is a typing error.

Sample code:

from warnings import filterwarnings
from typeguard import TypeChecker, TypeWarning

def my_method(a: float) -> None:
    pass

def test_01():
    filterwarnings('error', category=TypeWarning)
    with TypeChecker(['tests']):
        my_method(a=2)

(same applies to complex)

check_callable import raises on python 3.5.1+

I'm new to the type annotation system introduced in Python 3 and I tried to start a simple project (using asphalt, which in turns uses typeguard) on Debian stretch and it will traceback when importing from typeguard.
I also tried to compile the provided Python 3.5 & 3.6 tarballs from here: https://www.python.org/downloads/source/

python3.5 --version
Python 3.5.1+

The asphalt project is doing a simple

from asphalt.core.main import *

Which will do

from typeguard import check_argument_types

Which will provoke the following traceback:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File ".../tmpvenv/lib/python3.5/site-packages/typeguard.py", line 37, in <module>
    def check_callable(argname: str, value, expected_type, typevars_memo: Dict[TypeVar, type]) -> None:
  File "/usr/lib/python3.5/typing.py", line 1018, in __getitem__
    tvars = _type_vars(params)
  File "/usr/lib/python3.5/typing.py", line 282, in _type_vars
    _get_type_vars(types, tvars)
  File "/usr/lib/python3.5/typing.py", line 277, in _get_type_vars
    t._get_type_vars(tvars)
TypeError: _get_type_vars() missing 1 required positional argument: 'tvars'

However, it works as expected with a Python 3 on Ubuntu that I got from the ppa https://launchpad.net/~fkrull/+archive/ubuntu/deadsnakes (it seems there wasn't any _get_type_vars function in this version):

python3.5 --version
Python 3.5.1

Do you known if this is the expected behavior?

Does not work with stacked decorators

Not sure if this is fault of typeguard, but this does not work

    @typechecked
    @staticmethod
    def myMethod(path: str):

I'm getting

AttributeError: 'staticmethod' object has no attribute '__module__'

changing the order resolves the issue

    @staticmethod
    @typechecked
    def myMethod(path: str):

Bug: Exceptions not handled correctly

Situation:

I've a function here with return value constraint List[xyz]. This function throws an Exception for some reason.

Encountered Behavior:

typeguard complains that the return value of this function is None.

Expected Behavior:

The exception should be let through. typeguard should not consume that exception.

Details:

I conclude that there is a bug because if I modify the return value constraint from List[xyz] to Union[List[xyz], None] everything works fine. In that modified case the Exception from within that function is not swallowed but raised correctly. To my understanding exceptions should never be swallowed if they are raised. Return type checking should be performed only if no exception is raised. Exceptions should have priority here: No data is not returned anyway if an exception is raised within a function. So no return type checking should be performed if typeguard encounters an exception.

I guess some kind of very minor change within the code could fix that problem? Despite of such a minor glitch typeguard works quite well!

Error message not as explicit for Tuple when it is in an Union

Sadly, the helpful message obtained for a mismatching Tuple length is not shown when the Tuple is used inside an Union.

Version used:

pip show typeguard
Name: typeguard
Version: 2.1.3
...

Commented example:

from typing import Union, Tuple
from typeguard import typechecked

@typechecked
def example1(param: Tuple[int, int, int]) -> None:
    return None

@typechecked
def example2(param: Union[int, Tuple[int, int, int]]) -> None:
    return None

#example1((42, 42)) # TypeError: argument "param" has wrong number of elements (expected 3, got 2 instead)
example2((42, 42)) # TypeError: type of argument "param" must be one of (int, Tuple); got tuple instead

io.BytesIO not matches to IO[bytes] and BinaryIO

Python 3.7.1
Typeguard 2.2.2

Traceback:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/user/.local/share/virtualenvs/x-FhuHazVu/lib/python3.7/site-packages/typeguard.py", line 394, in check_type
    check_type(argname, value, origin_type, memo)
  File "/home/user/.local/share/virtualenvs/x-FhuHazVu/lib/python3.7/site-packages/typeguard.py", line 413, in check_type
    format(argname, qualified_name(expected_type), qualified_name(value)))
TypeError: type of x must be IO; got _io.BytesIO instead

Examples:

import io
import typeguard
from typing import IO, BinaryIO

buf = io.BytesIO()

typeguard.check_type('x', buf, IO[bytes]) # Failed
typeguard.check_type('x', buf, BinaryIO) # Failed

@typeguard.typechecked
def foo(a: IO[bytes]):
    pass
foo(buf) # Failed

# But... 
typeguard.check_type('x', buf, BinaryIO()) # Passed
typeguard.check_type('x', buf, IO[bytes]()) # Passed

@typeguard.typechecked
def bar(a: IO[bytes]()):
    pass

bar(buf) # Passed

Q: How to avoid double type-checking when calling super()?

This is mainly a request for comments rather than an issue report.

We have code like this:

AuxFileDict = Dict[str, bytes]
MaskedBandWithMetaDict = Dict[str, MaskedBandWithMeta]

class A:

    @typechecked
    def __init__(self, band_map: MaskedBandWithMetaDict,
                 aux_files: AuxFileDict, extent: dict):
        ...

class B(A):

    @typechecked
    def __init__(self, band_map: MaskedBandWithMetaDict,
                 aux_files: AuxFileDict, extent: dict,
                 bandless_metadata: BandlessSceneMetadata):
        super().__init__(band_map, aux_files, extent)
        ...

If I understand this correctly, type-checks on band_map, aux_files, extent are executed twice if one does b = B(band_map, aux_files, extent, bandless_metadata) - once on B.__init__() and once on A.__init__()

Is there some reasonable way to avoid this? I can drop type annotations on the "shared" parameters of B.__init__(), but then I loose documentation and IDE goodies. Or perhaps the performance overhead would not be that bad in this case? (the checked dicts have just a couple of elements)

CC @Indy2222, @mathewcohle, @asgeirrr.

_CallMemo is not using the scope in which the function call is declared

The following snippet helps explaining my issue:

from typeguard import typechecked

def tst():
    class A:

        @typechecked
        def clone(self) -> 'A':
            return A()


    val = A().clone()
    assert isinstance(val, A)

tst()

This makes sure that the class A is not available at module level because it is defined inside the function. This will result in the following stack trace:

Connected to pydev debugger (build 182.3684.100)
Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1664, in <module>
    main()
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1658, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1068, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/sjuul/workspace/typeguard/bla.py", line 14, in <module>
    tst()
  File "/Users/sjuul/workspace/typeguard/bla.py", line 11, in tst
    val = A().clone()
  File "/Users/sjuul/workspace/typeguard/typeguard.py", line 489, in wrapper
    memo = _CallMemo(func, args=args, kwargs=kwargs)
  File "/Users/sjuul/workspace/typeguard/typeguard.py", line 47, in __init__
    hints = get_type_hints(func)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py", line 1001, in get_type_hints
    value = _eval_type(value, globalns, localns)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py", line 260, in _eval_type
    return t._evaluate(globalns, localns)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py", line 464, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'A' is not defined

The function hints = get_type_hints(func) cannot find the forward reference 'A' this is because typeguard using the current context and should use the context in which the function is defined in. This fixes it (I'll add a PR after)

frame = inspect.stack()[2].frame
hints = get_type_hints(func, localns=frame.f_locals, globalns=frame.f_globals)

Explicit 'comparison' with mypy

Hey,

It might be good to put a typeguard vs mypy in readme. I know those are different projects (runtime vs static) but that was my first question to a friend who linked it, so I guess other ppl might ask it too :P.

NamedTuple arguments are not checked by the `@typechecked` annotation?

First of all, thanks a lot for writing this awesome framework, I find it to be the best way to gradually introducing typing to a Python codebase! I have one problem with it though...

Problem: I cannot get arguments that are derived from NamedTuple to be checked by the @typechecked annotation

Minimal reproducible example:

from typeguard import typechecked
from typing import NamedTuple

@typechecked
class MyNamedTuple(NamedTuple):
    my_field: str

@typechecked
def print_tuple(t: MyNamedTuple) -> None:
    print("type of the argument is {}, note that it should be {}".format(
        type(t),
        type(MyNamedTuple("example")),
    ))

# this works as it should
t_good = MyNamedTuple('a string')

# this works as it should
print_tuple(t_good)

# BUG: I think this should fail, but it works at runtime!
print_tuple(1)

# just to show that the runtime typechecker sometimes works
t_bad = MyNamedTuple(1)

Running it:

~ # python3 example.py 
type of the argument is <class '__main__.MyNamedTuple'>, note that it should be <class '__main__.MyNamedTuple'>
type of the argument is <class 'int'>, note that it should be <class '__main__.MyNamedTuple'>
Traceback (most recent call last):
  File "example.py", line 26, in <module>
    t_bad = MyNamedTuple(1)
  File "/usr/local/lib/python3.7/site-packages/typeguard.py", line 493, in wrapper
    check_argument_types(memo)
  File "/usr/local/lib/python3.7/site-packages/typeguard.py", line 462, in check_argument_types
    raise TypeError(exc) from None
TypeError: type of argument "my_field" must be str; got int instead
~ # pip freeze
typeguard==2.2.2
~ # python3 --version
Python 3.7.0

Is this a bug, a limitation, or am I using the framework in a wrong way? I would appreciate any kind of help!

Thanks!

Konrad

About performance

Hi!

Thanks for the project, looks very good and interesting.
I want to ask you some questions about performance: do you have any metrics (or thoughts) how usage of typechecked or TypeChecker affects on performance? And do you use your library in production or this just a prototype for fun?

typing.NewType not checked

Everything noted as typing.NewType is ignored for a typecheck. E.g.

        myint = NewType("myint", int)
        def foo(a: myint):
            assert check_argument_types()

        foo("a")  # should raise error, but does not

Django example?

Is there an elegant way to have typeguard check all types when running Django? I tried the third method (running the entire project under typeguard from manage.py) but the application just froze at startup.

Allow implicit Awaitable for async functions

For an API with lots of async functions it becomes a bit messy with Awaitable declarations for all return values. Would it be possible to have a flag to allow implicit Awaitable for the return type in async functions?

If the function is tagged async, the Awaitable is a bit redundant and could be inferred by the async declaration.

eg

@typechecked
async def foo() -> Awaitable[str]:
    return await getSomeString()

could then be written

@typechecked
async def foo() -> str:
    return await getSomeString()

Provide class decorator

Thank you so much for this library! Really valuable to implement solid python code!

I want to request a class decorator that would add typechecking to all methods of a class. Is that feasable?

Generator annotations are not interpreted according to PEP 484

Try this example:

from typing import Generator, no_type_check
from warnings import filterwarnings

from typeguard import TypeChecker, TypeWarning


def generator_example() -> Generator[int, None, None]:
    for i in range(3):
        yield i


def main() -> None:
    filterwarnings("error", category=TypeWarning)
    checker = TypeChecker("__main__")
    checker.start()
    results = generator_example()
    for result in results:
        print(result)


if __name__ == "__main__":
    main()

I get:
typeguard.TypeWarning: [MainThread] return from __main__.generator_example() at generator_fail.py:9: type of the return value must be collections.abc.Generator; got int instead

As a workaround, I decorate generators with @no_type_check.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.