Check type hints in stack trace printing

My idea consist in: Adding a method to perform type checking in traceback objects When printing stack traces search for mistyped arguments and warn about them to the user. Don't know if it is in the roadmap, but seems that have a good cost/benefit ratio to me.

El 14/06/18 a las 14:37, Steven D'Aprano escribió:
Example on how this should work from the user point of view: ~~~ python def one(arg: str) -> str: return two(arg) + "1" def two(arg: str) -> str: return three(arg) * 2 def three(arg: str) -> str: return "{}({}) ".format(arg, len(arg)) print(one("test")) print(one(0)) ~~~ Intended output: ~~~ test(4) test(4) 1 Traceback (most recent call last): File "test.py", line 9, in <module> print(one(0)) Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 2, in one return two(arg) + "1" Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 4, in two return three(arg) * 2 Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 6, in three return "{}({}) ".format(arg, len(arg)) TypeError: object of type 'int' has no len() ~~~ How could it be achieved? I don't know enough python to answer this. I suppose that it could be done.

2018-06-20 11:43 GMT+02:00 Daniel Sánchez Fábregas <daniel.sanchez.fabregas@xunta.gal>:
Looks like you could do this with a decorator, I think def type_check_on_traceback(func): ann = typing.get_type_hints(func) @functools.wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except: for key, value in kwargs.items(): if key in ann and not isinstance(value, ann[key]): # emit warning raise return func would be close, give or take some special cases. Not entirely sure how this'd work with Optional[whatever] types, so you'd have to test that. This takes a limitation on not checking the *args, but if you put some effort in, the inspect module can probably let you check those too.

To clarify some more, you'd then have to use the decorator like: @type_check_on_traceback def three(arg: str) -> str: return "{}({}) ".format(arg, len(arg)) on every function where you want this behaviour. Note that this will also emit warnings on tracebacks of exceptions that are later silenced, and afaik there's no easy way around that.

On Thu, Jun 14, 2018 at 4:03 AM Daniel Sánchez Fábregas <daniel.sanchez.fabregas@xunta.gal> wrote:
Isn't it faster and far more reliable to run your code through mypy (or whatever) and detect these problems statically, rather than wait and hope that you run into the problem dynamically?

On 14 June 2018 at 12:03, Daniel Sánchez Fábregas < daniel.sanchez.fabregas@xunta.gal> wrote:
It seems to me too this will be rather a work for a static type checker like mypy. There is a misconception that runtime objects can be attributed (or checked w.r.t.) static types, but this is true only in simple cases. Also such runtime check will be not able to correctly catch many type errors that can be detected statically, like a wrong assignment or a Liskov violation. -- Ivan

El 14/06/18 a las 14:37, Steven D'Aprano escribió:
Example on how this should work from the user point of view: ~~~ python def one(arg: str) -> str: return two(arg) + "1" def two(arg: str) -> str: return three(arg) * 2 def three(arg: str) -> str: return "{}({}) ".format(arg, len(arg)) print(one("test")) print(one(0)) ~~~ Intended output: ~~~ test(4) test(4) 1 Traceback (most recent call last): File "test.py", line 9, in <module> print(one(0)) Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 2, in one return two(arg) + "1" Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 4, in two return three(arg) * 2 Warning: TypeMistmatch argument 'arg' of type 'int' is declared as 'str' File "test.py", line 6, in three return "{}({}) ".format(arg, len(arg)) TypeError: object of type 'int' has no len() ~~~ How could it be achieved? I don't know enough python to answer this. I suppose that it could be done.

2018-06-20 11:43 GMT+02:00 Daniel Sánchez Fábregas <daniel.sanchez.fabregas@xunta.gal>:
Looks like you could do this with a decorator, I think def type_check_on_traceback(func): ann = typing.get_type_hints(func) @functools.wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except: for key, value in kwargs.items(): if key in ann and not isinstance(value, ann[key]): # emit warning raise return func would be close, give or take some special cases. Not entirely sure how this'd work with Optional[whatever] types, so you'd have to test that. This takes a limitation on not checking the *args, but if you put some effort in, the inspect module can probably let you check those too.

To clarify some more, you'd then have to use the decorator like: @type_check_on_traceback def three(arg: str) -> str: return "{}({}) ".format(arg, len(arg)) on every function where you want this behaviour. Note that this will also emit warnings on tracebacks of exceptions that are later silenced, and afaik there's no easy way around that.

On Thu, Jun 14, 2018 at 4:03 AM Daniel Sánchez Fábregas <daniel.sanchez.fabregas@xunta.gal> wrote:
Isn't it faster and far more reliable to run your code through mypy (or whatever) and detect these problems statically, rather than wait and hope that you run into the problem dynamically?

On 14 June 2018 at 12:03, Daniel Sánchez Fábregas < daniel.sanchez.fabregas@xunta.gal> wrote:
It seems to me too this will be rather a work for a static type checker like mypy. There is a misconception that runtime objects can be attributed (or checked w.r.t.) static types, but this is true only in simple cases. Also such runtime check will be not able to correctly catch many type errors that can be detected statically, like a wrong assignment or a Liskov violation. -- Ivan
participants (5)
-
Daniel Sánchez Fábregas
-
Eric Fahlgren
-
Ivan Levkivskyi
-
Jacco van Dorp
-
Steven D'Aprano