|
6 | 6 | import os |
7 | 7 | import sys |
8 | 8 | import unittest |
| 9 | +import warnings |
9 | 10 |
|
10 | 11 | from iptest import IronPythonTestCase, is_cli, is_mono, myint, big, run_test, skipUnlessIronPython |
11 | 12 |
|
@@ -2621,6 +2622,103 @@ class y(object): pass |
2621 | 2622 | self.assertEqual(x.__bases__, (object, )) |
2622 | 2623 | self.assertEqual(x.__name__, 'x') |
2623 | 2624 |
|
| 2625 | + def test_class_attribute(self): |
| 2626 | + class C: |
| 2627 | + def f(self): |
| 2628 | + return self.__class__ |
| 2629 | + def g(self): |
| 2630 | + return __class__ |
| 2631 | + def h(): |
| 2632 | + return __class__ |
| 2633 | + @staticmethod |
| 2634 | + def j(): |
| 2635 | + return __class__ |
| 2636 | + @classmethod |
| 2637 | + def k(cls): |
| 2638 | + return __class__ |
| 2639 | + |
| 2640 | + self.assertEqual(C().f(), C) |
| 2641 | + self.assertEqual(C().g(), C) |
| 2642 | + self.assertEqual(C.h(), C) |
| 2643 | + self.assertEqual(C.j(), C) |
| 2644 | + self.assertEqual(C.k(), C) |
| 2645 | + self.assertEqual(C().k(), C) |
| 2646 | + |
| 2647 | + # Test that a metaclass implemented as a function sets __class__ at a proper moment |
| 2648 | + def makeclass(name, bases, attrs): |
| 2649 | + attrNames = set(attrs.keys()) |
| 2650 | + self.assertRaisesMessage(NameError, "free variable '__class__' referenced before assignment in enclosing scope", attrs['getclass'], None) |
| 2651 | + if (is_cli or sys.version_info >= (3, 6)): |
| 2652 | + self.assertIn('__classcell__', attrNames) |
| 2653 | + |
| 2654 | + t = type(name, bases, attrs) |
| 2655 | + |
| 2656 | + if (is_cli or sys.version_info >= (3, 6)): |
| 2657 | + self.assertEqual(t.getclass(None), t) # __class__ is set right after the type is created |
| 2658 | + else: # CPython 3.5- |
| 2659 | + self.assertRaisesMessage(NameError, "free variable '__class__' referenced before assignment in enclosing scope", attrs['getclass'], None) |
| 2660 | + if not is_cli: |
| 2661 | + self.assertEqual(set(attrs.keys()), attrNames) # set of attrs is not modified by type creation |
| 2662 | + else: |
| 2663 | + # TODO: prevent modification of attrs in IronPython |
| 2664 | + self.assertEqual(set(attrs.keys()) | {'__classcell__'}, attrNames | {'__class__'}) |
| 2665 | + |
| 2666 | + return t |
| 2667 | + |
| 2668 | + class A(metaclass=makeclass): |
| 2669 | + def getclass(self): |
| 2670 | + return __class__ |
| 2671 | + |
| 2672 | + self.assertEquals(A.getclass(None), A) |
| 2673 | + self.assertEquals(A().getclass(), A) |
| 2674 | + |
| 2675 | + dirA = dir(A) |
| 2676 | + self.assertIn('getclass', dirA) |
| 2677 | + self.assertIn('getclass', A.__dict__) |
| 2678 | + self.assertIn('__class__', dirA) |
| 2679 | + self.assertNotIn('__class__', A.__dict__) |
| 2680 | + if not is_cli: |
| 2681 | + self.assertNotIn('__classcell__', dirA) |
| 2682 | + self.assertNotIn('__classcell__', A.__dict__) |
| 2683 | + else: |
| 2684 | + # TODO: filter out __classcell__ in IronPython |
| 2685 | + self.assertIn('__classcell__', dirA) |
| 2686 | + self.assertIn('__classcell__', A.__dict__) |
| 2687 | + |
| 2688 | + def test_classcell_propagation(self): |
| 2689 | + with warnings.catch_warnings(record=True) as ws: |
| 2690 | + warnings.simplefilter("always") |
| 2691 | + |
| 2692 | + with self.assertWarnsRegex(DeprecationWarning, r"^__class__ not set defining 'MyClass' as <class '.*\.MyClass'>\. Was __classcell__ propagated to type\.__new__\?$"): |
| 2693 | + class MyDict(dict): |
| 2694 | + def __setitem__(self, key, value): |
| 2695 | + pass |
| 2696 | + |
| 2697 | + class MetaClass(type): |
| 2698 | + @classmethod |
| 2699 | + def __prepare__(metacls, name, bases): |
| 2700 | + return MyDict() |
| 2701 | + |
| 2702 | + class MyClass(metaclass=MetaClass): |
| 2703 | + def test(self): |
| 2704 | + return __class__ |
| 2705 | + |
| 2706 | + self.assertEqual(len(ws), 0) # no unchecked warnings |
| 2707 | + |
| 2708 | + with warnings.catch_warnings(record=True) as ws: |
| 2709 | + warnings.simplefilter("always") |
| 2710 | + |
| 2711 | + with self.assertWarnsRegex(DeprecationWarning, r"^__class__ not set defining 'bar' as <class '.*\.gez'>\. Was __classcell__ propagated to type\.__new__\?$"): |
| 2712 | + class gez: pass |
| 2713 | + |
| 2714 | + def foo(*args): |
| 2715 | + return gez |
| 2716 | + |
| 2717 | + class bar(metaclass=foo): |
| 2718 | + def barfun(self): |
| 2719 | + return __class__ |
| 2720 | + |
| 2721 | + self.assertEqual(len(ws), 0) # no unchecked warnings |
2624 | 2722 |
|
2625 | 2723 | def test_issubclass(self): |
2626 | 2724 | # first argument doesn't need to be new-style or old-style class if it defines __bases__ |
|
0 commit comments