private-attribute-cpp 2.1.7__tar.gz → 2.1.8__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.7/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.8}/PKG-INFO +1 -1
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute.cpp +129 -1
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8/private_attribute_cpp.egg-info}/PKG-INFO +1 -1
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute_cpp.egg-info/SOURCES.txt +2 -1
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/setup.py +9 -3
- private_attribute_cpp-2.1.8/tests/test_slots_cycle.py +59 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/LICENSE +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/MANIFEST.in +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/README.md +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/picosha2.h +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute.pyi +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/private_attribute_cpp.egg-info/top_level.txt +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/setup.cfg +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_attr_classify_owner.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_class_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_gc_collect.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_abc.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_func.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_inheritance.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_method.py +0 -0
- {private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/tests/test_private_wrap.py +0 -0
{private_attribute_cpp-2.1.7/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.8}/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.8
|
|
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
|
|
@@ -1512,6 +1512,92 @@ public:
|
|
|
1512
1512
|
}
|
|
1513
1513
|
};
|
|
1514
1514
|
|
|
1515
|
+
// ========================================================================
|
|
1516
|
+
// __slots__ traversal/clear (mirror of CPython's subtype_traverse slot loop).
|
|
1517
|
+
//
|
|
1518
|
+
// A private-attribute class's LIVE tp_traverse/tp_clear are replaced with
|
|
1519
|
+
// PrivateAttr_tp_traverse / PrivateAttr_tp_clear. The saved "original" is
|
|
1520
|
+
// CPython's subtype_traverse/subtype_clear, but because subtype_traverse
|
|
1521
|
+
// resolves the nearest base through the LIVE slot, it treats our wrapper as
|
|
1522
|
+
// the nearest "different" traverse and skips its own traverse_slots loop. The
|
|
1523
|
+
// __slots__ members (Py_T_OBJECT_EX entries in the heap type's member table)
|
|
1524
|
+
// therefore become invisible to the collector, so a cycle formed purely
|
|
1525
|
+
// through __slots__ members leaks.
|
|
1526
|
+
//
|
|
1527
|
+
// These helpers walk the member table for each type level whose live
|
|
1528
|
+
// traverse/clear slot is still a delegating default (subtype_traverse /
|
|
1529
|
+
// subtype_clear or our own wrapper), exactly mirroring the
|
|
1530
|
+
// `while (base->tp_traverse == subtype_traverse)` walk in CPython.
|
|
1531
|
+
// ========================================================================
|
|
1532
|
+
|
|
1533
|
+
// Locate the member table of a heap type in a version-independent way.
|
|
1534
|
+
// - 3.12+ : PyObject_GetItemData((PyObject*)type) is the public accessor
|
|
1535
|
+
// (_PyHeapType_GET_MEMBERS is defined as that since 3.12).
|
|
1536
|
+
// - <=3.11 : members float right after the PyHeapTypeObject header:
|
|
1537
|
+
// _PyHeapType_GET_MEMBERS(et) == (char*)et + Py_TYPE(et)->tp_basicsize.
|
|
1538
|
+
static PyMemberDef*
|
|
1539
|
+
PrivateAttr_get_tp_members(PyTypeObject* type) noexcept
|
|
1540
|
+
{
|
|
1541
|
+
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
|
1542
|
+
return NULL;
|
|
1543
|
+
}
|
|
1544
|
+
#if PY_VERSION_HEX >= 0x030C0000
|
|
1545
|
+
return (PyMemberDef*)PyObject_GetItemData((PyObject*)type);
|
|
1546
|
+
#else
|
|
1547
|
+
return (PyMemberDef*)((char*)type + Py_TYPE(type)->tp_basicsize);
|
|
1548
|
+
#endif
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
static int
|
|
1552
|
+
PrivateAttr_traverse_slots(PyTypeObject* type, PyObject* self,
|
|
1553
|
+
visitproc visit, void* arg) noexcept
|
|
1554
|
+
{
|
|
1555
|
+
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
|
1556
|
+
return 0;
|
|
1557
|
+
}
|
|
1558
|
+
PyMemberDef* mp = PrivateAttr_get_tp_members(type);
|
|
1559
|
+
if (mp == NULL) {
|
|
1560
|
+
return 0;
|
|
1561
|
+
}
|
|
1562
|
+
Py_ssize_t n = Py_SIZE(type);
|
|
1563
|
+
for (Py_ssize_t i = 0; i < n; i++, mp++) {
|
|
1564
|
+
if (mp->type == T_OBJECT_EX) {
|
|
1565
|
+
char* addr = (char*)self + mp->offset;
|
|
1566
|
+
PyObject* obj = *(PyObject**)addr;
|
|
1567
|
+
if (obj != NULL) {
|
|
1568
|
+
int err = visit(obj, arg);
|
|
1569
|
+
if (err) {
|
|
1570
|
+
return err;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
return 0;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
static void
|
|
1579
|
+
PrivateAttr_clear_slots(PyTypeObject* type, PyObject* self) noexcept
|
|
1580
|
+
{
|
|
1581
|
+
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
PyMemberDef* mp = PrivateAttr_get_tp_members(type);
|
|
1585
|
+
if (mp == NULL) {
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
Py_ssize_t n = Py_SIZE(type);
|
|
1589
|
+
for (Py_ssize_t i = 0; i < n; i++, mp++) {
|
|
1590
|
+
if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) {
|
|
1591
|
+
char* addr = (char*)self + mp->offset;
|
|
1592
|
+
PyObject* obj = *(PyObject**)addr;
|
|
1593
|
+
if (obj != NULL) {
|
|
1594
|
+
*(PyObject**)addr = NULL;
|
|
1595
|
+
Py_DECREF(obj);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1515
1601
|
static int
|
|
1516
1602
|
PrivateAttr_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
1517
1603
|
{
|
|
@@ -1540,6 +1626,29 @@ PrivateAttr_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
|
|
|
1540
1626
|
}
|
|
1541
1627
|
}
|
|
1542
1628
|
|
|
1629
|
+
// __slots__ visit (mirror of subtype_traverse's slot-walking loop). See
|
|
1630
|
+
// PrivateAttr_traverse_slots above: once the LIVE tp_traverse is this
|
|
1631
|
+
// wrapper, subtype_traverse skips its own loop, so walk the member table
|
|
1632
|
+
// of this type and every delegating base explicitly. Stop at the first
|
|
1633
|
+
// base whose live traverse is a real implementation (it owns its slots).
|
|
1634
|
+
{
|
|
1635
|
+
PyTypeObject* base = typ;
|
|
1636
|
+
while (base) {
|
|
1637
|
+
if (base->tp_traverse == ::AllData::captured_subtype_traverse
|
|
1638
|
+
|| base->tp_traverse == PrivateAttr_tp_traverse) {
|
|
1639
|
+
if (Py_SIZE(base)) {
|
|
1640
|
+
int err = PrivateAttr_traverse_slots(base, self, visit, arg);
|
|
1641
|
+
if (err) {
|
|
1642
|
+
return err;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
base = base->tp_base;
|
|
1646
|
+
} else {
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1543
1652
|
// Instance-dict visit (mirror of subtype_traverse's dictoffset block).
|
|
1544
1653
|
// The saved "original" for a private-attribute class is subtype_traverse,
|
|
1545
1654
|
// but because the class's LIVE tp_traverse has been replaced with this
|
|
@@ -1645,6 +1754,25 @@ PrivateAttr_tp_clear(PyObject* self) noexcept
|
|
|
1645
1754
|
if (orig) orig(self);
|
|
1646
1755
|
}
|
|
1647
1756
|
|
|
1757
|
+
// __slots__ clear (mirror of subtype_clear's clear_slots loop). Same
|
|
1758
|
+
// reasoning as the traverse side: subtype_clear can't run its own loop
|
|
1759
|
+
// once the LIVE tp_clear is this wrapper, so clear the __slots__ members
|
|
1760
|
+
// of this type and every delegating base explicitly.
|
|
1761
|
+
{
|
|
1762
|
+
PyTypeObject* base = typ;
|
|
1763
|
+
while (base) {
|
|
1764
|
+
if (base->tp_clear == ::AllData::captured_subtype_clear
|
|
1765
|
+
|| base->tp_clear == PrivateAttr_tp_clear) {
|
|
1766
|
+
if (Py_SIZE(base)) {
|
|
1767
|
+
PrivateAttr_clear_slots(base, self);
|
|
1768
|
+
}
|
|
1769
|
+
base = base->tp_base;
|
|
1770
|
+
} else {
|
|
1771
|
+
break;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1648
1776
|
// Instance-dict clear (mirror of subtype_clear's dict handling). Same
|
|
1649
1777
|
// reasoning as the traverse side: subtype_clear can't run its dict block
|
|
1650
1778
|
// once the LIVE tp_clear has been replaced, so clear the instance dict
|
|
@@ -3867,7 +3995,7 @@ PrivateModule_get_PrivateAttrBase(PyObject* /*self*/, void* /*closure*/) noexcep
|
|
|
3867
3995
|
}
|
|
3868
3996
|
|
|
3869
3997
|
static int
|
|
3870
|
-
PyList_AppendString(PyObject* list, const char* str)
|
|
3998
|
+
PyList_AppendString(PyObject* list, const char* str) noexcept
|
|
3871
3999
|
{
|
|
3872
4000
|
PyObject* obj = PyUnicode_InternFromString(str);
|
|
3873
4001
|
if (!obj) return -1;
|
{private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8/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.8
|
|
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
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
from setuptools import setup, Extension
|
|
2
|
+
import os
|
|
2
3
|
import sys
|
|
3
4
|
|
|
4
5
|
if sys.platform == "win32":
|
|
5
|
-
extra_compile_args = ['/std:c++17']
|
|
6
|
+
extra_compile_args = ['/std:c++17', '/GR-']
|
|
7
|
+
extra_link_args = []
|
|
6
8
|
else:
|
|
7
|
-
extra_compile_args = ['-std=c++17']
|
|
9
|
+
extra_compile_args = ['-std=c++17', '-fno-exceptions', '-fno-rtti']
|
|
10
|
+
extra_link_args = []
|
|
11
|
+
if os.environ.get('PRIVATE_ATTR_KEEP_DEBUG') != '1':
|
|
12
|
+
extra_compile_args.append('-g0')
|
|
8
13
|
|
|
9
14
|
module = Extension(
|
|
10
15
|
'private_attribute',
|
|
@@ -12,13 +17,14 @@ module = Extension(
|
|
|
12
17
|
include_dirs=['.'],
|
|
13
18
|
language='c++',
|
|
14
19
|
extra_compile_args=extra_compile_args,
|
|
20
|
+
extra_link_args=extra_link_args,
|
|
15
21
|
)
|
|
16
22
|
|
|
17
23
|
readme = open('README.md').read()
|
|
18
24
|
|
|
19
25
|
setup(
|
|
20
26
|
name='private_attribute_cpp',
|
|
21
|
-
version='2.1.
|
|
27
|
+
version='2.1.8',
|
|
22
28
|
author="HuangHaoHua",
|
|
23
29
|
author_email="13140752715@example.com",
|
|
24
30
|
description='A Python package that provides a way to define private attributes in C++ implementation.',
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Regression test for __slots__ traversal in private-attribute types.
|
|
2
|
+
|
|
3
|
+
A private-attribute class whose __slots__ member participates in a reference
|
|
4
|
+
cycle must still be collected by the cyclic GC. The instance-level traverse
|
|
5
|
+
wrapper previously skipped the __slots__ storage entirely (it only visited the
|
|
6
|
+
instance __dict__ and AllData-stored private attributes), so a cycle formed
|
|
7
|
+
purely through __slots__ members leaked.
|
|
8
|
+
"""
|
|
9
|
+
import gc
|
|
10
|
+
import unittest
|
|
11
|
+
import weakref
|
|
12
|
+
|
|
13
|
+
import private_attribute
|
|
14
|
+
from private_attribute import PrivateAttrBase
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _collect():
|
|
18
|
+
for _ in range(3):
|
|
19
|
+
gc.collect()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestSlotsCycleCollect(unittest.TestCase):
|
|
23
|
+
def test_slots_pair_cycle_collected(self):
|
|
24
|
+
class A(PrivateAttrBase):
|
|
25
|
+
__private_attrs__ = ("_x",)
|
|
26
|
+
__slots__ = ("peer", "__weakref__")
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self._x = 1
|
|
30
|
+
self.peer = None
|
|
31
|
+
|
|
32
|
+
a = A()
|
|
33
|
+
b = A()
|
|
34
|
+
a.peer = b
|
|
35
|
+
b.peer = a
|
|
36
|
+
wa, wb = weakref.ref(a), weakref.ref(b)
|
|
37
|
+
del a, b
|
|
38
|
+
_collect()
|
|
39
|
+
self.assertIsNone(wa())
|
|
40
|
+
self.assertIsNone(wb())
|
|
41
|
+
|
|
42
|
+
def test_slots_self_cycle_collected(self):
|
|
43
|
+
class A(PrivateAttrBase):
|
|
44
|
+
__private_attrs__ = ("_x",)
|
|
45
|
+
__slots__ = ("self_ref", "__weakref__")
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self._x = 1
|
|
49
|
+
self.self_ref = self
|
|
50
|
+
|
|
51
|
+
a = A()
|
|
52
|
+
w = weakref.ref(a)
|
|
53
|
+
del a
|
|
54
|
+
_collect()
|
|
55
|
+
self.assertIsNone(w())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
unittest.main()
|
|
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.7 → private_attribute_cpp-2.1.8}/tests/test_attr_classify_owner.py
RENAMED
|
File without changes
|
{private_attribute_cpp-2.1.7 → private_attribute_cpp-2.1.8}/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.7 → private_attribute_cpp-2.1.8}/tests/test_private_inheritance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|