Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dc3111a

Browse files
committed
Support property decorator
1 parent e2744f9 commit dc3111a

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

IPython/core/guarded_eval.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -628,18 +628,36 @@ def eval_node(node: Union[ast.AST, None], context: EvaluationContext):
628628
return result
629629
if isinstance(node, ast.FunctionDef):
630630
# we ignore body and only extract the return type
631-
# TODO:support decorators?
631+
is_property = False
632+
633+
for decorator_node in node.decorator_list:
634+
try:
635+
decorator = eval_node(decorator_node, context)
636+
except NameError:
637+
# if the decorator is not yet defined this is fine
638+
# especialy because we don't handle imports yet
639+
continue
640+
if decorator is property:
641+
is_property = True
642+
643+
return_type = eval_node(node.returns, context=context)
644+
645+
if is_property:
646+
context.transient_locals[node.name] = _resolve_annotation(
647+
return_type, context
648+
)
649+
return None
650+
632651
def dummy_function(*args, **kwargs):
633652
pass
634653

635-
return_type = eval_node(node.returns, context=context)
636654
dummy_function.__annotations__["return"] = return_type
637655
dummy_function.__name__ = node.name
638656
dummy_function.__node__ = node
639657
context.transient_locals[node.name] = dummy_function
640658
return None
641659
if isinstance(node, ast.ClassDef):
642-
# TODO support decorators?
660+
# TODO support class decorators?
643661
class_locals = {}
644662
class_context = context.replace(transient_locals=class_locals)
645663
for child_node in node.body:
@@ -817,7 +835,7 @@ def dummy_function(*args, **kwargs):
817835
if node.msg:
818836
return eval_node(node.msg, context)
819837
return eval_node(node.test, context)
820-
raise SyntaxError(f"Unhandled node: {ast.dump(node)}")
838+
return None
821839

822840

823841
def _eval_return_type(func: Callable, node: ast.Call, context: EvaluationContext):
@@ -870,6 +888,8 @@ def _resolve_annotation(
870888
node: ast.Call | None = None,
871889
):
872890
"""Resolve annotation created by user with `typing` module and custom objects."""
891+
if annotation is None:
892+
return None
873893
annotation = _eval_annotation(annotation, context)
874894
origin = get_origin(annotation)
875895
if annotation is Self and func and hasattr(func, "__self__"):

tests/test_guarded_eval.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,33 @@ def test_mocks_items_of_call_results(data, code, expected_items):
490490
),
491491
int,
492492
],
493+
[
494+
"\n".join(
495+
[
496+
"class NotYetDefined:",
497+
" @property",
498+
" def my_prop(self) -> int:",
499+
" pass",
500+
"instance = NotYetDefined()",
501+
"instance.my_prop",
502+
]
503+
),
504+
int,
505+
],
506+
[
507+
"\n".join(
508+
[
509+
"class NotYetDefined:",
510+
" @unkown_decorator",
511+
" @property",
512+
" def my_prop(self) -> int:",
513+
" pass",
514+
"instance = NotYetDefined()",
515+
"instance.my_prop",
516+
]
517+
),
518+
int,
519+
],
493520
[
494521
"\n".join(
495522
[

0 commit comments

Comments
 (0)