Python: Add test of __new__ handling

This commit is contained in:
Rasmus Wriedt Larsen
2022-10-20 14:32:48 +02:00
parent 57c7dc8ea9
commit f040ad8dac
2 changed files with 42 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
# We want to ensure that the __new__ method is considered a classmethod even though it
# doesn't have a decorator. This means that the `cls` parameter should be considered a
# reference to the class (or subclass), and not an instance of the class. We can detect
# this from looking at the arguments passed in the `cls.foo` call. if we see a `self`
# argument, this means it has correct behavior (because we're targeting a classmethod),
# if there is no `self` argument, this means we've only considered `cls` to be a class
# instance, since we don't want to pass that to the `cls` parameter of the classmethod `WithNewImpl.foo`.
class WithNewImpl(object):
def __new__(cls):
print("WithNewImpl.foo")
cls.foo() # $ call=cls.foo() callType=CallTypeClassMethod MISSING: arg[self]=cls
@classmethod
def foo(cls):
print("WithNewImpl.foo")

View File

@@ -385,6 +385,32 @@ def test_potential_crosstalk_same_class(cond=True):
SINK_F(objx2.x)
class NewTest(object):
def __new__(cls, arg):
cls.foo = arg
return super().__new__(cls) # $ unresolved_call=super().__new__(..)
@expects(4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test__new__():
# we want to make sure that we DON'T pass the synthetic pre-update node for
# the class instance to __new__, like we do for __init__.
nt = NewTest(SOURCE)
# the __new__ implementation sets the foo attribute on THE CLASS itself. The
# attribute lookup on the class instance will go to the class itself when the
# attribute isn't defined on the class instance, so we will actually see `nt.foo`
# contain the source, but the point of this test is that we should see identical
# behavior between NewTest.foo and nt.foo, which we dont!
#
# Also note that we currently (October 2022) dont' model writes to classes very
# well.
SINK(NewTest.foo) # $ MISSING: flow="SOURCE, l:-10 -> NewTest.foo"
SINK(nt.foo) # $ flow="SOURCE, l:-11 -> nt.foo"
NewTest.foo = NONSOURCE
SINK_F(NewTest.foo)
SINK_F(nt.foo) # $ SPURIOUS: flow="SOURCE, l:-15 -> nt.foo"
# ------------------------------------------------------------------------------
# Global scope
# ------------------------------------------------------------------------------