|
32 | 32 | from functools import wraps |
33 | 33 | from pathlib import Path |
34 | 34 | from textwrap import dedent |
35 | | - from typing import get_args, Union, Any, get_origin |
| 35 | + from typing import get_args, Union, Any, get_origin, get_type_hints |
36 | 36 | from types import UnionType |
37 | 37 |
|
38 | 38 | import re |
@@ -728,40 +728,45 @@ def new_command(name, function=None, _self=cmd): |
728 | 728 | if function.__doc__ is not None: |
729 | 729 | function.__doc__ = dedent(function.__doc__).strip() |
730 | 730 |
|
731 | | - # Analysing arguments |
732 | | - spec = inspect.getfullargspec(function) |
733 | | - kwargs_ = {} |
734 | | - args_ = spec.args[:] |
735 | | - defaults = list(spec.defaults or []) |
736 | | - |
737 | | - args2_ = args_[:] |
738 | | - while args_ and defaults: |
739 | | - kwargs_[args_.pop(-1)] = defaults.pop(-1) |
740 | | - |
741 | | - funcs = {} |
742 | | - for idx, (var, func) in enumerate(spec.annotations.items()): |
743 | | - funcs[var] = func |
| 731 | + # Resolve strings into real class objects (PEP 563). |
| 732 | + try: |
| 733 | + resolved_hints = get_type_hints( |
| 734 | + function, |
| 735 | + globalns=sys.modules[function.__module__].__dict__ |
| 736 | + ) |
| 737 | + except Exception: |
| 738 | + resolved_hints = function.__annotations__ |
744 | 739 |
|
| 740 | + # Analysing arguments |
| 741 | + sign = inspect.signature(function) |
| 742 | + |
745 | 743 | # Inner function that will be callable every time the command is executed |
746 | 744 | @wraps(function) |
747 | 745 | def inner(*args, **kwargs): |
748 | 746 | caller = sys._getframe(1).f_code.co_filename |
749 | 747 | # It was called from command line or pml script, so parse arguments |
750 | 748 | if caller == _parser_filename: |
751 | | - kwargs = {**kwargs, **dict(zip(args2_, args))} |
752 | 749 | # special _self argument |
753 | 750 | kwargs.pop("_self", None) |
754 | 751 | new_kwargs = {} |
755 | | - for var, type in funcs.items(): |
| 752 | + for var, param in sign.parameters.items(): |
756 | 753 | if var in kwargs: |
757 | 754 | value = kwargs[var] |
758 | 755 | # special 'quiet' argument |
759 | 756 | if var == 'quiet' and isinstance(value, int): |
760 | 757 | new_kwargs[var] = bool(value) |
761 | 758 | else: |
762 | | - new_kwargs[var] = _into_types(var, type, value) |
| 759 | + actual_type = resolved_hints.get(var, param.annotation) |
| 760 | + new_kwargs[var] = _into_types(var, actual_type, value) |
| 761 | + else: |
| 762 | + if param.default is sign.empty: |
| 763 | + raise RuntimeError(f"Unknow variable '{var}'.") |
| 764 | + defaults = { |
| 765 | + k: v.default for k, v in sign.parameters.items() |
| 766 | + if v.default is not sign.empty |
| 767 | + } |
763 | 768 | final_kwargs = { |
764 | | - **kwargs_, |
| 769 | + **defaults, |
765 | 770 | **new_kwargs |
766 | 771 | } |
767 | 772 | return function(**final_kwargs) |
|
0 commit comments