mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
1 parent
c0a436ffbd
commit
48e4e7c661
37 files changed
+3044
-422
No files matched your search
@@ -10,6 +10,7 @@ badcert
|
||||
badsyntax
|
||||
baseinfo
|
||||
basetype
|
||||
binop
|
||||
boolop
|
||||
BUILDSTDLIB
|
||||
bxor
|
||||
@@ -73,6 +74,7 @@ HASPOINTER
|
||||
HASSTRUCT
|
||||
HASUNION
|
||||
heaptype
|
||||
hexdigit
|
||||
HIGHRES
|
||||
IFUNC
|
||||
IMMUTABLETYPE
|
||||
|
||||
Vendored
+214
@@ -0,0 +1,214 @@
|
||||
Module(body=[Expr(value=Constant(value='module docstring', kind=None))], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=Constant(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[arg(...)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[arg(...)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=arg(...), kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=arg(...), kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=arg(...), kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=arg(...), kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=arg(...), defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[arg(...), ..., arg(...)], vararg=arg(...), kwonlyargs=[arg(...)], kw_defaults=[Constant(...)], kwarg=arg(...), defaults=[Constant(...), ..., Dict(...)]), body=[Expr(value=Constant(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='C', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='C', bases=[], keywords=[], body=[Expr(value=Constant(...))], decorator_list=[], type_params=[])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='C', bases=[Name(id='object', ctx=Load(...))], keywords=[], body=[Pass()], decorator_list=[], type_params=[])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='C', bases=[Name(id='A', ctx=Load(...)), Name(id='B', ctx=Load(...))], keywords=[], body=[Pass()], decorator_list=[], type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Return(value=Constant(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Return(value=None)], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[Delete(targets=[Name(id='v', ctx=Del(...))])], type_ignores=[])
|
||||
Module(body=[Assign(targets=[Name(id='v', ctx=Store(...))], value=Constant(value=1, kind=None), type_comment=None)], type_ignores=[])
|
||||
Module(body=[Assign(targets=[Tuple(elts=[Name(...), Name(...)], ctx=Store(...))], value=Name(id='c', ctx=Load(...)), type_comment=None)], type_ignores=[])
|
||||
Module(body=[Assign(targets=[Tuple(elts=[Name(...), Name(...)], ctx=Store(...))], value=Name(id='c', ctx=Load(...)), type_comment=None)], type_ignores=[])
|
||||
Module(body=[Assign(targets=[List(elts=[Name(...), Name(...)], ctx=Store(...))], value=Name(id='c', ctx=Load(...)), type_comment=None)], type_ignores=[])
|
||||
Module(body=[Assign(targets=[Subscript(value=Name(...), slice=Name(...), ctx=Store(...))], value=Name(id='c', ctx=Load(...)), type_comment=None)], type_ignores=[])
|
||||
Module(body=[AnnAssign(target=Name(id='x', ctx=Store(...)), annotation=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), value=None, simple=1)], type_ignores=[])
|
||||
Module(body=[AnnAssign(target=Name(id='x', ctx=Store(...)), annotation=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), value=None, simple=1)], type_ignores=[])
|
||||
Module(body=[AnnAssign(target=Name(id='x', ctx=Store(...)), annotation=Subscript(value=Name(...), slice=Tuple(...), ctx=Load(...)), value=None, simple=1)], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Add(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Sub(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Mult(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=MatMult(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Div(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Mod(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=Pow(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=LShift(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=RShift(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=BitOr(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=BitXor(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=BitAnd(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[AugAssign(target=Name(id='v', ctx=Store(...)), op=FloorDiv(), value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[For(target=Name(id='v', ctx=Store(...)), iter=Name(id='v', ctx=Load(...)), body=[Pass()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[For(target=Name(id='v', ctx=Store(...)), iter=Name(id='v', ctx=Load(...)), body=[Pass()], orelse=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[While(test=Name(id='v', ctx=Load(...)), body=[Pass()], orelse=[])], type_ignores=[])
|
||||
Module(body=[While(test=Name(id='v', ctx=Load(...)), body=[Pass()], orelse=[Pass()])], type_ignores=[])
|
||||
Module(body=[If(test=Name(id='v', ctx=Load(...)), body=[Pass()], orelse=[])], type_ignores=[])
|
||||
Module(body=[If(test=Name(id='a', ctx=Load(...)), body=[Pass()], orelse=[If(test=Name(...), body=[Pass(...)], orelse=[])])], type_ignores=[])
|
||||
Module(body=[If(test=Name(id='a', ctx=Load(...)), body=[Pass()], orelse=[Pass()])], type_ignores=[])
|
||||
Module(body=[If(test=Name(id='a', ctx=Load(...)), body=[Pass()], orelse=[If(test=Name(...), body=[Pass(...)], orelse=[Pass(...)])])], type_ignores=[])
|
||||
Module(body=[If(test=Name(id='a', ctx=Load(...)), body=[Pass()], orelse=[If(test=Name(...), body=[Pass(...)], orelse=[If(...)])])], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=None)], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=None), withitem(context_expr=Name(...), optional_vars=None)], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=Name(...))], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=Name(...)), withitem(context_expr=Name(...), optional_vars=Name(...))], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=Name(...))], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[With(items=[withitem(context_expr=Name(...), optional_vars=None), withitem(context_expr=Name(...), optional_vars=None)], body=[Pass()], type_comment=None)], type_ignores=[])
|
||||
Module(body=[Raise(exc=None, cause=None)], type_ignores=[])
|
||||
Module(body=[Raise(exc=Call(func=Name(...), args=[Constant(...)], keywords=[]), cause=None)], type_ignores=[])
|
||||
Module(body=[Raise(exc=Name(id='Exception', ctx=Load(...)), cause=None)], type_ignores=[])
|
||||
Module(body=[Raise(exc=Call(func=Name(...), args=[Constant(...)], keywords=[]), cause=Constant(value=None, kind=None))], type_ignores=[])
|
||||
Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name=None, body=[Pass(...)])], orelse=[], finalbody=[])], type_ignores=[])
|
||||
Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[], finalbody=[])], type_ignores=[])
|
||||
Module(body=[Try(body=[Pass()], handlers=[], orelse=[], finalbody=[Pass()])], type_ignores=[])
|
||||
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name=None, body=[Pass(...)])], orelse=[], finalbody=[])], type_ignores=[])
|
||||
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[], finalbody=[])], type_ignores=[])
|
||||
Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name=None, body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
|
||||
Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
|
||||
Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[])
|
||||
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=None)], type_ignores=[])
|
||||
Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=Constant(value='message', kind=None))], type_ignores=[])
|
||||
Module(body=[Import(names=[alias(name='sys', asname=None)])], type_ignores=[])
|
||||
Module(body=[Import(names=[alias(name='foo', asname='bar')])], type_ignores=[])
|
||||
Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0)], type_ignores=[])
|
||||
Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0)], type_ignores=[])
|
||||
Module(body=[Global(names=['v'])], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=1, kind=None))], type_ignores=[])
|
||||
Module(body=[Pass()], type_ignores=[])
|
||||
Module(body=[For(target=Name(id='v', ctx=Store(...)), iter=Name(id='v', ctx=Load(...)), body=[Break()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[For(target=Name(id='v', ctx=Store(...)), iter=Name(id='v', ctx=Load(...)), body=[Continue()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[For(target=Tuple(elts=[Name(...), Name(...)], ctx=Store(...)), iter=Name(id='c', ctx=Load(...)), body=[Pass()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[For(target=Tuple(elts=[Name(...), Name(...)], ctx=Store(...)), iter=Name(id='c', ctx=Load(...)), body=[Pass()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[For(target=List(elts=[Name(...), Name(...)], ctx=Store(...)), iter=Name(id='c', ctx=Load(...)), body=[Pass()], orelse=[], type_comment=None)], type_ignores=[])
|
||||
Module(body=[Expr(value=GeneratorExp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=DictComp(key=Name(...), value=Name(...), generators=[comprehension(...), comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=DictComp(key=Name(...), value=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[AsyncFunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=Constant(...)), Expr(value=Await(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[AsyncFunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[AsyncFor(target=Name(...), iter=Name(...), body=[Expr(...)], orelse=[Expr(...)], type_comment=None)], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[AsyncFunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[AsyncWith(items=[withitem(...)], body=[Expr(...)], type_comment=None)], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[Expr(value=Dict(keys=[None, Constant(...)], values=[Dict(...), Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Set(elts=[Starred(...), Constant(...)]))], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=Yield(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=YieldFrom(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[AsyncFunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=ListComp(...))], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[Name(id='deco1', ctx=Load(...)), ..., Call(func=Name(...), args=[Constant(...)], keywords=[])], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[AsyncFunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[Name(id='deco1', ctx=Load(...)), ..., Call(func=Name(...), args=[Constant(...)], keywords=[])], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='C', bases=[], keywords=[], body=[Pass()], decorator_list=[Name(id='deco1', ctx=Load(...)), ..., Call(func=Name(...), args=[Constant(...)], keywords=[])], type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[Call(func=Name(...), args=[GeneratorExp(...)], keywords=[])], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[Attribute(value=Attribute(...), attr='c', ctx=Load(...))], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[Expr(value=NamedExpr(target=Name(...), value=Constant(...)))], type_ignores=[])
|
||||
Module(body=[If(test=NamedExpr(target=Name(...), value=Call(...)), body=[Pass()], orelse=[])], type_ignores=[])
|
||||
Module(body=[While(test=NamedExpr(target=Name(...), value=Call(...)), body=[Pass()], orelse=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...), ..., arg(...)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...), arg(...)], kw_defaults=[None, None], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...), arg(...)], kw_defaults=[None, None], kwarg=arg(...), defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...), arg(...)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[Constant(...), ..., Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...)], kw_defaults=[Constant(...)], kwarg=None, defaults=[Constant(...), Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...)], kw_defaults=[None], kwarg=None, defaults=[Constant(...), Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...)], kw_defaults=[Constant(...)], kwarg=arg(...), defaults=[Constant(...), Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[arg(...)], args=[arg(...)], vararg=None, kwonlyargs=[arg(...)], kw_defaults=[None], kwarg=arg(...), defaults=[Constant(...), Constant(...)]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[])], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[], value=Name(id='int', ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[TypeVar(name='T', bound=None, default_value=None)], value=Name(id='int', ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[TypeVar(name='T', bound=None, default_value=None), ..., ParamSpec(name='P', default_value=None)], value=Tuple(elts=[Name(...), ..., Name(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[TypeVar(name='T', bound=Name(...), default_value=None), ..., ParamSpec(name='P', default_value=None)], value=Tuple(elts=[Name(...), ..., Name(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[TypeVar(name='T', bound=Tuple(...), default_value=None), ..., ParamSpec(name='P', default_value=None)], value=Tuple(elts=[Name(...), ..., Name(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[TypeAlias(name=Name(id='X', ctx=Store(...)), type_params=[TypeVar(name='T', bound=Name(...), default_value=Constant(...)), ..., ParamSpec(name='P', default_value=Constant(...))], value=Tuple(elts=[Name(...), ..., Name(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[ClassDef(name='X', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[TypeVar(name='T', bound=None, default_value=None)])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='X', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[TypeVar(name='T', bound=None, default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='X', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[TypeVar(name='T', bound=Name(...), default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='X', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[TypeVar(name='T', bound=Tuple(...), default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[ClassDef(name='X', bases=[], keywords=[], body=[Pass()], decorator_list=[], type_params=[TypeVar(name='T', bound=Name(...), default_value=Constant(...)), ..., ParamSpec(name='P', default_value=Constant(...))])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[TypeVar(name='T', bound=None, default_value=None)])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[TypeVar(name='T', bound=None, default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[TypeVar(name='T', bound=Name(...), default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[TypeVar(name='T', bound=Tuple(...), default_value=None), ..., ParamSpec(name='P', default_value=None)])], type_ignores=[])
|
||||
Module(body=[FunctionDef(name='f', args=arguments(posonlyargs=[], args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Pass()], decorator_list=[], returns=None, type_comment=None, type_params=[TypeVar(name='T', bound=Name(...), default_value=Constant(...)), ..., ParamSpec(name='P', default_value=Constant(...))])], type_ignores=[])
|
||||
Module(body=[Match(subject=Name(id='x', ctx=Load(...)), cases=[match_case(pattern=MatchValue(...), guard=None, body=[Pass(...)])])], type_ignores=[])
|
||||
Module(body=[Match(subject=Name(id='x', ctx=Load(...)), cases=[match_case(pattern=MatchValue(...), guard=None, body=[Pass(...)]), match_case(pattern=MatchAs(...), guard=None, body=[Pass(...)])])], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=None, kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=True, kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=False, kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=BoolOp(op=And(...), values=[Name(...), Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=BoolOp(op=Or(...), values=[Name(...), Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Add(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Sub(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Mult(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Div(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=MatMult(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=FloorDiv(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Pow(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=Mod(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=RShift(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=LShift(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=BitXor(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=BitOr(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=BinOp(left=Name(...), op=BitAnd(...), right=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=UnaryOp(op=Not(...), operand=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=UnaryOp(op=UAdd(...), operand=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=UnaryOp(op=USub(...), operand=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=UnaryOp(op=Invert(...), operand=Name(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Lambda(args=arguments(...), body=Constant(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Dict(keys=[Constant(...)], values=[Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Dict(keys=[], values=[]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Set(elts=[Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Dict(keys=[Constant(...)], values=[Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=List(elts=[Constant(...), Constant(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Tuple(elts=[Constant(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Set(elts=[Constant(...), Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=ListComp(elt=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=GeneratorExp(elt=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=DictComp(key=Name(...), value=Name(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=ListComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=ListComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=ListComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=SetComp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=GeneratorExp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=GeneratorExp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=GeneratorExp(elt=Tuple(...), generators=[comprehension(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Constant(...), ops=[Lt(...), Lt(...)], comparators=[Constant(...), Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[Eq(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[LtE(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[GtE(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[NotEq(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[Is(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[IsNot(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[In(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Compare(left=Name(...), ops=[NotIn(...)], comparators=[Name(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Call(func=Name(...), args=[], keywords=[]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Call(func=Name(...), args=[Constant(...), ..., Starred(...)], keywords=[keyword(...), keyword(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Call(func=Name(...), args=[Starred(...)], keywords=[]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Call(func=Name(...), args=[GeneratorExp(...)], keywords=[]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=10, kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value=1j, kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=Constant(value='string', kind=None))], type_ignores=[])
|
||||
Module(body=[Expr(value=Attribute(value=Name(...), attr='b', ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Subscript(value=Name(...), slice=Slice(...), ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Name(id='v', ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=List(elts=[Constant(...), ..., Constant(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=List(elts=[], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Tuple(elts=[Constant(...), ..., Constant(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Tuple(elts=[Constant(...), ..., Constant(...)], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Tuple(elts=[], ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Call(func=Attribute(...), args=[Subscript(...)], keywords=[]))], type_ignores=[])
|
||||
Module(body=[Expr(value=Subscript(value=List(...), slice=Slice(...), ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Subscript(value=List(...), slice=Slice(...), ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Subscript(value=List(...), slice=Slice(...), ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=Subscript(value=List(...), slice=Slice(...), ctx=Load(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=IfExp(test=Name(...), body=Call(...), orelse=Call(...)))], type_ignores=[])
|
||||
Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=JoinedStr(values=[Constant(...), ..., Constant(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
|
||||
Module(body=[Expr(value=TemplateStr(values=[Constant(...), ..., Constant(...)]))], type_ignores=[])
|
||||
Vendored
-65
@@ -97,7 +97,6 @@ class AST_Tests(unittest.TestCase):
|
||||
# "ast.AST constructor takes 0 positional arguments"
|
||||
ast.AST(2)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "type object 'ast.AST' has no attribute '_fields'" does not match "'AST' object has no attribute '_fields'"
|
||||
def test_AST_fields_NULL_check(self):
|
||||
# See: https://github.com/python/cpython/issues/126105
|
||||
old_value = ast.AST._fields
|
||||
@@ -127,7 +126,6 @@ class AST_Tests(unittest.TestCase):
|
||||
support.gc_collect()
|
||||
self.assertIsNone(ref())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_snippets(self):
|
||||
for input, output, kind in ((exec_tests, exec_results, "exec"),
|
||||
(single_tests, single_results, "single"),
|
||||
@@ -140,7 +138,6 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.subTest(action="compiling", input=i, kind=kind):
|
||||
compile(ast_tree, "?", kind)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: expected some sort of expr, but got <_ast.TemplateStr object at 0x7e85c34e0>
|
||||
def test_ast_validation(self):
|
||||
# compile() is the only function that calls PyAST_Validate
|
||||
snippets_to_validate = exec_tests + single_tests + eval_tests
|
||||
@@ -170,7 +167,6 @@ class AST_Tests(unittest.TestCase):
|
||||
self.assertIsInstance(res.body[0].value, ast.Name)
|
||||
self.assertEqual(res.body[0].value.id, expected)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_invalid_position_information(self):
|
||||
invalid_linenos = [
|
||||
(10, 1), (-10, -11), (10, -11), (-5, -2), (-5, 1)
|
||||
@@ -226,7 +222,6 @@ class AST_Tests(unittest.TestCase):
|
||||
# This also must not crash:
|
||||
ast.parse(tree, optimize=2)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_docstring_optimization_single_node(self):
|
||||
# https://github.com/python/cpython/issues/137308
|
||||
class_example1 = textwrap.dedent('''
|
||||
@@ -293,7 +288,6 @@ class AST_Tests(unittest.TestCase):
|
||||
compile(mod, "a", "exec")
|
||||
compile(mod, "a", "exec", optimize=opt_level)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_docstring_optimization_multiple_nodes(self):
|
||||
# https://github.com/python/cpython/issues/137308
|
||||
class_example = textwrap.dedent(
|
||||
@@ -415,7 +409,6 @@ class AST_Tests(unittest.TestCase):
|
||||
self.assertIsSubclass(ast.comprehension, ast.AST)
|
||||
self.assertIsSubclass(ast.Gt, ast.AST)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; type object 'Module' has no attribute '__annotations__'
|
||||
def test_field_attr_existence(self):
|
||||
for name, item in ast.__dict__.items():
|
||||
# constructor has a different signature
|
||||
@@ -439,7 +432,6 @@ class AST_Tests(unittest.TestCase):
|
||||
kwargs[name] = self._construct_ast_class(typ)
|
||||
return cls(**kwargs)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'arguments' has no attribute '__annotations__'
|
||||
def test_arguments(self):
|
||||
x = ast.arguments()
|
||||
self.assertEqual(x._fields, ('posonlyargs', 'args', 'vararg', 'kwonlyargs',
|
||||
@@ -467,7 +459,6 @@ class AST_Tests(unittest.TestCase):
|
||||
x._fields = 666
|
||||
self.assertEqual(x._fields, 666)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
|
||||
def test_classattrs(self):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
x = ast.Constant()
|
||||
@@ -538,7 +529,6 @@ class AST_Tests(unittest.TestCase):
|
||||
x = ast.Module(body, [])
|
||||
self.assertEqual(x.body, body)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
|
||||
def test_nodeclasses(self):
|
||||
# Zero arguments constructor explicitly allowed (but deprecated)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
@@ -590,7 +580,6 @@ class AST_Tests(unittest.TestCase):
|
||||
x = ast.Sub()
|
||||
self.assertEqual(x._fields, ())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'but got expr()' not found in 'expected some sort of expr, but got <_ast.expr object at 0x7e911a9a0>'
|
||||
def test_invalid_sum(self):
|
||||
pos = dict(lineno=2, col_offset=3)
|
||||
m = ast.Module([ast.Expr(ast.expr(**pos), **pos)], [])
|
||||
@@ -670,7 +659,6 @@ class AST_Tests(unittest.TestCase):
|
||||
attr_b = tree.body[0].decorator_list[0].value
|
||||
self.assertEqual(attr_b.end_col_offset, 4)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None != 'withitem(expr context_expr, expr? optional_vars)'
|
||||
def test_ast_asdl_signature(self):
|
||||
self.assertEqual(ast.withitem.__doc__, "withitem(expr context_expr, expr? optional_vars)")
|
||||
self.assertEqual(ast.GtE.__doc__, "GtE")
|
||||
@@ -688,7 +676,6 @@ class AST_Tests(unittest.TestCase):
|
||||
ast.compare(ast.parse("x = 10;y = 20"), ast.parse("class C:pass"))
|
||||
)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Cannot add list and tuple
|
||||
def test_compare_modified_ast(self):
|
||||
# The ast API is a bit underspecified. The objects are mutable,
|
||||
# and even _fields and _attributes are mutable. The compare() does
|
||||
@@ -808,7 +795,6 @@ class AST_Tests(unittest.TestCase):
|
||||
del a2.lineno
|
||||
self.assertTrue(ast.compare(a1, a2, compare_attributes=True))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError not raised
|
||||
def test_positional_only_feature_version(self):
|
||||
ast.parse('def foo(x, /): ...', feature_version=(3, 8))
|
||||
ast.parse('def bar(x=1, /): ...', feature_version=(3, 8))
|
||||
@@ -824,20 +810,17 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.assertRaises(SyntaxError):
|
||||
ast.parse('lambda x=1, /: ...', feature_version=(3, 7))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError not raised
|
||||
def test_assignment_expression_feature_version(self):
|
||||
ast.parse('(x := 0)', feature_version=(3, 8))
|
||||
with self.assertRaises(SyntaxError):
|
||||
ast.parse('(x := 0)', feature_version=(3, 7))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
|
||||
def test_pep750_tstring(self):
|
||||
code = 't""'
|
||||
ast.parse(code, feature_version=(3, 14))
|
||||
with self.assertRaises(SyntaxError):
|
||||
ast.parse(code, feature_version=(3, 13))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
|
||||
def test_pep758_except_without_parens(self):
|
||||
code = textwrap.dedent("""
|
||||
try:
|
||||
@@ -906,7 +889,6 @@ class AST_Tests(unittest.TestCase):
|
||||
ast.parse(code, feature_version=(3, 14))
|
||||
ast.parse(code, feature_version=(3, 13))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxError not raised
|
||||
def test_pep758_except_star_without_parens(self):
|
||||
code = textwrap.dedent("""
|
||||
try:
|
||||
@@ -922,7 +904,6 @@ class AST_Tests(unittest.TestCase):
|
||||
# regression test for gh-115881
|
||||
ast.parse('with (x() if y else z()): ...', feature_version=(3, 8))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError not raised
|
||||
def test_exception_groups_feature_version(self):
|
||||
code = dedent('''
|
||||
try: ...
|
||||
@@ -932,7 +913,6 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.assertRaises(SyntaxError):
|
||||
ast.parse(code, feature_version=(3, 10))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError not raised
|
||||
def test_type_params_feature_version(self):
|
||||
samples = [
|
||||
"type X = int",
|
||||
@@ -964,7 +944,6 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
ast.parse('pass', feature_version=(4, 0))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_constant_as_name(self):
|
||||
for constant in "True", "False", "None":
|
||||
expr = ast.Expression(ast.Name(constant, ast.Load()))
|
||||
@@ -1072,7 +1051,6 @@ class AST_Tests(unittest.TestCase):
|
||||
for node, attr, source in tests:
|
||||
self.assert_none_check(node, attr, source)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; FileNotFoundError: [Errno 2] No such file or directory: '/Users/youknowone/Projects/RustPython/crates/pylib/Lib/test/test_ast/data/ast_repr.txt'
|
||||
def test_repr(self) -> None:
|
||||
snapshots = AST_REPR_DATA_FILE.read_text().split("\n")
|
||||
for test, snapshot in zip(ast_repr_get_test_cases(), snapshots, strict=True):
|
||||
@@ -1130,7 +1108,6 @@ class CopyTests(unittest.TestCase):
|
||||
|
||||
yield from do(ast.AST)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pickling(self):
|
||||
import pickle
|
||||
|
||||
@@ -1266,7 +1243,6 @@ class CopyTests(unittest.TestCase):
|
||||
self.assertIs(getattr(repl, a), new_attr)
|
||||
self.assertFalse(ast.compare(node, repl, compare_attributes=True))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: replace() does not support Name objects
|
||||
def test_replace_accept_known_class_fields(self):
|
||||
nid, ctx = object(), object()
|
||||
|
||||
@@ -1283,7 +1259,6 @@ class CopyTests(unittest.TestCase):
|
||||
self.assertIs(repl.id, new_nid)
|
||||
self.assertIs(repl.ctx, node.ctx) # no changes
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: replace() does not support Name objects
|
||||
def test_replace_accept_known_class_attributes(self):
|
||||
node = ast.parse('x').body[0].value
|
||||
self.assertEqual(node.id, 'x')
|
||||
@@ -1309,7 +1284,6 @@ class CopyTests(unittest.TestCase):
|
||||
self.assertEqual(state['ctx'], node.ctx)
|
||||
self.assertEqual(state['lineno'], lineno)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: replace() does not support MyNode objects
|
||||
def test_replace_accept_known_custom_class_fields(self):
|
||||
class MyNode(ast.AST):
|
||||
_fields = ('name', 'data')
|
||||
@@ -1341,7 +1315,6 @@ class CopyTests(unittest.TestCase):
|
||||
self.assertIs(repl.name, node.name)
|
||||
self.assertIs(repl.data, repl_data)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: replace() does not support MyNode objects
|
||||
def test_replace_accept_known_custom_class_attributes(self):
|
||||
class MyNode(ast.AST):
|
||||
x = 0
|
||||
@@ -1423,7 +1396,6 @@ class CopyTests(unittest.TestCase):
|
||||
self.assertIs(repl.id, 'y')
|
||||
self.assertIs(repl.ctx, context)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'FunctionDef' object has no attribute 'returns'
|
||||
def test_replace_accept_missing_field_with_default(self):
|
||||
node = ast.FunctionDef(name="foo", args=ast.arguments())
|
||||
self.assertIs(node.returns, None)
|
||||
@@ -1783,7 +1755,6 @@ Module(
|
||||
self.assertEqual(ast.increment_lineno(src).lineno, 2)
|
||||
self.assertIsNone(ast.increment_lineno(src).end_lineno)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; IndexError: index out of range
|
||||
def test_increment_lineno_on_module(self):
|
||||
src = ast.parse(dedent("""\
|
||||
a = 1
|
||||
@@ -2046,7 +2017,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
mod = ast.Module([stmt], [])
|
||||
self.mod(mod, msg)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_module(self):
|
||||
m = ast.Interactive([ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.mod(m, "must have Load context", "single")
|
||||
@@ -2103,7 +2073,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
return ast.FunctionDef("x", args, [ast.Pass()], [], None, None, [])
|
||||
self._check_arguments(fac, self.stmt)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: class pattern defines no positional sub-patterns (__match_args__ missing)
|
||||
def test_funcdef_pattern_matching(self):
|
||||
# gh-104799: New fields on FunctionDef should be added at the end
|
||||
def matcher(node):
|
||||
@@ -2167,7 +2136,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
ast.Name("y", ast.Store())),
|
||||
"must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_augassign(self):
|
||||
aug = ast.AugAssign(ast.Name("x", ast.Load()), ast.Add(),
|
||||
ast.Name("y", ast.Load()))
|
||||
@@ -2176,7 +2144,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
ast.Name("y", ast.Store()))
|
||||
self.stmt(aug, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_for(self):
|
||||
x = ast.Name("x", ast.Store())
|
||||
y = ast.Name("y", ast.Load())
|
||||
@@ -2190,7 +2157,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
self.stmt(ast.For(x, y, [e], []), "must have Load context")
|
||||
self.stmt(ast.For(x, y, [p], [e]), "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_while(self):
|
||||
self.stmt(ast.While(ast.Constant(3), [], []), "empty body on While")
|
||||
self.stmt(ast.While(ast.Name("x", ast.Store()), [ast.Pass()], []),
|
||||
@@ -2199,7 +2165,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
[ast.Expr(ast.Name("x", ast.Store()))]),
|
||||
"must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_if(self):
|
||||
self.stmt(ast.If(ast.Constant(3), [], []), "empty body on If")
|
||||
i = ast.If(ast.Name("x", ast.Store()), [ast.Pass()], [])
|
||||
@@ -2210,7 +2175,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
[ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(i, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: empty items on With
|
||||
def test_with(self):
|
||||
p = ast.Pass()
|
||||
self.stmt(ast.With([], [p]), "empty items on With")
|
||||
@@ -2221,7 +2185,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
i = ast.withitem(ast.Constant(3), ast.Name("x", ast.Load()))
|
||||
self.stmt(ast.With([i], [p]), "must have Store context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_raise(self):
|
||||
r = ast.Raise(None, ast.Constant(3))
|
||||
self.stmt(r, "Raise with cause but no exception")
|
||||
@@ -2230,7 +2193,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
r = ast.Raise(ast.Constant(4), ast.Name("x", ast.Store()))
|
||||
self.stmt(r, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_try(self):
|
||||
p = ast.Pass()
|
||||
t = ast.Try([], [], [], [p])
|
||||
@@ -2251,7 +2213,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
t = ast.Try([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(t, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: ValueError not raised
|
||||
def test_try_star(self):
|
||||
p = ast.Pass()
|
||||
t = ast.TryStar([], [], [], [p])
|
||||
@@ -2272,7 +2233,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
t = ast.TryStar([p], e, [p], [ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(t, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_assert(self):
|
||||
self.stmt(ast.Assert(ast.Name("x", ast.Store()), None),
|
||||
"must have Load context")
|
||||
@@ -2280,25 +2240,20 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
ast.Name("y", ast.Store()))
|
||||
self.stmt(assrt, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_import(self):
|
||||
self.stmt(ast.Import([]), "empty names on Import")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; OverflowError: Python int too large to convert to Rust u32
|
||||
def test_importfrom(self):
|
||||
imp = ast.ImportFrom(None, [ast.alias("x", None)], -42)
|
||||
self.stmt(imp, "Negative ImportFrom level")
|
||||
self.stmt(ast.ImportFrom(None, [], 0), "empty names on ImportFrom")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_global(self):
|
||||
self.stmt(ast.Global([]), "empty names on Global")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_nonlocal(self):
|
||||
self.stmt(ast.Nonlocal([]), "empty names on Nonlocal")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_expr(self):
|
||||
e = ast.Expr(ast.Name("x", ast.Store()))
|
||||
self.stmt(e, "must have Load context")
|
||||
@@ -2314,7 +2269,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
b = ast.BoolOp(ast.And(), [ast.Constant(4), ast.Name("x", ast.Store())])
|
||||
self.expr(b, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_unaryop(self):
|
||||
u = ast.UnaryOp(ast.Not(), ast.Name("x", ast.Store()))
|
||||
self.expr(u, "must have Load context")
|
||||
@@ -2328,7 +2282,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
return ast.Lambda(args, ast.Name("x", ast.Load()))
|
||||
self._check_arguments(fac, self.expr)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_ifexp(self):
|
||||
l = ast.Name("x", ast.Load())
|
||||
s = ast.Name("y", ast.Store())
|
||||
@@ -2400,7 +2353,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
return ast.DictComp(k, v, comps)
|
||||
self._check_comprehension(factory)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: 'yield' outside function
|
||||
def test_yield(self):
|
||||
self.expr(ast.Yield(ast.Name("x", ast.Store())), "must have Load")
|
||||
self.expr(ast.YieldFrom(ast.Name("x", ast.Store())), "must have Load")
|
||||
@@ -2430,12 +2382,10 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
call = ast.Call(func, args, bad_keywords)
|
||||
self.expr(call, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_attribute(self):
|
||||
attr = ast.Attribute(ast.Name("x", ast.Store()), "y", ast.Load())
|
||||
self.expr(attr, "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_subscript(self):
|
||||
sub = ast.Subscript(ast.Name("x", ast.Store()), ast.Constant(3),
|
||||
ast.Load())
|
||||
@@ -2454,7 +2404,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
sl = ast.Tuple([s], ast.Load())
|
||||
self.expr(ast.Subscript(x, sl, ast.Load()), "must have Load context")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ValueError not raised
|
||||
def test_starred(self):
|
||||
left = ast.List([ast.Starred(ast.Name("x", ast.Load()), ast.Store())],
|
||||
ast.Store())
|
||||
@@ -2474,7 +2423,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
def test_tuple(self):
|
||||
self._sequence(ast.Tuple)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@support.requires_resource('cpu')
|
||||
def test_stdlib_validates(self):
|
||||
for module in STDLIB_FILES:
|
||||
@@ -2620,7 +2568,6 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
ast.MatchMapping([], [], rest="_"),
|
||||
]
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_match_validation_pattern(self):
|
||||
name_x = ast.Name('x', ast.Load())
|
||||
for pattern in self._MATCH_PATTERNS:
|
||||
@@ -2669,7 +2616,6 @@ class ConstantTests(unittest.TestCase):
|
||||
value = self.compile_constant(const)
|
||||
self.assertIs(value, const)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_values(self):
|
||||
nested_tuple = (1,)
|
||||
nested_frozenset = frozenset({1})
|
||||
@@ -2685,7 +2631,6 @@ class ConstantTests(unittest.TestCase):
|
||||
result = self.compile_constant(value)
|
||||
self.assertEqual(result, value)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: cannot assign to literal
|
||||
def test_assign_to_constant(self):
|
||||
tree = ast.parse("x = 1")
|
||||
|
||||
@@ -3250,7 +3195,6 @@ class NodeTransformerTests(ASTTestMixin, unittest.TestCase):
|
||||
class ASTConstructorTests(unittest.TestCase):
|
||||
"""Test the autogenerated constructors for AST nodes."""
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: DeprecationWarning not triggered
|
||||
def test_FunctionDef(self):
|
||||
args = ast.arguments()
|
||||
self.assertEqual(args.args, [])
|
||||
@@ -3264,7 +3208,6 @@ class ASTConstructorTests(unittest.TestCase):
|
||||
self.assertEqual(node.name, 'foo')
|
||||
self.assertEqual(node.decorator_list, [])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not an instance of <class '_ast.Load'>
|
||||
def test_expr_context(self):
|
||||
name = ast.Name("x")
|
||||
self.assertEqual(name.id, "x")
|
||||
@@ -3311,7 +3254,6 @@ class ASTConstructorTests(unittest.TestCase):
|
||||
obj = FieldsAndTypes(a=1)
|
||||
self.assertEqual(obj.a, 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
|
||||
def test_custom_attributes(self):
|
||||
class MyAttrs(ast.AST):
|
||||
_attributes = ("a", "b")
|
||||
@@ -3528,7 +3470,6 @@ class CommandLineTests(unittest.TestCase):
|
||||
ast.main(args=flag)
|
||||
self.assertStartsWith(output.getvalue(), 'usage: ')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_exec_mode_flag(self):
|
||||
# test 'python -m ast -m/--mode exec'
|
||||
source = 'x: bool = 1 # type: ignore[assignment]'
|
||||
@@ -3547,7 +3488,6 @@ class CommandLineTests(unittest.TestCase):
|
||||
with self.subTest(flag=flag):
|
||||
self.check_output(source, expect, flag)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_single_mode_flag(self):
|
||||
# test 'python -m ast -m/--mode single'
|
||||
source = 'pass'
|
||||
@@ -3576,7 +3516,6 @@ class CommandLineTests(unittest.TestCase):
|
||||
with self.subTest(flag=flag):
|
||||
self.check_output(source, expect, flag)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_func_type_mode_flag(self):
|
||||
# test 'python -m ast -m/--mode func_type'
|
||||
source = '(int, str) -> list[int]'
|
||||
@@ -3636,7 +3575,6 @@ class CommandLineTests(unittest.TestCase):
|
||||
with self.subTest(flag=flag):
|
||||
self.check_output(source, expect, flag)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object '_ast.Module' has no attribute '_field_types'
|
||||
def test_feature_version_flag(self):
|
||||
# test 'python -m ast --feature-version 3.9/3.10'
|
||||
source = '''
|
||||
@@ -3686,7 +3624,6 @@ class CommandLineTests(unittest.TestCase):
|
||||
with self.subTest(flag=flag):
|
||||
self.check_output(source, expect, flag)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_optimize_flag(self):
|
||||
# test 'python -m ast -O/--optimize 1/2'
|
||||
source = '''
|
||||
@@ -3781,7 +3718,6 @@ class ASTOptimizationTests(unittest.TestCase):
|
||||
|
||||
self.assert_ast(code, non_optimized_target, optimized_target)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_folding_match_case_allowed_expressions(self):
|
||||
def get_match_case_values(node):
|
||||
result = []
|
||||
@@ -3843,7 +3779,6 @@ class ASTOptimizationTests(unittest.TestCase):
|
||||
values = get_match_case_values(case.pattern)
|
||||
self.assertListEqual(constants, values)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object '_ast.Module' has no attribute '_field_types'
|
||||
def test_match_case_not_folded_in_unoptimized_ast(self):
|
||||
src = textwrap.dedent("""
|
||||
match a:
|
||||
|
||||
Vendored
+4
-17
@@ -445,7 +445,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
rv = ns['f']()
|
||||
self.assertEqual(rv, tuple(expected))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_compile_top_level_await_no_coro(self):
|
||||
"""Make sure top level non-await codes get the correct coroutine flags"""
|
||||
modes = ('single', 'exec')
|
||||
@@ -552,7 +551,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
run_yielding_async_fn(lambda: eval(co, globals_))
|
||||
self.assertEqual(globals_['a'], 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_compile_top_level_await_invalid_cases(self):
|
||||
# helper function just to check we can run top=level async-for
|
||||
async def arange(n):
|
||||
@@ -593,7 +591,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
mode,
|
||||
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_compile_async_generator(self):
|
||||
"""
|
||||
With the PyCF_ALLOW_TOP_LEVEL_AWAIT flag added in 3.8, we want to
|
||||
@@ -640,7 +637,7 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
msg = r"^attribute name must be string, not 'int'$"
|
||||
self.assertRaisesRegex(TypeError, msg, delattr, sys, 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: '__repr__' unexpectedly found in ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar']
|
||||
def test_dir(self):
|
||||
# dir(wrong number of arguments)
|
||||
self.assertRaises(TypeError, dir, 42, 42)
|
||||
@@ -894,7 +891,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
exec('global z\nz = 1', locals=g)
|
||||
self.assertEqual(g, {})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_exec_globals(self):
|
||||
code = compile("print('Hello World!')", "", "exec")
|
||||
# no builtin function
|
||||
@@ -904,7 +900,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertRaises(TypeError,
|
||||
exec, code, {'__builtins__': 123})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_exec_globals_frozen(self):
|
||||
class frozendict_error(Exception):
|
||||
pass
|
||||
@@ -937,7 +932,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertRaises(frozendict_error,
|
||||
exec, code, namespace)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_exec_globals_error_on_get(self):
|
||||
# custom `globals` or `builtins` can raise errors on item access
|
||||
class setonlyerror(Exception):
|
||||
@@ -957,7 +951,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertRaises(setonlyerror, exec, code,
|
||||
{'__builtins__': setonlydict({'superglobal': 1})})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_exec_globals_dict_subclass(self):
|
||||
class customdict(dict): # this one should not do anything fancy
|
||||
pass
|
||||
@@ -969,7 +962,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertRaisesRegex(NameError, "name 'superglobal' is not defined",
|
||||
exec, code, {'__builtins__': customdict()})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; NameError: name 'superglobal' is not defined
|
||||
def test_eval_builtins_mapping(self):
|
||||
code = compile("superglobal", "test", "eval")
|
||||
# works correctly
|
||||
@@ -1009,7 +1001,7 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
finally:
|
||||
sys.stdout = savestdout
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Unexpected keyword argument closure
|
||||
def test_exec_closure(self):
|
||||
def function_without_closures():
|
||||
return 3 * 5
|
||||
@@ -1680,7 +1672,6 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertRaises(ValueError, open, 'a\x00b')
|
||||
self.assertRaises(ValueError, open, b'a\x00b')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.flags.utf8_mode, "utf-8 mode is enabled")
|
||||
def test_open_default_encoding(self):
|
||||
with EnvironmentVarGuard() as env:
|
||||
@@ -2715,7 +2706,6 @@ class PtyTests(unittest.TestCase):
|
||||
else:
|
||||
yield
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_input_tty(self):
|
||||
# Test input() functionality when wired to a tty
|
||||
self.check_input_tty("prompt", b"quux")
|
||||
@@ -2730,20 +2720,17 @@ class PtyTests(unittest.TestCase):
|
||||
# Check stdin/stdout error handler is used when invoking PyOS_Readline()
|
||||
self.check_input_tty("prompté", b"quux\xe9", "ascii")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_input_tty_null_in_prompt(self):
|
||||
self.check_input_tty("prompt\0", b"",
|
||||
expected='ValueError: input: prompt string cannot contain '
|
||||
'null characters')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_input_tty_nonencodable_prompt(self):
|
||||
self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict',
|
||||
expected="UnicodeEncodeError: 'ascii' codec can't encode "
|
||||
"character '\\xe9' in position 6: ordinal not in "
|
||||
"range(128)")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_input_tty_nondecodable_input(self):
|
||||
self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict',
|
||||
expected="UnicodeDecodeError: 'ascii' codec can't decode "
|
||||
@@ -2960,7 +2947,7 @@ class TestType(unittest.TestCase):
|
||||
A.__qualname__ = b'B'
|
||||
self.assertEqual(A.__qualname__, 'D.E')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: '__firstlineno__' unexpectedly found in mappingproxy({'__firstlineno__': 42, '__module__': 'testmodule', '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None})
|
||||
def test_type_firstlineno(self):
|
||||
A = type('A', (), {'__firstlineno__': 42})
|
||||
self.assertEqual(A.__name__, 'A')
|
||||
@@ -2972,7 +2959,7 @@ class TestType(unittest.TestCase):
|
||||
A.__firstlineno__ = 43
|
||||
self.assertEqual(A.__dict__['__firstlineno__'], 43)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Expected type 'tuple' but 'str' found.
|
||||
def test_type_typeparams(self):
|
||||
class A[T]:
|
||||
pass
|
||||
|
||||
Vendored
-2
@@ -76,8 +76,6 @@ class FunctionPropertiesTest(FuncAttrsTest):
|
||||
self.cannot_set_attr(self.b, '__globals__', 2,
|
||||
(AttributeError, TypeError))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test___builtins__(self):
|
||||
self.assertIs(self.b.__builtins__, __builtins__)
|
||||
self.cannot_set_attr(self.b, '__builtins__', 2,
|
||||
|
||||
Vendored
+142
-134
@@ -82,8 +82,7 @@ class TracebackCases(unittest.TestCase):
|
||||
def tokenizer_error_with_caret_range(self):
|
||||
compile("blech ( ", "?", "exec")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 11 != 14
|
||||
def test_caret(self):
|
||||
err = self.get_exception_format(self.syntax_error_with_caret,
|
||||
SyntaxError)
|
||||
@@ -196,8 +195,7 @@ class TracebackCases(unittest.TestCase):
|
||||
finally:
|
||||
unlink(TESTFN)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 3 != 4
|
||||
def test_bad_indentation(self):
|
||||
err = self.get_exception_format(self.syntax_error_bad_indentation,
|
||||
IndentationError)
|
||||
@@ -477,8 +475,7 @@ class TracebackCases(unittest.TestCase):
|
||||
# Issue #18960: coding spec should have no effect
|
||||
do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + b'ZeroDivisionError: division by zero']
|
||||
def test_print_traceback_at_exit(self):
|
||||
# Issue #22599: Ensure that it is possible to use the traceback module
|
||||
# to display an exception at Python exit
|
||||
@@ -619,8 +616,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
"""
|
||||
Tests for printing code error expressions as part of PEP 657
|
||||
"""
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_basic_caret(self):
|
||||
# NOTE: In caret tests, "if True:" is used as a way to force indicator
|
||||
# display, since the raising expression spans only part of the line.
|
||||
@@ -640,8 +635,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_line_with_unicode(self):
|
||||
# Make sure that even if a line contains multi-byte unicode characters
|
||||
# the correct carets are printed.
|
||||
@@ -661,8 +654,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_unicode)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_in_type_annotation(self):
|
||||
def f_with_type():
|
||||
def foo(a: THIS_DOES_NOT_EXIST ) -> int:
|
||||
@@ -681,8 +672,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_type)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_multiline_expression(self):
|
||||
# Make sure no carets are printed for expressions spanning multiple
|
||||
# lines.
|
||||
@@ -708,8 +697,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_multiline)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_multiline_expression_syntax_error(self):
|
||||
# Make sure an expression spanning multiple lines that has
|
||||
# a syntax error is correctly marked with carets.
|
||||
@@ -774,8 +761,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_multiline)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_multiline_expression_bin_op(self):
|
||||
# Make sure no carets are printed for expressions spanning multiple
|
||||
# lines.
|
||||
@@ -800,8 +785,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_multiline)
|
||||
self.assertEqual(result_lines, expected_f.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators(self):
|
||||
def f_with_binary_operator():
|
||||
divisor = 20
|
||||
@@ -820,8 +803,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_with_unicode(self):
|
||||
def f_with_binary_operator():
|
||||
áóí = 20
|
||||
@@ -840,8 +821,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_two_char(self):
|
||||
def f_with_binary_operator():
|
||||
divisor = 20
|
||||
@@ -860,8 +839,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_with_spaces_and_parenthesis(self):
|
||||
def f_with_binary_operator():
|
||||
a = 1
|
||||
@@ -881,8 +858,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_multiline(self):
|
||||
def f_with_binary_operator():
|
||||
b = 1
|
||||
@@ -909,8 +884,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_multiline_two_char(self):
|
||||
def f_with_binary_operator():
|
||||
b = 1
|
||||
@@ -948,8 +921,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_binary_operators_multiline_with_unicode(self):
|
||||
def f_with_binary_operator():
|
||||
b = 1
|
||||
@@ -972,8 +943,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_subscript(self):
|
||||
def f_with_subscript():
|
||||
some_dict = {'x': {'y': None}}
|
||||
@@ -992,8 +961,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_subscript)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_subscript_unicode(self):
|
||||
def f_with_subscript():
|
||||
some_dict = {'ó': {'á': {'í': {'theta': 1}}}}
|
||||
@@ -1012,8 +979,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_subscript)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_subscript_with_spaces_and_parenthesis(self):
|
||||
def f_with_binary_operator():
|
||||
a = []
|
||||
@@ -1033,8 +998,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_subscript_multiline(self):
|
||||
def f_with_subscript():
|
||||
bbbbb = {}
|
||||
@@ -1071,8 +1034,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_subscript)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_call(self):
|
||||
def f_with_call():
|
||||
def f1(a):
|
||||
@@ -1096,8 +1057,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_call)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_call_unicode(self):
|
||||
def f_with_call():
|
||||
def f1(a):
|
||||
@@ -1121,8 +1080,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_call)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_call_with_spaces_and_parenthesis(self):
|
||||
def f_with_binary_operator():
|
||||
def f(a):
|
||||
@@ -1144,8 +1101,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_for_call_multiline(self):
|
||||
def f_with_call():
|
||||
class C:
|
||||
@@ -1179,8 +1134,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_call)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_many_lines(self):
|
||||
def f():
|
||||
x = 1
|
||||
@@ -1205,8 +1158,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_many_lines_no_caret(self):
|
||||
def f():
|
||||
x = 1
|
||||
@@ -1229,8 +1180,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_many_lines_binary_op(self):
|
||||
def f_with_binary_operator():
|
||||
b = 1
|
||||
@@ -1269,8 +1218,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_binary_operator)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_traceback_specialization_with_syntax_error(self):
|
||||
bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec")
|
||||
|
||||
@@ -1294,8 +1241,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_traceback_very_long_line(self):
|
||||
source = "if True: " + "a" * 256
|
||||
bytecode = compile(source, TESTFN, "exec")
|
||||
@@ -1319,8 +1264,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_secondary_caret_not_elided(self):
|
||||
# Always show a line's indicators if they include the secondary character.
|
||||
def f_with_subscript():
|
||||
@@ -1340,8 +1283,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
result_lines = self.get_exception(f_with_subscript)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_caret_exception_group(self):
|
||||
# Notably, this covers whether indicators handle margin strings correctly.
|
||||
# (Exception groups use margin strings to display vertical indicators.)
|
||||
@@ -1372,8 +1313,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
specialization_line = result_lines[-1]
|
||||
self.assertEqual(specialization_line.lstrip(), expected_specialization)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_specialization_variations(self):
|
||||
self.assertSpecialized(lambda: 1/0,
|
||||
"~^~")
|
||||
@@ -1406,8 +1345,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
self.assertSpecialized(lambda: 1// 0,
|
||||
"~^^~~")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_decorator_application_lineno_correct(self):
|
||||
def dec_error(func):
|
||||
raise TypeError
|
||||
@@ -1452,8 +1389,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
)
|
||||
self.assertEqual(result_lines, expected_error.splitlines())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_multiline_method_call_a(self):
|
||||
def f():
|
||||
(None
|
||||
@@ -1471,8 +1406,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_multiline_method_call_b(self):
|
||||
def f():
|
||||
(None.
|
||||
@@ -1489,8 +1422,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_multiline_method_call_c(self):
|
||||
def f():
|
||||
(None
|
||||
@@ -1508,8 +1439,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_wide_characters_unicode_with_problematic_byte_offset(self):
|
||||
def f():
|
||||
width
|
||||
@@ -1526,8 +1455,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_byte_offset_with_wide_characters_middle(self):
|
||||
def f():
|
||||
width = 1
|
||||
@@ -1544,8 +1471,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_byte_offset_multiline(self):
|
||||
def f():
|
||||
www = 1
|
||||
@@ -1568,8 +1493,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_byte_offset_with_wide_characters_term_highlight(self):
|
||||
def f():
|
||||
说明说明 = 1
|
||||
@@ -1588,8 +1511,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_byte_offset_with_emojis_term_highlight(self):
|
||||
def f():
|
||||
return "✨🐍" + func_说明说明("📗🚛",
|
||||
@@ -1607,8 +1528,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_byte_offset_wide_chars_subscript(self):
|
||||
def f():
|
||||
my_dct = {
|
||||
@@ -1632,8 +1551,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_memory_error(self):
|
||||
def f():
|
||||
raise MemoryError()
|
||||
@@ -1647,8 +1564,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
' raise MemoryError()']
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_anchors_for_simple_return_statements_are_elided(self):
|
||||
def g():
|
||||
1/0
|
||||
@@ -1738,8 +1653,6 @@ class TracebackErrorLocationCaretTestBase:
|
||||
]
|
||||
self.assertEqual(result_lines, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_anchors_for_simple_assign_statements_are_elided(self):
|
||||
def g():
|
||||
1/0
|
||||
@@ -1842,6 +1755,118 @@ class PurePythonTracebackErrorCaretTests(
|
||||
traceback printing in traceback.py.
|
||||
"""
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~^^~~']
|
||||
def test_caret_for_binary_operators_two_char(self):
|
||||
return super().test_caret_for_binary_operators_two_char()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~^~~']
|
||||
def test_caret_for_binary_operators(self):
|
||||
return super().test_caret_for_binary_operators()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~^^^^^^^^^']
|
||||
def test_caret_for_subscript_with_spaces_and_parenthesis(self):
|
||||
return super().test_caret_for_subscript_with_spaces_and_parenthesis()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~^~~~~~~~~~~~']
|
||||
def test_byte_offset_with_wide_characters_term_highlight(self):
|
||||
return super().test_byte_offset_with_wide_characters_term_highlight()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~~^~']
|
||||
def test_caret_for_binary_operators_with_spaces_and_parenthesis(self):
|
||||
return super().test_caret_for_binary_operators_with_spaces_and_parenthesis()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~~~~~~~~~~~^^^^^']
|
||||
def test_caret_for_subscript(self):
|
||||
return super().test_caret_for_subscript()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^']
|
||||
def test_byte_offset_wide_chars_subscript(self):
|
||||
return super().test_byte_offset_wide_chars_subscript()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^']
|
||||
def test_caret_for_subscript_unicode(self):
|
||||
return super().test_caret_for_subscript_unicode()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~']
|
||||
def test_caret_for_binary_operators_multiline(self):
|
||||
return super().test_caret_for_binary_operators_multiline()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~^~~']
|
||||
def test_caret_for_binary_operators_multiline_with_unicode(self):
|
||||
return super().test_caret_for_binary_operators_multiline_with_unicode()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ^^^^^']
|
||||
def test_traceback_specialization_with_syntax_error(self):
|
||||
return super().test_traceback_specialization_with_syntax_error()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ^^^^^^^^^^^^^']
|
||||
def test_caret_multiline_expression_syntax_error(self):
|
||||
return super().test_caret_multiline_expression_syntax_error()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~']
|
||||
def test_caret_multiline_expression_bin_op(self):
|
||||
return super().test_caret_multiline_expression_bin_op()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ' ~~~~~~~~~~~~~~~~~~~^^^^^']
|
||||
def test_secondary_caret_not_elided(self):
|
||||
return super().test_secondary_caret_not_elided()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + ~^~
|
||||
def test_specialization_variations(self):
|
||||
return super().test_specialization_variations()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; - ' ^^^^']
|
||||
def test_multiline_method_call_b(self):
|
||||
return super().test_multiline_method_call_b()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^^^^ ++
|
||||
def test_caret_for_binary_operators_with_unicode(self):
|
||||
return super().test_caret_for_binary_operators_with_unicode()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ++
|
||||
def test_multiline_method_call_a(self):
|
||||
return super().test_multiline_method_call_a()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? +++
|
||||
def test_multiline_method_call_c(self):
|
||||
return super().test_multiline_method_call_c()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^
|
||||
def test_many_lines(self):
|
||||
return super().test_many_lines()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^
|
||||
def test_many_lines_no_caret(self):
|
||||
return super().test_many_lines_no_caret()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^ +
|
||||
def test_anchors_for_simple_assign_statements_are_elided(self):
|
||||
return super().test_anchors_for_simple_assign_statements_are_elided()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^ +
|
||||
def test_anchors_for_simple_return_statements_are_elided(self):
|
||||
return super().test_anchors_for_simple_return_statements_are_elided()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: No exception thrown.
|
||||
def test_caret_in_type_annotation(self):
|
||||
return super().test_caret_in_type_annotation()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 652 characters long. Set self.maxDiff to None to see it.
|
||||
def test_decorator_application_lineno_correct(self):
|
||||
return super().test_decorator_application_lineno_correct()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 684 characters long. Set self.maxDiff to None to see it.
|
||||
def test_many_lines_binary_op(self):
|
||||
return super().test_many_lines_binary_op()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 726 characters long. Set self.maxDiff to None to see it.
|
||||
def test_caret_for_binary_operators_multiline_two_char(self):
|
||||
return super().test_caret_for_binary_operators_multiline_two_char()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 732 characters long. Set self.maxDiff to None to see it.
|
||||
def test_caret_for_subscript_multiline(self):
|
||||
return super().test_caret_for_subscript_multiline()
|
||||
|
||||
|
||||
@cpython_only
|
||||
# @requires_debug_ranges() # XXX: RUSTPYTHON patch
|
||||
@@ -2163,8 +2188,6 @@ class TracebackFormatMixin:
|
||||
actual = stderr_g.getvalue().splitlines()
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
# @requires_debug_ranges() # XXX: RUSTPYTHON patch
|
||||
def test_recursive_traceback(self):
|
||||
if self.DEBUG_RANGES:
|
||||
@@ -2653,8 +2676,6 @@ class BaseExceptionReportingTests:
|
||||
|
||||
# #### Exception Groups ####
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_basic(self):
|
||||
def exc():
|
||||
raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
|
||||
@@ -2676,8 +2697,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_cause(self):
|
||||
def exc():
|
||||
EG = ExceptionGroup
|
||||
@@ -2714,8 +2733,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_context_with_context(self):
|
||||
def exc():
|
||||
EG = ExceptionGroup
|
||||
@@ -2763,8 +2780,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_nested(self):
|
||||
def exc():
|
||||
EG = ExceptionGroup
|
||||
@@ -2941,8 +2956,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_with_notes(self):
|
||||
def exc():
|
||||
try:
|
||||
@@ -2993,8 +3006,6 @@ class BaseExceptionReportingTests:
|
||||
report = self.get_report(exc)
|
||||
self.assertEqual(report, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_group_with_multiple_notes(self):
|
||||
def exc():
|
||||
try:
|
||||
@@ -3172,8 +3183,7 @@ class LimitTests(unittest.TestCase):
|
||||
def last_returns_frame5(self):
|
||||
return self.last_returns_frame4()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 1 not greater than 5
|
||||
def test_extract_stack(self):
|
||||
frame = self.last_returns_frame5()
|
||||
def extract(**kwargs):
|
||||
@@ -3264,8 +3274,7 @@ class MiscTracebackCases(unittest.TestCase):
|
||||
# Check non-printing functions in traceback module
|
||||
#
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 1 != 0
|
||||
def test_clear(self):
|
||||
def outer():
|
||||
middle()
|
||||
@@ -3481,8 +3490,7 @@ class TestStack(unittest.TestCase):
|
||||
f' File "{__file__}", line {lno}, in f\n 1/0\n'
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Actual: _should_show_carets(13, 14, ['# this line will be used during rendering'], None)
|
||||
def test_summary_should_show_carets(self):
|
||||
# See: https://github.com/python/cpython/issues/122353
|
||||
|
||||
@@ -3639,8 +3647,7 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 11 not greater than 1000
|
||||
def test_long_context_chain(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -3864,8 +3871,7 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(list(exc.format()), ["Exception: haven\n"])
|
||||
|
||||
# @requires_debug_ranges() # XXX: RUSTPYTHON patch
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ? ^ +
|
||||
def test_print(self):
|
||||
def f():
|
||||
x = 12
|
||||
@@ -3968,8 +3974,7 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
||||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 2265 characters long. Set self.maxDiff to None to see it.
|
||||
def test_exception_group_format(self):
|
||||
teg = traceback.TracebackException.from_exception(self.eg)
|
||||
|
||||
@@ -4314,8 +4319,6 @@ class SuggestionFormattingTestBase:
|
||||
)
|
||||
self.assertNotIn("?", result_lines[-1])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_attribute_error_inside_nested_getattr(self):
|
||||
class A:
|
||||
bluch = 1
|
||||
@@ -4359,8 +4362,6 @@ class SuggestionFormattingTestBase:
|
||||
)
|
||||
return result_lines[0]
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_import_from_suggestions(self):
|
||||
substitution = textwrap.dedent("""\
|
||||
noise = more_noise = a = bc = None
|
||||
@@ -4411,8 +4412,6 @@ class SuggestionFormattingTestBase:
|
||||
actual = self.get_import_from_suggestion(code, 'bluch')
|
||||
self.assertIn(suggestion, actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_import_from_suggestions_underscored(self):
|
||||
code = "bluch = None"
|
||||
self.assertIn("'bluch'", self.get_import_from_suggestion(code, 'blach'))
|
||||
@@ -4424,8 +4423,6 @@ class SuggestionFormattingTestBase:
|
||||
self.assertIn("'_bluch'", self.get_import_from_suggestion(code, '_luch'))
|
||||
self.assertNotIn("'_bluch'", self.get_import_from_suggestion(code, 'bluch'))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_import_from_suggestions_non_string(self):
|
||||
modWithNonStringAttr = textwrap.dedent("""\
|
||||
globals()[0] = 1
|
||||
@@ -4759,6 +4756,22 @@ class PurePythonSuggestionFormattingTests(
|
||||
traceback printing in traceback.py.
|
||||
"""
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "'bluch'" not found in "ImportError: cannot import name 'blach'"
|
||||
def test_import_from_suggestions_underscored(self):
|
||||
return super().test_import_from_suggestions_underscored()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "'bluch'" not found in "ImportError: cannot import name 'blech'"
|
||||
def test_import_from_suggestions_non_string(self):
|
||||
return super().test_import_from_suggestions_non_string()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "'bluchin'?" not found in "ImportError: cannot import name 'bluch'"
|
||||
def test_import_from_suggestions(self):
|
||||
return super().test_import_from_suggestions()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'Did you mean' not found in "AttributeError: 'A' object has no attribute 'blich'"
|
||||
def test_attribute_error_inside_nested_getattr(self):
|
||||
return super().test_attribute_error_inside_nested_getattr()
|
||||
|
||||
|
||||
@cpython_only
|
||||
class CPythonSuggestionFormattingTests(
|
||||
@@ -4813,8 +4826,7 @@ class MiscTest(unittest.TestCase):
|
||||
CHECK("AttributeError", "AttributeErrorTests", 10)
|
||||
CHECK("ABA", "AAB", 4)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: crates/pylib/Lib/test/levenshtein_examples.json is missing. Run `make regen-test-levenshtein`
|
||||
@support.requires_resource('cpu')
|
||||
def test_levenshtein_distance_short_circuit(self):
|
||||
if not LEVENSHTEIN_DATA_FILE.is_file():
|
||||
@@ -4871,8 +4883,7 @@ class MiscTest(unittest.TestCase):
|
||||
|
||||
|
||||
class TestColorizedTraceback(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "y = \x1b[31mx['a']['b']\x1b[0m\x1b[1;31m['c']\x1b[0m" not found in 'Traceback (most recent call last):\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4764\x1b[0m, in \x1b[35mtest_colorized_traceback\x1b[0m\n \x1b[31mbar\x1b[0m\x1b[1;31m()\x1b[0m\n \x1b[31m~~~\x1b[0m\x1b[1;31m^^\x1b[0m\n bar = <function TestColorizedTraceback.test_colorized_traceback.<locals>.bar at 0xb57b09180>\n baz1 = <function TestColorizedTraceback.test_colorized_traceback.<locals>.baz1 at 0xb57b09e00>\n baz2 = <function TestColorizedTraceback.test_colorized_traceback.<locals>.baz2 at 0xb57b09cc0>\n e = TypeError("\'NoneType\' object is not subscriptable")\n foo = <function TestColorizedTraceback.test_colorized_traceback.<locals>.foo at 0xb57b08140>\n self = <test.test_traceback.TestColorizedTraceback testMethod=test_colorized_traceback>\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4760\x1b[0m, in \x1b[35mbar\x1b[0m\n return baz1(1,\n 2,3\n ,4)\n baz1 = <function TestColorizedTraceback.test_colorized_traceback.<locals>.baz1 at 0xb57b09e00>\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4757\x1b[0m, in \x1b[35mbaz1\x1b[0m\n return baz2(1,2,3,4)\n args = (1, 2, 3, 4)\n baz2 = <function TestColorizedTraceback.test_colorized_traceback.<locals>.baz2 at 0xb57b09cc0>\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4754\x1b[0m, in \x1b[35mbaz2\x1b[0m\n return \x1b[31m(lambda *args: foo(*args))\x1b[0m\x1b[1;31m(1,2,3,4)\x1b[0m\n \x1b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~\x1b[0m\x1b[1;31m^^^^^^^^^\x1b[0m\n args = (1, 2, 3, 4)\n foo = <function TestColorizedTraceback.test_colorized_traceback.<locals>.foo at 0xb57b08140>\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4754\x1b[0m, in \x1b[35m<lambda>\x1b[0m\n return (lambda *args: \x1b[31mfoo\x1b[0m\x1b[1;31m(*args)\x1b[0m)(1,2,3,4)\n \x1b[31m~~~\x1b[0m\x1b[1;31m^^^^^^^\x1b[0m\n args = (1, 2, 3, 4)\n foo = <function TestColorizedTraceback.test_colorized_traceback.<locals>.foo at 0xb57b08140>\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4751\x1b[0m, in \x1b[35mfoo\x1b[0m\n y = x[\'a\'][\'b\'][\x1b[1;31m\'c\'\x1b[0m]\n \x1b[1;31m^^^\x1b[0m\n args = (1, 2, 3, 4)\n x = {\'a\': {\'b\': None}}\n\x1b[1;35mTypeError\x1b[0m: \x1b[35m\'NoneType\' object is not subscriptable\x1b[0m\n'
|
||||
def test_colorized_traceback(self):
|
||||
def foo(*args):
|
||||
x = {'a':{'b': None}}
|
||||
@@ -4905,8 +4916,7 @@ class TestColorizedTraceback(unittest.TestCase):
|
||||
self.assertIn("return baz1(1,\n 2,3\n ,4)", lines)
|
||||
self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: ' File \x1b[35m"<string>"\x1b[0m, line \x1b[35m1\x1b[0m\n a \x1b[1;31m$\x1b[0m b\n \x1b[1;31m^\x1b[0m\n\x1b[1;35mSyntaxError\x1b[0m: \x1b[35minvalid syntax\x1b[0m\n' not found in 'Traceback (most recent call last):\n File \x1b[35m"crates/pylib/Lib/test/test_traceback.py"\x1b[0m, line \x1b[35m4782\x1b[0m, in \x1b[35mtest_colorized_syntax_error\x1b[0m\n \x1b[31mcompile\x1b[0m\x1b[1;31m("a $ b", "<string>", "exec")\x1b[0m\n \x1b[31m~~~~~~~\x1b[0m\x1b[1;31m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\x1b[0m\n e = SyntaxError(\'got unexpected token $\')\n self = <test.test_traceback.TestColorizedTraceback testMethod=test_colorized_syntax_error>\n File \x1b[35m"<string>"\x1b[0m, line \x1b[35m1\x1b[0m\n a \x1b[1;31m$\x1b[0m b\n \x1b[1;31m^\x1b[0m\n\x1b[1;35mSyntaxError\x1b[0m: \x1b[35mgot unexpected token $\x1b[0m\n'
|
||||
def test_colorized_syntax_error(self):
|
||||
try:
|
||||
compile("a $ b", "<string>", "exec")
|
||||
@@ -4928,8 +4938,7 @@ class TestColorizedTraceback(unittest.TestCase):
|
||||
)
|
||||
self.assertIn(expected, actual)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; ModuleNotFoundError: No module named '_testcapi'
|
||||
def test_colorized_traceback_is_the_default(self):
|
||||
def foo():
|
||||
1/0
|
||||
@@ -4962,8 +4971,7 @@ class TestColorizedTraceback(unittest.TestCase):
|
||||
f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}']
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; Diff is 1795 characters long. Set self.maxDiff to None to see it.
|
||||
def test_colorized_traceback_from_exception_group(self):
|
||||
def foo():
|
||||
exceptions = []
|
||||
|
||||
Vendored
-6
@@ -202,7 +202,6 @@ class UnparseTestCase(ASTTestCase):
|
||||
self.check_ast_roundtrip('f" something { my_dict["key"] } something else "')
|
||||
self.check_ast_roundtrip('f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_tstrings(self):
|
||||
self.check_ast_roundtrip("t'foo'")
|
||||
self.check_ast_roundtrip("t'foo {bar}'")
|
||||
@@ -527,7 +526,6 @@ class UnparseTestCase(ASTTestCase):
|
||||
locs(ast.Module([ast.Expr(ast.Constant(value=(1, 2, 3)))])), "(1, 2, 3)"
|
||||
)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_function_type(self):
|
||||
for function_type in (
|
||||
"() -> int",
|
||||
@@ -567,7 +565,6 @@ class UnparseTestCase(ASTTestCase):
|
||||
):
|
||||
self.check_ast_roundtrip(statement, type_comments=True)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'TypeVar' object has no attribute 'default_value'
|
||||
def test_unparse_interactive_semicolons(self):
|
||||
# gh-129598: Fix ast.unparse() when ast.Interactive contains multiple statements
|
||||
self.check_src_roundtrip("i = 1; 'expr'; raise Exception", mode='single')
|
||||
@@ -853,7 +850,6 @@ class CosmeticTestCase(ASTTestCase):
|
||||
self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g")
|
||||
self.check_src_roundtrip("a, b = [c, d] = e, f = g")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_multiquote_joined_string(self):
|
||||
self.check_ast_roundtrip("f\"'''{1}\\\"\\\"\\\"\" ")
|
||||
self.check_ast_roundtrip("""f"'''{1}""\\"" """)
|
||||
@@ -868,7 +864,6 @@ class CosmeticTestCase(ASTTestCase):
|
||||
self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """)
|
||||
self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" '''\\n'''}''' """)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: SyntaxWarning not triggered
|
||||
def test_backslash_in_format_spec(self):
|
||||
import re
|
||||
msg = re.escape('"\\ " is an invalid escape sequence. '
|
||||
@@ -1054,7 +1049,6 @@ class DirectoryTestCase(ASTTestCase):
|
||||
|
||||
return items
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_files(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', SyntaxWarning)
|
||||
|
||||
+10
-2
@@ -49,7 +49,7 @@ class DocEntry(typing.NamedTuple):
|
||||
def doc(self) -> str:
|
||||
assert self.raw_doc is not None
|
||||
|
||||
return re.sub(UNICODE_ESCAPE, r"\\u{\1}", inspect.cleandoc(self.raw_doc))
|
||||
return re.sub(UNICODE_ESCAPE, r"\\u{\1}", self.raw_doc.strip())
|
||||
|
||||
|
||||
def is_c_extension(module: types.ModuleType) -> bool:
|
||||
@@ -90,7 +90,15 @@ def is_child_of(obj: typing.Any, module: types.ModuleType) -> bool:
|
||||
-------
|
||||
bool
|
||||
"""
|
||||
return inspect.getmodule(obj) is module
|
||||
if inspect.getmodule(obj) is module:
|
||||
return True
|
||||
# Some C modules (e.g. _ast) set __module__ to a different name (e.g. "ast"),
|
||||
# causing inspect.getmodule() to return a different module object.
|
||||
# Fall back to checking the module's namespace directly.
|
||||
obj_name = getattr(obj, "__name__", None)
|
||||
if obj_name is not None:
|
||||
return module.__dict__.get(obj_name) is obj
|
||||
return False
|
||||
|
||||
|
||||
def iter_modules() -> "Iterable[types.ModuleType]":
|
||||
|
||||
@@ -12,6 +12,131 @@ pub static DB: phf::Map<&'static str, &'static str> = phf::phf_map! {
|
||||
"_abc._reset_caches" => "Internal ABC helper to reset both caches of a given class.\n\nShould be only used by refleak.py",
|
||||
"_abc._reset_registry" => "Internal ABC helper to reset registry of a given class.\n\nShould be only used by refleak.py",
|
||||
"_abc.get_cache_token" => "Returns the current ABC cache token.\n\nThe token is an opaque object (supporting equality testing) identifying the\ncurrent version of the ABC cache for virtual subclasses. The token changes\nwith every call to register() on any ABC.",
|
||||
"_ast.Add" => "Add",
|
||||
"_ast.And" => "And",
|
||||
"_ast.AnnAssign" => "AnnAssign(expr target, expr annotation, expr? value, int simple)",
|
||||
"_ast.Assert" => "Assert(expr test, expr? msg)",
|
||||
"_ast.Assign" => "Assign(expr* targets, expr value, string? type_comment)",
|
||||
"_ast.AsyncFor" => "AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)",
|
||||
"_ast.AsyncFunctionDef" => "AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)",
|
||||
"_ast.AsyncWith" => "AsyncWith(withitem* items, stmt* body, string? type_comment)",
|
||||
"_ast.Attribute" => "Attribute(expr value, identifier attr, expr_context ctx)",
|
||||
"_ast.AugAssign" => "AugAssign(expr target, operator op, expr value)",
|
||||
"_ast.Await" => "Await(expr value)",
|
||||
"_ast.BinOp" => "BinOp(expr left, operator op, expr right)",
|
||||
"_ast.BitAnd" => "BitAnd",
|
||||
"_ast.BitOr" => "BitOr",
|
||||
"_ast.BitXor" => "BitXor",
|
||||
"_ast.BoolOp" => "BoolOp(boolop op, expr* values)",
|
||||
"_ast.Break" => "Break",
|
||||
"_ast.Call" => "Call(expr func, expr* args, keyword* keywords)",
|
||||
"_ast.ClassDef" => "ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, expr* decorator_list, type_param* type_params)",
|
||||
"_ast.Compare" => "Compare(expr left, cmpop* ops, expr* comparators)",
|
||||
"_ast.Constant" => "Constant(constant value, string? kind)",
|
||||
"_ast.Continue" => "Continue",
|
||||
"_ast.Del" => "Del",
|
||||
"_ast.Delete" => "Delete(expr* targets)",
|
||||
"_ast.Dict" => "Dict(expr?* keys, expr* values)",
|
||||
"_ast.DictComp" => "DictComp(expr key, expr value, comprehension* generators)",
|
||||
"_ast.Div" => "Div",
|
||||
"_ast.Eq" => "Eq",
|
||||
"_ast.ExceptHandler" => "ExceptHandler(expr? type, identifier? name, stmt* body)",
|
||||
"_ast.Expr" => "Expr(expr value)",
|
||||
"_ast.Expression" => "Expression(expr body)",
|
||||
"_ast.FloorDiv" => "FloorDiv",
|
||||
"_ast.For" => "For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)",
|
||||
"_ast.FormattedValue" => "FormattedValue(expr value, int conversion, expr? format_spec)",
|
||||
"_ast.FunctionDef" => "FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)",
|
||||
"_ast.FunctionType" => "FunctionType(expr* argtypes, expr returns)",
|
||||
"_ast.GeneratorExp" => "GeneratorExp(expr elt, comprehension* generators)",
|
||||
"_ast.Global" => "Global(identifier* names)",
|
||||
"_ast.Gt" => "Gt",
|
||||
"_ast.GtE" => "GtE",
|
||||
"_ast.If" => "If(expr test, stmt* body, stmt* orelse)",
|
||||
"_ast.IfExp" => "IfExp(expr test, expr body, expr orelse)",
|
||||
"_ast.Import" => "Import(alias* names)",
|
||||
"_ast.ImportFrom" => "ImportFrom(identifier? module, alias* names, int? level)",
|
||||
"_ast.In" => "In",
|
||||
"_ast.Interactive" => "Interactive(stmt* body)",
|
||||
"_ast.Interpolation" => "Interpolation(expr value, constant str, int conversion, expr? format_spec)",
|
||||
"_ast.Invert" => "Invert",
|
||||
"_ast.Is" => "Is",
|
||||
"_ast.IsNot" => "IsNot",
|
||||
"_ast.JoinedStr" => "JoinedStr(expr* values)",
|
||||
"_ast.LShift" => "LShift",
|
||||
"_ast.Lambda" => "Lambda(arguments args, expr body)",
|
||||
"_ast.List" => "List(expr* elts, expr_context ctx)",
|
||||
"_ast.ListComp" => "ListComp(expr elt, comprehension* generators)",
|
||||
"_ast.Load" => "Load",
|
||||
"_ast.Lt" => "Lt",
|
||||
"_ast.LtE" => "LtE",
|
||||
"_ast.MatMult" => "MatMult",
|
||||
"_ast.Match" => "Match(expr subject, match_case* cases)",
|
||||
"_ast.MatchAs" => "MatchAs(pattern? pattern, identifier? name)",
|
||||
"_ast.MatchClass" => "MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)",
|
||||
"_ast.MatchMapping" => "MatchMapping(expr* keys, pattern* patterns, identifier? rest)",
|
||||
"_ast.MatchOr" => "MatchOr(pattern* patterns)",
|
||||
"_ast.MatchSequence" => "MatchSequence(pattern* patterns)",
|
||||
"_ast.MatchSingleton" => "MatchSingleton(constant value)",
|
||||
"_ast.MatchStar" => "MatchStar(identifier? name)",
|
||||
"_ast.MatchValue" => "MatchValue(expr value)",
|
||||
"_ast.Mod" => "Mod",
|
||||
"_ast.Module" => "Module(stmt* body, type_ignore* type_ignores)",
|
||||
"_ast.Mult" => "Mult",
|
||||
"_ast.Name" => "Name(identifier id, expr_context ctx)",
|
||||
"_ast.NamedExpr" => "NamedExpr(expr target, expr value)",
|
||||
"_ast.Nonlocal" => "Nonlocal(identifier* names)",
|
||||
"_ast.Not" => "Not",
|
||||
"_ast.NotEq" => "NotEq",
|
||||
"_ast.NotIn" => "NotIn",
|
||||
"_ast.Or" => "Or",
|
||||
"_ast.ParamSpec" => "ParamSpec(identifier name, expr? default_value)",
|
||||
"_ast.Pass" => "Pass",
|
||||
"_ast.Pow" => "Pow",
|
||||
"_ast.RShift" => "RShift",
|
||||
"_ast.Raise" => "Raise(expr? exc, expr? cause)",
|
||||
"_ast.Return" => "Return(expr? value)",
|
||||
"_ast.Set" => "Set(expr* elts)",
|
||||
"_ast.SetComp" => "SetComp(expr elt, comprehension* generators)",
|
||||
"_ast.Slice" => "Slice(expr? lower, expr? upper, expr? step)",
|
||||
"_ast.Starred" => "Starred(expr value, expr_context ctx)",
|
||||
"_ast.Store" => "Store",
|
||||
"_ast.Sub" => "Sub",
|
||||
"_ast.Subscript" => "Subscript(expr value, expr slice, expr_context ctx)",
|
||||
"_ast.TemplateStr" => "TemplateStr(expr* values)",
|
||||
"_ast.Try" => "Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)",
|
||||
"_ast.TryStar" => "TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)",
|
||||
"_ast.Tuple" => "Tuple(expr* elts, expr_context ctx)",
|
||||
"_ast.TypeAlias" => "TypeAlias(expr name, type_param* type_params, expr value)",
|
||||
"_ast.TypeIgnore" => "TypeIgnore(int lineno, string tag)",
|
||||
"_ast.TypeVar" => "TypeVar(identifier name, expr? bound, expr? default_value)",
|
||||
"_ast.TypeVarTuple" => "TypeVarTuple(identifier name, expr? default_value)",
|
||||
"_ast.UAdd" => "UAdd",
|
||||
"_ast.USub" => "USub",
|
||||
"_ast.UnaryOp" => "UnaryOp(unaryop op, expr operand)",
|
||||
"_ast.While" => "While(expr test, stmt* body, stmt* orelse)",
|
||||
"_ast.With" => "With(withitem* items, stmt* body, string? type_comment)",
|
||||
"_ast.Yield" => "Yield(expr? value)",
|
||||
"_ast.YieldFrom" => "YieldFrom(expr value)",
|
||||
"_ast.alias" => "alias(identifier name, identifier? asname)",
|
||||
"_ast.arg" => "arg(identifier arg, expr? annotation, string? type_comment)",
|
||||
"_ast.arguments" => "arguments(arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults)",
|
||||
"_ast.boolop" => "boolop = And | Or",
|
||||
"_ast.cmpop" => "cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn",
|
||||
"_ast.comprehension" => "comprehension(expr target, expr iter, expr* ifs, int is_async)",
|
||||
"_ast.excepthandler" => "excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)",
|
||||
"_ast.expr" => "expr = BoolOp(boolop op, expr* values)\n | NamedExpr(expr target, expr value)\n | BinOp(expr left, operator op, expr right)\n | UnaryOp(unaryop op, expr operand)\n | Lambda(arguments args, expr body)\n | IfExp(expr test, expr body, expr orelse)\n | Dict(expr?* keys, expr* values)\n | Set(expr* elts)\n | ListComp(expr elt, comprehension* generators)\n | SetComp(expr elt, comprehension* generators)\n | DictComp(expr key, expr value, comprehension* generators)\n | GeneratorExp(expr elt, comprehension* generators)\n | Await(expr value)\n | Yield(expr? value)\n | YieldFrom(expr value)\n | Compare(expr left, cmpop* ops, expr* comparators)\n | Call(expr func, expr* args, keyword* keywords)\n | FormattedValue(expr value, int conversion, expr? format_spec)\n | Interpolation(expr value, constant str, int conversion, expr? format_spec)\n | JoinedStr(expr* values)\n | TemplateStr(expr* values)\n | Constant(constant value, string? kind)\n | Attribute(expr value, identifier attr, expr_context ctx)\n | Subscript(expr value, expr slice, expr_context ctx)\n | Starred(expr value, expr_context ctx)\n | Name(identifier id, expr_context ctx)\n | List(expr* elts, expr_context ctx)\n | Tuple(expr* elts, expr_context ctx)\n | Slice(expr? lower, expr? upper, expr? step)",
|
||||
"_ast.expr_context" => "expr_context = Load | Store | Del",
|
||||
"_ast.keyword" => "keyword(identifier? arg, expr value)",
|
||||
"_ast.match_case" => "match_case(pattern pattern, expr? guard, stmt* body)",
|
||||
"_ast.mod" => "mod = Module(stmt* body, type_ignore* type_ignores)\n | Interactive(stmt* body)\n | Expression(expr body)\n | FunctionType(expr* argtypes, expr returns)",
|
||||
"_ast.operator" => "operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift | RShift | BitOr | BitXor | BitAnd | FloorDiv",
|
||||
"_ast.pattern" => "pattern = MatchValue(expr value)\n | MatchSingleton(constant value)\n | MatchSequence(pattern* patterns)\n | MatchMapping(expr* keys, pattern* patterns, identifier? rest)\n | MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)\n | MatchStar(identifier? name)\n | MatchAs(pattern? pattern, identifier? name)\n | MatchOr(pattern* patterns)",
|
||||
"_ast.stmt" => "stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)\n | AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, string? type_comment, type_param* type_params)\n | ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, expr* decorator_list, type_param* type_params)\n | Return(expr? value)\n | Delete(expr* targets)\n | Assign(expr* targets, expr value, string? type_comment)\n | TypeAlias(expr name, type_param* type_params, expr value)\n | AugAssign(expr target, operator op, expr value)\n | AnnAssign(expr target, expr annotation, expr? value, int simple)\n | For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)\n | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)\n | While(expr test, stmt* body, stmt* orelse)\n | If(expr test, stmt* body, stmt* orelse)\n | With(withitem* items, stmt* body, string? type_comment)\n | AsyncWith(withitem* items, stmt* body, string? type_comment)\n | Match(expr subject, match_case* cases)\n | Raise(expr? exc, expr? cause)\n | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n | Assert(expr test, expr? msg)\n | Import(alias* names)\n | ImportFrom(identifier? module, alias* names, int? level)\n | Global(identifier* names)\n | Nonlocal(identifier* names)\n | Expr(expr value)\n | Pass\n | Break\n | Continue",
|
||||
"_ast.type_ignore" => "type_ignore = TypeIgnore(int lineno, string tag)",
|
||||
"_ast.type_param" => "type_param = TypeVar(identifier name, expr? bound, expr? default_value)\n | ParamSpec(identifier name, expr? default_value)\n | TypeVarTuple(identifier name, expr? default_value)",
|
||||
"_ast.unaryop" => "unaryop = Invert | Not | UAdd | USub",
|
||||
"_ast.withitem" => "withitem(expr context_expr, expr? optional_vars)",
|
||||
"_asyncio" => "Accelerator module for asyncio",
|
||||
"_asyncio.Future" => "This class is *almost* compatible with concurrent.futures.Future.\n\nDifferences:\n\n- result() and exception() do not take a timeout argument and\n raise an exception when the future isn't done yet.\n\n- Callbacks registered with add_done_callback() are always called\n via the event loop's call_soon_threadsafe().\n\n- This class is not compatible with the wait() and as_completed()\n methods in the concurrent.futures package.",
|
||||
"_asyncio.Future.__await__" => "Return an iterator to be used in await expression.",
|
||||
|
||||
@@ -23,7 +23,7 @@ fn parse_inner(literal: &[u8]) -> Option<f64> {
|
||||
}
|
||||
|
||||
pub fn is_integer(v: f64) -> bool {
|
||||
(v - v.round()).abs() < f64::EPSILON
|
||||
v.is_finite() && v.fract() == 0.0
|
||||
}
|
||||
|
||||
fn format_nan(case: Case) -> String {
|
||||
|
||||
@@ -42,7 +42,7 @@ impl Frame {
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn f_builtins(&self) -> PyDictRef {
|
||||
fn f_builtins(&self) -> PyObjectRef {
|
||||
self.builtins.clone()
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ impl PyFunction {
|
||||
let builtins = globals.get_item("__builtins__", vm).unwrap_or_else(|_| {
|
||||
// If not in globals, inherit from current execution context
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.builtins.clone().into()
|
||||
frame.builtins.clone()
|
||||
} else {
|
||||
vm.builtins.dict().into()
|
||||
}
|
||||
@@ -515,7 +515,7 @@ impl Py<PyFunction> {
|
||||
let frame = Frame::new(
|
||||
code.clone(),
|
||||
Scope::new(Some(locals), self.globals.clone()),
|
||||
vm.builtins.dict(),
|
||||
self.builtins.clone(),
|
||||
self.closure.as_ref().map_or(&[], |c| c.as_slice()),
|
||||
Some(self.to_owned().into()),
|
||||
vm,
|
||||
|
||||
@@ -937,6 +937,37 @@ impl PyType {
|
||||
|
||||
#[pygetset]
|
||||
fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
let attrs = self.attributes.read();
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations__)).cloned() {
|
||||
// Ignore the __annotations__ descriptor stored on type itself.
|
||||
if !annotations.class().is(vm.ctx.types.getset_type) {
|
||||
if vm.is_none(&annotations)
|
||||
|| annotations.class().is(vm.ctx.types.dict_type)
|
||||
|| self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
{
|
||||
return Ok(annotations);
|
||||
}
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '__annotations__'",
|
||||
self.name()
|
||||
)));
|
||||
}
|
||||
}
|
||||
// Then try __annotations_cache__
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations_cache__)).cloned() {
|
||||
if vm.is_none(&annotations)
|
||||
|| annotations.class().is(vm.ctx.types.dict_type)
|
||||
|| self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
|
||||
{
|
||||
return Ok(annotations);
|
||||
}
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '__annotations__'",
|
||||
self.name()
|
||||
)));
|
||||
}
|
||||
drop(attrs);
|
||||
|
||||
if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '__annotations__'",
|
||||
@@ -944,17 +975,6 @@ impl PyType {
|
||||
)));
|
||||
}
|
||||
|
||||
// First try __annotations__ (e.g. for "from __future__ import annotations")
|
||||
let attrs = self.attributes.read();
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations__)).cloned() {
|
||||
return Ok(annotations);
|
||||
}
|
||||
// Then try __annotations_cache__
|
||||
if let Some(annotations) = attrs.get(identifier!(vm, __annotations_cache__)).cloned() {
|
||||
return Ok(annotations);
|
||||
}
|
||||
drop(attrs);
|
||||
|
||||
// Get __annotate__ and call it if callable
|
||||
let annotate = self.__annotate__(vm)?;
|
||||
let annotations = if annotate.is_callable() {
|
||||
|
||||
+47
-8
@@ -72,7 +72,7 @@ pub struct Frame {
|
||||
pub(crate) cells_frees: Box<[PyCellRef]>,
|
||||
pub locals: ArgMapping,
|
||||
pub globals: PyDictRef,
|
||||
pub builtins: PyDictRef,
|
||||
pub builtins: PyObjectRef,
|
||||
|
||||
// on feature=threading, this is a duplicate of FrameState.lasti, but it's faster to do an
|
||||
// atomic store than it is to do a fetch_add, for every instruction executed
|
||||
@@ -137,7 +137,7 @@ impl Frame {
|
||||
pub(crate) fn new(
|
||||
code: PyRef<PyCode>,
|
||||
scope: Scope,
|
||||
builtins: PyDictRef,
|
||||
builtins: PyObjectRef,
|
||||
closure: &[PyCellRef],
|
||||
func_obj: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
@@ -352,7 +352,7 @@ struct ExecutingFrame<'a> {
|
||||
cells_frees: &'a [PyCellRef],
|
||||
locals: &'a ArgMapping,
|
||||
globals: &'a PyDictRef,
|
||||
builtins: &'a PyDictRef,
|
||||
builtins: &'a PyObjectRef,
|
||||
object: &'a Py<Frame>,
|
||||
lasti: &'a Lasti,
|
||||
state: &'a mut FrameState,
|
||||
@@ -1207,7 +1207,31 @@ impl ExecutingFrame<'_> {
|
||||
Instruction::LoadAttr { idx } => self.load_attr(vm, idx.get(arg)),
|
||||
Instruction::LoadSuperAttr { arg: idx } => self.load_super_attr(vm, idx.get(arg)),
|
||||
Instruction::LoadBuildClass => {
|
||||
self.push_value(vm.builtins.get_attr(identifier!(vm, __build_class__), vm)?);
|
||||
let build_class =
|
||||
if let Some(builtins_dict) = self.builtins.downcast_ref::<PyDict>() {
|
||||
builtins_dict
|
||||
.get_item_opt(identifier!(vm, __build_class__), vm)?
|
||||
.ok_or_else(|| {
|
||||
vm.new_name_error(
|
||||
"__build_class__ not found".to_owned(),
|
||||
identifier!(vm, __build_class__).to_owned(),
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
self.builtins
|
||||
.get_item(identifier!(vm, __build_class__), vm)
|
||||
.map_err(|e| {
|
||||
if e.fast_isinstance(vm.ctx.exceptions.key_error) {
|
||||
vm.new_name_error(
|
||||
"__build_class__ not found".to_owned(),
|
||||
identifier!(vm, __build_class__).to_owned(),
|
||||
)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})?
|
||||
};
|
||||
self.push_value(build_class);
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::LoadLocals => {
|
||||
@@ -2124,11 +2148,26 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
#[inline]
|
||||
fn load_global_or_builtin(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
|
||||
self.globals
|
||||
.get_chain(self.builtins, name, vm)?
|
||||
.ok_or_else(|| {
|
||||
vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
|
||||
if let Some(builtins_dict) = self.builtins.downcast_ref::<PyDict>() {
|
||||
// Fast path: builtins is a dict
|
||||
self.globals
|
||||
.get_chain(builtins_dict, name, vm)?
|
||||
.ok_or_else(|| {
|
||||
vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
|
||||
})
|
||||
} else {
|
||||
// Slow path: builtins is not a dict, use generic __getitem__
|
||||
if let Some(value) = self.globals.get_item_opt(name, vm)? {
|
||||
return Ok(value);
|
||||
}
|
||||
self.builtins.get_item(name, vm).map_err(|e| {
|
||||
if e.fast_isinstance(vm.ctx.exceptions.key_error) {
|
||||
vm.new_name_error(format!("name '{name}' is not defined"), name.to_owned())
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "flame-it", flame("Frame"))]
|
||||
|
||||
+426
-9
@@ -9,7 +9,7 @@ pub(crate) use python::_ast::module_def;
|
||||
mod pyast;
|
||||
|
||||
use crate::builtins::{PyInt, PyStr};
|
||||
use crate::stdlib::ast::module::{Mod, ModInteractive};
|
||||
use crate::stdlib::ast::module::{Mod, ModFunctionType, ModInteractive};
|
||||
use crate::stdlib::ast::node::BoxedSlice;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
@@ -36,6 +36,8 @@ use rustpython_codegen as codegen;
|
||||
pub(crate) use python::_ast::NodeAst;
|
||||
|
||||
mod python;
|
||||
mod repr;
|
||||
mod validate;
|
||||
|
||||
mod argument;
|
||||
mod basic;
|
||||
@@ -196,6 +198,27 @@ fn range_from_object(
|
||||
let start_col_val: i32 = start_column.try_to_primitive(vm)?;
|
||||
let end_col_val: i32 = end_column.try_to_primitive(vm)?;
|
||||
|
||||
if start_row_val > end_row_val {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"AST node line range ({}, {}) is not valid",
|
||||
start_row_val, end_row_val
|
||||
)));
|
||||
}
|
||||
if (start_row_val < 0 && end_row_val != start_row_val)
|
||||
|| (start_col_val < 0 && end_col_val != start_col_val)
|
||||
{
|
||||
return Err(vm.new_value_error(format!(
|
||||
"AST node column range ({}, {}) for line range ({}, {}) is not valid",
|
||||
start_col_val, end_col_val, start_row_val, end_row_val
|
||||
)));
|
||||
}
|
||||
if start_row_val == end_row_val && start_col_val > end_col_val {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"line {}, column {}-{} is not a valid range",
|
||||
start_row_val, start_col_val, end_col_val
|
||||
)));
|
||||
}
|
||||
|
||||
let location = PySourceRange {
|
||||
start: PySourceLocation {
|
||||
row: Row(if start_row_val > 0 {
|
||||
@@ -306,10 +329,116 @@ pub(crate) fn parse(
|
||||
vm: &VirtualMachine,
|
||||
source: &str,
|
||||
mode: parser::Mode,
|
||||
optimize: u8,
|
||||
target_version: Option<ast::PythonVersion>,
|
||||
type_comments: bool,
|
||||
) -> Result<PyObjectRef, CompileError> {
|
||||
let source_file = SourceFileBuilder::new("".to_owned(), source.to_owned()).finish();
|
||||
let top = parser::parse(source, mode.into())
|
||||
.map_err(|parse_error| {
|
||||
let mut options = parser::ParseOptions::from(mode);
|
||||
let target_version = target_version.unwrap_or(ast::PythonVersion::PY314);
|
||||
options = options.with_target_version(target_version);
|
||||
let parsed = parser::parse(source, options).map_err(|parse_error| {
|
||||
let range = text_range_to_source_range(&source_file, parse_error.location);
|
||||
ParseError {
|
||||
error: parse_error.error,
|
||||
raw_location: parse_error.location,
|
||||
location: range.start.to_source_location(),
|
||||
end_location: range.end.to_source_location(),
|
||||
source_path: "<unknown>".to_string(),
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Some(error) = parsed.unsupported_syntax_errors().first() {
|
||||
let range = text_range_to_source_range(&source_file, error.range());
|
||||
return Err(ParseError {
|
||||
error: parser::ParseErrorType::OtherError(error.to_string()),
|
||||
raw_location: error.range(),
|
||||
location: range.start.to_source_location(),
|
||||
end_location: range.end.to_source_location(),
|
||||
source_path: "<unknown>".to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut top = parsed.into_syntax();
|
||||
if optimize > 0 {
|
||||
fold_match_value_constants(&mut top);
|
||||
}
|
||||
if optimize >= 2 {
|
||||
strip_docstrings(&mut top);
|
||||
}
|
||||
let top = match top {
|
||||
ast::Mod::Module(m) => Mod::Module(m),
|
||||
ast::Mod::Expression(e) => Mod::Expression(e),
|
||||
};
|
||||
let obj = top.ast_to_object(vm, &source_file);
|
||||
if type_comments && obj.class().is(pyast::NodeModModule::static_type()) {
|
||||
let type_ignores = type_ignores_from_source(vm, source)?;
|
||||
let dict = obj.as_object().dict().unwrap();
|
||||
dict.set_item("type_ignores", vm.ctx.new_list(type_ignores).into(), vm)
|
||||
.unwrap();
|
||||
}
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn wrap_interactive(vm: &VirtualMachine, module_obj: PyObjectRef) -> PyResult {
|
||||
if !module_obj.class().is(pyast::NodeModModule::static_type()) {
|
||||
return Err(vm.new_type_error("expected Module node".to_owned()));
|
||||
}
|
||||
let body = get_node_field(vm, &module_obj, "body", "Module")?;
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeModInteractive::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body, vm).unwrap();
|
||||
Ok(node.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn parse_func_type(
|
||||
vm: &VirtualMachine,
|
||||
source: &str,
|
||||
optimize: u8,
|
||||
target_version: Option<ast::PythonVersion>,
|
||||
) -> Result<PyObjectRef, CompileError> {
|
||||
let _ = optimize;
|
||||
let _ = target_version;
|
||||
let source = source.trim();
|
||||
let mut depth = 0i32;
|
||||
let mut split_at = None;
|
||||
let mut chars = source.chars().peekable();
|
||||
let mut idx = 0usize;
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'(' | '[' | '{' => depth += 1,
|
||||
')' | ']' | '}' => depth -= 1,
|
||||
'-' if depth == 0 && chars.peek() == Some(&'>') => {
|
||||
split_at = Some(idx);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
idx += ch.len_utf8();
|
||||
}
|
||||
|
||||
let Some(split_at) = split_at else {
|
||||
return Err(ParseError {
|
||||
error: parser::ParseErrorType::OtherError("invalid func_type".to_owned()),
|
||||
raw_location: TextRange::default(),
|
||||
location: SourceLocation::default(),
|
||||
end_location: SourceLocation::default(),
|
||||
source_path: "<unknown>".to_owned(),
|
||||
}
|
||||
.into());
|
||||
};
|
||||
|
||||
let left = source[..split_at].trim();
|
||||
let right = source[split_at + 2..].trim();
|
||||
|
||||
let parse_expr = |expr_src: &str| -> Result<ast::Expr, CompileError> {
|
||||
let source_file = SourceFileBuilder::new("".to_owned(), expr_src.to_owned()).finish();
|
||||
let parsed = parser::parse_expression(expr_src).map_err(|parse_error| {
|
||||
let range = text_range_to_source_range(&source_file, parse_error.location);
|
||||
ParseError {
|
||||
error: parse_error.error,
|
||||
@@ -318,13 +447,286 @@ pub(crate) fn parse(
|
||||
end_location: range.end.to_source_location(),
|
||||
source_path: "<unknown>".to_string(),
|
||||
}
|
||||
})?
|
||||
.into_syntax();
|
||||
let top = match top {
|
||||
ast::Mod::Module(m) => Mod::Module(m),
|
||||
ast::Mod::Expression(e) => Mod::Expression(e),
|
||||
})?;
|
||||
Ok(*parsed.into_syntax().body)
|
||||
};
|
||||
Ok(top.ast_to_object(vm, &source_file))
|
||||
|
||||
let arg_expr = parse_expr(left)?;
|
||||
let returns = parse_expr(right)?;
|
||||
|
||||
let argtypes: Vec<ast::Expr> = match arg_expr {
|
||||
ast::Expr::Tuple(tup) => tup.elts,
|
||||
ast::Expr::Name(_) | ast::Expr::Subscript(_) | ast::Expr::Attribute(_) => vec![arg_expr],
|
||||
other => vec![other],
|
||||
};
|
||||
|
||||
let func_type = ModFunctionType {
|
||||
argtypes: argtypes.into_boxed_slice(),
|
||||
returns,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let source_file = SourceFileBuilder::new("".to_owned(), source.to_owned()).finish();
|
||||
Ok(func_type.ast_to_object(vm, &source_file))
|
||||
}
|
||||
|
||||
fn type_ignores_from_source(
|
||||
vm: &VirtualMachine,
|
||||
source: &str,
|
||||
) -> Result<Vec<PyObjectRef>, CompileError> {
|
||||
let mut ignores = Vec::new();
|
||||
for (idx, line) in source.lines().enumerate() {
|
||||
let Some(pos) = line.find("#") else {
|
||||
continue;
|
||||
};
|
||||
let comment = &line[pos + 1..];
|
||||
let comment = comment.trim_start();
|
||||
let Some(rest) = comment.strip_prefix("type: ignore") else {
|
||||
continue;
|
||||
};
|
||||
let tag = rest.trim_start();
|
||||
let tag = if tag.is_empty() { "" } else { tag };
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(
|
||||
vm,
|
||||
pyast::NodeTypeIgnoreTypeIgnore::static_type().to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
let lineno = idx + 1;
|
||||
dict.set_item("lineno", vm.ctx.new_int(lineno).into(), vm)
|
||||
.unwrap();
|
||||
dict.set_item("tag", vm.ctx.new_str(tag).into(), vm)
|
||||
.unwrap();
|
||||
ignores.push(node.into());
|
||||
}
|
||||
Ok(ignores)
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_match_value_constants(top: &mut ast::Mod) {
|
||||
match top {
|
||||
ast::Mod::Module(module) => fold_stmts(&mut module.body),
|
||||
ast::Mod::Expression(_expr) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn strip_docstrings(top: &mut ast::Mod) {
|
||||
match top {
|
||||
ast::Mod::Module(module) => strip_docstring_in_body(&mut module.body),
|
||||
ast::Mod::Expression(_expr) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn strip_docstring_in_body(body: &mut Vec<ast::Stmt>) {
|
||||
if let Some(range) = take_docstring(body)
|
||||
&& body.is_empty()
|
||||
{
|
||||
let start_offset = range.start();
|
||||
let end_offset = start_offset + TextSize::from(4);
|
||||
let pass_range = TextRange::new(start_offset, end_offset);
|
||||
body.push(ast::Stmt::Pass(ast::StmtPass {
|
||||
node_index: Default::default(),
|
||||
range: pass_range,
|
||||
}));
|
||||
}
|
||||
for stmt in body {
|
||||
match stmt {
|
||||
ast::Stmt::FunctionDef(def) => strip_docstring_in_body(&mut def.body),
|
||||
ast::Stmt::ClassDef(def) => strip_docstring_in_body(&mut def.body),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn take_docstring(body: &mut Vec<ast::Stmt>) -> Option<TextRange> {
|
||||
let ast::Stmt::Expr(expr_stmt) = body.first()? else {
|
||||
return None;
|
||||
};
|
||||
if matches!(expr_stmt.value.as_ref(), ast::Expr::StringLiteral(_)) {
|
||||
let range = expr_stmt.range;
|
||||
body.remove(0);
|
||||
return Some(range);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_stmts(stmts: &mut [ast::Stmt]) {
|
||||
for stmt in stmts {
|
||||
fold_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_stmt(stmt: &mut ast::Stmt) {
|
||||
use ast::Stmt;
|
||||
match stmt {
|
||||
Stmt::FunctionDef(def) => fold_stmts(&mut def.body),
|
||||
Stmt::ClassDef(def) => fold_stmts(&mut def.body),
|
||||
Stmt::For(stmt) => {
|
||||
fold_stmts(&mut stmt.body);
|
||||
fold_stmts(&mut stmt.orelse);
|
||||
}
|
||||
Stmt::While(stmt) => {
|
||||
fold_stmts(&mut stmt.body);
|
||||
fold_stmts(&mut stmt.orelse);
|
||||
}
|
||||
Stmt::If(stmt) => {
|
||||
fold_stmts(&mut stmt.body);
|
||||
for clause in &mut stmt.elif_else_clauses {
|
||||
fold_stmts(&mut clause.body);
|
||||
}
|
||||
}
|
||||
Stmt::With(stmt) => {
|
||||
fold_stmts(&mut stmt.body);
|
||||
}
|
||||
Stmt::Try(stmt) => {
|
||||
fold_stmts(&mut stmt.body);
|
||||
fold_stmts(&mut stmt.orelse);
|
||||
fold_stmts(&mut stmt.finalbody);
|
||||
}
|
||||
Stmt::Match(stmt) => {
|
||||
for case in &mut stmt.cases {
|
||||
fold_pattern(&mut case.pattern);
|
||||
if let Some(expr) = case.guard.as_deref_mut() {
|
||||
fold_expr(expr);
|
||||
}
|
||||
fold_stmts(&mut case.body);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_pattern(pattern: &mut ast::Pattern) {
|
||||
use ast::Pattern;
|
||||
match pattern {
|
||||
Pattern::MatchValue(value) => fold_expr(&mut value.value),
|
||||
Pattern::MatchSequence(seq) => {
|
||||
for pattern in &mut seq.patterns {
|
||||
fold_pattern(pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchMapping(mapping) => {
|
||||
for key in &mut mapping.keys {
|
||||
fold_expr(key);
|
||||
}
|
||||
for pattern in &mut mapping.patterns {
|
||||
fold_pattern(pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchClass(class) => {
|
||||
for pattern in &mut class.arguments.patterns {
|
||||
fold_pattern(pattern);
|
||||
}
|
||||
for keyword in &mut class.arguments.keywords {
|
||||
fold_pattern(&mut keyword.pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchAs(match_as) => {
|
||||
if let Some(pattern) = match_as.pattern.as_deref_mut() {
|
||||
fold_pattern(pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchOr(match_or) => {
|
||||
for pattern in &mut match_or.patterns {
|
||||
fold_pattern(pattern);
|
||||
}
|
||||
}
|
||||
Pattern::MatchSingleton(_) | Pattern::MatchStar(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_expr(expr: &mut ast::Expr) {
|
||||
use ast::Expr;
|
||||
if let Expr::UnaryOp(unary) = expr {
|
||||
fold_expr(&mut unary.operand);
|
||||
if matches!(unary.op, ast::UnaryOp::USub)
|
||||
&& let Expr::NumberLiteral(number_literal) = unary.operand.as_ref()
|
||||
{
|
||||
let number = match &number_literal.value {
|
||||
ast::Number::Int(value) => {
|
||||
if *value == ast::Int::ZERO {
|
||||
Some(ast::Number::Int(ast::Int::ZERO))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ast::Number::Float(value) => Some(ast::Number::Float(-value)),
|
||||
ast::Number::Complex { real, imag } => Some(ast::Number::Complex {
|
||||
real: -real,
|
||||
imag: -imag,
|
||||
}),
|
||||
};
|
||||
if let Some(number) = number {
|
||||
*expr = Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
node_index: unary.node_index.clone(),
|
||||
range: unary.range,
|
||||
value: number,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Expr::BinOp(binop) = expr {
|
||||
fold_expr(&mut binop.left);
|
||||
fold_expr(&mut binop.right);
|
||||
|
||||
let Expr::NumberLiteral(left) = binop.left.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let Expr::NumberLiteral(right) = binop.right.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(number) = fold_number_binop(&left.value, &binop.op, &right.value) {
|
||||
*expr = Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
node_index: binop.node_index.clone(),
|
||||
range: binop.range,
|
||||
value: number,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn fold_number_binop(
|
||||
left: &ast::Number,
|
||||
op: &ast::Operator,
|
||||
right: &ast::Number,
|
||||
) -> Option<ast::Number> {
|
||||
let (left_real, left_imag, left_is_complex) = number_to_complex(left)?;
|
||||
let (right_real, right_imag, right_is_complex) = number_to_complex(right)?;
|
||||
|
||||
if !(left_is_complex || right_is_complex) {
|
||||
return None;
|
||||
}
|
||||
|
||||
match op {
|
||||
ast::Operator::Add => Some(ast::Number::Complex {
|
||||
real: left_real + right_real,
|
||||
imag: left_imag + right_imag,
|
||||
}),
|
||||
ast::Operator::Sub => Some(ast::Number::Complex {
|
||||
real: left_real - right_real,
|
||||
imag: left_imag - right_imag,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
fn number_to_complex(number: &ast::Number) -> Option<(f64, f64, bool)> {
|
||||
match number {
|
||||
ast::Number::Complex { real, imag } => Some((*real, *imag, true)),
|
||||
ast::Number::Float(value) => Some((*value, 0.0, false)),
|
||||
ast::Number::Int(value) => value.as_i64().map(|value| (value as f64, 0.0, false)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
@@ -342,6 +744,7 @@ pub(crate) fn compile(
|
||||
|
||||
let source_file = SourceFileBuilder::new(filename.to_owned(), "".to_owned()).finish();
|
||||
let ast: Mod = Node::ast_from_object(vm, &source_file, object)?;
|
||||
validate::validate_mod(vm, &ast)?;
|
||||
let ast = match ast {
|
||||
Mod::Module(m) => ast::Mod::Module(m),
|
||||
Mod::Interactive(ModInteractive { range, body }) => ast::Mod::Module(ast::ModModule {
|
||||
@@ -360,16 +763,27 @@ pub(crate) fn compile(
|
||||
Ok(vm.ctx.new_code(code).into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
pub(crate) fn validate_ast_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<()> {
|
||||
let source_file = SourceFileBuilder::new("<ast>".to_owned(), "".to_owned()).finish();
|
||||
let ast: Mod = Node::ast_from_object(vm, &source_file, object)?;
|
||||
validate::validate_mod(vm, &ast)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Used by builtins::compile()
|
||||
pub const PY_COMPILE_FLAG_AST_ONLY: i32 = 0x0400;
|
||||
|
||||
// The following flags match the values from Include/cpython/compile.h
|
||||
// Caveat emptor: These flags are undocumented on purpose and depending
|
||||
// on their effect outside the standard library is **unsupported**.
|
||||
pub const PY_CF_SOURCE_IS_UTF8: i32 = 0x0100;
|
||||
pub const PY_CF_DONT_IMPLY_DEDENT: i32 = 0x200;
|
||||
pub const PY_CF_IGNORE_COOKIE: i32 = 0x0800;
|
||||
pub const PY_CF_ALLOW_INCOMPLETE_INPUT: i32 = 0x4000;
|
||||
pub const PY_CF_OPTIMIZED_AST: i32 = 0x8000 | PY_COMPILE_FLAG_AST_ONLY;
|
||||
pub const PY_CF_TYPE_COMMENTS: i32 = 0x1000;
|
||||
pub const PY_CF_ALLOW_TOP_LEVEL_AWAIT: i32 = 0x2000;
|
||||
|
||||
// __future__ flags - sync with Lib/__future__.py
|
||||
// TODO: These flags aren't being used in rust code
|
||||
@@ -389,7 +803,10 @@ const CO_FUTURE_ANNOTATIONS: i32 = 0x1000000;
|
||||
|
||||
// Used by builtins::compile() - the summary of all flags
|
||||
pub const PY_COMPILE_FLAGS_MASK: i32 = PY_COMPILE_FLAG_AST_ONLY
|
||||
| PY_CF_SOURCE_IS_UTF8
|
||||
| PY_CF_DONT_IMPLY_DEDENT
|
||||
| PY_CF_IGNORE_COOKIE
|
||||
| PY_CF_ALLOW_TOP_LEVEL_AWAIT
|
||||
| PY_CF_ALLOW_INCOMPLETE_INPUT
|
||||
| PY_CF_OPTIMIZED_AST
|
||||
| PY_CF_TYPE_COMMENTS
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustpython_compiler_core::SourceFile;
|
||||
impl Node for ast::Identifier {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
|
||||
let id = self.as_str();
|
||||
vm.ctx.new_str(id).into()
|
||||
vm.ctx.intern_str(id).to_object()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
|
||||
@@ -301,31 +301,41 @@ fn constant_to_ruff_expr(value: Constant) -> ast::Expr {
|
||||
// TODO: Does this matter?
|
||||
parenthesized: true,
|
||||
}),
|
||||
ConstantLiteral::FrozenSet(value) => ast::Expr::Call(ast::ExprCall {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
// idk lol
|
||||
func: Box::new(ast::Expr::Name(ast::ExprName {
|
||||
node_index: Default::default(),
|
||||
range: TextRange::default(),
|
||||
id: ast::name::Name::new_static("frozenset"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
})),
|
||||
arguments: ast::Arguments {
|
||||
ConstantLiteral::FrozenSet(value) => {
|
||||
let args = if value.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![ast::Expr::Set(ast::ExprSet {
|
||||
node_index: Default::default(),
|
||||
range: TextRange::default(),
|
||||
elts: value
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
constant_to_ruff_expr(Constant {
|
||||
range: TextRange::default(),
|
||||
value,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
})]
|
||||
};
|
||||
ast::Expr::Call(ast::ExprCall {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
args: value
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
constant_to_ruff_expr(Constant {
|
||||
range: TextRange::default(),
|
||||
value,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
keywords: Box::default(),
|
||||
},
|
||||
}),
|
||||
func: Box::new(ast::Expr::Name(ast::ExprName {
|
||||
node_index: Default::default(),
|
||||
range: TextRange::default(),
|
||||
id: ast::name::Name::new_static("frozenset"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
})),
|
||||
arguments: ast::Arguments {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
args: args.into(),
|
||||
keywords: Box::default(),
|
||||
},
|
||||
})
|
||||
}
|
||||
ConstantLiteral::Float(value) => ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
|
||||
@@ -29,6 +29,10 @@ pub(super) fn ast_to_object(
|
||||
|
||||
let orelse = if let Some(next) = rest.next() {
|
||||
if next.test.is_some() {
|
||||
let next = ast::ElifElseClause {
|
||||
range: TextRange::new(next.range.start(), range.end()),
|
||||
..next
|
||||
};
|
||||
vm.ctx
|
||||
.new_list(vec![ast_to_object(next, rest, vm, source_file)])
|
||||
.into()
|
||||
@@ -58,7 +62,9 @@ pub(super) fn ast_from_object(
|
||||
)?;
|
||||
let range = range_from_object(vm, source_file, object, "If")?;
|
||||
|
||||
let elif_else_clauses = if let [ast::Stmt::If(_)] = &*orelse {
|
||||
let elif_else_clauses = if orelse.is_empty() {
|
||||
vec![]
|
||||
} else if let [ast::Stmt::If(_)] = &*orelse {
|
||||
let Some(ast::Stmt::If(ast::StmtIf {
|
||||
node_index: _,
|
||||
range,
|
||||
|
||||
@@ -126,6 +126,13 @@ impl Node for ast::Expr {
|
||||
Constant::ast_from_object(vm, source_file, object)?.into_expr()
|
||||
} else if cls.is(pyast::NodeExprJoinedStr::static_type()) {
|
||||
JoinedStr::ast_from_object(vm, source_file, object)?.into_expr()
|
||||
} else if cls.is(pyast::NodeExprTemplateStr::static_type()) {
|
||||
let template = string::TemplateStr::ast_from_object(vm, source_file, object)?;
|
||||
return string::template_str_to_expr(vm, template);
|
||||
} else if cls.is(pyast::NodeExprInterpolation::static_type()) {
|
||||
let interpolation =
|
||||
string::TStringInterpolation::ast_from_object(vm, source_file, object)?;
|
||||
return string::interpolation_to_expr(vm, interpolation);
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"expected some sort of expr, but got {}",
|
||||
@@ -455,6 +462,11 @@ impl Node for ast::ExprDict {
|
||||
source_file,
|
||||
get_node_field(vm, &object, "values", "Dict")?,
|
||||
)?;
|
||||
if keys.len() != values.len() {
|
||||
return Err(vm.new_value_error(
|
||||
"Dict doesn't have the same number of keys as values".to_owned(),
|
||||
));
|
||||
}
|
||||
let items = keys
|
||||
.into_iter()
|
||||
.zip(values)
|
||||
@@ -647,8 +659,18 @@ impl Node for ast::ExprGenerator {
|
||||
elt,
|
||||
generators,
|
||||
range,
|
||||
parenthesized: _,
|
||||
parenthesized,
|
||||
} = self;
|
||||
let range = if parenthesized {
|
||||
range
|
||||
} else {
|
||||
TextRange::new(
|
||||
range
|
||||
.start()
|
||||
.saturating_sub(ruff_text_size::TextSize::from(1)),
|
||||
range.end() + ruff_text_size::TextSize::from(1),
|
||||
)
|
||||
};
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(vm, pyast::NodeExprGeneratorExp::static_type().to_owned())
|
||||
.unwrap();
|
||||
@@ -1234,6 +1256,9 @@ impl Node for ast::ExprContext {
|
||||
unimplemented!("Invalid expression context is not allowed in Python AST")
|
||||
}
|
||||
};
|
||||
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
|
||||
return instance;
|
||||
}
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
|
||||
@@ -86,7 +86,7 @@ impl Node for ast::ModModule {
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
let _ = range;
|
||||
node.into()
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ impl Node for ModInteractive {
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_file), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
let _ = range;
|
||||
node.into()
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ impl Node for ast::ModExpression {
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
dict.set_item("body", body.ast_to_object(vm, source_file), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
let _ = range;
|
||||
node.into()
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ impl Node for ModFunctionType {
|
||||
.unwrap();
|
||||
dict.set_item("returns", returns.ast_to_object(vm, source_file), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
let _ = range;
|
||||
node.into()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ impl Node for ast::BoolOp {
|
||||
Self::And => pyast::NodeBoolOpAnd::static_type(),
|
||||
Self::Or => pyast::NodeBoolOpOr::static_type(),
|
||||
};
|
||||
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
|
||||
return instance;
|
||||
}
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
@@ -51,6 +54,9 @@ impl Node for ast::Operator {
|
||||
Self::BitAnd => pyast::NodeOperatorBitAnd::static_type(),
|
||||
Self::FloorDiv => pyast::NodeOperatorFloorDiv::static_type(),
|
||||
};
|
||||
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
|
||||
return instance;
|
||||
}
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
@@ -107,6 +113,9 @@ impl Node for ast::UnaryOp {
|
||||
Self::UAdd => pyast::NodeUnaryOpUAdd::static_type(),
|
||||
Self::USub => pyast::NodeUnaryOpUSub::static_type(),
|
||||
};
|
||||
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
|
||||
return instance;
|
||||
}
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
@@ -151,6 +160,9 @@ impl Node for ast::CmpOp {
|
||||
Self::In => pyast::NodeCmpOpIn::static_type(),
|
||||
Self::NotIn => pyast::NodeCmpOpNotIn::static_type(),
|
||||
};
|
||||
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
|
||||
return instance;
|
||||
}
|
||||
NodeAst
|
||||
.into_ref_with_type(vm, node_type.to_owned())
|
||||
.unwrap()
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Node for ast::ConversionFlag {
|
||||
// /// This is just a string, not strictly an AST node. But it makes AST conversions easier.
|
||||
impl Node for ast::name::Name {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, _source_file: &SourceFile) -> PyObjectRef {
|
||||
vm.ctx.new_str(self.as_str()).to_pyobject(vm)
|
||||
vm.ctx.intern_str(self.as_str()).to_object()
|
||||
}
|
||||
|
||||
fn ast_from_object(
|
||||
|
||||
@@ -42,7 +42,7 @@ impl Node for ast::Parameters {
|
||||
.unwrap();
|
||||
dict.set_item("defaults", defaults.ast_to_object(vm, source_file), vm)
|
||||
.unwrap();
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
let _ = range;
|
||||
node.into()
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ impl Node for ast::Parameters {
|
||||
source_file,
|
||||
get_node_field(vm, &object, "kw_defaults", "arguments")?,
|
||||
)?;
|
||||
let kwonlyargs = merge_keyword_parameter_defaults(kwonlyargs, kw_defaults);
|
||||
let kwonlyargs = merge_keyword_parameter_defaults(vm, kwonlyargs, kw_defaults)?;
|
||||
|
||||
let posonlyargs = Node::ast_from_object(
|
||||
vm,
|
||||
@@ -78,7 +78,8 @@ impl Node for ast::Parameters {
|
||||
source_file,
|
||||
get_node_field(vm, &object, "defaults", "arguments")?,
|
||||
)?;
|
||||
let (posonlyargs, args) = merge_positional_parameter_defaults(posonlyargs, args, defaults);
|
||||
let (posonlyargs, args) =
|
||||
merge_positional_parameter_defaults(vm, posonlyargs, args, defaults)?;
|
||||
|
||||
Ok(Self {
|
||||
node_index: Default::default(),
|
||||
@@ -321,13 +322,14 @@ fn extract_positional_parameter_defaults(
|
||||
|
||||
/// Merges the keyword parameters with their default values, opposite of [`extract_positional_parameter_defaults`].
|
||||
fn merge_positional_parameter_defaults(
|
||||
vm: &VirtualMachine,
|
||||
posonlyargs: PositionalParameters,
|
||||
args: PositionalParameters,
|
||||
defaults: ParameterDefaults,
|
||||
) -> (
|
||||
) -> PyResult<(
|
||||
Vec<ast::ParameterWithDefault>,
|
||||
Vec<ast::ParameterWithDefault>,
|
||||
) {
|
||||
)> {
|
||||
let posonlyargs = posonlyargs.args;
|
||||
let args = args.args;
|
||||
let defaults = defaults.defaults;
|
||||
@@ -352,7 +354,11 @@ fn merge_positional_parameter_defaults(
|
||||
// If an argument has a default value, insert it
|
||||
// Note that "defaults" will only contain default values for the last "n" parameters
|
||||
// so we need to skip the first "total_argument_count - n" arguments.
|
||||
let default_argument_count = posonlyargs.len() + args.len() - defaults.len();
|
||||
let total_args = posonlyargs.len() + args.len();
|
||||
if defaults.len() > total_args {
|
||||
return Err(vm.new_value_error("more positional defaults than args on arguments"));
|
||||
}
|
||||
let default_argument_count = total_args - defaults.len();
|
||||
for (arg, default) in posonlyargs
|
||||
.iter_mut()
|
||||
.chain(args.iter_mut())
|
||||
@@ -362,7 +368,7 @@ fn merge_positional_parameter_defaults(
|
||||
arg.default = default;
|
||||
}
|
||||
|
||||
(posonlyargs, args)
|
||||
Ok((posonlyargs, args))
|
||||
}
|
||||
|
||||
fn extract_keyword_parameter_defaults(
|
||||
@@ -400,15 +406,21 @@ fn extract_keyword_parameter_defaults(
|
||||
|
||||
/// Merges the keyword parameters with their default values, opposite of [`extract_keyword_parameter_defaults`].
|
||||
fn merge_keyword_parameter_defaults(
|
||||
vm: &VirtualMachine,
|
||||
kw_only_args: KeywordParameters,
|
||||
defaults: ParameterDefaults,
|
||||
) -> Vec<ast::ParameterWithDefault> {
|
||||
core::iter::zip(kw_only_args.keywords, defaults.defaults)
|
||||
) -> PyResult<Vec<ast::ParameterWithDefault>> {
|
||||
if kw_only_args.keywords.len() != defaults.defaults.len() {
|
||||
return Err(
|
||||
vm.new_value_error("length of kwonlyargs is not the same as kw_defaults on arguments")
|
||||
);
|
||||
}
|
||||
Ok(core::iter::zip(kw_only_args.keywords, defaults.defaults)
|
||||
.map(|(parameter, default)| ast::ParameterWithDefault {
|
||||
node_index: Default::default(),
|
||||
parameter,
|
||||
default,
|
||||
range: Default::default(),
|
||||
})
|
||||
.collect()
|
||||
.collect())
|
||||
}
|
||||
@@ -357,16 +357,21 @@ impl Node for ast::PatternMatchClass {
|
||||
source_file,
|
||||
get_node_field(vm, &object, "patterns", "MatchClass")?,
|
||||
)?;
|
||||
let kwd_attrs = Node::ast_from_object(
|
||||
let kwd_attrs: PatternMatchClassKeywordAttributes = Node::ast_from_object(
|
||||
vm,
|
||||
source_file,
|
||||
get_node_field(vm, &object, "kwd_attrs", "MatchClass")?,
|
||||
)?;
|
||||
let kwd_patterns = Node::ast_from_object(
|
||||
let kwd_patterns: PatternMatchClassKeywordPatterns = Node::ast_from_object(
|
||||
vm,
|
||||
source_file,
|
||||
get_node_field(vm, &object, "kwd_patterns", "MatchClass")?,
|
||||
)?;
|
||||
if kwd_attrs.0.len() != kwd_patterns.0.len() {
|
||||
return Err(vm.new_value_error(
|
||||
"MatchClass has mismatched kwd_attrs and kwd_patterns".to_owned(),
|
||||
));
|
||||
}
|
||||
let (patterns, keywords) = merge_pattern_match_class(patterns, kwd_attrs, kwd_patterns);
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use super::*;
|
||||
use crate::builtins::{PyGenericAlias, PyTuple, PyTypeRef, make_union};
|
||||
use crate::builtins::{PyGenericAlias, PyTuple, PyTupleRef, PyTypeRef, make_union};
|
||||
use crate::common::ascii;
|
||||
use crate::convert::ToPyObject;
|
||||
use crate::function::FuncArgs;
|
||||
@@ -18,35 +18,7 @@ macro_rules! impl_node {
|
||||
#[repr(transparent)]
|
||||
$vis struct $name($base);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl $name {
|
||||
#[extend_class]
|
||||
fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
|
||||
class.set_attr(
|
||||
identifier!(ctx, _fields),
|
||||
ctx.new_tuple(vec![
|
||||
$(
|
||||
ctx.new_str(ascii!($field)).into()
|
||||
),*
|
||||
]).into(),
|
||||
);
|
||||
|
||||
class.set_attr(
|
||||
identifier!(ctx, _attributes),
|
||||
ctx.new_list(vec![
|
||||
$(
|
||||
ctx.new_str(ascii!($attr)).into()
|
||||
),*
|
||||
]).into(),
|
||||
);
|
||||
|
||||
// Signal that this is a built-in AST node with field defaults
|
||||
class.set_attr(
|
||||
ctx.intern_str("_field_types"),
|
||||
ctx.new_dict().into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
impl_base_node!($name, fields: [$($field),*], attributes: [$($attr),*]);
|
||||
};
|
||||
// Without attributes
|
||||
(
|
||||
@@ -88,11 +60,85 @@ macro_rules! impl_node {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_base_node {
|
||||
// Base node without fields/attributes (e.g. NodeMod, NodeExpr)
|
||||
($name:ident) => {
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl $name {
|
||||
#[pymethod]
|
||||
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
super::python::_ast::ast_reduce(zelf, vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __replace__(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
super::python::_ast::ast_replace(zelf, args, vm)
|
||||
}
|
||||
|
||||
#[extend_class]
|
||||
fn extend_class(_ctx: &Context, _class: &'static Py<PyType>) {}
|
||||
}
|
||||
};
|
||||
// Leaf node with fields and attributes
|
||||
($name:ident, fields: [$($field:expr),*], attributes: [$($attr:expr),*]) => {
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl $name {
|
||||
#[pymethod]
|
||||
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
super::python::_ast::ast_reduce(zelf, vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __replace__(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
super::python::_ast::ast_replace(zelf, args, vm)
|
||||
}
|
||||
|
||||
#[extend_class]
|
||||
fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
|
||||
class.set_attr(
|
||||
identifier!(ctx, _fields),
|
||||
ctx.new_tuple(vec![
|
||||
$(
|
||||
ctx.new_str(ascii!($field)).into()
|
||||
),*
|
||||
])
|
||||
.into(),
|
||||
);
|
||||
|
||||
class.set_str_attr(
|
||||
"__match_args__",
|
||||
ctx.new_tuple(vec![
|
||||
$(
|
||||
ctx.new_str(ascii!($field)).into()
|
||||
),*
|
||||
]),
|
||||
ctx,
|
||||
);
|
||||
|
||||
class.set_attr(
|
||||
identifier!(ctx, _attributes),
|
||||
ctx.new_tuple(vec![
|
||||
$(
|
||||
ctx.new_str(ascii!($attr)).into()
|
||||
),*
|
||||
])
|
||||
.into(),
|
||||
);
|
||||
|
||||
// Signal that this is a built-in AST node with field defaults
|
||||
class.set_attr(
|
||||
ctx.intern_str("_field_types"),
|
||||
ctx.new_dict().into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[pyclass(module = "_ast", name = "mod", base = NodeAst)]
|
||||
pub(crate) struct NodeMod(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeMod {}
|
||||
impl_base_node!(NodeMod);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Module", base = NodeMod)]
|
||||
@@ -116,8 +162,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeStmt(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeStmt {}
|
||||
impl_base_node!(NodeStmt);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FunctionType", base = NodeMod)]
|
||||
@@ -316,8 +361,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExpr(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeExpr {}
|
||||
impl_base_node!(NodeExpr);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Continue", base = NodeStmt)]
|
||||
@@ -490,9 +534,18 @@ impl NodeExprConstant {
|
||||
.into(),
|
||||
);
|
||||
|
||||
class.set_str_attr(
|
||||
"__match_args__",
|
||||
ctx.new_tuple(vec![
|
||||
ctx.new_str(ascii!("value")).into(),
|
||||
ctx.new_str(ascii!("kind")).into(),
|
||||
]),
|
||||
ctx,
|
||||
);
|
||||
|
||||
class.set_attr(
|
||||
identifier!(ctx, _attributes),
|
||||
ctx.new_list(vec![
|
||||
ctx.new_tuple(vec![
|
||||
ctx.new_str(ascii!("lineno")).into(),
|
||||
ctx.new_str(ascii!("col_offset")).into(),
|
||||
ctx.new_str(ascii!("end_lineno")).into(),
|
||||
@@ -567,8 +620,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExprContext(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeExprContext {}
|
||||
impl_base_node!(NodeExprContext);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Slice", base = NodeExpr)]
|
||||
@@ -591,8 +643,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeBoolOp(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeBoolOp {}
|
||||
impl_base_node!(NodeBoolOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Del", base = NodeExprContext)]
|
||||
@@ -608,8 +659,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeOperator(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeOperator {}
|
||||
impl_base_node!(NodeOperator);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "Or", base = NodeBoolOp)]
|
||||
@@ -680,8 +730,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeUnaryOp(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeUnaryOp {}
|
||||
impl_base_node!(NodeUnaryOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "FloorDiv", base = NodeOperator)]
|
||||
@@ -707,8 +756,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeCmpOp(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeCmpOp {}
|
||||
impl_base_node!(NodeCmpOp);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "USub", base = NodeUnaryOp)]
|
||||
@@ -769,8 +817,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeExceptHandler(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeExceptHandler {}
|
||||
impl_base_node!(NodeExceptHandler);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "comprehension", base = NodeAst)]
|
||||
@@ -822,8 +869,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodePattern(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodePattern {}
|
||||
impl_base_node!(NodePattern);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "match_case", base = NodeAst)]
|
||||
@@ -884,8 +930,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeTypeIgnore(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeTypeIgnore {}
|
||||
impl_base_node!(NodeTypeIgnore);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "MatchOr", base = NodePattern)]
|
||||
@@ -898,8 +943,7 @@ impl_node!(
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct NodeTypeParam(NodeAst);
|
||||
|
||||
#[pyclass(flags(HAS_DICT, BASETYPE))]
|
||||
impl NodeTypeParam {}
|
||||
impl_base_node!(NodeTypeParam);
|
||||
|
||||
impl_node!(
|
||||
#[pyclass(module = "_ast", name = "TypeIgnore", base = NodeTypeIgnore)]
|
||||
@@ -1453,6 +1497,7 @@ const FIELD_TYPES: &[(&str, &[(&str, FieldType)])] = &[
|
||||
|
||||
pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
extend_module!(vm, module, {
|
||||
"AST" => NodeAst::make_class(&vm.ctx),
|
||||
"mod" => NodeMod::make_class(&vm.ctx),
|
||||
"Module" => NodeModModule::make_class(&vm.ctx),
|
||||
"Interactive" => NodeModInteractive::make_class(&vm.ctx),
|
||||
@@ -1582,6 +1627,9 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
|
||||
// Populate _field_types with real Python type objects
|
||||
populate_field_types(vm, module);
|
||||
populate_singletons(vm, module);
|
||||
force_ast_module_name(vm, module);
|
||||
populate_match_args_and_attributes(vm, module);
|
||||
}
|
||||
|
||||
fn populate_field_types(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
@@ -1607,6 +1655,10 @@ fn populate_field_types(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
.unwrap_or_else(|_| panic!("AST node type '{name}' not found in module"))
|
||||
};
|
||||
|
||||
let field_types_attr = vm.ctx.intern_str("_field_types");
|
||||
let annotations_attr = vm.ctx.intern_str("__annotations__");
|
||||
let empty_dict: PyObjectRef = vm.ctx.new_dict().into();
|
||||
|
||||
for &(class_name, fields) in FIELD_TYPES {
|
||||
if fields.is_empty() {
|
||||
continue;
|
||||
@@ -1648,10 +1700,8 @@ fn populate_field_types(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
|
||||
let dict_obj: PyObjectRef = dict.into();
|
||||
if let Some(type_obj) = class.downcast_ref::<PyType>() {
|
||||
type_obj.set_attr(vm.ctx.intern_str("_field_types"), dict_obj);
|
||||
// NOTE: CPython also sets __annotations__ = _field_types, but
|
||||
// RustPython AST types are not heap types so __annotations__
|
||||
// is not accessible via the type descriptor.
|
||||
type_obj.set_attr(field_types_attr, dict_obj.clone());
|
||||
type_obj.set_attr(annotations_attr, dict_obj);
|
||||
|
||||
// Set None as class-level default for optional fields.
|
||||
// When ast_type_init skips optional fields, the instance
|
||||
@@ -1667,4 +1717,109 @@ fn populate_field_types(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CPython sets __annotations__ for all built-in AST node classes, even
|
||||
// when _field_types is an empty dict (e.g., operators, Load/Store/Del).
|
||||
for (_name, value) in &module.dict() {
|
||||
let Some(type_obj) = value.downcast_ref::<PyType>() else {
|
||||
continue;
|
||||
};
|
||||
if let Some(field_types) = type_obj.get_attr(field_types_attr) {
|
||||
type_obj.set_attr(annotations_attr, field_types);
|
||||
}
|
||||
}
|
||||
|
||||
// Base AST classes (e.g., expr, stmt) should still expose __annotations__.
|
||||
const BASE_AST_TYPES: &[&str] = &[
|
||||
"mod",
|
||||
"stmt",
|
||||
"expr",
|
||||
"expr_context",
|
||||
"boolop",
|
||||
"operator",
|
||||
"unaryop",
|
||||
"cmpop",
|
||||
"excepthandler",
|
||||
"pattern",
|
||||
"type_ignore",
|
||||
"type_param",
|
||||
];
|
||||
for &class_name in BASE_AST_TYPES {
|
||||
let class = module
|
||||
.get_attr(class_name, vm)
|
||||
.unwrap_or_else(|_| panic!("AST class '{class_name}' not found in module"));
|
||||
let Some(type_obj) = class.downcast_ref::<PyType>() else {
|
||||
continue;
|
||||
};
|
||||
if type_obj.get_attr(field_types_attr).is_none() {
|
||||
type_obj.set_attr(field_types_attr, empty_dict.clone());
|
||||
}
|
||||
if type_obj.get_attr(annotations_attr).is_none() {
|
||||
type_obj.set_attr(annotations_attr, empty_dict.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_singletons(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
let instance_attr = vm.ctx.intern_str("_instance");
|
||||
const SINGLETON_TYPES: &[&str] = &[
|
||||
// expr_context
|
||||
"Load", "Store", "Del", // boolop
|
||||
"And", "Or", // operator
|
||||
"Add", "Sub", "Mult", "MatMult", "Div", "Mod", "Pow", "LShift", "RShift", "BitOr",
|
||||
"BitXor", "BitAnd", "FloorDiv", // unaryop
|
||||
"Invert", "Not", "UAdd", "USub", // cmpop
|
||||
"Eq", "NotEq", "Lt", "LtE", "Gt", "GtE", "Is", "IsNot", "In", "NotIn",
|
||||
];
|
||||
|
||||
for &class_name in SINGLETON_TYPES {
|
||||
let class = module
|
||||
.get_attr(class_name, vm)
|
||||
.unwrap_or_else(|_| panic!("AST class '{class_name}' not found in module"));
|
||||
let Some(type_obj) = class.downcast_ref::<PyType>() else {
|
||||
continue;
|
||||
};
|
||||
let instance = vm
|
||||
.ctx
|
||||
.new_base_object(type_obj.to_owned(), Some(vm.ctx.new_dict()));
|
||||
type_obj.set_attr(instance_attr, instance);
|
||||
}
|
||||
}
|
||||
|
||||
fn force_ast_module_name(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
let ast_name = vm.ctx.new_str("ast");
|
||||
for (_name, value) in &module.dict() {
|
||||
let Some(type_obj) = value.downcast_ref::<PyType>() else {
|
||||
continue;
|
||||
};
|
||||
type_obj.set_attr(identifier!(vm, __module__), ast_name.clone().into());
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_match_args_and_attributes(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
let fields_attr = vm.ctx.intern_str("_fields");
|
||||
let match_args_attr = vm.ctx.intern_str("__match_args__");
|
||||
let attributes_attr = vm.ctx.intern_str("_attributes");
|
||||
let empty_tuple: PyObjectRef = vm.ctx.empty_tuple.clone().into();
|
||||
|
||||
for (_name, value) in &module.dict() {
|
||||
let Some(type_obj) = value.downcast_ref::<PyType>() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
type_obj
|
||||
.slots
|
||||
.repr
|
||||
.store(Some(super::python::_ast::ast_repr));
|
||||
|
||||
if type_obj.get_attr(match_args_attr).is_none() {
|
||||
if let Some(fields) = type_obj.get_attr(fields_attr) {
|
||||
type_obj.set_attr(match_args_attr, fields);
|
||||
}
|
||||
}
|
||||
|
||||
if type_obj.get_attr(attributes_attr).is_none() {
|
||||
type_obj.set_attr(attributes_attr, empty_tuple.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
use super::{PY_CF_OPTIMIZED_AST, PY_CF_TYPE_COMMENTS, PY_COMPILE_FLAG_AST_ONLY};
|
||||
use super::{
|
||||
PY_CF_ALLOW_INCOMPLETE_INPUT, PY_CF_ALLOW_TOP_LEVEL_AWAIT, PY_CF_DONT_IMPLY_DEDENT,
|
||||
PY_CF_IGNORE_COOKIE, PY_CF_OPTIMIZED_AST, PY_CF_SOURCE_IS_UTF8, PY_CF_TYPE_COMMENTS,
|
||||
PY_COMPILE_FLAG_AST_ONLY,
|
||||
};
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _ast {
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTupleRef, PyType, PyTypeRef},
|
||||
class::PyClassImpl,
|
||||
function::FuncArgs,
|
||||
builtins::{PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef},
|
||||
class::{PyClassImpl, StaticType},
|
||||
function::{FuncArgs, KwArgs, PyMethodDef, PyMethodFlags},
|
||||
stdlib::ast::repr,
|
||||
types::{Constructor, Initializer},
|
||||
warn,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_ast", name = "AST")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
@@ -16,16 +23,225 @@ pub(crate) mod _ast {
|
||||
|
||||
#[pyclass(with(Constructor, Initializer), flags(BASETYPE, HAS_DICT))]
|
||||
impl NodeAst {
|
||||
#[extend_class]
|
||||
fn extend_class(ctx: &Context, class: &'static Py<PyType>) {
|
||||
let empty_tuple = ctx.empty_tuple.clone();
|
||||
class.set_str_attr("_fields", empty_tuple.clone(), ctx);
|
||||
class.set_str_attr("_attributes", empty_tuple.clone(), ctx);
|
||||
class.set_str_attr("__match_args__", empty_tuple.clone(), ctx);
|
||||
|
||||
const AST_REDUCE: PyMethodDef = PyMethodDef::new_const(
|
||||
"__reduce__",
|
||||
|zelf: PyObjectRef, vm: &VirtualMachine| -> PyResult<PyTupleRef> {
|
||||
ast_reduce(zelf, vm)
|
||||
},
|
||||
PyMethodFlags::METHOD,
|
||||
None,
|
||||
);
|
||||
const AST_REPLACE: PyMethodDef = PyMethodDef::new_const(
|
||||
"__replace__",
|
||||
|zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine| -> PyResult {
|
||||
ast_replace(zelf, args, vm)
|
||||
},
|
||||
PyMethodFlags::METHOD,
|
||||
None,
|
||||
);
|
||||
|
||||
class.set_str_attr("__reduce__", AST_REDUCE.to_proper_method(class, ctx), ctx);
|
||||
class.set_str_attr("__replace__", AST_REPLACE.to_proper_method(class, ctx), ctx);
|
||||
class.slots.repr.store(Some(ast_repr));
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
fn _fields(ctx: &Context) -> PyTupleRef {
|
||||
ctx.empty_tuple.clone()
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
fn _attributes(ctx: &Context) -> PyTupleRef {
|
||||
ctx.empty_tuple.clone()
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
fn __match_args__(ctx: &Context) -> PyTupleRef {
|
||||
ctx.empty_tuple.clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
ast_reduce(zelf, vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __replace__(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
ast_replace(zelf, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ast_reduce(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
let dict = zelf.as_object().dict();
|
||||
let cls = zelf.class();
|
||||
let type_obj: PyObjectRef = cls.to_owned().into();
|
||||
|
||||
let Some(dict) = dict else {
|
||||
return Ok(vm.ctx.new_tuple(vec![type_obj]));
|
||||
};
|
||||
|
||||
let fields = cls.get_attr(vm.ctx.intern_str("_fields"));
|
||||
if let Some(fields) = fields {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let mut positional: Vec<PyObjectRef> = Vec::new();
|
||||
for field in fields {
|
||||
if let Some(value) = dict.get_item_opt::<str>(field.as_str(), vm)? {
|
||||
positional.push(vm.ctx.none());
|
||||
drop(value);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let args: PyObjectRef = vm.ctx.new_tuple(positional).into();
|
||||
let dict_obj: PyObjectRef = dict.into();
|
||||
return Ok(vm.ctx.new_tuple(vec![type_obj, args, dict_obj]));
|
||||
}
|
||||
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_tuple(vec![type_obj, vm.ctx.new_tuple(vec![]).into(), dict.into()]))
|
||||
}
|
||||
|
||||
pub(crate) fn ast_replace(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
if !args.args.is_empty() {
|
||||
return Err(vm.new_type_error("__replace__() takes no positional arguments".to_owned()));
|
||||
}
|
||||
|
||||
let cls = zelf.class();
|
||||
let fields = cls.get_attr(vm.ctx.intern_str("_fields"));
|
||||
let attributes = cls.get_attr(vm.ctx.intern_str("_attributes"));
|
||||
let dict = zelf.as_object().dict();
|
||||
|
||||
let mut expecting: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
if let Some(fields) = fields.clone() {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
for field in fields {
|
||||
expecting.insert(field.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
expecting.insert(attr.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
for (key, _value) in &args.kwargs {
|
||||
if !expecting.remove(key) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{}.__replace__ got an unexpected keyword argument '{}'.",
|
||||
cls.name(),
|
||||
key
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dict) = dict.as_ref() {
|
||||
for (key, _value) in dict.items_vec() {
|
||||
if let Ok(key) = key.downcast::<PyStr>() {
|
||||
expecting.remove(key.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
expecting.remove(attr.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discard optional fields (T | None).
|
||||
if let Some(field_types) = cls.get_attr(vm.ctx.intern_str("_field_types"))
|
||||
&& let Ok(field_types) = field_types.downcast::<crate::builtins::PyDict>()
|
||||
{
|
||||
for (key, value) in field_types.items_vec() {
|
||||
let Ok(key) = key.downcast::<PyStr>() else {
|
||||
continue;
|
||||
};
|
||||
if value.fast_isinstance(vm.ctx.types.union_type) {
|
||||
expecting.remove(key.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !expecting.is_empty() {
|
||||
let mut names: Vec<String> = expecting
|
||||
.into_iter()
|
||||
.map(|name| format!("{name:?}"))
|
||||
.collect();
|
||||
names.sort();
|
||||
let missing = names.join(", ");
|
||||
let count = names.len();
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{}.__replace__ missing {} keyword argument{}: {}.",
|
||||
cls.name(),
|
||||
count,
|
||||
if count == 1 { "" } else { "s" },
|
||||
missing
|
||||
)));
|
||||
}
|
||||
|
||||
let payload = vm.ctx.new_dict();
|
||||
if let Some(dict) = dict {
|
||||
if let Some(fields) = fields.clone() {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
for field in fields {
|
||||
if let Some(value) = dict.get_item_opt::<str>(field.as_str(), vm)? {
|
||||
payload.set_item(field.as_object(), value, vm)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
if let Some(value) = dict.get_item_opt::<str>(attr.as_str(), vm)? {
|
||||
payload.set_item(attr.as_object(), value, vm)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (key, value) in args.kwargs {
|
||||
payload.set_item(vm.ctx.intern_str(key), value, vm)?;
|
||||
}
|
||||
|
||||
let type_obj: PyObjectRef = cls.to_owned().into();
|
||||
let kwargs = payload
|
||||
.items_vec()
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
let key = key
|
||||
.downcast::<PyStr>()
|
||||
.map_err(|_| vm.new_type_error("keywords must be strings".to_owned()))?;
|
||||
Ok((key.as_str().to_owned(), value))
|
||||
})
|
||||
.collect::<PyResult<IndexMap<String, PyObjectRef>>>()?;
|
||||
let result = type_obj.call(FuncArgs::new(vec![], KwArgs::new(kwargs)), vm)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_repr(zelf: &crate::PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
let repr = repr::repr_ast_node(vm, &zelf.to_owned(), 3)?;
|
||||
Ok(vm.ctx.new_str(repr))
|
||||
}
|
||||
|
||||
impl Constructor for NodeAst {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
if args.args.is_empty()
|
||||
&& args.kwargs.is_empty()
|
||||
&& let Some(instance) = cls.get_attr(vm.ctx.intern_str("_instance"))
|
||||
{
|
||||
return Ok(instance);
|
||||
}
|
||||
|
||||
// AST nodes accept extra arguments (unlike object.__new__)
|
||||
// This matches CPython's behavior where AST has its own tp_new
|
||||
let dict = if cls
|
||||
@@ -55,7 +271,21 @@ pub(crate) mod _ast {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let fields = zelf.get_attr("_fields", vm)?;
|
||||
let fields = zelf
|
||||
.class()
|
||||
.get_attr(vm.ctx.intern_str("_fields"))
|
||||
.ok_or_else(|| {
|
||||
let module = zelf
|
||||
.class()
|
||||
.get_attr(vm.ctx.intern_str("__module__"))
|
||||
.and_then(|obj| obj.try_to_value::<String>(vm).ok())
|
||||
.unwrap_or_else(|| "ast".to_owned());
|
||||
vm.new_attribute_error(format!(
|
||||
"type object '{}.{}' has no attribute '_fields'",
|
||||
module,
|
||||
zelf.class().name()
|
||||
))
|
||||
})?;
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let n_args = args.args.len();
|
||||
if n_args > fields.len() {
|
||||
@@ -69,6 +299,7 @@ pub(crate) mod _ast {
|
||||
|
||||
// Track which fields were set
|
||||
let mut set_fields = std::collections::HashSet::new();
|
||||
let mut attributes: Option<Vec<PyStrRef>> = None;
|
||||
|
||||
for (name, arg) in fields.iter().zip(args.args) {
|
||||
zelf.set_attr(name, arg, vm)?;
|
||||
@@ -84,6 +315,36 @@ pub(crate) mod _ast {
|
||||
key
|
||||
)));
|
||||
}
|
||||
|
||||
if fields.iter().all(|field| field.as_str() != key) {
|
||||
let attrs = if let Some(attrs) = &attributes {
|
||||
attrs
|
||||
} else {
|
||||
let attrs = zelf
|
||||
.class()
|
||||
.get_attr(vm.ctx.intern_str("_attributes"))
|
||||
.and_then(|attr| attr.try_to_value::<Vec<PyStrRef>>(vm).ok())
|
||||
.unwrap_or_default();
|
||||
attributes = Some(attrs);
|
||||
attributes.as_ref().unwrap()
|
||||
};
|
||||
if attrs.iter().all(|attr| attr.as_str() != key) {
|
||||
let message = vm.ctx.new_str(format!(
|
||||
"{}.__init__ got an unexpected keyword argument '{}'. \
|
||||
Support for arbitrary keyword arguments is deprecated and will be removed in Python 3.15.",
|
||||
zelf.class().name(),
|
||||
key
|
||||
));
|
||||
warn::warn(
|
||||
message,
|
||||
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
|
||||
1,
|
||||
None,
|
||||
vm,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
set_fields.insert(key.clone());
|
||||
zelf.set_attr(vm.ctx.intern_str(key), value, vm)?;
|
||||
}
|
||||
@@ -112,11 +373,27 @@ pub(crate) mod _ast {
|
||||
// expr_context — default to Load()
|
||||
let load_type =
|
||||
super::super::pyast::NodeExprContextLoad::make_class(&vm.ctx);
|
||||
let load_instance =
|
||||
vm.ctx.new_base_object(load_type, Some(vm.ctx.new_dict()));
|
||||
let load_instance = load_type
|
||||
.get_attr(vm.ctx.intern_str("_instance"))
|
||||
.unwrap_or_else(|| {
|
||||
vm.ctx.new_base_object(load_type, Some(vm.ctx.new_dict()))
|
||||
});
|
||||
zelf.set_attr(vm.ctx.intern_str(field.as_str()), load_instance, vm)?;
|
||||
} else {
|
||||
// Required field missing: emit DeprecationWarning (CPython behavior).
|
||||
let message = vm.ctx.new_str(format!(
|
||||
"{}.__init__ missing 1 required positional argument: '{}'",
|
||||
zelf.class().name(),
|
||||
field.as_str()
|
||||
));
|
||||
warn::warn(
|
||||
message,
|
||||
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
|
||||
1,
|
||||
None,
|
||||
vm,
|
||||
)?;
|
||||
}
|
||||
// else: required field, no default set
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,21 +406,110 @@ pub(crate) mod _ast {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr(name = "PyCF_SOURCE_IS_UTF8")]
|
||||
use super::PY_CF_SOURCE_IS_UTF8;
|
||||
|
||||
#[pyattr(name = "PyCF_DONT_IMPLY_DEDENT")]
|
||||
use super::PY_CF_DONT_IMPLY_DEDENT;
|
||||
|
||||
#[pyattr(name = "PyCF_ONLY_AST")]
|
||||
use super::PY_COMPILE_FLAG_AST_ONLY;
|
||||
|
||||
#[pyattr(name = "PyCF_OPTIMIZED_AST")]
|
||||
use super::PY_CF_OPTIMIZED_AST;
|
||||
#[pyattr(name = "PyCF_IGNORE_COOKIE")]
|
||||
use super::PY_CF_IGNORE_COOKIE;
|
||||
|
||||
#[pyattr(name = "PyCF_TYPE_COMMENTS")]
|
||||
use super::PY_CF_TYPE_COMMENTS;
|
||||
|
||||
#[pyattr(name = "PyCF_ALLOW_TOP_LEVEL_AWAIT")]
|
||||
use super::PY_CF_ALLOW_TOP_LEVEL_AWAIT;
|
||||
|
||||
#[pyattr(name = "PyCF_ALLOW_INCOMPLETE_INPUT")]
|
||||
use super::PY_CF_ALLOW_INCOMPLETE_INPUT;
|
||||
|
||||
#[pyattr(name = "PyCF_OPTIMIZED_AST")]
|
||||
use super::PY_CF_OPTIMIZED_AST;
|
||||
|
||||
pub(crate) fn module_exec(
|
||||
vm: &VirtualMachine,
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
) -> PyResult<()> {
|
||||
__module_exec(vm, module);
|
||||
super::super::pyast::extend_module_nodes(vm, module);
|
||||
|
||||
let ast_type = module
|
||||
.get_attr("AST", vm)?
|
||||
.downcast::<PyType>()
|
||||
.map_err(|_| vm.new_type_error("AST is not a type".to_owned()))?;
|
||||
let ctx = &vm.ctx;
|
||||
let empty_tuple = ctx.empty_tuple.clone();
|
||||
ast_type.set_str_attr("_fields", empty_tuple.clone(), ctx);
|
||||
ast_type.set_str_attr("_attributes", empty_tuple.clone(), ctx);
|
||||
ast_type.set_str_attr("__match_args__", empty_tuple.clone(), ctx);
|
||||
|
||||
const AST_REDUCE: PyMethodDef = PyMethodDef::new_const(
|
||||
"__reduce__",
|
||||
|zelf: PyObjectRef, vm: &VirtualMachine| -> PyResult<PyTupleRef> {
|
||||
ast_reduce(zelf, vm)
|
||||
},
|
||||
PyMethodFlags::METHOD,
|
||||
None,
|
||||
);
|
||||
const AST_REPLACE: PyMethodDef = PyMethodDef::new_const(
|
||||
"__replace__",
|
||||
|zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine| -> PyResult {
|
||||
ast_replace(zelf, args, vm)
|
||||
},
|
||||
PyMethodFlags::METHOD,
|
||||
None,
|
||||
);
|
||||
let base_type = NodeAst::static_type();
|
||||
ast_type.set_str_attr(
|
||||
"__reduce__",
|
||||
AST_REDUCE.to_proper_method(base_type, ctx),
|
||||
ctx,
|
||||
);
|
||||
ast_type.set_str_attr(
|
||||
"__replace__",
|
||||
AST_REPLACE.to_proper_method(base_type, ctx),
|
||||
ctx,
|
||||
);
|
||||
ast_type.slots.repr.store(Some(ast_repr));
|
||||
|
||||
const EXPR_DOC: &str = "expr = BoolOp(boolop op, expr* values)\n\
|
||||
| NamedExpr(expr target, expr value)\n\
|
||||
| BinOp(expr left, operator op, expr right)\n\
|
||||
| UnaryOp(unaryop op, expr operand)\n\
|
||||
| Lambda(arguments args, expr body)\n\
|
||||
| IfExp(expr test, expr body, expr orelse)\n\
|
||||
| Dict(expr?* keys, expr* values)\n\
|
||||
| Set(expr* elts)\n\
|
||||
| ListComp(expr elt, comprehension* generators)\n\
|
||||
| SetComp(expr elt, comprehension* generators)\n\
|
||||
| DictComp(expr key, expr value, comprehension* generators)\n\
|
||||
| GeneratorExp(expr elt, comprehension* generators)\n\
|
||||
| Await(expr value)\n\
|
||||
| Yield(expr? value)\n\
|
||||
| YieldFrom(expr value)\n\
|
||||
| Compare(expr left, cmpop* ops, expr* comparators)\n\
|
||||
| Call(expr func, expr* args, keyword* keywords)\n\
|
||||
| FormattedValue(expr value, int conversion, expr? format_spec)\n\
|
||||
| Interpolation(expr value, constant str, int conversion, expr? format_spec)\n\
|
||||
| JoinedStr(expr* values)\n\
|
||||
| TemplateStr(expr* values)\n\
|
||||
| Constant(constant value, string? kind)\n\
|
||||
| Attribute(expr value, identifier attr, expr_context ctx)\n\
|
||||
| Subscript(expr value, expr slice, expr_context ctx)\n\
|
||||
| Starred(expr value, expr_context ctx)\n\
|
||||
| Name(identifier id, expr_context ctx)\n\
|
||||
| List(expr* elts, expr_context ctx)\n\
|
||||
| Tuple(expr* elts, expr_context ctx)\n\
|
||||
| Slice(expr? lower, expr? upper, expr? step)";
|
||||
let expr_type = super::super::pyast::NodeExpr::static_type();
|
||||
expr_type.set_attr(
|
||||
identifier!(vm.ctx, __doc__),
|
||||
vm.ctx.new_str(EXPR_DOC).into(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use crate::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyList, PyTuple},
|
||||
class::PyClassImpl,
|
||||
stdlib::ast::NodeAst,
|
||||
};
|
||||
|
||||
fn repr_ast_list(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<String> {
|
||||
if items.is_empty() {
|
||||
let empty_list: PyObjectRef = vm.ctx.new_list(vec![]).into();
|
||||
return Ok(empty_list.repr(vm)?.to_string());
|
||||
}
|
||||
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
let first = &items[0];
|
||||
let last = items.last().unwrap();
|
||||
|
||||
for (idx, item) in [first, last].iter().enumerate() {
|
||||
if idx == 1 && items.len() == 1 {
|
||||
break;
|
||||
}
|
||||
let repr = if item.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, item, depth.saturating_sub(1))?
|
||||
} else {
|
||||
item.repr(vm)?.to_string()
|
||||
};
|
||||
parts.push(repr);
|
||||
}
|
||||
|
||||
let mut rendered = String::from("[");
|
||||
if !parts.is_empty() {
|
||||
rendered.push_str(&parts[0]);
|
||||
}
|
||||
if items.len() > 2 {
|
||||
if !parts[0].is_empty() {
|
||||
rendered.push_str(", ...");
|
||||
}
|
||||
if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
}
|
||||
} else if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
}
|
||||
rendered.push(']');
|
||||
Ok(rendered)
|
||||
}
|
||||
|
||||
fn repr_ast_tuple(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<String> {
|
||||
if items.is_empty() {
|
||||
let empty_tuple: PyObjectRef = vm.ctx.empty_tuple.clone().into();
|
||||
return Ok(empty_tuple.repr(vm)?.to_string());
|
||||
}
|
||||
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
let first = &items[0];
|
||||
let last = items.last().unwrap();
|
||||
|
||||
for (idx, item) in [first, last].iter().enumerate() {
|
||||
if idx == 1 && items.len() == 1 {
|
||||
break;
|
||||
}
|
||||
let repr = if item.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, item, depth.saturating_sub(1))?
|
||||
} else {
|
||||
item.repr(vm)?.to_string()
|
||||
};
|
||||
parts.push(repr);
|
||||
}
|
||||
|
||||
let mut rendered = String::from("(");
|
||||
if !parts.is_empty() {
|
||||
rendered.push_str(&parts[0]);
|
||||
}
|
||||
if items.len() > 2 {
|
||||
if !parts[0].is_empty() {
|
||||
rendered.push_str(", ...");
|
||||
}
|
||||
if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
}
|
||||
} else if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
}
|
||||
if items.len() == 1 {
|
||||
rendered.push(',');
|
||||
}
|
||||
rendered.push(')');
|
||||
Ok(rendered)
|
||||
}
|
||||
|
||||
pub(crate) fn repr_ast_node(
|
||||
vm: &VirtualMachine,
|
||||
obj: &PyObjectRef,
|
||||
depth: usize,
|
||||
) -> PyResult<String> {
|
||||
let cls = obj.class();
|
||||
if depth == 0 {
|
||||
return Ok(format!("{}(...)", cls.name()));
|
||||
}
|
||||
|
||||
let fields = cls.get_attr(vm.ctx.intern_str("_fields"));
|
||||
let fields = match fields {
|
||||
Some(fields) => fields.try_to_value::<Vec<crate::builtins::PyStrRef>>(vm)?,
|
||||
None => return Ok(format!("{}(...)", cls.name())),
|
||||
};
|
||||
|
||||
if fields.is_empty() {
|
||||
return Ok(format!("{}()", cls.name()));
|
||||
}
|
||||
|
||||
let mut rendered = String::new();
|
||||
rendered.push_str(&cls.name());
|
||||
rendered.push('(');
|
||||
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let value = obj.get_attr(field, vm)?;
|
||||
let value_repr = if value.fast_isinstance(vm.ctx.types.list_type) {
|
||||
let list = value
|
||||
.downcast::<PyList>()
|
||||
.expect("list type should downcast");
|
||||
repr_ast_list(vm, list.borrow_vec().to_vec(), depth)?
|
||||
} else if value.fast_isinstance(vm.ctx.types.tuple_type) {
|
||||
let tuple = value
|
||||
.downcast::<PyTuple>()
|
||||
.expect("tuple type should downcast");
|
||||
repr_ast_tuple(vm, tuple.as_slice().to_vec(), depth)?
|
||||
} else if value.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, &value, depth.saturating_sub(1))?
|
||||
} else {
|
||||
value.repr(vm)?.to_string()
|
||||
};
|
||||
|
||||
if idx > 0 {
|
||||
rendered.push_str(", ");
|
||||
}
|
||||
rendered.push_str(field.as_str());
|
||||
rendered.push('=');
|
||||
rendered.push_str(&value_repr);
|
||||
}
|
||||
|
||||
rendered.push(')');
|
||||
Ok(rendered)
|
||||
}
|
||||
@@ -159,6 +159,9 @@ impl Node for ast::StmtFunctionDef {
|
||||
is_async,
|
||||
range: _range,
|
||||
} = self;
|
||||
let source_code = source_file.to_source_code();
|
||||
let def_line = source_code.line_index(name.range.start());
|
||||
let range = TextRange::new(source_code.line_start(def_line), _range.end());
|
||||
|
||||
let cls = if !is_async {
|
||||
pyast::NodeStmtFunctionDef::static_type().to_owned()
|
||||
@@ -192,7 +195,7 @@ impl Node for ast::StmtFunctionDef {
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, vm, source_file);
|
||||
node_add_location(&dict, range, vm, source_file);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
@@ -202,6 +205,7 @@ impl Node for ast::StmtFunctionDef {
|
||||
) -> PyResult<Self> {
|
||||
let _cls = _object.class();
|
||||
let is_async = _cls.is(pyast::NodeStmtAsyncFunctionDef::static_type());
|
||||
let range = range_from_object(_vm, source_file, _object.clone(), "FunctionDef")?;
|
||||
Ok(Self {
|
||||
node_index: Default::default(),
|
||||
name: Node::ast_from_object(
|
||||
@@ -234,9 +238,10 @@ impl Node for ast::StmtFunctionDef {
|
||||
type_params: Node::ast_from_object(
|
||||
_vm,
|
||||
source_file,
|
||||
get_node_field_opt(_vm, &_object, "type_params")?.unwrap_or_else(|| _vm.ctx.none()),
|
||||
get_node_field_opt(_vm, &_object, "type_params")?
|
||||
.unwrap_or_else(|| _vm.ctx.new_list(Vec::new()).into()),
|
||||
)?,
|
||||
range: range_from_object(_vm, source_file, _object, "FunctionDef")?,
|
||||
range,
|
||||
is_async,
|
||||
})
|
||||
}
|
||||
@@ -255,6 +260,9 @@ impl Node for ast::StmtClassDef {
|
||||
range: _range,
|
||||
} = self;
|
||||
let (bases, keywords) = split_class_def_args(arguments);
|
||||
let source_code = source_file.to_source_code();
|
||||
let class_line = source_code.line_index(name.range.start());
|
||||
let range = TextRange::new(source_code.line_start(class_line), _range.end());
|
||||
let node = NodeAst
|
||||
.into_ref_with_type(_vm, pyast::NodeStmtClassDef::static_type().to_owned())
|
||||
.unwrap();
|
||||
@@ -293,7 +301,7 @@ impl Node for ast::StmtClassDef {
|
||||
_vm,
|
||||
)
|
||||
.unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_file);
|
||||
node_add_location(&dict, range, _vm, source_file);
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
@@ -332,7 +340,8 @@ impl Node for ast::StmtClassDef {
|
||||
type_params: Node::ast_from_object(
|
||||
_vm,
|
||||
source_file,
|
||||
get_node_field_opt(_vm, &_object, "type_params")?.unwrap_or_else(|| _vm.ctx.none()),
|
||||
get_node_field_opt(_vm, &_object, "type_params")?
|
||||
.unwrap_or_else(|| _vm.ctx.new_list(Vec::new()).into()),
|
||||
)?,
|
||||
range: range_from_object(_vm, source_file, _object, "ClassDef")?,
|
||||
})
|
||||
@@ -469,7 +478,9 @@ impl Node for ast::StmtTypeAlias {
|
||||
.unwrap();
|
||||
dict.set_item(
|
||||
"type_params",
|
||||
type_params.ast_to_object(_vm, source_file),
|
||||
type_params
|
||||
.map(|tp| tp.ast_to_object(_vm, source_file))
|
||||
.unwrap_or_else(|| _vm.ctx.new_list(Vec::new()).into()),
|
||||
_vm,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1099,7 +1110,13 @@ impl Node for ast::StmtImportFrom {
|
||||
level: get_node_field_opt(vm, &_object, "level")?
|
||||
.map(|obj| -> PyResult<u32> {
|
||||
let int: PyRef<PyInt> = obj.try_into_value(vm)?;
|
||||
int.try_to_primitive(vm)
|
||||
let value: i64 = int.try_to_primitive(vm)?;
|
||||
if value < 0 {
|
||||
return Err(vm.new_value_error("Negative ImportFrom level".to_owned()));
|
||||
}
|
||||
u32::try_from(value).map_err(|_| {
|
||||
vm.new_overflow_error("ImportFrom level out of range".to_owned())
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(0),
|
||||
@@ -1217,7 +1234,28 @@ impl Node for ast::StmtPass {
|
||||
.into_ref_with_type(_vm, pyast::NodeStmtPass::static_type().to_owned())
|
||||
.unwrap();
|
||||
let dict = node.as_object().dict().unwrap();
|
||||
node_add_location(&dict, _range, _vm, source_file);
|
||||
let location = super::text_range_to_source_range(source_file, _range);
|
||||
let start_row = location.start.row.get();
|
||||
let start_col = location.start.column.get();
|
||||
let mut end_row = location.end.row.get();
|
||||
let mut end_col = location.end.column.get();
|
||||
|
||||
// Align with CPython: when docstring optimization replaces a lone
|
||||
// docstring with `pass`, the end position is on the same line even if
|
||||
// it extends past the physical line length.
|
||||
if end_row != start_row && _range.len() == TextSize::from(4) {
|
||||
end_row = start_row;
|
||||
end_col = start_col + 4;
|
||||
}
|
||||
|
||||
dict.set_item("lineno", _vm.ctx.new_int(start_row).into(), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("col_offset", _vm.ctx.new_int(start_col).into(), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("end_lineno", _vm.ctx.new_int(end_row).into(), _vm)
|
||||
.unwrap();
|
||||
dict.set_item("end_col_offset", _vm.ctx.new_int(end_col).into(), _vm)
|
||||
.unwrap();
|
||||
node.into()
|
||||
}
|
||||
fn ast_from_object(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use super::constant::{Constant, ConstantLiteral};
|
||||
use super::*;
|
||||
use crate::warn;
|
||||
use ast::str_prefix::StringLiteralPrefix;
|
||||
|
||||
fn ruff_fstring_element_into_iter(
|
||||
mut fstring_element: ast::InterpolatedStringElements,
|
||||
@@ -45,6 +47,193 @@ fn ruff_fstring_element_to_joined_str_part(
|
||||
}
|
||||
}
|
||||
|
||||
fn push_joined_str_literal(
|
||||
output: &mut Vec<JoinedStrPart>,
|
||||
pending: &mut Option<(String, StringLiteralPrefix, TextRange)>,
|
||||
) {
|
||||
if let Some((value, prefix, range)) = pending.take()
|
||||
&& !value.is_empty()
|
||||
{
|
||||
output.push(JoinedStrPart::Constant(Constant::new_str(
|
||||
value, prefix, range,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_joined_str_parts(values: Vec<JoinedStrPart>) -> Vec<JoinedStrPart> {
|
||||
let mut output = Vec::with_capacity(values.len());
|
||||
let mut pending: Option<(String, StringLiteralPrefix, TextRange)> = None;
|
||||
|
||||
for part in values {
|
||||
match part {
|
||||
JoinedStrPart::Constant(constant) => {
|
||||
let ConstantLiteral::Str { value, prefix } = constant.value else {
|
||||
push_joined_str_literal(&mut output, &mut pending);
|
||||
output.push(JoinedStrPart::Constant(constant));
|
||||
continue;
|
||||
};
|
||||
let value: String = value.into();
|
||||
if let Some((pending_value, _, _)) = pending.as_mut() {
|
||||
pending_value.push_str(&value);
|
||||
} else {
|
||||
pending = Some((value, prefix, constant.range));
|
||||
}
|
||||
}
|
||||
JoinedStrPart::FormattedValue(value) => {
|
||||
push_joined_str_literal(&mut output, &mut pending);
|
||||
output.push(JoinedStrPart::FormattedValue(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push_joined_str_literal(&mut output, &mut pending);
|
||||
output
|
||||
}
|
||||
|
||||
fn push_template_str_literal(
|
||||
output: &mut Vec<TemplateStrPart>,
|
||||
pending: &mut Option<(String, StringLiteralPrefix, TextRange)>,
|
||||
) {
|
||||
if let Some((value, prefix, range)) = pending.take()
|
||||
&& !value.is_empty()
|
||||
{
|
||||
output.push(TemplateStrPart::Constant(Constant::new_str(
|
||||
value, prefix, range,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_template_str_parts(values: Vec<TemplateStrPart>) -> Vec<TemplateStrPart> {
|
||||
let mut output = Vec::with_capacity(values.len());
|
||||
let mut pending: Option<(String, StringLiteralPrefix, TextRange)> = None;
|
||||
|
||||
for part in values {
|
||||
match part {
|
||||
TemplateStrPart::Constant(constant) => {
|
||||
let ConstantLiteral::Str { value, prefix } = constant.value else {
|
||||
push_template_str_literal(&mut output, &mut pending);
|
||||
output.push(TemplateStrPart::Constant(constant));
|
||||
continue;
|
||||
};
|
||||
let value: String = value.into();
|
||||
if let Some((pending_value, _, _)) = pending.as_mut() {
|
||||
pending_value.push_str(&value);
|
||||
} else {
|
||||
pending = Some((value, prefix, constant.range));
|
||||
}
|
||||
}
|
||||
TemplateStrPart::Interpolation(value) => {
|
||||
push_template_str_literal(&mut output, &mut pending);
|
||||
output.push(TemplateStrPart::Interpolation(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push_template_str_literal(&mut output, &mut pending);
|
||||
output
|
||||
}
|
||||
|
||||
fn warn_invalid_escape_sequences_in_format_spec(
|
||||
vm: &VirtualMachine,
|
||||
source_file: &SourceFile,
|
||||
range: TextRange,
|
||||
) {
|
||||
let source = source_file.source_text();
|
||||
let start = range.start().to_usize();
|
||||
let end = range.end().to_usize();
|
||||
if start >= end || end > source.len() {
|
||||
return;
|
||||
}
|
||||
let mut raw = &source[start..end];
|
||||
if raw.starts_with(':') {
|
||||
raw = &raw[1..];
|
||||
}
|
||||
|
||||
let mut chars = raw.chars().peekable();
|
||||
while let Some(ch) = chars.next() {
|
||||
if ch != '\\' {
|
||||
continue;
|
||||
}
|
||||
let Some(next) = chars.next() else {
|
||||
break;
|
||||
};
|
||||
let valid = match next {
|
||||
'\\' | '\'' | '"' | 'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' => true,
|
||||
'\n' => true,
|
||||
'\r' => {
|
||||
if let Some('\n') = chars.peek().copied() {
|
||||
chars.next();
|
||||
}
|
||||
true
|
||||
}
|
||||
'0'..='7' => {
|
||||
for _ in 0..2 {
|
||||
if let Some('0'..='7') = chars.peek().copied() {
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
'x' => {
|
||||
for _ in 0..2 {
|
||||
if chars.peek().is_some_and(|c| c.is_ascii_hexdigit()) {
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
'u' => {
|
||||
for _ in 0..4 {
|
||||
if chars.peek().is_some_and(|c| c.is_ascii_hexdigit()) {
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
'U' => {
|
||||
for _ in 0..8 {
|
||||
if chars.peek().is_some_and(|c| c.is_ascii_hexdigit()) {
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
'N' => {
|
||||
if let Some('{') = chars.peek().copied() {
|
||||
chars.next();
|
||||
for c in chars.by_ref() {
|
||||
if c == '}' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if !valid {
|
||||
let message = vm.ctx.new_str(format!(
|
||||
"\"\\{next}\" is an invalid escape sequence. Such sequences will not work in the future. Did you mean \"\\\\{next}\"? A raw string is also an option."
|
||||
));
|
||||
let _ = warn::warn(
|
||||
message,
|
||||
Some(vm.ctx.exceptions.syntax_warning.to_owned()),
|
||||
1,
|
||||
None,
|
||||
vm,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ruff_format_spec_to_joined_str(
|
||||
format_spec: Option<Box<ast::InterpolatedStringFormatSpec>>,
|
||||
) -> Option<Box<JoinedStr>> {
|
||||
@@ -56,10 +245,18 @@ fn ruff_format_spec_to_joined_str(
|
||||
elements,
|
||||
node_index: _,
|
||||
} = *format_spec;
|
||||
let range = if range.start() > ruff_text_size::TextSize::from(0) {
|
||||
TextRange::new(
|
||||
range.start() - ruff_text_size::TextSize::from(1),
|
||||
range.end(),
|
||||
)
|
||||
} else {
|
||||
range
|
||||
};
|
||||
let values: Vec<_> = ruff_fstring_element_into_iter(elements)
|
||||
.map(ruff_fstring_element_to_joined_str_part)
|
||||
.collect();
|
||||
let values = values.into_boxed_slice();
|
||||
let values = normalize_joined_str_parts(values).into_boxed_slice();
|
||||
Some(Box::new(JoinedStr { range, values }))
|
||||
}
|
||||
}
|
||||
@@ -353,6 +550,14 @@ pub(super) fn fstring_to_object(
|
||||
}
|
||||
}
|
||||
}
|
||||
let values = normalize_joined_str_parts(values);
|
||||
for part in &values {
|
||||
if let JoinedStrPart::FormattedValue(value) = part
|
||||
&& let Some(format_spec) = &value.format_spec
|
||||
{
|
||||
warn_invalid_escape_sequences_in_format_spec(vm, source_file, format_spec.range);
|
||||
}
|
||||
}
|
||||
let c = JoinedStr {
|
||||
range,
|
||||
values: values.into_boxed_slice(),
|
||||
@@ -384,40 +589,106 @@ fn ruff_tstring_element_to_template_str_part(
|
||||
format_spec,
|
||||
node_index: _,
|
||||
}) => {
|
||||
// Get the expression source text for the "str" field
|
||||
let expr_str = debug_text
|
||||
.map(|dt| dt.leading.to_string() + &dt.trailing)
|
||||
.unwrap_or_else(|| source_file.slice(expression.range()).to_string());
|
||||
let expr_range =
|
||||
extend_expr_range_with_wrapping_parens(source_file, range, expression.range())
|
||||
.unwrap_or_else(|| expression.range());
|
||||
let expr_str = if let Some(debug_text) = debug_text {
|
||||
let expr_source = source_file.slice(expr_range);
|
||||
let mut expr_with_debug = String::with_capacity(
|
||||
debug_text.leading.len() + expr_source.len() + debug_text.trailing.len(),
|
||||
);
|
||||
expr_with_debug.push_str(&debug_text.leading);
|
||||
expr_with_debug.push_str(expr_source);
|
||||
expr_with_debug.push_str(&debug_text.trailing);
|
||||
strip_interpolation_expr(&expr_with_debug)
|
||||
} else {
|
||||
tstring_interpolation_expr_str(source_file, range, expr_range)
|
||||
};
|
||||
TemplateStrPart::Interpolation(TStringInterpolation {
|
||||
value: expression,
|
||||
str: expr_str,
|
||||
conversion,
|
||||
format_spec: ruff_format_spec_to_template_str(format_spec, source_file),
|
||||
format_spec: ruff_format_spec_to_joined_str(format_spec),
|
||||
range,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ruff_format_spec_to_template_str(
|
||||
format_spec: Option<Box<ast::InterpolatedStringFormatSpec>>,
|
||||
fn tstring_interpolation_expr_str(
|
||||
source_file: &SourceFile,
|
||||
) -> Option<Box<TemplateStr>> {
|
||||
match format_spec {
|
||||
None => None,
|
||||
Some(format_spec) => {
|
||||
let ast::InterpolatedStringFormatSpec {
|
||||
range,
|
||||
elements,
|
||||
node_index: _,
|
||||
} = *format_spec;
|
||||
let values: Vec<_> = ruff_fstring_element_into_iter(elements)
|
||||
.map(|e| ruff_tstring_element_to_template_str_part(e, source_file))
|
||||
.collect();
|
||||
let values = values.into_boxed_slice();
|
||||
Some(Box::new(TemplateStr { range, values }))
|
||||
interpolation_range: TextRange,
|
||||
expr_range: TextRange,
|
||||
) -> String {
|
||||
let expr_range =
|
||||
extend_expr_range_with_wrapping_parens(source_file, interpolation_range, expr_range)
|
||||
.unwrap_or(expr_range);
|
||||
let start = interpolation_range.start() + TextSize::from(1);
|
||||
let start = if start > expr_range.end() {
|
||||
expr_range.start()
|
||||
} else {
|
||||
start
|
||||
};
|
||||
let expr_source = source_file.slice(TextRange::new(start, expr_range.end()));
|
||||
strip_interpolation_expr(expr_source)
|
||||
}
|
||||
|
||||
fn extend_expr_range_with_wrapping_parens(
|
||||
source_file: &SourceFile,
|
||||
interpolation_range: TextRange,
|
||||
expr_range: TextRange,
|
||||
) -> Option<TextRange> {
|
||||
let left_slice = source_file.slice(TextRange::new(
|
||||
interpolation_range.start(),
|
||||
expr_range.start(),
|
||||
));
|
||||
let mut left_char: Option<(usize, char)> = None;
|
||||
for (idx, ch) in left_slice
|
||||
.char_indices()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev()
|
||||
{
|
||||
if !ch.is_whitespace() {
|
||||
left_char = Some((idx, ch));
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (left_idx, left_ch) = left_char?;
|
||||
if left_ch != '(' {
|
||||
return None;
|
||||
}
|
||||
|
||||
let right_slice =
|
||||
source_file.slice(TextRange::new(expr_range.end(), interpolation_range.end()));
|
||||
let mut right_char: Option<(usize, char)> = None;
|
||||
for (idx, ch) in right_slice.char_indices() {
|
||||
if !ch.is_whitespace() {
|
||||
right_char = Some((idx, ch));
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (right_idx, right_ch) = right_char?;
|
||||
if right_ch != ')' {
|
||||
return None;
|
||||
}
|
||||
|
||||
let left_pos = interpolation_range.start() + TextSize::from(left_idx as u32);
|
||||
let right_pos = expr_range.end() + TextSize::from(right_idx as u32);
|
||||
Some(TextRange::new(left_pos, right_pos + TextSize::from(1)))
|
||||
}
|
||||
|
||||
fn strip_interpolation_expr(expr_source: &str) -> String {
|
||||
let mut end = expr_source.len();
|
||||
for (idx, ch) in expr_source.char_indices().rev() {
|
||||
if ch.is_whitespace() || ch == '=' {
|
||||
end = idx;
|
||||
continue;
|
||||
}
|
||||
end = idx + ch.len_utf8();
|
||||
break;
|
||||
}
|
||||
expr_source[..end].to_owned()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -426,6 +697,98 @@ pub(super) struct TemplateStr {
|
||||
pub(super) values: Box<[TemplateStrPart]>,
|
||||
}
|
||||
|
||||
pub(super) fn template_str_to_expr(
|
||||
vm: &VirtualMachine,
|
||||
template: TemplateStr,
|
||||
) -> PyResult<ast::Expr> {
|
||||
let TemplateStr { range, values } = template;
|
||||
let elements = template_parts_to_elements(vm, values)?;
|
||||
let tstring = ast::TString {
|
||||
range,
|
||||
node_index: Default::default(),
|
||||
elements,
|
||||
flags: ast::TStringFlags::empty(),
|
||||
};
|
||||
Ok(ast::Expr::TString(ast::ExprTString {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
value: ast::TStringValue::single(tstring),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn interpolation_to_expr(
|
||||
vm: &VirtualMachine,
|
||||
interpolation: TStringInterpolation,
|
||||
) -> PyResult<ast::Expr> {
|
||||
let part = TemplateStrPart::Interpolation(interpolation);
|
||||
let elements = template_parts_to_elements(vm, vec![part].into_boxed_slice())?;
|
||||
let range = TextRange::default();
|
||||
let tstring = ast::TString {
|
||||
range,
|
||||
node_index: Default::default(),
|
||||
elements,
|
||||
flags: ast::TStringFlags::empty(),
|
||||
};
|
||||
Ok(ast::Expr::TString(ast::ExprTString {
|
||||
node_index: Default::default(),
|
||||
range,
|
||||
value: ast::TStringValue::single(tstring),
|
||||
}))
|
||||
}
|
||||
|
||||
fn template_parts_to_elements(
|
||||
vm: &VirtualMachine,
|
||||
values: Box<[TemplateStrPart]>,
|
||||
) -> PyResult<ast::InterpolatedStringElements> {
|
||||
let mut elements = Vec::with_capacity(values.len());
|
||||
for value in values.into_vec() {
|
||||
elements.push(template_part_to_element(vm, value)?);
|
||||
}
|
||||
Ok(ast::InterpolatedStringElements::from(elements))
|
||||
}
|
||||
|
||||
fn template_part_to_element(
|
||||
vm: &VirtualMachine,
|
||||
part: TemplateStrPart,
|
||||
) -> PyResult<ast::InterpolatedStringElement> {
|
||||
match part {
|
||||
TemplateStrPart::Constant(constant) => {
|
||||
let ConstantLiteral::Str { value, .. } = constant.value else {
|
||||
return Err(
|
||||
vm.new_type_error("TemplateStr constant values must be strings".to_owned())
|
||||
);
|
||||
};
|
||||
Ok(ast::InterpolatedStringElement::Literal(
|
||||
ast::InterpolatedStringLiteralElement {
|
||||
range: constant.range,
|
||||
node_index: Default::default(),
|
||||
value,
|
||||
},
|
||||
))
|
||||
}
|
||||
TemplateStrPart::Interpolation(interpolation) => {
|
||||
let TStringInterpolation {
|
||||
value,
|
||||
conversion,
|
||||
format_spec,
|
||||
range,
|
||||
..
|
||||
} = interpolation;
|
||||
let format_spec = joined_str_to_ruff_format_spec(format_spec);
|
||||
Ok(ast::InterpolatedStringElement::Interpolation(
|
||||
ast::InterpolatedElement {
|
||||
range,
|
||||
node_index: Default::default(),
|
||||
expression: value,
|
||||
debug_text: None,
|
||||
conversion,
|
||||
format_spec,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
impl Node for TemplateStr {
|
||||
fn ast_to_object(self, vm: &VirtualMachine, source_file: &SourceFile) -> PyObjectRef {
|
||||
@@ -501,7 +864,7 @@ pub(super) struct TStringInterpolation {
|
||||
value: Box<ast::Expr>,
|
||||
str: String,
|
||||
conversion: ast::ConversionFlag,
|
||||
format_spec: Option<Box<TemplateStr>>,
|
||||
format_spec: Option<Box<JoinedStr>>,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
@@ -587,6 +950,7 @@ pub(super) fn tstring_to_object(
|
||||
));
|
||||
}
|
||||
}
|
||||
let values = normalize_template_str_parts(values);
|
||||
let c = TemplateStr {
|
||||
range,
|
||||
values: values.into_boxed_slice(),
|
||||
|
||||
@@ -0,0 +1,670 @@
|
||||
// spell-checker: ignore assignlist ifexp
|
||||
|
||||
use super::module::Mod;
|
||||
use crate::{PyResult, VirtualMachine};
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
fn expr_context_name(ctx: ast::ExprContext) -> &'static str {
|
||||
match ctx {
|
||||
ast::ExprContext::Load => "Load",
|
||||
ast::ExprContext::Store => "Store",
|
||||
ast::ExprContext::Del => "Del",
|
||||
ast::ExprContext::Invalid => "Invalid",
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_name(vm: &VirtualMachine, name: &ast::name::Name) -> PyResult<()> {
|
||||
match name.as_str() {
|
||||
"None" | "True" | "False" => Err(vm.new_value_error(format!(
|
||||
"identifier field can't represent '{}' constant",
|
||||
name.as_str()
|
||||
))),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_comprehension(vm: &VirtualMachine, gens: &[ast::Comprehension]) -> PyResult<()> {
|
||||
if gens.is_empty() {
|
||||
return Err(vm.new_value_error("comprehension with no generators".to_owned()));
|
||||
}
|
||||
for comp in gens {
|
||||
validate_expr(vm, &comp.target, ast::ExprContext::Store)?;
|
||||
validate_expr(vm, &comp.iter, ast::ExprContext::Load)?;
|
||||
validate_exprs(vm, &comp.ifs, ast::ExprContext::Load, false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_keywords(vm: &VirtualMachine, keywords: &[ast::Keyword]) -> PyResult<()> {
|
||||
for keyword in keywords {
|
||||
validate_expr(vm, &keyword.value, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_parameters(vm: &VirtualMachine, params: &ast::Parameters) -> PyResult<()> {
|
||||
for param in params
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(¶ms.args)
|
||||
.chain(¶ms.kwonlyargs)
|
||||
{
|
||||
if let Some(annotation) = ¶m.parameter.annotation {
|
||||
validate_expr(vm, annotation, ast::ExprContext::Load)?;
|
||||
}
|
||||
if let Some(default) = ¶m.default {
|
||||
validate_expr(vm, default, ast::ExprContext::Load)?;
|
||||
}
|
||||
}
|
||||
if let Some(vararg) = ¶ms.vararg
|
||||
&& let Some(annotation) = &vararg.annotation
|
||||
{
|
||||
validate_expr(vm, annotation, ast::ExprContext::Load)?;
|
||||
}
|
||||
if let Some(kwarg) = ¶ms.kwarg
|
||||
&& let Some(annotation) = &kwarg.annotation
|
||||
{
|
||||
validate_expr(vm, annotation, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_nonempty_seq(
|
||||
vm: &VirtualMachine,
|
||||
len: usize,
|
||||
what: &'static str,
|
||||
owner: &'static str,
|
||||
) -> PyResult<()> {
|
||||
if len == 0 {
|
||||
return Err(vm.new_value_error(format!("empty {what} on {owner}")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_assignlist(
|
||||
vm: &VirtualMachine,
|
||||
targets: &[ast::Expr],
|
||||
ctx: ast::ExprContext,
|
||||
) -> PyResult<()> {
|
||||
validate_nonempty_seq(
|
||||
vm,
|
||||
targets.len(),
|
||||
"targets",
|
||||
if ctx == ast::ExprContext::Del {
|
||||
"Delete"
|
||||
} else {
|
||||
"Assign"
|
||||
},
|
||||
)?;
|
||||
validate_exprs(vm, targets, ctx, false)
|
||||
}
|
||||
|
||||
fn validate_body(vm: &VirtualMachine, body: &[ast::Stmt], owner: &'static str) -> PyResult<()> {
|
||||
validate_nonempty_seq(vm, body.len(), "body", owner)?;
|
||||
validate_stmts(vm, body)
|
||||
}
|
||||
|
||||
fn validate_interpolated_elements<'a>(
|
||||
vm: &VirtualMachine,
|
||||
elements: impl IntoIterator<Item = ast::InterpolatedStringElementRef<'a>>,
|
||||
) -> PyResult<()> {
|
||||
for element in elements {
|
||||
if let ast::InterpolatedStringElementRef::Interpolation(interpolation) = element {
|
||||
validate_expr(vm, &interpolation.expression, ast::ExprContext::Load)?;
|
||||
if let Some(format_spec) = &interpolation.format_spec {
|
||||
for spec_element in &format_spec.elements {
|
||||
if let ast::InterpolatedStringElement::Interpolation(spec_interp) = spec_element
|
||||
{
|
||||
validate_expr(vm, &spec_interp.expression, ast::ExprContext::Load)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_pattern_match_value(vm: &VirtualMachine, expr: &ast::Expr) -> PyResult<()> {
|
||||
validate_expr(vm, expr, ast::ExprContext::Load)?;
|
||||
match expr {
|
||||
ast::Expr::NumberLiteral(_) | ast::Expr::StringLiteral(_) | ast::Expr::BytesLiteral(_) => {
|
||||
Ok(())
|
||||
}
|
||||
ast::Expr::Attribute(_) => Ok(()),
|
||||
ast::Expr::UnaryOp(op) => match &*op.operand {
|
||||
ast::Expr::NumberLiteral(_) => Ok(()),
|
||||
_ => Err(vm.new_value_error(
|
||||
"patterns may only match literals and attribute lookups".to_owned(),
|
||||
)),
|
||||
},
|
||||
ast::Expr::BinOp(bin) => match (&*bin.left, &*bin.right) {
|
||||
(ast::Expr::NumberLiteral(_), ast::Expr::NumberLiteral(_)) => Ok(()),
|
||||
_ => Err(vm.new_value_error(
|
||||
"patterns may only match literals and attribute lookups".to_owned(),
|
||||
)),
|
||||
},
|
||||
ast::Expr::FString(_) | ast::Expr::TString(_) => Ok(()),
|
||||
ast::Expr::BooleanLiteral(_)
|
||||
| ast::Expr::NoneLiteral(_)
|
||||
| ast::Expr::EllipsisLiteral(_) => {
|
||||
Err(vm.new_value_error("unexpected constant inside of a literal pattern".to_owned()))
|
||||
}
|
||||
_ => Err(
|
||||
vm.new_value_error("patterns may only match literals and attribute lookups".to_owned())
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_capture(vm: &VirtualMachine, name: &ast::Identifier) -> PyResult<()> {
|
||||
if name.as_str() == "_" {
|
||||
return Err(vm.new_value_error("can't capture name '_' in patterns".to_owned()));
|
||||
}
|
||||
validate_name(vm, name.id())
|
||||
}
|
||||
|
||||
fn validate_pattern(vm: &VirtualMachine, pattern: &ast::Pattern, star_ok: bool) -> PyResult<()> {
|
||||
match pattern {
|
||||
ast::Pattern::MatchValue(value) => validate_pattern_match_value(vm, &value.value),
|
||||
ast::Pattern::MatchSingleton(singleton) => match singleton.value {
|
||||
ast::Singleton::None | ast::Singleton::True | ast::Singleton::False => Ok(()),
|
||||
},
|
||||
ast::Pattern::MatchSequence(seq) => validate_patterns(vm, &seq.patterns, true),
|
||||
ast::Pattern::MatchMapping(mapping) => {
|
||||
if mapping.keys.len() != mapping.patterns.len() {
|
||||
return Err(vm.new_value_error(
|
||||
"MatchMapping doesn't have the same number of keys as patterns".to_owned(),
|
||||
));
|
||||
}
|
||||
if let Some(rest) = &mapping.rest {
|
||||
validate_capture(vm, rest)?;
|
||||
}
|
||||
for key in &mapping.keys {
|
||||
if let ast::Expr::BooleanLiteral(_) | ast::Expr::NoneLiteral(_) = key {
|
||||
continue;
|
||||
}
|
||||
validate_pattern_match_value(vm, key)?;
|
||||
}
|
||||
validate_patterns(vm, &mapping.patterns, false)
|
||||
}
|
||||
ast::Pattern::MatchClass(match_class) => {
|
||||
validate_expr(vm, &match_class.cls, ast::ExprContext::Load)?;
|
||||
let mut cls = match_class.cls.as_ref();
|
||||
loop {
|
||||
match cls {
|
||||
ast::Expr::Name(_) => break,
|
||||
ast::Expr::Attribute(attr) => {
|
||||
cls = &attr.value;
|
||||
}
|
||||
_ => {
|
||||
return Err(vm.new_value_error(
|
||||
"MatchClass cls field can only contain Name or Attribute nodes."
|
||||
.to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
for keyword in &match_class.arguments.keywords {
|
||||
validate_name(vm, keyword.attr.id())?;
|
||||
}
|
||||
validate_patterns(vm, &match_class.arguments.patterns, false)?;
|
||||
for keyword in &match_class.arguments.keywords {
|
||||
validate_pattern(vm, &keyword.pattern, false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Pattern::MatchStar(star) => {
|
||||
if !star_ok {
|
||||
return Err(vm.new_value_error("can't use MatchStar here".to_owned()));
|
||||
}
|
||||
if let Some(name) = &star.name {
|
||||
validate_capture(vm, name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Pattern::MatchAs(match_as) => {
|
||||
if let Some(name) = &match_as.name {
|
||||
validate_capture(vm, name)?;
|
||||
}
|
||||
match &match_as.pattern {
|
||||
None => Ok(()),
|
||||
Some(pattern) => {
|
||||
if match_as.name.is_none() {
|
||||
return Err(vm.new_value_error(
|
||||
"MatchAs must specify a target name if a pattern is given".to_owned(),
|
||||
));
|
||||
}
|
||||
validate_pattern(vm, pattern, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Pattern::MatchOr(match_or) => {
|
||||
if match_or.patterns.len() < 2 {
|
||||
return Err(vm.new_value_error("MatchOr requires at least 2 patterns".to_owned()));
|
||||
}
|
||||
validate_patterns(vm, &match_or.patterns, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_patterns(
|
||||
vm: &VirtualMachine,
|
||||
patterns: &[ast::Pattern],
|
||||
star_ok: bool,
|
||||
) -> PyResult<()> {
|
||||
for pattern in patterns {
|
||||
validate_pattern(vm, pattern, star_ok)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_typeparam(vm: &VirtualMachine, tp: &ast::TypeParam) -> PyResult<()> {
|
||||
match tp {
|
||||
ast::TypeParam::TypeVar(tp) => {
|
||||
validate_name(vm, tp.name.id())?;
|
||||
if let Some(bound) = &tp.bound {
|
||||
validate_expr(vm, bound, ast::ExprContext::Load)?;
|
||||
}
|
||||
if let Some(default) = &tp.default {
|
||||
validate_expr(vm, default, ast::ExprContext::Load)?;
|
||||
}
|
||||
}
|
||||
ast::TypeParam::ParamSpec(tp) => {
|
||||
validate_name(vm, tp.name.id())?;
|
||||
if let Some(default) = &tp.default {
|
||||
validate_expr(vm, default, ast::ExprContext::Load)?;
|
||||
}
|
||||
}
|
||||
ast::TypeParam::TypeVarTuple(tp) => {
|
||||
validate_name(vm, tp.name.id())?;
|
||||
if let Some(default) = &tp.default {
|
||||
validate_expr(vm, default, ast::ExprContext::Load)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_type_params(
|
||||
vm: &VirtualMachine,
|
||||
type_params: &Option<Box<ast::TypeParams>>,
|
||||
) -> PyResult<()> {
|
||||
if let Some(type_params) = type_params {
|
||||
for tp in &type_params.type_params {
|
||||
validate_typeparam(vm, tp)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_exprs(
|
||||
vm: &VirtualMachine,
|
||||
exprs: &[ast::Expr],
|
||||
ctx: ast::ExprContext,
|
||||
_null_ok: bool,
|
||||
) -> PyResult<()> {
|
||||
for expr in exprs {
|
||||
validate_expr(vm, expr, ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_expr(vm: &VirtualMachine, expr: &ast::Expr, ctx: ast::ExprContext) -> PyResult<()> {
|
||||
let mut check_ctx = true;
|
||||
let actual_ctx = match expr {
|
||||
ast::Expr::Attribute(attr) => attr.ctx,
|
||||
ast::Expr::Subscript(sub) => sub.ctx,
|
||||
ast::Expr::Starred(star) => star.ctx,
|
||||
ast::Expr::Name(name) => {
|
||||
validate_name(vm, name.id())?;
|
||||
name.ctx
|
||||
}
|
||||
ast::Expr::List(list) => list.ctx,
|
||||
ast::Expr::Tuple(tuple) => tuple.ctx,
|
||||
_ => {
|
||||
if ctx != ast::ExprContext::Load {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"expression which can't be assigned to in {} context",
|
||||
expr_context_name(ctx)
|
||||
)));
|
||||
}
|
||||
check_ctx = false;
|
||||
ast::ExprContext::Invalid
|
||||
}
|
||||
};
|
||||
if check_ctx && actual_ctx != ctx {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"expression must have {} context but has {} instead",
|
||||
expr_context_name(ctx),
|
||||
expr_context_name(actual_ctx)
|
||||
)));
|
||||
}
|
||||
|
||||
match expr {
|
||||
ast::Expr::BoolOp(op) => {
|
||||
if op.values.len() < 2 {
|
||||
return Err(vm.new_value_error("BoolOp with less than 2 values".to_owned()));
|
||||
}
|
||||
validate_exprs(vm, &op.values, ast::ExprContext::Load, false)
|
||||
}
|
||||
ast::Expr::Named(named) => {
|
||||
if !matches!(&*named.target, ast::Expr::Name(_)) {
|
||||
return Err(vm.new_type_error("NamedExpr target must be a Name".to_owned()));
|
||||
}
|
||||
validate_expr(vm, &named.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::BinOp(bin) => {
|
||||
validate_expr(vm, &bin.left, ast::ExprContext::Load)?;
|
||||
validate_expr(vm, &bin.right, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::UnaryOp(unary) => validate_expr(vm, &unary.operand, ast::ExprContext::Load),
|
||||
ast::Expr::Lambda(lambda) => {
|
||||
if let Some(parameters) = &lambda.parameters {
|
||||
validate_parameters(vm, parameters)?;
|
||||
}
|
||||
validate_expr(vm, &lambda.body, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::If(ifexp) => {
|
||||
validate_expr(vm, &ifexp.test, ast::ExprContext::Load)?;
|
||||
validate_expr(vm, &ifexp.body, ast::ExprContext::Load)?;
|
||||
validate_expr(vm, &ifexp.orelse, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Dict(dict) => {
|
||||
for item in &dict.items {
|
||||
if let Some(key) = &item.key {
|
||||
validate_expr(vm, key, ast::ExprContext::Load)?;
|
||||
}
|
||||
validate_expr(vm, &item.value, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Expr::Set(set) => validate_exprs(vm, &set.elts, ast::ExprContext::Load, false),
|
||||
ast::Expr::ListComp(list) => {
|
||||
validate_comprehension(vm, &list.generators)?;
|
||||
validate_expr(vm, &list.elt, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::SetComp(set) => {
|
||||
validate_comprehension(vm, &set.generators)?;
|
||||
validate_expr(vm, &set.elt, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::DictComp(dict) => {
|
||||
validate_comprehension(vm, &dict.generators)?;
|
||||
validate_expr(vm, &dict.key, ast::ExprContext::Load)?;
|
||||
validate_expr(vm, &dict.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Generator(generator) => {
|
||||
validate_comprehension(vm, &generator.generators)?;
|
||||
validate_expr(vm, &generator.elt, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Yield(yield_expr) => {
|
||||
if let Some(value) = &yield_expr.value {
|
||||
validate_expr(vm, value, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Expr::YieldFrom(yield_expr) => {
|
||||
validate_expr(vm, &yield_expr.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Await(await_expr) => {
|
||||
validate_expr(vm, &await_expr.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Compare(compare) => {
|
||||
if compare.comparators.is_empty() {
|
||||
return Err(vm.new_value_error("Compare with no comparators".to_owned()));
|
||||
}
|
||||
if compare.comparators.len() != compare.ops.len() {
|
||||
return Err(vm.new_value_error(
|
||||
"Compare has a different number of comparators and operands".to_owned(),
|
||||
));
|
||||
}
|
||||
validate_exprs(vm, &compare.comparators, ast::ExprContext::Load, false)?;
|
||||
validate_expr(vm, &compare.left, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Call(call) => {
|
||||
validate_expr(vm, &call.func, ast::ExprContext::Load)?;
|
||||
validate_exprs(vm, &call.arguments.args, ast::ExprContext::Load, false)?;
|
||||
validate_keywords(vm, &call.arguments.keywords)
|
||||
}
|
||||
ast::Expr::FString(fstring) => validate_interpolated_elements(
|
||||
vm,
|
||||
fstring
|
||||
.value
|
||||
.elements()
|
||||
.map(ast::InterpolatedStringElementRef::from),
|
||||
),
|
||||
ast::Expr::TString(tstring) => validate_interpolated_elements(
|
||||
vm,
|
||||
tstring
|
||||
.value
|
||||
.elements()
|
||||
.map(ast::InterpolatedStringElementRef::from),
|
||||
),
|
||||
ast::Expr::StringLiteral(_)
|
||||
| ast::Expr::BytesLiteral(_)
|
||||
| ast::Expr::NumberLiteral(_)
|
||||
| ast::Expr::BooleanLiteral(_)
|
||||
| ast::Expr::NoneLiteral(_)
|
||||
| ast::Expr::EllipsisLiteral(_) => Ok(()),
|
||||
ast::Expr::Attribute(attr) => validate_expr(vm, &attr.value, ast::ExprContext::Load),
|
||||
ast::Expr::Subscript(sub) => {
|
||||
validate_expr(vm, &sub.slice, ast::ExprContext::Load)?;
|
||||
validate_expr(vm, &sub.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Expr::Starred(star) => validate_expr(vm, &star.value, ctx),
|
||||
ast::Expr::Name(_) => Ok(()),
|
||||
ast::Expr::List(list) => validate_exprs(vm, &list.elts, ctx, false),
|
||||
ast::Expr::Tuple(tuple) => validate_exprs(vm, &tuple.elts, ctx, false),
|
||||
ast::Expr::Slice(slice) => {
|
||||
if let Some(lower) = &slice.lower {
|
||||
validate_expr(vm, lower, ast::ExprContext::Load)?;
|
||||
}
|
||||
if let Some(upper) = &slice.upper {
|
||||
validate_expr(vm, upper, ast::ExprContext::Load)?;
|
||||
}
|
||||
if let Some(step) = &slice.step {
|
||||
validate_expr(vm, step, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Expr::IpyEscapeCommand(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_decorators(vm: &VirtualMachine, decorators: &[ast::Decorator]) -> PyResult<()> {
|
||||
for decorator in decorators {
|
||||
validate_expr(vm, &decorator.expression, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_stmt(vm: &VirtualMachine, stmt: &ast::Stmt) -> PyResult<()> {
|
||||
match stmt {
|
||||
ast::Stmt::FunctionDef(func) => {
|
||||
let owner = if func.is_async {
|
||||
"AsyncFunctionDef"
|
||||
} else {
|
||||
"FunctionDef"
|
||||
};
|
||||
validate_body(vm, &func.body, owner)?;
|
||||
validate_type_params(vm, &func.type_params)?;
|
||||
validate_parameters(vm, &func.parameters)?;
|
||||
validate_decorators(vm, &func.decorator_list)?;
|
||||
if let Some(returns) = &func.returns {
|
||||
validate_expr(vm, returns, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::ClassDef(class_def) => {
|
||||
validate_body(vm, &class_def.body, "ClassDef")?;
|
||||
validate_type_params(vm, &class_def.type_params)?;
|
||||
if let Some(arguments) = &class_def.arguments {
|
||||
validate_exprs(vm, &arguments.args, ast::ExprContext::Load, false)?;
|
||||
validate_keywords(vm, &arguments.keywords)?;
|
||||
}
|
||||
validate_decorators(vm, &class_def.decorator_list)
|
||||
}
|
||||
ast::Stmt::Return(ret) => {
|
||||
if let Some(value) = &ret.value {
|
||||
validate_expr(vm, value, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Delete(del) => validate_assignlist(vm, &del.targets, ast::ExprContext::Del),
|
||||
ast::Stmt::Assign(assign) => {
|
||||
validate_assignlist(vm, &assign.targets, ast::ExprContext::Store)?;
|
||||
validate_expr(vm, &assign.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Stmt::AugAssign(assign) => {
|
||||
validate_expr(vm, &assign.target, ast::ExprContext::Store)?;
|
||||
validate_expr(vm, &assign.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Stmt::AnnAssign(assign) => {
|
||||
if assign.simple && !matches!(&*assign.target, ast::Expr::Name(_)) {
|
||||
return Err(vm.new_type_error("AnnAssign with simple non-Name target".to_owned()));
|
||||
}
|
||||
validate_expr(vm, &assign.target, ast::ExprContext::Store)?;
|
||||
if let Some(value) = &assign.value {
|
||||
validate_expr(vm, value, ast::ExprContext::Load)?;
|
||||
}
|
||||
validate_expr(vm, &assign.annotation, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Stmt::TypeAlias(alias) => {
|
||||
if !matches!(&*alias.name, ast::Expr::Name(_)) {
|
||||
return Err(vm.new_type_error("TypeAlias with non-Name name".to_owned()));
|
||||
}
|
||||
validate_expr(vm, &alias.name, ast::ExprContext::Store)?;
|
||||
validate_type_params(vm, &alias.type_params)?;
|
||||
validate_expr(vm, &alias.value, ast::ExprContext::Load)
|
||||
}
|
||||
ast::Stmt::For(for_stmt) => {
|
||||
let owner = if for_stmt.is_async { "AsyncFor" } else { "For" };
|
||||
validate_expr(vm, &for_stmt.target, ast::ExprContext::Store)?;
|
||||
validate_expr(vm, &for_stmt.iter, ast::ExprContext::Load)?;
|
||||
validate_body(vm, &for_stmt.body, owner)?;
|
||||
validate_stmts(vm, &for_stmt.orelse)
|
||||
}
|
||||
ast::Stmt::While(while_stmt) => {
|
||||
validate_expr(vm, &while_stmt.test, ast::ExprContext::Load)?;
|
||||
validate_body(vm, &while_stmt.body, "While")?;
|
||||
validate_stmts(vm, &while_stmt.orelse)
|
||||
}
|
||||
ast::Stmt::If(if_stmt) => {
|
||||
validate_expr(vm, &if_stmt.test, ast::ExprContext::Load)?;
|
||||
validate_body(vm, &if_stmt.body, "If")?;
|
||||
for clause in &if_stmt.elif_else_clauses {
|
||||
if let Some(test) = &clause.test {
|
||||
validate_expr(vm, test, ast::ExprContext::Load)?;
|
||||
}
|
||||
validate_body(vm, &clause.body, "If")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::With(with_stmt) => {
|
||||
let owner = if with_stmt.is_async {
|
||||
"AsyncWith"
|
||||
} else {
|
||||
"With"
|
||||
};
|
||||
validate_nonempty_seq(vm, with_stmt.items.len(), "items", owner)?;
|
||||
for item in &with_stmt.items {
|
||||
validate_expr(vm, &item.context_expr, ast::ExprContext::Load)?;
|
||||
if let Some(optional_vars) = &item.optional_vars {
|
||||
validate_expr(vm, optional_vars, ast::ExprContext::Store)?;
|
||||
}
|
||||
}
|
||||
validate_body(vm, &with_stmt.body, owner)
|
||||
}
|
||||
ast::Stmt::Match(match_stmt) => {
|
||||
validate_expr(vm, &match_stmt.subject, ast::ExprContext::Load)?;
|
||||
validate_nonempty_seq(vm, match_stmt.cases.len(), "cases", "Match")?;
|
||||
for case in &match_stmt.cases {
|
||||
validate_pattern(vm, &case.pattern, false)?;
|
||||
if let Some(guard) = &case.guard {
|
||||
validate_expr(vm, guard, ast::ExprContext::Load)?;
|
||||
}
|
||||
validate_body(vm, &case.body, "match_case")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Raise(raise) => {
|
||||
if let Some(exc) = &raise.exc {
|
||||
validate_expr(vm, exc, ast::ExprContext::Load)?;
|
||||
if let Some(cause) = &raise.cause {
|
||||
validate_expr(vm, cause, ast::ExprContext::Load)?;
|
||||
}
|
||||
} else if raise.cause.is_some() {
|
||||
return Err(vm.new_value_error("Raise with cause but no exception".to_owned()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Try(try_stmt) => {
|
||||
let owner = if try_stmt.is_star { "TryStar" } else { "Try" };
|
||||
validate_body(vm, &try_stmt.body, owner)?;
|
||||
if try_stmt.handlers.is_empty() && try_stmt.finalbody.is_empty() {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"{owner} has neither except handlers nor finalbody"
|
||||
)));
|
||||
}
|
||||
if try_stmt.handlers.is_empty() && !try_stmt.orelse.is_empty() {
|
||||
return Err(
|
||||
vm.new_value_error(format!("{owner} has orelse but no except handlers"))
|
||||
);
|
||||
}
|
||||
for handler in &try_stmt.handlers {
|
||||
let ast::ExceptHandler::ExceptHandler(handler) = handler;
|
||||
if let Some(type_expr) = &handler.type_ {
|
||||
validate_expr(vm, type_expr, ast::ExprContext::Load)?;
|
||||
}
|
||||
validate_body(vm, &handler.body, "ExceptHandler")?;
|
||||
}
|
||||
validate_stmts(vm, &try_stmt.finalbody)?;
|
||||
validate_stmts(vm, &try_stmt.orelse)
|
||||
}
|
||||
ast::Stmt::Assert(assert_stmt) => {
|
||||
validate_expr(vm, &assert_stmt.test, ast::ExprContext::Load)?;
|
||||
if let Some(msg) = &assert_stmt.msg {
|
||||
validate_expr(vm, msg, ast::ExprContext::Load)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Import(import) => {
|
||||
validate_nonempty_seq(vm, import.names.len(), "names", "Import")?;
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::ImportFrom(import) => {
|
||||
validate_nonempty_seq(vm, import.names.len(), "names", "ImportFrom")?;
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Global(global) => {
|
||||
validate_nonempty_seq(vm, global.names.len(), "names", "Global")?;
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Nonlocal(nonlocal) => {
|
||||
validate_nonempty_seq(vm, nonlocal.names.len(), "names", "Nonlocal")?;
|
||||
Ok(())
|
||||
}
|
||||
ast::Stmt::Expr(expr) => validate_expr(vm, &expr.value, ast::ExprContext::Load),
|
||||
ast::Stmt::Pass(_)
|
||||
| ast::Stmt::Break(_)
|
||||
| ast::Stmt::Continue(_)
|
||||
| ast::Stmt::IpyEscapeCommand(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_stmts(vm: &VirtualMachine, stmts: &[ast::Stmt]) -> PyResult<()> {
|
||||
for stmt in stmts {
|
||||
validate_stmt(vm, stmt)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn validate_mod(vm: &VirtualMachine, module: &Mod) -> PyResult<()> {
|
||||
match module {
|
||||
Mod::Module(module) => validate_stmts(vm, &module.body),
|
||||
Mod::Interactive(module) => validate_stmts(vm, &module.body),
|
||||
Mod::Expression(expr) => validate_expr(vm, &expr.body, ast::ExprContext::Load),
|
||||
Mod::FunctionType(func_type) => {
|
||||
validate_exprs(vm, &func_type.argtypes, ast::ExprContext::Load, false)?;
|
||||
validate_expr(vm, &func_type.returns, ast::ExprContext::Load)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,9 +122,7 @@ mod builtins {
|
||||
{
|
||||
use crate::{class::PyClassImpl, stdlib::ast};
|
||||
|
||||
if args._feature_version.is_present() {
|
||||
// TODO: add support for _feature_version
|
||||
}
|
||||
let feature_version = feature_version_from_arg(args._feature_version, vm)?;
|
||||
|
||||
let mode_str = args.mode.as_str();
|
||||
|
||||
@@ -168,6 +166,7 @@ mod builtins {
|
||||
args.source.class().name()
|
||||
)));
|
||||
}
|
||||
ast::validate_ast_object(vm, args.source.clone())?;
|
||||
return Ok(args.source);
|
||||
}
|
||||
|
||||
@@ -215,6 +214,9 @@ mod builtins {
|
||||
}
|
||||
|
||||
let allow_incomplete = !(flags & ast::PY_CF_ALLOW_INCOMPLETE_INPUT).is_zero();
|
||||
let type_comments = !(flags & ast::PY_CF_TYPE_COMMENTS).is_zero();
|
||||
|
||||
let optimize_level = optimize;
|
||||
|
||||
if (flags & ast::PY_COMPILE_FLAG_AST_ONLY).is_zero() {
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
@@ -223,6 +225,21 @@ mod builtins {
|
||||
}
|
||||
#[cfg(feature = "compiler")]
|
||||
{
|
||||
if let Some(feature_version) = feature_version {
|
||||
let mode = mode_str
|
||||
.parse::<parser::Mode>()
|
||||
.map_err(|err| vm.new_value_error(err.to_string()))?;
|
||||
let _ = ast::parse(
|
||||
vm,
|
||||
source,
|
||||
mode,
|
||||
optimize_level,
|
||||
Some(feature_version),
|
||||
type_comments,
|
||||
)
|
||||
.map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))?;
|
||||
}
|
||||
|
||||
let mode = mode_str
|
||||
.parse::<crate::compiler::Mode>()
|
||||
.map_err(|err| vm.new_value_error(err.to_string()))?;
|
||||
@@ -243,16 +260,53 @@ mod builtins {
|
||||
Ok(code.into())
|
||||
}
|
||||
} else {
|
||||
if mode_str == "func_type" {
|
||||
return ast::parse_func_type(vm, source, optimize_level, feature_version)
|
||||
.map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm));
|
||||
}
|
||||
|
||||
let mode = mode_str
|
||||
.parse::<parser::Mode>()
|
||||
.map_err(|err| vm.new_value_error(err.to_string()))?;
|
||||
ast::parse(vm, source, mode)
|
||||
.map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))
|
||||
let parsed = ast::parse(
|
||||
vm,
|
||||
source,
|
||||
mode,
|
||||
optimize_level,
|
||||
feature_version,
|
||||
type_comments,
|
||||
)
|
||||
.map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))?;
|
||||
|
||||
if mode_str == "single" {
|
||||
return ast::wrap_interactive(vm, parsed);
|
||||
}
|
||||
|
||||
Ok(parsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ast")]
|
||||
fn feature_version_from_arg(
|
||||
feature_version: OptionalArg<i32>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<ruff_python_ast::PythonVersion>> {
|
||||
let minor = match feature_version.into_option() {
|
||||
Some(minor) => minor,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
if minor < 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let minor = u8::try_from(minor)
|
||||
.map_err(|_| vm.new_value_error("compile() _feature_version out of range"))?;
|
||||
Ok(Some(ruff_python_ast::PythonVersion { major: 3, minor }))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn delattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let attr = attr.try_to_ref::<PyStr>(vm).map_err(|_e| {
|
||||
|
||||
@@ -645,7 +645,7 @@ fn wstring_at_impl(ptr: usize, size: isize, vm: &VirtualMachine) -> PyResult {
|
||||
{
|
||||
let s: String = wchars
|
||||
.iter()
|
||||
.filter_map(|&c| char::from_u32(c as u32))
|
||||
.filter_map(|&c| u32::try_from(c).ok().and_then(char::from_u32))
|
||||
.collect();
|
||||
Ok(vm.ctx.new_str(s).into())
|
||||
}
|
||||
@@ -1938,7 +1938,7 @@ fn ffi_to_python(ty: &Py<PyType>, ptr: *const c_void, vm: &VirtualMachine) -> Py
|
||||
{
|
||||
let s: String = slice
|
||||
.iter()
|
||||
.filter_map(|&c| char::from_u32(c as u32))
|
||||
.filter_map(|&c| u32::try_from(c).ok().and_then(char::from_u32))
|
||||
.collect();
|
||||
vm.ctx.new_str(s).into()
|
||||
}
|
||||
|
||||
@@ -1181,7 +1181,7 @@ impl PyCSimple {
|
||||
{
|
||||
let s: String = wchars
|
||||
.iter()
|
||||
.filter_map(|&c| char::from_u32(c as u32))
|
||||
.filter_map(|&c| u32::try_from(c).ok().and_then(char::from_u32))
|
||||
.collect();
|
||||
return Ok(vm.ctx.new_str(s).into());
|
||||
}
|
||||
|
||||
@@ -1813,7 +1813,7 @@ pub(super) mod _os {
|
||||
} else {
|
||||
let encoding = unsafe {
|
||||
let encoding = libc::nl_langinfo(libc::CODESET);
|
||||
if encoding.is_null() || encoding.read() == '\0' as libc::c_char {
|
||||
if encoding.is_null() || encoding.read() == b'\0' as libc::c_char {
|
||||
"UTF-8".to_owned()
|
||||
} else {
|
||||
core::ffi::CStr::from_ptr(encoding).to_string_lossy().into_owned()
|
||||
|
||||
@@ -68,7 +68,7 @@ pub fn offer_suggestions(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> Opti
|
||||
return Some(suggestions);
|
||||
};
|
||||
|
||||
let builtins: Vec<_> = tb.frame.builtins.as_object().try_to_value(vm).ok()?;
|
||||
let builtins: Vec<_> = tb.frame.builtins.try_to_value(vm).ok()?;
|
||||
calculate_suggestions(builtins.iter(), &name)
|
||||
} else {
|
||||
None
|
||||
|
||||
+18
-3
@@ -477,14 +477,29 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn run_code_obj(&self, code: PyRef<PyCode>, scope: Scope) -> PyResult {
|
||||
use crate::builtins::PyFunction;
|
||||
use crate::builtins::{PyFunction, PyModule};
|
||||
|
||||
// Create a function object for module code, similar to CPython's PyEval_EvalCode
|
||||
let func = PyFunction::new(code.clone(), scope.globals.clone(), self)?;
|
||||
let func_obj = func.into_ref(&self.ctx).into();
|
||||
|
||||
let frame = Frame::new(code, scope, self.builtins.dict(), &[], Some(func_obj), self)
|
||||
.into_ref(&self.ctx);
|
||||
// Extract builtins from globals["__builtins__"], like PyEval_EvalCode
|
||||
let builtins = match scope
|
||||
.globals
|
||||
.get_item_opt(identifier!(self, __builtins__), self)?
|
||||
{
|
||||
Some(b) => {
|
||||
if let Some(module) = b.downcast_ref::<PyModule>() {
|
||||
module.dict().into()
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
None => self.builtins.dict().into(),
|
||||
};
|
||||
|
||||
let frame =
|
||||
Frame::new(code, scope, builtins, &[], Some(func_obj), self).into_ref(&self.ctx);
|
||||
self.run_frame(frame)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,19 +22,15 @@ def _run_missing_type_params_regression():
|
||||
kwarg=None,
|
||||
defaults=[],
|
||||
)
|
||||
fn = _ast.FunctionDef("f", args, [], [], None, None)
|
||||
pass_stmt = _ast.Pass(lineno=1, col_offset=4, end_lineno=1, end_col_offset=8)
|
||||
fn = _ast.FunctionDef("f", args, [pass_stmt], [], None, None)
|
||||
fn.lineno = 1
|
||||
fn.col_offset = 0
|
||||
fn.end_lineno = 1
|
||||
fn.end_col_offset = 0
|
||||
fn.end_col_offset = 8
|
||||
mod = _ast.Module([fn], [])
|
||||
mod.lineno = 1
|
||||
mod.col_offset = 0
|
||||
mod.end_lineno = 1
|
||||
mod.end_col_offset = 0
|
||||
compiled = compile(mod, "<stdlib_types_missing_type_params>", "exec")
|
||||
exec(compiled, {})
|
||||
|
||||
|
||||
if platform.python_implementation() == "RustPython":
|
||||
_run_missing_type_params_regression()
|
||||
_run_missing_type_params_regression()
|
||||
Reference in new issue
Block a user