mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
* Align compiler futures and annotations with CPython Handle Barry parsing consistently across AST, type-comment, REPL, and WASM paths. Preserve deferred annotation source and scope metadata, and make _symtable conversion cached and linear-time. Assisted-by: Codex:gpt-5.4 * Fix f-string debug ranges and update Barry/annotate test expectations Compute the debug f-string source range from the raw leading and trailing text instead of the comment-stripped text, so a comment inside a multi-line replacement field no longer shifts the emitted LOAD_CONST location. Restrict the obsolete-operator location shift to `<>` by also requiring a `>` at the reported offset; without it any `ExpectedExpression` that followed a `<` (`2 <;`) was reported one column to the left. Add `CodeFlags::FUTURE_MASK` and use it in builtins, the shell, and the WASM VM instead of repeating the eight-flag union, and collapse the duplicated Barry diagnostic sequences into `BarrySource::diagnostic`. Update the `__annotate__` disassembly snapshot to LOAD_FAST_BORROW and replace the RustPython-only `barry_as_FLUFL` co_flags assertion in extra_tests with the CPython-matching one. Assisted-by: Claude Code:claude-opus-5 * Restore the annotated function in __annotate__ qualnames Signature annotation scopes are compiled in the scope enclosing the annotated function, so the function name has to be folded back into the qualname: `f.__annotate__`, `C.m.__annotate__`, `outer.<locals>.inner.__annotate__`. test_type_annotations test_annotate_qualname covers this. test_pyrepl test_future_barry_as_flufl now passes, so drop its expectedFailure marker. Assisted-by: Claude Code:claude-opus-5 * Pass safepoints in the attached-thread interpreter tests `main_and_subinterpreter_run_sections_overlap` parked its workers on a condvar while attached, and `busy_main_interpreter_does_not_block_subinterpreter` looped over protocol calls that never reach a safepoint. An attached thread that never reaches a safepoint cannot be suspended, so a concurrent process-wide stop-the-world (from the gc test running in parallel) never completes and the already-stopped sibling interpreter stays stopped; the second worker then waits in `wait_while_suspended` until the 30s deadline. Both loops now call `vm.check_signals()` each iteration, and the condvar wait uses `wait_timeout` so the lock is released between safepoints. Reproduced on Linux (4 CPUs): the two tests with `--test-threads=2` failed 2/40 before and 0/40 after; the full `rustpython-vm` lib binary failed 1/30 before and 0/30 after. Assisted-by: Claude Code:claude-opus-5 * Report the obsolete `<>` operator from one place `<>` was located twice: `BarrySource` recorded the token ranges it rewrote to `!=`, and `cpython_parse_diagnostic_override` separately peeked at the bytes around an `ExpectedExpression` location. Both produced "invalid syntax" spanning the whole operator. `prepare_barry_as_flufl_source` now records the operator ranges in the non-Barry case too -- by plain text search, since nothing is rewritten there -- so `BarrySource::invalid_legacy_operator` covers both, and the byte-peeking helper is gone. It also defers to an unclosed bracket earlier in the source, which the diagnostic chain it used to sit in did for it. A `<>` that starts a statement now spans both characters: the parser stops at the `<` rather than one character in, which the byte peek did not handle. Assisted-by: Claude Code:claude-opus-5 * Drop a py object's payload before its class `PyInner` declares `typ` before `payload`, so `drop_in_place` dropped the class first and `PyAtomicRef::drop` left it null. A `PyWeak` payload is still linked into the weakref list of the object it points at until its own `Drop` unlinks it under the stripe lock, which happens after that. `WeakRefList::add` walks the same list looking for a proxy to reuse and reads `node.class()` off every node it passes, so it could dereference the null class of a node on its way out. `PyInner::dealloc` now runs the two destructors in the other order at all three of its exit paths, with a compile-time check that no other field has one. Reproduced by the new test in about 0.1s, as the panic CI hit in the c-api `weakrefobject::tests::new_proxy` run: panicked at crates/vm/src/object/ext.rs:312: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached Assisted-by: Claude Code:claude-opus-5
617 lines
19 KiB
Python
Vendored
617 lines
19 KiB
Python
Vendored
"""Unit tests for zero-argument super() & related machinery."""
|
|
|
|
import copy
|
|
import pickle
|
|
import textwrap
|
|
import threading
|
|
import unittest
|
|
from unittest.mock import patch
|
|
from test.support import import_helper, threading_helper
|
|
|
|
|
|
class A:
|
|
def f(self):
|
|
return 'A'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, 'A')
|
|
|
|
class B(A):
|
|
def f(self):
|
|
return super().f() + 'B'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'B')
|
|
|
|
class C(A):
|
|
def f(self):
|
|
return super().f() + 'C'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'C')
|
|
|
|
class D(C, B):
|
|
def f(self):
|
|
return super().f() + 'D'
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'D')
|
|
|
|
class E(D):
|
|
pass
|
|
|
|
class F(E):
|
|
f = E.f
|
|
|
|
class G(A):
|
|
pass
|
|
|
|
|
|
class TestSuper(unittest.TestCase):
|
|
|
|
def tearDown(self):
|
|
# This fixes the damage that test_various___class___pathologies does.
|
|
nonlocal __class__
|
|
__class__ = TestSuper
|
|
|
|
def test_basics_working(self):
|
|
self.assertEqual(D().f(), 'ABCD')
|
|
|
|
def test_class_getattr_working(self):
|
|
self.assertEqual(D.f(D()), 'ABCD')
|
|
|
|
def test_subclass_no_override_working(self):
|
|
self.assertEqual(E().f(), 'ABCD')
|
|
self.assertEqual(E.f(E()), 'ABCD')
|
|
|
|
def test_unbound_method_transfer_working(self):
|
|
self.assertEqual(F().f(), 'ABCD')
|
|
self.assertEqual(F.f(F()), 'ABCD')
|
|
|
|
def test_class_methods_still_working(self):
|
|
self.assertEqual(A.cm(), (A, 'A'))
|
|
self.assertEqual(A().cm(), (A, 'A'))
|
|
self.assertEqual(G.cm(), (G, 'A'))
|
|
self.assertEqual(G().cm(), (G, 'A'))
|
|
|
|
def test_super_in_class_methods_working(self):
|
|
d = D()
|
|
self.assertEqual(d.cm(), (d, (D, (D, (D, 'A'), 'B'), 'C'), 'D'))
|
|
e = E()
|
|
self.assertEqual(e.cm(), (e, (E, (E, (E, 'A'), 'B'), 'C'), 'D'))
|
|
|
|
def test_super_with_closure(self):
|
|
# Issue4360: super() did not work in a function that
|
|
# contains a closure
|
|
class E(A):
|
|
def f(self):
|
|
def nested():
|
|
self
|
|
return super().f() + 'E'
|
|
|
|
self.assertEqual(E().f(), 'AE')
|
|
|
|
def test_various___class___pathologies(self):
|
|
# See issue #12370
|
|
class X(A):
|
|
def f(self):
|
|
return super().f()
|
|
__class__ = 413
|
|
x = X()
|
|
self.assertEqual(x.f(), 'A')
|
|
self.assertEqual(x.__class__, 413)
|
|
class X:
|
|
x = __class__
|
|
def f():
|
|
__class__
|
|
self.assertIs(X.x, type(self))
|
|
with self.assertRaises(NameError) as e:
|
|
exec("""class X:
|
|
__class__
|
|
def f():
|
|
__class__""", globals(), {})
|
|
self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
|
|
class X:
|
|
global __class__
|
|
__class__ = 42
|
|
def f():
|
|
__class__
|
|
self.assertEqual(globals()["__class__"], 42)
|
|
del globals()["__class__"]
|
|
self.assertNotIn("__class__", X.__dict__)
|
|
class X:
|
|
nonlocal __class__
|
|
__class__ = 42
|
|
def f():
|
|
__class__
|
|
self.assertEqual(__class__, 42)
|
|
|
|
def test___class___instancemethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
def f(self):
|
|
return __class__
|
|
self.assertIs(X().f(), X)
|
|
|
|
def test___class___classmethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
@classmethod
|
|
def f(cls):
|
|
return __class__
|
|
self.assertIs(X.f(), X)
|
|
|
|
def test___class___staticmethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
self.assertIs(X.f(), X)
|
|
|
|
def test___class___new(self):
|
|
# See issue #23722
|
|
# Ensure zero-arg super() works as soon as type.__new__() is completed
|
|
test_class = None
|
|
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
nonlocal test_class
|
|
self = super().__new__(cls, name, bases, namespace)
|
|
test_class = self.f()
|
|
return self
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
|
|
self.assertIs(test_class, A)
|
|
|
|
def test___class___delayed(self):
|
|
# See issue #23722
|
|
test_namespace = None
|
|
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
nonlocal test_namespace
|
|
test_namespace = namespace
|
|
return None
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
|
|
self.assertIs(A, None)
|
|
|
|
B = type("B", (), test_namespace)
|
|
self.assertIs(B.f(), B)
|
|
|
|
@unittest.expectedFailure # TODO: RUSTPYTHON
|
|
def test___class___mro(self):
|
|
# See issue #23722
|
|
test_class = None
|
|
|
|
class Meta(type):
|
|
def mro(self):
|
|
# self.f() doesn't work yet...
|
|
self.__dict__["f"]()
|
|
return super().mro()
|
|
|
|
class A(metaclass=Meta):
|
|
def f():
|
|
nonlocal test_class
|
|
test_class = __class__
|
|
|
|
self.assertIs(test_class, A)
|
|
|
|
def test___classcell___expected_behaviour(self):
|
|
# See issue #23722
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
nonlocal namespace_snapshot
|
|
namespace_snapshot = namespace.copy()
|
|
return super().__new__(cls, name, bases, namespace)
|
|
|
|
# __classcell__ is injected into the class namespace by the compiler
|
|
# when at least one method needs it, and should be omitted otherwise
|
|
namespace_snapshot = None
|
|
class WithoutClassRef(metaclass=Meta):
|
|
pass
|
|
self.assertNotIn("__classcell__", namespace_snapshot)
|
|
|
|
# With zero-arg super() or an explicit __class__ reference,
|
|
# __classcell__ is the exact cell reference to be populated by
|
|
# type.__new__
|
|
namespace_snapshot = None
|
|
class WithClassRef(metaclass=Meta):
|
|
def f(self):
|
|
return __class__
|
|
|
|
class_cell = namespace_snapshot["__classcell__"]
|
|
method_closure = WithClassRef.f.__closure__
|
|
self.assertEqual(len(method_closure), 1)
|
|
self.assertIs(class_cell, method_closure[0])
|
|
# Ensure the cell reference *doesn't* get turned into an attribute
|
|
with self.assertRaises(AttributeError):
|
|
WithClassRef.__classcell__
|
|
|
|
def test___classcell___missing(self):
|
|
# See issue #23722
|
|
# Some metaclasses may not pass the original namespace to type.__new__
|
|
# We test that case here by forcibly deleting __classcell__
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
namespace.pop('__classcell__', None)
|
|
return super().__new__(cls, name, bases, namespace)
|
|
|
|
# The default case should continue to work without any errors
|
|
class WithoutClassRef(metaclass=Meta):
|
|
pass
|
|
|
|
# With zero-arg super() or an explicit __class__ reference, we expect
|
|
# __build_class__ to raise a RuntimeError complaining that
|
|
# __class__ was not set, and asking if __classcell__ was propagated
|
|
# to type.__new__.
|
|
expected_error = '__class__ not set.*__classcell__ propagated'
|
|
with self.assertRaisesRegex(RuntimeError, expected_error):
|
|
class WithClassRef(metaclass=Meta):
|
|
def f(self):
|
|
return __class__
|
|
|
|
def test___classcell___overwrite(self):
|
|
# See issue #23722
|
|
# Overwriting __classcell__ with nonsense is explicitly prohibited
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace, cell):
|
|
namespace['__classcell__'] = cell
|
|
return super().__new__(cls, name, bases, namespace)
|
|
|
|
for bad_cell in (None, 0, "", object()):
|
|
with self.subTest(bad_cell=bad_cell):
|
|
with self.assertRaises(TypeError):
|
|
class A(metaclass=Meta, cell=bad_cell):
|
|
pass
|
|
|
|
def test___classcell___wrong_cell(self):
|
|
# See issue #23722
|
|
# Pointing the cell reference at the wrong class is also prohibited
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
cls = super().__new__(cls, name, bases, namespace)
|
|
B = type("B", (), namespace)
|
|
return cls
|
|
|
|
with self.assertRaises(TypeError):
|
|
class A(metaclass=Meta):
|
|
def f(self):
|
|
return __class__
|
|
|
|
def test_obscure_super_errors(self):
|
|
def f():
|
|
super()
|
|
with self.assertRaisesRegex(RuntimeError, r"no arguments"):
|
|
f()
|
|
|
|
class C:
|
|
def f():
|
|
super()
|
|
with self.assertRaisesRegex(RuntimeError, r"no arguments"):
|
|
C.f()
|
|
|
|
def f(x):
|
|
del x
|
|
super()
|
|
with self.assertRaisesRegex(RuntimeError, r"arg\[0\] deleted"):
|
|
f(None)
|
|
|
|
class X:
|
|
def f(x):
|
|
nonlocal __class__
|
|
del __class__
|
|
super()
|
|
with self.assertRaisesRegex(RuntimeError, r"empty __class__ cell"):
|
|
X().f()
|
|
|
|
def test_cell_as_self(self):
|
|
class X:
|
|
def meth(self):
|
|
super()
|
|
|
|
def f():
|
|
k = X()
|
|
def g():
|
|
return k
|
|
return g
|
|
c = f().__closure__[0]
|
|
self.assertRaises(TypeError, X.meth, c)
|
|
|
|
def test_super_init_leaks(self):
|
|
# Issue #26718: super.__init__ leaked memory if called multiple times.
|
|
# This will be caught by regrtest.py -R if this leak.
|
|
# NOTE: Despite the use in the test a direct call of super.__init__
|
|
# is not endorsed.
|
|
sp = super(float, 1.0)
|
|
for i in range(1000):
|
|
super.__init__(sp, int, i)
|
|
|
|
def test_super_argcount(self):
|
|
with self.assertRaisesRegex(TypeError, "expected at most"):
|
|
super(int, int, int)
|
|
|
|
def test_super_argtype(self):
|
|
with self.assertRaisesRegex(TypeError, "argument 1 must be a type"):
|
|
super(1, int)
|
|
|
|
def test_shadowed_global(self):
|
|
source = textwrap.dedent(
|
|
"""
|
|
class super:
|
|
msg = "truly super"
|
|
|
|
class C:
|
|
def method(self):
|
|
return super().msg
|
|
""",
|
|
)
|
|
with import_helper.ready_to_import(name="shadowed_super", source=source):
|
|
import shadowed_super
|
|
self.assertEqual(shadowed_super.C().method(), "truly super")
|
|
import_helper.unload("shadowed_super")
|
|
|
|
def test_shadowed_local(self):
|
|
class super:
|
|
msg = "quite super"
|
|
|
|
class C:
|
|
def method(self):
|
|
return super().msg
|
|
|
|
self.assertEqual(C().method(), "quite super")
|
|
|
|
def test_shadowed_dynamic(self):
|
|
class MySuper:
|
|
msg = "super super"
|
|
|
|
class C:
|
|
def method(self):
|
|
return super().msg
|
|
|
|
with patch(f"{__name__}.super", MySuper) as m:
|
|
self.assertEqual(C().method(), "super super")
|
|
|
|
def test_shadowed_dynamic_two_arg(self):
|
|
call_args = []
|
|
class MySuper:
|
|
def __init__(self, *args):
|
|
call_args.append(args)
|
|
msg = "super super"
|
|
|
|
class C:
|
|
def method(self):
|
|
return super(1, 2).msg
|
|
|
|
with patch(f"{__name__}.super", MySuper) as m:
|
|
self.assertEqual(C().method(), "super super")
|
|
self.assertEqual(call_args, [(1, 2)])
|
|
|
|
def test_attribute_error(self):
|
|
class C:
|
|
def method(self):
|
|
return super().msg
|
|
|
|
with self.assertRaisesRegex(AttributeError, "'super' object has no attribute 'msg'"):
|
|
C().method()
|
|
|
|
def test_bad_first_arg(self):
|
|
class C:
|
|
def method(self):
|
|
return super(1, self).method()
|
|
|
|
with self.assertRaisesRegex(TypeError, "argument 1 must be a type"):
|
|
C().method()
|
|
|
|
def test_supercheck_fail(self):
|
|
class C:
|
|
def method(self, type_, obj):
|
|
return super(type_, obj).method()
|
|
|
|
c = C()
|
|
err_msg = (
|
|
r"super\(type, obj\): obj \({} {}\) is not "
|
|
r"an instance or subtype of type \({}\)."
|
|
)
|
|
|
|
cases = (
|
|
(int, c, int.__name__, C.__name__, "instance of"),
|
|
# obj is instance of type
|
|
(C, list(), C.__name__, list.__name__, "instance of"),
|
|
# obj is type itself
|
|
(C, list, C.__name__, list.__name__, "type"),
|
|
)
|
|
|
|
for case in cases:
|
|
with self.subTest(case=case):
|
|
type_, obj, type_str, obj_str, instance_or_type = case
|
|
regex = err_msg.format(instance_or_type, obj_str, type_str)
|
|
|
|
with self.assertRaisesRegex(TypeError, regex):
|
|
c.method(type_, obj)
|
|
|
|
def test_super___class__(self):
|
|
class C:
|
|
def method(self):
|
|
return super().__class__
|
|
|
|
self.assertEqual(C().method(), super)
|
|
|
|
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: type 'super' is not an acceptable base type
|
|
def test_super_subclass___class__(self):
|
|
class mysuper(super):
|
|
pass
|
|
|
|
class C:
|
|
def method(self):
|
|
return mysuper(C, self).__class__
|
|
|
|
self.assertEqual(C().method(), mysuper)
|
|
|
|
def test_unusual_getattro(self):
|
|
class MyType(type):
|
|
pass
|
|
|
|
def test(name):
|
|
mytype = MyType(name, (MyType,), {})
|
|
super(MyType, type(mytype)).__setattr__(mytype, "bar", 1)
|
|
self.assertEqual(mytype.bar, 1)
|
|
|
|
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
|
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
|
|
test("foo1")
|
|
|
|
def test_reassigned_new(self):
|
|
class A:
|
|
def __new__(cls):
|
|
pass
|
|
|
|
def __init_subclass__(cls):
|
|
if "__new__" not in cls.__dict__:
|
|
cls.__new__ = cls.__new__
|
|
|
|
class B(A):
|
|
pass
|
|
|
|
class C(B):
|
|
def __new__(cls):
|
|
return super().__new__(cls)
|
|
|
|
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
|
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
|
|
C()
|
|
|
|
def test_mixed_staticmethod_hierarchy(self):
|
|
# This test is just a desugared version of `test_reassigned_new`
|
|
class A:
|
|
@staticmethod
|
|
def some(cls, *args, **kwargs):
|
|
self.assertFalse(args)
|
|
self.assertFalse(kwargs)
|
|
|
|
class B(A):
|
|
def some(cls, *args, **kwargs):
|
|
return super().some(cls, *args, **kwargs)
|
|
|
|
class C(B):
|
|
@staticmethod
|
|
def some(cls):
|
|
return super().some(cls)
|
|
|
|
_testinternalcapi = import_helper.import_module("_testinternalcapi")
|
|
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
|
|
C.some(C)
|
|
|
|
@threading_helper.requires_working_threading()
|
|
def test___class___modification_multithreaded(self):
|
|
""" Note: this test isn't actually testing anything on its own.
|
|
It requires a sys audithook to be set to crash on older Python.
|
|
This should be the case anyways as our test suite sets
|
|
an audit hook.
|
|
"""
|
|
|
|
class Foo:
|
|
pass
|
|
|
|
class Bar:
|
|
pass
|
|
|
|
thing = Foo()
|
|
def work():
|
|
foo = thing
|
|
for _ in range(200):
|
|
foo.__class__ = Bar
|
|
type(foo)
|
|
foo.__class__ = Foo
|
|
type(foo)
|
|
|
|
|
|
threads = []
|
|
for _ in range(6):
|
|
thread = threading.Thread(target=work)
|
|
thread.start()
|
|
threads.append(thread)
|
|
|
|
for thread in threads:
|
|
thread.join()
|
|
|
|
def test_special_methods(self):
|
|
for e in E(), E:
|
|
s = super(C, e)
|
|
self.assertEqual(s.__reduce__, e.__reduce__)
|
|
self.assertEqual(s.__reduce_ex__, e.__reduce_ex__)
|
|
self.assertEqual(s.__getstate__, e.__getstate__)
|
|
self.assertNotHasAttr(s, '__getnewargs__')
|
|
self.assertNotHasAttr(s, '__getnewargs_ex__')
|
|
self.assertNotHasAttr(s, '__setstate__')
|
|
self.assertNotHasAttr(s, '__copy__')
|
|
self.assertNotHasAttr(s, '__deepcopy__')
|
|
|
|
def test_pickling(self):
|
|
e = E()
|
|
e.x = 1
|
|
s = super(C, e)
|
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
|
with self.subTest(proto=proto):
|
|
u = pickle.loads(pickle.dumps(s, proto))
|
|
self.assertEqual(u.f(), s.f())
|
|
self.assertIs(type(u), type(s))
|
|
self.assertIs(type(u.__self__), E)
|
|
self.assertEqual(u.__self__.x, 1)
|
|
self.assertIs(u.__thisclass__, C)
|
|
self.assertIs(u.__self_class__, E)
|
|
|
|
s = super(C, E)
|
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
|
with self.subTest(proto=proto):
|
|
u = pickle.loads(pickle.dumps(s, proto))
|
|
self.assertEqual(u.cm(), s.cm())
|
|
self.assertEqual(u.f, s.f)
|
|
self.assertIs(type(u), type(s))
|
|
self.assertIs(u.__self__, E)
|
|
self.assertIs(u.__thisclass__, C)
|
|
self.assertIs(u.__self_class__, E)
|
|
|
|
def test_shallow_copying(self):
|
|
s = super(C, E())
|
|
self.assertIs(copy.copy(s), s)
|
|
s = super(C, E)
|
|
self.assertIs(copy.copy(s), s)
|
|
|
|
def test_deep_copying(self):
|
|
e = E()
|
|
e.x = [1]
|
|
s = super(C, e)
|
|
u = copy.deepcopy(s)
|
|
self.assertEqual(u.f(), s.f())
|
|
self.assertIs(type(u), type(s))
|
|
self.assertIsNot(u, s)
|
|
self.assertIs(type(u.__self__), E)
|
|
self.assertIsNot(u.__self__, e)
|
|
self.assertIsNot(u.__self__.x, e.x)
|
|
self.assertEqual(u.__self__.x, [1])
|
|
self.assertIs(u.__thisclass__, C)
|
|
self.assertIs(u.__self_class__, E)
|
|
|
|
s = super(C, E)
|
|
u = copy.deepcopy(s)
|
|
self.assertEqual(u.cm(), s.cm())
|
|
self.assertEqual(u.f, s.f)
|
|
self.assertIsNot(u, s)
|
|
self.assertIs(type(u), type(s))
|
|
self.assertIs(u.__self__, E)
|
|
self.assertIs(u.__thisclass__, C)
|
|
self.assertIs(u.__self_class__, E)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|