private-attribute-cpp 2.1.5__tar.gz → 2.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {private_attribute_cpp-2.1.5/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.7}/PKG-INFO +1 -1
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute.cpp +267 -254
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7/private_attribute_cpp.egg-info}/PKG-INFO +1 -1
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/setup.py +1 -1
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/LICENSE +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/MANIFEST.in +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/README.md +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/picosha2.h +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute.pyi +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute_cpp.egg-info/SOURCES.txt +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/private_attribute_cpp.egg-info/top_level.txt +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/setup.cfg +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_attr_classify_owner.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_class_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_gc_collect.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_abc.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_func.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_inheritance.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_method.py +0 -0
- {private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_wrap.py +0 -0
{private_attribute_cpp-2.1.5/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.7}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: private_attribute_cpp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: A Python package that provides a way to define private attributes in C++ implementation.
|
|
5
5
|
Home-page: https://github.com/Locked-chess-official/private_attribute_cpp
|
|
6
6
|
Author: HuangHaoHua
|
|
@@ -220,9 +220,6 @@ namespace {
|
|
|
220
220
|
// defaults as "not a real original" and keep walking upward.
|
|
221
221
|
static traverseproc captured_subtype_traverse = NULL;
|
|
222
222
|
static inquiry captured_subtype_clear = NULL;
|
|
223
|
-
// all type tp_finalizer map
|
|
224
|
-
static std::unordered_map<uintptr_t, destructor> all_type_finalize;
|
|
225
|
-
// all type tp_del map (old-style destructor; see PrivateAttr_tp_del)
|
|
226
223
|
static std::unordered_map<uintptr_t, destructor> all_type_del;
|
|
227
224
|
|
|
228
225
|
static std::shared_mutex all_register_new_metaclass_mutex;
|
|
@@ -299,7 +296,6 @@ clean_all_storages() noexcept
|
|
|
299
296
|
AllData::all_type_setattro.clear();
|
|
300
297
|
AllData::all_type_traverse.clear();
|
|
301
298
|
AllData::all_type_clear.clear();
|
|
302
|
-
AllData::all_type_finalize.clear();
|
|
303
299
|
AllData::all_type_del.clear();
|
|
304
300
|
AllData::all_register_type_weak_ref.clear();
|
|
305
301
|
AllData::store_module_self.clear();
|
|
@@ -1355,7 +1351,6 @@ typedef struct {
|
|
|
1355
1351
|
static void PrivateAttr_object_init_private_dict(uintptr_t obj_id, uintptr_t type_id) noexcept;
|
|
1356
1352
|
static int PrivateAttr_tp_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept;
|
|
1357
1353
|
static void PrivateAttr_tp_finalize(PyObject* self) noexcept;
|
|
1358
|
-
static void PrivateAttr_tp_del(PyObject* self) noexcept;
|
|
1359
1354
|
static void clear_object_related(uintptr_t id_self, uintptr_t typ_id) noexcept;
|
|
1360
1355
|
|
|
1361
1356
|
static PyObject*
|
|
@@ -1434,58 +1429,6 @@ PrivateAttr_tp_setattro(PyObject* self, PyObject* name, PyObject* value) noexcep
|
|
|
1434
1429
|
return PyObject_GenericSetAttr(self, name, value);
|
|
1435
1430
|
}
|
|
1436
1431
|
|
|
1437
|
-
static void
|
|
1438
|
-
PrivateAttr_tp_finalize(PyObject* self) noexcept
|
|
1439
|
-
{
|
|
1440
|
-
PyTypeObject* typ = Py_TYPE(self);
|
|
1441
|
-
uintptr_t typ_id = (uintptr_t)typ;
|
|
1442
|
-
if (::AllData::all_type_finalize.find(typ_id) != ::AllData::all_type_finalize.end()){
|
|
1443
|
-
if (::AllData::all_type_finalize[typ_id]) {
|
|
1444
|
-
::AllData::all_type_finalize[typ_id](self);
|
|
1445
|
-
ensure_tp(typ);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
// Per-object cleanup moved to PrivateAttr_tp_del (see below).
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
// ========================================================================
|
|
1452
|
-
// tp_del : per-object cleanup for private attributes.
|
|
1453
|
-
//
|
|
1454
|
-
// subtype_dealloc calls tp_del in BOTH dealloc branches (non-GC typeobject.c
|
|
1455
|
-
// ~2737 and GC ~2814) before clear_slots / basedealloc, and explicitly checks
|
|
1456
|
-
// Py_REFCNT(self) > 0 to detect resurrection. This is the natural hook for
|
|
1457
|
-
// clearing ::AllData per-object entries:
|
|
1458
|
-
// - it is reached on every object death path (refcount drop AND cyclic GC),
|
|
1459
|
-
// - the resurrection check is explicit and readable here (Py_REFCNT > 0),
|
|
1460
|
-
// - no dependency on tp_finalize / weakref / slots / __dict__.
|
|
1461
|
-
// We keep PrivateAttr_tp_finalize (so user __del__ / tp_finalize still runs
|
|
1462
|
-
// via PyObject_CallFinalizerFromDealloc) but it no longer clears storage.
|
|
1463
|
-
// ========================================================================
|
|
1464
|
-
static void
|
|
1465
|
-
PrivateAttr_tp_del(PyObject* self) noexcept
|
|
1466
|
-
{
|
|
1467
|
-
uintptr_t id_self = (uintptr_t)self;
|
|
1468
|
-
PyTypeObject* typ = Py_TYPE(self);
|
|
1469
|
-
uintptr_t typ_id = (uintptr_t)typ;
|
|
1470
|
-
|
|
1471
|
-
// If a user tp_finalize / tp_del resurrected the object, subtype_dealloc
|
|
1472
|
-
// returns without freeing, so we must NOT clear the private storage yet.
|
|
1473
|
-
if (Py_REFCNT(self) > 0) {
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
// Chain to the original tp_del if there is one (we are called instead of
|
|
1478
|
-
// the original, so we must invoke it to keep old-style destructors working).
|
|
1479
|
-
if (::AllData::all_type_del.find(typ_id) != ::AllData::all_type_del.end()) {
|
|
1480
|
-
destructor orig = ::AllData::all_type_del[typ_id];
|
|
1481
|
-
if (orig && orig != PrivateAttr_tp_del) {
|
|
1482
|
-
orig(self);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
clear_object_related(id_self, typ_id);
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
1432
|
// shared helper for clearing object-related data (used by tp_clear and tp_finalize)
|
|
1490
1433
|
static void
|
|
1491
1434
|
clear_object_related(uintptr_t id_self, uintptr_t typ_id) noexcept
|
|
@@ -1597,6 +1540,61 @@ PrivateAttr_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
|
1597
1540
|
}
|
|
1598
1541
|
}
|
|
1599
1542
|
|
|
1543
|
+
// Instance-dict visit (mirror of subtype_traverse's dictoffset block).
|
|
1544
|
+
// The saved "original" for a private-attribute class is subtype_traverse,
|
|
1545
|
+
// but because the class's LIVE tp_traverse has been replaced with this
|
|
1546
|
+
// wrapper, subtype_traverse skips its own slot-walking loop, keeps base ==
|
|
1547
|
+
// typ, and its `type->tp_dictoffset != base->tp_dictoffset` check sees equal
|
|
1548
|
+
// offsets -> it never visits the instance __dict__. The instance __dict__
|
|
1549
|
+
// (e.g. `self.__dict__['x'] = self` self-cycles) would then be invisible to
|
|
1550
|
+
// the collector and leak. Do the visit explicitly here instead.
|
|
1551
|
+
//
|
|
1552
|
+
// The managed-dict API is version-dependent: 3.13+ exposes the public
|
|
1553
|
+
// PyObject_VisitManagedDict, 3.12 only the internal _PyObject_VisitManagedDict,
|
|
1554
|
+
// and <=3.11 has no managed dict at all (classic positive tp_dictoffset,
|
|
1555
|
+
// visited through _PyObject_GetDictPtr like subtype_traverse does).
|
|
1556
|
+
#if PY_VERSION_HEX >= 0x030D0000
|
|
1557
|
+
if ((typ->tp_flags & Py_TPFLAGS_MANAGED_DICT)
|
|
1558
|
+
&& typ->tp_dictoffset == -1) {
|
|
1559
|
+
int err = PyObject_VisitManagedDict(self, visit, arg);
|
|
1560
|
+
if (err) {
|
|
1561
|
+
return err;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
#elif PY_VERSION_HEX >= 0x030C0000
|
|
1565
|
+
if ((typ->tp_flags & Py_TPFLAGS_MANAGED_DICT)
|
|
1566
|
+
&& typ->tp_dictoffset == -1) {
|
|
1567
|
+
int err = _PyObject_VisitManagedDict(self, visit, arg);
|
|
1568
|
+
if (err) {
|
|
1569
|
+
return err;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
#else
|
|
1573
|
+
PyObject** dictptr = _PyObject_GetDictPtr(self);
|
|
1574
|
+
if (dictptr && *dictptr) {
|
|
1575
|
+
int err = visit(*dictptr, arg);
|
|
1576
|
+
if (err) {
|
|
1577
|
+
return err;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
#endif
|
|
1581
|
+
|
|
1582
|
+
// Instance->type link (mirror of subtype_traverse's heap-type visit).
|
|
1583
|
+
// subtype_traverse(self) does Py_VISIT(Py_TYPE(self)) only when the
|
|
1584
|
+
// resolved base traverse does NOT belong to a heap type. Because the
|
|
1585
|
+
// instance's LIVE tp_traverse has been replaced with this wrapper,
|
|
1586
|
+
// subtype_traverse keeps base == typ (heap), so that Py_VISIT is skipped
|
|
1587
|
+
// and a cycle like A ->(tp_dict)-> a and a ->(ob_type)-> A is invisible
|
|
1588
|
+
// to the collector. Visit the instance's type explicitly when it is a heap
|
|
1589
|
+
// type, matching subtype_traverse's intent. (This is the same fix applied
|
|
1590
|
+
// to the metaclass wrapper PrivateAttrType_tp_traverse.)
|
|
1591
|
+
{
|
|
1592
|
+
PyTypeObject* meta = Py_TYPE(self);
|
|
1593
|
+
if (meta && (meta->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
|
1594
|
+
Py_VISIT((PyObject*)meta);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1600
1598
|
// traverse per-object private attributes for this type and its parents
|
|
1601
1599
|
std::vector<uintptr_t> parent_ids;
|
|
1602
1600
|
if (::AllData::all_type_parent_id.find(typ_id) != ::AllData::all_type_parent_id.end()){
|
|
@@ -1647,11 +1645,99 @@ PrivateAttr_tp_clear(PyObject* self) noexcept
|
|
|
1647
1645
|
if (orig) orig(self);
|
|
1648
1646
|
}
|
|
1649
1647
|
|
|
1648
|
+
// Instance-dict clear (mirror of subtype_clear's dict handling). Same
|
|
1649
|
+
// reasoning as the traverse side: subtype_clear can't run its dict block
|
|
1650
|
+
// once the LIVE tp_clear has been replaced, so clear the instance dict
|
|
1651
|
+
// explicitly to break __dict__ self-cycles. Version-dependent APIs, see
|
|
1652
|
+
// the matching traverse block above.
|
|
1653
|
+
#if PY_VERSION_HEX >= 0x030D0000
|
|
1654
|
+
if ((typ->tp_flags & Py_TPFLAGS_MANAGED_DICT)
|
|
1655
|
+
&& typ->tp_dictoffset == -1) {
|
|
1656
|
+
PyObject_ClearManagedDict(self);
|
|
1657
|
+
}
|
|
1658
|
+
#elif PY_VERSION_HEX >= 0x030C0000
|
|
1659
|
+
if ((typ->tp_flags & Py_TPFLAGS_MANAGED_DICT)
|
|
1660
|
+
&& typ->tp_dictoffset == -1) {
|
|
1661
|
+
_PyObject_ClearManagedDict(self);
|
|
1662
|
+
}
|
|
1663
|
+
#else
|
|
1664
|
+
PyObject** dictptr = _PyObject_GetDictPtr(self);
|
|
1665
|
+
if (dictptr && *dictptr) {
|
|
1666
|
+
Py_CLEAR(*dictptr);
|
|
1667
|
+
}
|
|
1668
|
+
#endif
|
|
1669
|
+
|
|
1650
1670
|
// clear our per-object data
|
|
1651
1671
|
clear_object_related(id_self, typ_id);
|
|
1652
1672
|
return 0;
|
|
1653
1673
|
}
|
|
1654
1674
|
|
|
1675
|
+
class KeepPythonException {
|
|
1676
|
+
private:
|
|
1677
|
+
PyObject* typ;
|
|
1678
|
+
PyObject* exc;
|
|
1679
|
+
PyObject* tb;
|
|
1680
|
+
public:
|
|
1681
|
+
KeepPythonException() noexcept {
|
|
1682
|
+
// under 3.12 use PyErr_Fetch
|
|
1683
|
+
#if PY_VERSION_HEX < 0x030C0000
|
|
1684
|
+
PyErr_Fetch(&typ, &exc, &tb);
|
|
1685
|
+
#else
|
|
1686
|
+
exc = PyErr_GetRaisedException();
|
|
1687
|
+
#endif
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
~KeepPythonException() noexcept {
|
|
1691
|
+
// under 3.12 use PyErr_Restore
|
|
1692
|
+
#if PY_VERSION_HEX < 0x030C0000
|
|
1693
|
+
PyErr_Restore(typ, exc, tb);
|
|
1694
|
+
#else
|
|
1695
|
+
PyErr_SetRaisedException(exc);
|
|
1696
|
+
#endif
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
static void
|
|
1701
|
+
PrivateAttr_tp_finalize(PyObject* self) noexcept
|
|
1702
|
+
{
|
|
1703
|
+
KeepPythonException _;
|
|
1704
|
+
uintptr_t id_self = (uintptr_t)self;
|
|
1705
|
+
PyTypeObject* typ = Py_TYPE(self);
|
|
1706
|
+
uintptr_t typ_id = (uintptr_t)typ;
|
|
1707
|
+
|
|
1708
|
+
Py_ssize_t refcnt = Py_REFCNT(self);
|
|
1709
|
+
// Chain to the original tp_finalize if there is one.
|
|
1710
|
+
if (::AllData::all_type_del.find(typ_id) != ::AllData::all_type_del.end()) {
|
|
1711
|
+
destructor orig = ::AllData::all_type_del[typ_id];
|
|
1712
|
+
if (orig && orig != PrivateAttr_tp_finalize) {
|
|
1713
|
+
orig(self);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
if (Py_REFCNT(self) > refcnt) {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
clear_object_related(id_self, typ_id);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
static destructor
|
|
1723
|
+
get_need_tp_finalize(PyTypeObject* cls) noexcept
|
|
1724
|
+
{
|
|
1725
|
+
PyTypeObject* base = cls->tp_base;
|
|
1726
|
+
while (base) {
|
|
1727
|
+
if (base->tp_finalize != PrivateAttr_tp_finalize) {
|
|
1728
|
+
return base->tp_finalize;
|
|
1729
|
+
} else if (::AllData::all_type_del.find((uintptr_t)base) != ::AllData::all_type_del.end()) {
|
|
1730
|
+
if (::AllData::all_type_del[(uintptr_t)base]) {
|
|
1731
|
+
return ::AllData::all_type_del[(uintptr_t)base];
|
|
1732
|
+
}
|
|
1733
|
+
base = base->tp_base;
|
|
1734
|
+
} else {
|
|
1735
|
+
base = base->tp_base;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
return NULL;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1655
1741
|
// get_type_need_tp_* : the metaclass counterparts of get_need_tp_*.
|
|
1656
1742
|
// get_need_tp_* -> find the original slots of *types created by* the
|
|
1657
1743
|
// metaclasses (wrapped with the instance-level
|
|
@@ -1659,20 +1745,19 @@ PrivateAttr_tp_clear(PyObject* self) noexcept
|
|
|
1659
1745
|
// get_type_need_tp_* -> find the original slots of the *metaclasses*
|
|
1660
1746
|
// themselves (wrapped with the metaclass-level
|
|
1661
1747
|
// PrivateAttrType_getattr / PrivateAttrType_setattr /
|
|
1662
|
-
//
|
|
1748
|
+
// PrivateAttrType_tp_traverse /
|
|
1663
1749
|
// PrivateAttrType_tp_clear).
|
|
1664
1750
|
static getattrofunc get_type_need_tp_getattro(PyTypeObject* cls) noexcept;
|
|
1665
1751
|
static setattrofunc get_type_need_tp_setattro(PyTypeObject* cls) noexcept;
|
|
1666
1752
|
static traverseproc get_type_need_tp_traverse(PyTypeObject* cls) noexcept;
|
|
1667
1753
|
static inquiry get_type_need_tp_clear(PyTypeObject* cls) noexcept;
|
|
1668
|
-
static destructor get_type_need_tp_finalize(PyTypeObject* cls) noexcept;
|
|
1669
1754
|
|
|
1670
1755
|
// metaclass-level (type object) traverse/clear: call original if any, and handle AllData-stored type attributes
|
|
1671
1756
|
static int
|
|
1672
1757
|
PrivateAttrType_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
1673
1758
|
{
|
|
1674
|
-
PyTypeObject* typ = (
|
|
1675
|
-
uintptr_t typ_id = (uintptr_t)
|
|
1759
|
+
PyTypeObject* typ = Py_TYPE(self);
|
|
1760
|
+
uintptr_t typ_id = (uintptr_t)self;
|
|
1676
1761
|
|
|
1677
1762
|
// Re-entrancy guard: see PrivateAttrRecursionGuard above.
|
|
1678
1763
|
if (!PrivateAttrRecursionGuard::try_enter((uintptr_t)self)) {
|
|
@@ -1680,20 +1765,39 @@ PrivateAttrType_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
|
1680
1765
|
}
|
|
1681
1766
|
PrivateAttrRecursionGuard guard((uintptr_t)self);
|
|
1682
1767
|
|
|
1683
|
-
//
|
|
1684
|
-
//
|
|
1685
|
-
//
|
|
1686
|
-
//
|
|
1687
|
-
//
|
|
1688
|
-
//
|
|
1768
|
+
// Recover the original traverse via get_type_need_tp_traverse, skipping
|
|
1769
|
+
// any delegating default (subtype_traverse / PrivateAttr_tp_traverse / our
|
|
1770
|
+
// own wrapper). For a type object the only correct "real" traverse is
|
|
1771
|
+
// CPython's type_traverse; when no real one exists (plain class objects
|
|
1772
|
+
// delegate through subtype_traverse, which would follow the LIVE tp_traverse
|
|
1773
|
+
// slot straight back into this wrapper), we rely on the AllData traversal
|
|
1774
|
+
// below to reach the class's hard self-cycle (tp_dict / tp_mro / tp_bases)
|
|
1775
|
+
// through the type-level private attributes we visit.
|
|
1689
1776
|
{
|
|
1690
|
-
traverseproc orig =
|
|
1777
|
+
traverseproc orig = get_type_need_tp_traverse(typ);
|
|
1691
1778
|
if (orig) {
|
|
1692
1779
|
int res = orig(self, visit, arg);
|
|
1693
1780
|
if (res) return res;
|
|
1694
1781
|
}
|
|
1695
1782
|
}
|
|
1696
1783
|
|
|
1784
|
+
// Mirror CPython's subtype_traverse for the instance->type link.
|
|
1785
|
+
// subtype_traverse(self) does Py_VISIT(Py_TYPE(self)) when the instance is
|
|
1786
|
+
// a heap type and the resolved base traverse does not belong to a heap
|
|
1787
|
+
// type, so cycles between a class object and its (heap) metaclass are
|
|
1788
|
+
// visible to the collector. type_traverse alone does NOT visit ob_type:
|
|
1789
|
+
// it only walks tp_dict / tp_mro / tp_bases, so without this step a cycle
|
|
1790
|
+
// like M ->(dict attr)-> C and C ->(ob_type)-> M is invisible to GC
|
|
1791
|
+
// and leaks. self here is a TYPE object; its Py_TYPE is the metaclass
|
|
1792
|
+
// (which is a heap type when registered/created by type()). We only emit
|
|
1793
|
+
// the visit when the metaclass is a heap type, matching subtype_traverse's
|
|
1794
|
+
// condition and avoiding the trivial/static type link.
|
|
1795
|
+
{
|
|
1796
|
+
if (typ->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
|
1797
|
+
Py_VISIT((PyObject*)typ);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1697
1801
|
// Traverse PyObjects stored in AllData for this type:
|
|
1698
1802
|
// - direct type attributes ::AllData::type_attr_dict[typ_id]
|
|
1699
1803
|
// - attributes stored under parents for this type ::AllData::all_type_subclass_attr[parent_id][typ_id]
|
|
@@ -1760,8 +1864,8 @@ PrivateAttrType_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
|
1760
1864
|
static int
|
|
1761
1865
|
PrivateAttrType_tp_clear(PyObject* self) noexcept
|
|
1762
1866
|
{
|
|
1763
|
-
PyTypeObject* typ = (
|
|
1764
|
-
uintptr_t typ_id = (uintptr_t)
|
|
1867
|
+
PyTypeObject* typ = Py_TYPE(self);
|
|
1868
|
+
uintptr_t typ_id = (uintptr_t)self;
|
|
1765
1869
|
|
|
1766
1870
|
// Re-entrancy guard: see PrivateAttrRecursionGuard above.
|
|
1767
1871
|
if (!PrivateAttrRecursionGuard::try_enter((uintptr_t)self)) {
|
|
@@ -1769,14 +1873,17 @@ PrivateAttrType_tp_clear(PyObject* self) noexcept
|
|
|
1769
1873
|
}
|
|
1770
1874
|
PrivateAttrRecursionGuard guard((uintptr_t)self);
|
|
1771
1875
|
|
|
1772
|
-
//
|
|
1773
|
-
//
|
|
1774
|
-
//
|
|
1775
|
-
//
|
|
1776
|
-
//
|
|
1777
|
-
//
|
|
1876
|
+
// Recover the original clear via get_type_need_tp_clear, skipping any
|
|
1877
|
+
// delegating default (subtype_clear / PrivateAttr_tp_clear / our own
|
|
1878
|
+
// wrapper). subtype_clear on a type object would clear the class's own
|
|
1879
|
+
// tp_dict (its namespace), which must never happen here; when the walker
|
|
1880
|
+
// finds no real clear, the AllData cleanup below still breaks the cycle by
|
|
1881
|
+
// clearing the type-level private attributes we stored (their PyObjectStorage
|
|
1882
|
+
// DECREFs release the references that keep the class's tp_dict / tp_mro /
|
|
1883
|
+
// ht_module alive), after which CPython's own type_clear runs on the class
|
|
1884
|
+
// once the collector re-visits it.
|
|
1778
1885
|
{
|
|
1779
|
-
inquiry orig =
|
|
1886
|
+
inquiry orig = get_type_need_tp_clear(typ);
|
|
1780
1887
|
if (orig) {
|
|
1781
1888
|
int result = orig(self);
|
|
1782
1889
|
if (result) return result;
|
|
@@ -2549,13 +2656,10 @@ static getattrofunc get_need_tp_getattro(PyTypeObject* cls) noexcept;
|
|
|
2549
2656
|
static setattrofunc get_need_tp_setattro(PyTypeObject* cls) noexcept;
|
|
2550
2657
|
static traverseproc get_need_tp_traverse(PyTypeObject* cls) noexcept;
|
|
2551
2658
|
static inquiry get_need_tp_clear(PyTypeObject* cls) noexcept;
|
|
2552
|
-
static destructor get_need_tp_finalize(PyTypeObject* cls) noexcept;
|
|
2553
|
-
static destructor get_need_tp_del(PyTypeObject* cls) noexcept;
|
|
2554
2659
|
|
|
2555
2660
|
// instance-level traverse/clear for types that use private attributes
|
|
2556
2661
|
static int PrivateAttr_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept;
|
|
2557
2662
|
static int PrivateAttr_tp_clear(PyObject* self) noexcept;
|
|
2558
|
-
static void PrivateAttr_tp_del(PyObject* self) noexcept;
|
|
2559
2663
|
// shared helper to clear object related data (avoid double clear)
|
|
2560
2664
|
static void clear_object_related(uintptr_t id_self, uintptr_t typ_id) noexcept;
|
|
2561
2665
|
// metaclass-level wrappers (if needed)
|
|
@@ -2640,38 +2744,12 @@ ensure_tp(PyTypeObject* type_instance) noexcept
|
|
|
2640
2744
|
}
|
|
2641
2745
|
{
|
|
2642
2746
|
if (type_instance->tp_finalize != PrivateAttr_tp_finalize) {
|
|
2643
|
-
if (!type_instance->tp_finalize && ::AllData::
|
|
2644
|
-
::AllData::
|
|
2747
|
+
if (!type_instance->tp_finalize && ::AllData::all_type_del.find(type_id) == ::AllData::all_type_del.end()) {
|
|
2748
|
+
::AllData::all_type_del[type_id] = get_need_tp_finalize(type_instance);
|
|
2645
2749
|
} else {
|
|
2646
|
-
::AllData::
|
|
2750
|
+
::AllData::all_type_del[type_id] = type_instance->tp_finalize;
|
|
2647
2751
|
}
|
|
2648
2752
|
type_instance->tp_finalize = PrivateAttr_tp_finalize;
|
|
2649
|
-
} else if (::AllData::all_type_finalize.find(type_id) == ::AllData::all_type_finalize.end()) {
|
|
2650
|
-
PyTypeObject* base = type_instance->tp_base;
|
|
2651
|
-
uintptr_t base_id = (uintptr_t)(base);
|
|
2652
|
-
if (::AllData::all_type_finalize.find(base_id) != ::AllData::all_type_finalize.end()) {
|
|
2653
|
-
::AllData::all_type_finalize[type_id] = ::AllData::all_type_finalize[base_id];
|
|
2654
|
-
} else if (base && base->tp_finalize && base->tp_finalize != PrivateAttr_tp_finalize) {
|
|
2655
|
-
::AllData::all_type_finalize[type_id] = base->tp_finalize;
|
|
2656
|
-
}
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
{
|
|
2660
|
-
if (type_instance->tp_del != PrivateAttr_tp_del) {
|
|
2661
|
-
if (!type_instance->tp_del && ::AllData::all_type_del.find(type_id) == ::AllData::all_type_del.end()) {
|
|
2662
|
-
::AllData::all_type_del[type_id] = get_need_tp_del(type_instance);
|
|
2663
|
-
} else {
|
|
2664
|
-
::AllData::all_type_del[type_id] = type_instance->tp_del;
|
|
2665
|
-
}
|
|
2666
|
-
type_instance->tp_del = PrivateAttr_tp_del;
|
|
2667
|
-
} else if (::AllData::all_type_del.find(type_id) == ::AllData::all_type_del.end()) {
|
|
2668
|
-
PyTypeObject* base = type_instance->tp_base;
|
|
2669
|
-
uintptr_t base_id = (uintptr_t)(base);
|
|
2670
|
-
if (::AllData::all_type_del.find(base_id) != ::AllData::all_type_del.end()) {
|
|
2671
|
-
::AllData::all_type_del[type_id] = ::AllData::all_type_del[base_id];
|
|
2672
|
-
} else if (base && base->tp_del && base->tp_del != PrivateAttr_tp_del) {
|
|
2673
|
-
::AllData::all_type_del[type_id] = base->tp_del;
|
|
2674
|
-
}
|
|
2675
2753
|
}
|
|
2676
2754
|
}
|
|
2677
2755
|
}
|
|
@@ -3032,25 +3110,6 @@ get_need_tp_setattro(PyTypeObject* cls) noexcept
|
|
|
3032
3110
|
return NULL;
|
|
3033
3111
|
}
|
|
3034
3112
|
|
|
3035
|
-
static destructor
|
|
3036
|
-
get_need_tp_finalize(PyTypeObject* cls) noexcept
|
|
3037
|
-
{
|
|
3038
|
-
PyTypeObject* base = cls->tp_base;
|
|
3039
|
-
while (base) {
|
|
3040
|
-
if (base->tp_finalize != PrivateAttr_tp_finalize) {
|
|
3041
|
-
return base->tp_finalize;
|
|
3042
|
-
} else if (::AllData::all_type_finalize.find((uintptr_t)base) != ::AllData::all_type_finalize.end()) {
|
|
3043
|
-
if (::AllData::all_type_finalize[(uintptr_t)base]) {
|
|
3044
|
-
return ::AllData::all_type_finalize[(uintptr_t)base];
|
|
3045
|
-
}
|
|
3046
|
-
base = base->tp_base;
|
|
3047
|
-
} else {
|
|
3048
|
-
base = base->tp_base;
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
return NULL;
|
|
3052
|
-
}
|
|
3053
|
-
|
|
3054
3113
|
static traverseproc
|
|
3055
3114
|
get_need_tp_traverse(PyTypeObject* cls) noexcept
|
|
3056
3115
|
{
|
|
@@ -3093,29 +3152,6 @@ get_need_tp_clear(PyTypeObject* cls) noexcept
|
|
|
3093
3152
|
return NULL;
|
|
3094
3153
|
}
|
|
3095
3154
|
|
|
3096
|
-
static destructor
|
|
3097
|
-
get_need_tp_del(PyTypeObject* cls) noexcept
|
|
3098
|
-
{
|
|
3099
|
-
PyTypeObject* base = cls->tp_base;
|
|
3100
|
-
while (base) {
|
|
3101
|
-
if (base->tp_del != PrivateAttr_tp_del) {
|
|
3102
|
-
return base->tp_del;
|
|
3103
|
-
} else if (::AllData::all_type_del.find((uintptr_t)base) != ::AllData::all_type_del.end()) {
|
|
3104
|
-
if (::AllData::all_type_del[(uintptr_t)base]) {
|
|
3105
|
-
return ::AllData::all_type_del[(uintptr_t)base];
|
|
3106
|
-
}
|
|
3107
|
-
base = base->tp_base;
|
|
3108
|
-
} else {
|
|
3109
|
-
base = base->tp_base;
|
|
3110
|
-
}
|
|
3111
|
-
}
|
|
3112
|
-
return NULL;
|
|
3113
|
-
}
|
|
3114
|
-
|
|
3115
|
-
// NOTE: register_finalize is defined later in this file; forward-declare it
|
|
3116
|
-
// here because get_type_need_tp_finalize() needs to compare against it.
|
|
3117
|
-
static void register_finalize(PyObject* cls) noexcept;
|
|
3118
|
-
|
|
3119
3155
|
// ========================================================================
|
|
3120
3156
|
// get_type_need_tp_* : find the ORIGINAL slot implementation for METACLASS
|
|
3121
3157
|
// slots.
|
|
@@ -3125,7 +3161,6 @@ static void register_finalize(PyObject* cls) noexcept;
|
|
|
3125
3161
|
// wrappers:
|
|
3126
3162
|
// tp_getattro -> PrivateAttrType_getattr
|
|
3127
3163
|
// tp_setattro -> PrivateAttrType_setattr
|
|
3128
|
-
// tp_finalize -> register_finalize
|
|
3129
3164
|
// tp_traverse -> PrivateAttrType_tp_traverse
|
|
3130
3165
|
// tp_clear -> PrivateAttrType_tp_clear
|
|
3131
3166
|
//
|
|
@@ -3175,65 +3210,81 @@ get_type_need_tp_setattro(PyTypeObject* cls) noexcept
|
|
|
3175
3210
|
return NULL;
|
|
3176
3211
|
}
|
|
3177
3212
|
|
|
3178
|
-
static destructor
|
|
3179
|
-
get_type_need_tp_finalize(PyTypeObject* cls) noexcept
|
|
3180
|
-
{
|
|
3181
|
-
PyTypeObject* base = cls->tp_base;
|
|
3182
|
-
while (base) {
|
|
3183
|
-
if (base->tp_finalize != register_finalize) {
|
|
3184
|
-
return base->tp_finalize;
|
|
3185
|
-
} else if (::AllData::all_type_finalize.find((uintptr_t)base) != ::AllData::all_type_finalize.end()) {
|
|
3186
|
-
if (::AllData::all_type_finalize[(uintptr_t)base]) {
|
|
3187
|
-
return ::AllData::all_type_finalize[(uintptr_t)base];
|
|
3188
|
-
}
|
|
3189
|
-
base = base->tp_base;
|
|
3190
|
-
} else {
|
|
3191
|
-
base = base->tp_base;
|
|
3192
|
-
}
|
|
3193
|
-
}
|
|
3194
|
-
return NULL;
|
|
3195
|
-
}
|
|
3196
|
-
|
|
3197
3213
|
static traverseproc
|
|
3198
3214
|
get_type_need_tp_traverse(PyTypeObject* cls) noexcept
|
|
3199
3215
|
{
|
|
3216
|
+
// cls is a type object (an instance of a metaclass). CPython only invokes
|
|
3217
|
+
// a real traverse on a tracked type object directly; otherwise it delegates
|
|
3218
|
+
// through subtype_traverse. So walk the metaclass base chain and skip every
|
|
3219
|
+
// delegating default (subtype_traverse, PrivateAttr_tp_traverse, our own
|
|
3220
|
+
// wrapper) AND NULL slots, returning the nearest real implementation.
|
|
3221
|
+
//
|
|
3222
|
+
// For a plain metaclass chain the walk is
|
|
3223
|
+
// cls -> ... -> PrivateAttrBase -> object -> (tp_base == NULL)
|
|
3224
|
+
// and object->tp_traverse is NULL, so the loop keeps walking to the end and
|
|
3225
|
+
// falls through to the PyType_Type.tp_traverse fallback below: for a type
|
|
3226
|
+
// object the ONLY correct real traverse is CPython's own type_traverse,
|
|
3227
|
+
// which is what makes the class's hard self-cycle (tp_dict / tp_mro /
|
|
3228
|
+
// tp_bases) visible to the cyclic GC. Without that fallback the class
|
|
3229
|
+
// becomes uncollectable. For a REGISTERED metaclass chain the saved-map
|
|
3230
|
+
// branch may find a real implementation before the fallback is reached.
|
|
3200
3231
|
PyTypeObject* base = cls->tp_base;
|
|
3201
3232
|
while (base) {
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3233
|
+
traverseproc slot = base->tp_traverse;
|
|
3234
|
+
if (slot
|
|
3235
|
+
&& slot != PrivateAttrType_tp_traverse
|
|
3236
|
+
&& slot != ::AllData::captured_subtype_traverse
|
|
3237
|
+
&& slot != PrivateAttr_tp_traverse) {
|
|
3238
|
+
return slot;
|
|
3239
|
+
}
|
|
3240
|
+
auto it = ::AllData::all_type_traverse.find((uintptr_t)base);
|
|
3241
|
+
if (it != ::AllData::all_type_traverse.end()) {
|
|
3242
|
+
traverseproc saved = it->second;
|
|
3243
|
+
if (saved
|
|
3244
|
+
&& saved != ::AllData::captured_subtype_traverse
|
|
3245
|
+
&& saved != PrivateAttr_tp_traverse
|
|
3246
|
+
&& saved != PrivateAttrType_tp_traverse) {
|
|
3208
3247
|
return saved;
|
|
3209
3248
|
}
|
|
3210
|
-
base = base->tp_base;
|
|
3211
|
-
} else {
|
|
3212
|
-
base = base->tp_base;
|
|
3213
3249
|
}
|
|
3250
|
+
base = base->tp_base;
|
|
3214
3251
|
}
|
|
3215
|
-
|
|
3252
|
+
// Fallback to CPython's real type_traverse (PyType_Type.tp_traverse). A
|
|
3253
|
+
// NULL slot is never a real implementation, so exhausting the chain here
|
|
3254
|
+
// means no custom traverse exists and the only correct original for a type
|
|
3255
|
+
// object is type_traverse itself.
|
|
3256
|
+
return PyType_Type.tp_traverse;
|
|
3216
3257
|
}
|
|
3217
3258
|
|
|
3218
3259
|
static inquiry
|
|
3219
3260
|
get_type_need_tp_clear(PyTypeObject* cls) noexcept
|
|
3220
3261
|
{
|
|
3262
|
+
// Same as get_type_need_tp_traverse: skip every delegating default
|
|
3263
|
+
// (subtype_clear, PrivateAttr_tp_clear, our own wrapper) AND NULL slots,
|
|
3264
|
+
// then fall back to CPython's real type_clear (PyType_Type.tp_clear) so a
|
|
3265
|
+
// plain metaclass chain still breaks the class's hard self-cycle.
|
|
3221
3266
|
PyTypeObject* base = cls->tp_base;
|
|
3222
3267
|
while (base) {
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3268
|
+
inquiry slot = base->tp_clear;
|
|
3269
|
+
if (slot
|
|
3270
|
+
&& slot != PrivateAttrType_tp_clear
|
|
3271
|
+
&& slot != ::AllData::captured_subtype_clear
|
|
3272
|
+
&& slot != PrivateAttr_tp_clear) {
|
|
3273
|
+
return slot;
|
|
3274
|
+
}
|
|
3275
|
+
auto it = ::AllData::all_type_clear.find((uintptr_t)base);
|
|
3276
|
+
if (it != ::AllData::all_type_clear.end()) {
|
|
3277
|
+
inquiry saved = it->second;
|
|
3278
|
+
if (saved
|
|
3279
|
+
&& saved != ::AllData::captured_subtype_clear
|
|
3280
|
+
&& saved != PrivateAttr_tp_clear
|
|
3281
|
+
&& saved != PrivateAttrType_tp_clear) {
|
|
3229
3282
|
return saved;
|
|
3230
3283
|
}
|
|
3231
|
-
base = base->tp_base;
|
|
3232
|
-
} else {
|
|
3233
|
-
base = base->tp_base;
|
|
3234
3284
|
}
|
|
3285
|
+
base = base->tp_base;
|
|
3235
3286
|
}
|
|
3236
|
-
return
|
|
3287
|
+
return PyType_Type.tp_clear;
|
|
3237
3288
|
}
|
|
3238
3289
|
|
|
3239
3290
|
static int
|
|
@@ -3328,12 +3379,6 @@ PrivateAttrType_finalize(PyObject* cls) noexcept
|
|
|
3328
3379
|
if (::AllData::all_type_setattro.find(typ_id) != ::AllData::all_type_setattro.end()) {
|
|
3329
3380
|
::AllData::all_type_setattro.erase(typ_id);
|
|
3330
3381
|
}
|
|
3331
|
-
if (::AllData::all_type_finalize.find(typ_id) != ::AllData::all_type_finalize.end()) {
|
|
3332
|
-
::AllData::all_type_finalize.erase(typ_id);
|
|
3333
|
-
}
|
|
3334
|
-
if (::AllData::all_type_del.find(typ_id) != ::AllData::all_type_del.end()) {
|
|
3335
|
-
::AllData::all_type_del.erase(typ_id);
|
|
3336
|
-
}
|
|
3337
3382
|
::AllData::all_type_mutex.erase(typ_id);
|
|
3338
3383
|
// Clear the per-type buckets of the object-attr tables (created in
|
|
3339
3384
|
// PrivateAttrType_postprocess / PrivateAttr_object_init_private_dict);
|
|
@@ -3554,21 +3599,6 @@ postprocess_for_PrivateAttr(PyObject* /*self*/, PyObject* args) noexcept
|
|
|
3554
3599
|
Py_RETURN_NONE;
|
|
3555
3600
|
}
|
|
3556
3601
|
|
|
3557
|
-
static void
|
|
3558
|
-
register_finalize(PyObject* cls) noexcept
|
|
3559
|
-
{
|
|
3560
|
-
Py_ssize_t original_ref = Py_REFCNT(cls);
|
|
3561
|
-
PyTypeObject* base = Py_TYPE(cls)->tp_base;
|
|
3562
|
-
while (base && base->tp_finalize == register_finalize) {
|
|
3563
|
-
base = base->tp_base;
|
|
3564
|
-
}
|
|
3565
|
-
if (base && base->tp_finalize) base->tp_finalize(cls);
|
|
3566
|
-
if (Py_REFCNT(cls) != original_ref) {
|
|
3567
|
-
return;
|
|
3568
|
-
}
|
|
3569
|
-
PrivateAttrType_finalize(cls);
|
|
3570
|
-
}
|
|
3571
|
-
|
|
3572
3602
|
#define METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(name) do {\
|
|
3573
3603
|
if (::AllData::all_type_##name.find(id) != ::AllData::all_type_##name.end()) {\
|
|
3574
3604
|
::AllData::all_type_##name.erase(id);\
|
|
@@ -3584,6 +3614,13 @@ metaclass_weakref_callback(PyObject* self, PyObject* /*weakref*/) noexcept
|
|
|
3584
3614
|
}
|
|
3585
3615
|
uintptr_t id = (uintptr_t)pointer;
|
|
3586
3616
|
|
|
3617
|
+
// Clean up the class-level AllData owned by this registered metaclass
|
|
3618
|
+
// (type_attr_dict / all_type_subclass_attr / object buckets / caches).
|
|
3619
|
+
// This replaces the old tp_finalize hook (register_finalize) which has
|
|
3620
|
+
// been removed: a weakref callback fires on every death path (refcount and
|
|
3621
|
+
// cyclic GC) without making the metaclass "finalizable".
|
|
3622
|
+
PrivateAttrType_finalize((PyObject*)id);
|
|
3623
|
+
|
|
3587
3624
|
std::unique_lock lock(::AllData::all_register_new_metaclass_mutex);
|
|
3588
3625
|
|
|
3589
3626
|
auto it = ::AllData::all_register_type_weak_ref.find(id);
|
|
@@ -3595,7 +3632,6 @@ metaclass_weakref_callback(PyObject* self, PyObject* /*weakref*/) noexcept
|
|
|
3595
3632
|
METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(setattro);
|
|
3596
3633
|
METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(traverse);
|
|
3597
3634
|
METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(clear);
|
|
3598
|
-
METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(finalize);
|
|
3599
3635
|
METACLASS_WEAKREF_CALLBACK_REMOVE_ITEM(del);
|
|
3600
3636
|
|
|
3601
3637
|
Py_RETURN_NONE;
|
|
@@ -3688,48 +3724,37 @@ register_metaclass_head(PyObject* metaclass) noexcept
|
|
|
3688
3724
|
}
|
|
3689
3725
|
::AllData::all_register_type_weak_ref[id] = ref;
|
|
3690
3726
|
PyTypeObject* mt = (PyTypeObject*)metaclass;
|
|
3727
|
+
// Save the ORIGINAL slots before wrapping. Always resolve through the
|
|
3728
|
+
// get_type_need_tp_* walkers so a delegating default (CPython's
|
|
3729
|
+
// subtype_traverse/subtype_clear, which a plain heap metaclass inherits)
|
|
3730
|
+
// is never stored as the "original". The walkers skip those defaults and
|
|
3731
|
+
// fall back to PyType_Type.tp_* (the real implementation for a type
|
|
3732
|
+
// object). If an entry already exists for this id, keep it (idempotent).
|
|
3691
3733
|
// save and replace tp_getattro
|
|
3692
3734
|
if (mt->tp_getattro != PrivateAttrType_getattr) {
|
|
3693
|
-
if (
|
|
3735
|
+
if (::AllData::all_type_getattro.find(id) == ::AllData::all_type_getattro.end()) {
|
|
3694
3736
|
::AllData::all_type_getattro[id] = get_type_need_tp_getattro(mt);
|
|
3695
|
-
} else {
|
|
3696
|
-
::AllData::all_type_getattro[id] = mt->tp_getattro;
|
|
3697
3737
|
}
|
|
3698
3738
|
mt->tp_getattro = PrivateAttrType_getattr;
|
|
3699
3739
|
}
|
|
3700
3740
|
// save and replace tp_setattro
|
|
3701
3741
|
if (mt->tp_setattro != PrivateAttrType_setattr) {
|
|
3702
|
-
if (
|
|
3742
|
+
if (::AllData::all_type_setattro.find(id) == ::AllData::all_type_setattro.end()) {
|
|
3703
3743
|
::AllData::all_type_setattro[id] = get_type_need_tp_setattro(mt);
|
|
3704
|
-
} else {
|
|
3705
|
-
::AllData::all_type_setattro[id] = mt->tp_setattro;
|
|
3706
3744
|
}
|
|
3707
3745
|
mt->tp_setattro = PrivateAttrType_setattr;
|
|
3708
3746
|
}
|
|
3709
|
-
// save and replace tp_finalize
|
|
3710
|
-
if (mt->tp_finalize != register_finalize) {
|
|
3711
|
-
if (!mt->tp_finalize && ::AllData::all_type_finalize.find(id) == ::AllData::all_type_finalize.end()) {
|
|
3712
|
-
::AllData::all_type_finalize[id] = get_type_need_tp_finalize(mt);
|
|
3713
|
-
} else {
|
|
3714
|
-
::AllData::all_type_finalize[id] = mt->tp_finalize;
|
|
3715
|
-
}
|
|
3716
|
-
mt->tp_finalize = register_finalize;
|
|
3717
|
-
}
|
|
3718
3747
|
// save and replace tp_traverse
|
|
3719
3748
|
if (mt->tp_traverse != PrivateAttrType_tp_traverse) {
|
|
3720
|
-
if (
|
|
3749
|
+
if (::AllData::all_type_traverse.find(id) == ::AllData::all_type_traverse.end()) {
|
|
3721
3750
|
::AllData::all_type_traverse[id] = get_type_need_tp_traverse(mt);
|
|
3722
|
-
} else {
|
|
3723
|
-
::AllData::all_type_traverse[id] = mt->tp_traverse;
|
|
3724
3751
|
}
|
|
3725
3752
|
mt->tp_traverse = PrivateAttrType_tp_traverse;
|
|
3726
3753
|
}
|
|
3727
3754
|
// save and replace tp_clear
|
|
3728
3755
|
if (mt->tp_clear != PrivateAttrType_tp_clear) {
|
|
3729
|
-
if (
|
|
3756
|
+
if (::AllData::all_type_clear.find(id) == ::AllData::all_type_clear.end()) {
|
|
3730
3757
|
::AllData::all_type_clear[id] = get_type_need_tp_clear(mt);
|
|
3731
|
-
} else {
|
|
3732
|
-
::AllData::all_type_clear[id] = mt->tp_clear;
|
|
3733
3758
|
}
|
|
3734
3759
|
mt->tp_clear = PrivateAttrType_tp_clear;
|
|
3735
3760
|
}
|
|
@@ -3778,43 +3803,31 @@ ensure_metaclass_tp(PyObject* /*self*/, PyObject* metaclass) noexcept
|
|
|
3778
3803
|
uintptr_t id = (uintptr_t)metaclass;
|
|
3779
3804
|
if (::AllData::all_register_type_weak_ref.find(id) != ::AllData::all_register_type_weak_ref.end()) {
|
|
3780
3805
|
PyTypeObject* mt = (PyTypeObject*)metaclass;
|
|
3806
|
+
// Same as register_metaclass_head: always resolve the ORIGINAL slots
|
|
3807
|
+
// through the get_type_need_tp_* walkers (never store a delegating
|
|
3808
|
+
// default such as subtype_traverse/subtype_clear). Idempotent when an
|
|
3809
|
+
// entry already exists.
|
|
3781
3810
|
if (mt->tp_getattro != PrivateAttrType_getattr) {
|
|
3782
|
-
if (
|
|
3811
|
+
if (::AllData::all_type_getattro.find(id) == ::AllData::all_type_getattro.end()) {
|
|
3783
3812
|
::AllData::all_type_getattro[id] = get_type_need_tp_getattro(mt);
|
|
3784
|
-
} else {
|
|
3785
|
-
::AllData::all_type_getattro[id] = mt->tp_getattro;
|
|
3786
3813
|
}
|
|
3787
3814
|
mt->tp_getattro = PrivateAttrType_getattr;
|
|
3788
3815
|
}
|
|
3789
3816
|
if (mt->tp_setattro != PrivateAttrType_setattr) {
|
|
3790
|
-
if (
|
|
3817
|
+
if (::AllData::all_type_setattro.find(id) == ::AllData::all_type_setattro.end()) {
|
|
3791
3818
|
::AllData::all_type_setattro[id] = get_type_need_tp_setattro(mt);
|
|
3792
|
-
} else {
|
|
3793
|
-
::AllData::all_type_setattro[id] = mt->tp_setattro;
|
|
3794
3819
|
}
|
|
3795
3820
|
mt->tp_setattro = PrivateAttrType_setattr;
|
|
3796
3821
|
}
|
|
3797
|
-
if (mt->tp_finalize != register_finalize) {
|
|
3798
|
-
if (!mt->tp_finalize && ::AllData::all_type_finalize.find(id) == ::AllData::all_type_finalize.end()) {
|
|
3799
|
-
::AllData::all_type_finalize[id] = get_type_need_tp_finalize(mt);
|
|
3800
|
-
} else {
|
|
3801
|
-
::AllData::all_type_finalize[id] = mt->tp_finalize;
|
|
3802
|
-
}
|
|
3803
|
-
mt->tp_finalize = register_finalize;
|
|
3804
|
-
}
|
|
3805
3822
|
if (mt->tp_traverse != PrivateAttrType_tp_traverse) {
|
|
3806
|
-
if (
|
|
3823
|
+
if (::AllData::all_type_traverse.find(id) == ::AllData::all_type_traverse.end()) {
|
|
3807
3824
|
::AllData::all_type_traverse[id] = get_type_need_tp_traverse(mt);
|
|
3808
|
-
} else {
|
|
3809
|
-
::AllData::all_type_traverse[id] = mt->tp_traverse;
|
|
3810
3825
|
}
|
|
3811
3826
|
mt->tp_traverse = PrivateAttrType_tp_traverse;
|
|
3812
3827
|
}
|
|
3813
3828
|
if (mt->tp_clear != PrivateAttrType_tp_clear) {
|
|
3814
|
-
if (
|
|
3829
|
+
if (::AllData::all_type_clear.find(id) == ::AllData::all_type_clear.end()) {
|
|
3815
3830
|
::AllData::all_type_clear[id] = get_type_need_tp_clear(mt);
|
|
3816
|
-
} else {
|
|
3817
|
-
::AllData::all_type_clear[id] = mt->tp_clear;
|
|
3818
3831
|
}
|
|
3819
3832
|
mt->tp_clear = PrivateAttrType_tp_clear;
|
|
3820
3833
|
}
|
{private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7/private_attribute_cpp.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: private_attribute_cpp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: A Python package that provides a way to define private attributes in C++ implementation.
|
|
5
5
|
Home-page: https://github.com/Locked-chess-official/private_attribute_cpp
|
|
6
6
|
Author: HuangHaoHua
|
|
@@ -18,7 +18,7 @@ readme = open('README.md').read()
|
|
|
18
18
|
|
|
19
19
|
setup(
|
|
20
20
|
name='private_attribute_cpp',
|
|
21
|
-
version='2.1.
|
|
21
|
+
version='2.1.7',
|
|
22
22
|
author="HuangHaoHua",
|
|
23
23
|
author_email="13140752715@example.com",
|
|
24
24
|
description='A Python package that provides a way to define private attributes in C++ implementation.',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_attr_classify_owner.py
RENAMED
|
File without changes
|
{private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_class_private_attrs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{private_attribute_cpp-2.1.5 → private_attribute_cpp-2.1.7}/tests/test_private_inheritance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|