private-attribute-cpp 2.1.0__tar.gz → 2.1.2__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.
Files changed (21) hide show
  1. {private_attribute_cpp-2.1.0/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.2}/PKG-INFO +1 -1
  2. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute.cpp +126 -40
  3. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2/private_attribute_cpp.egg-info}/PKG-INFO +1 -1
  4. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/setup.py +1 -1
  5. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/LICENSE +0 -0
  6. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/MANIFEST.in +0 -0
  7. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/README.md +0 -0
  8. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/picosha2.h +0 -0
  9. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute.pyi +0 -0
  10. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute_cpp.egg-info/SOURCES.txt +0 -0
  11. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
  12. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
  13. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/private_attribute_cpp.egg-info/top_level.txt +0 -0
  14. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/setup.cfg +0 -0
  15. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_class_private_attrs.py +0 -0
  16. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_abc.py +0 -0
  17. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_attrs.py +0 -0
  18. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_func.py +0 -0
  19. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_inheritance.py +0 -0
  20. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_method.py +0 -0
  21. {private_attribute_cpp-2.1.0 → private_attribute_cpp-2.1.2}/tests/test_private_wrap.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 2.1.0
3
+ Version: 2.1.2
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
@@ -125,6 +125,10 @@ public:
125
125
  PyObjectStorage(PyObject* obj) noexcept : obj(obj) {
126
126
  Py_XINCREF(obj);
127
127
  }
128
+ // support for PyCodeObject*
129
+ PyObjectStorage(PyCodeObject* obj) noexcept : obj(reinterpret_cast<PyObject*>(obj)) {
130
+ Py_XINCREF(obj);
131
+ }
128
132
  PyObjectStorage(const PyObjectStorage& other) noexcept : obj(other.obj) {
129
133
  Py_XINCREF(obj);
130
134
  }
@@ -155,13 +159,26 @@ public:
155
159
  obj = new_obj;
156
160
  return *this;
157
161
  }
158
- ~PyObjectStorage() noexcept {
162
+ PyObjectStorage& operator=(PyCodeObject* new_obj) noexcept {
163
+ Py_XINCREF(reinterpret_cast<PyObject*>(new_obj));
159
164
  Py_XDECREF(obj);
165
+ obj = reinterpret_cast<PyObject*>(new_obj);
166
+ return *this;
167
+ }
168
+ ~PyObjectStorage() noexcept {
169
+ // if python has exit we don't do anything
170
+ if (Py_IsInitialized()) {
171
+ Py_XDECREF(obj);
172
+ }
160
173
  }
161
174
 
162
175
  operator PyObject*() const noexcept {
163
176
  return obj;
164
177
  }
178
+
179
+ operator PyCodeObject*() const noexcept {
180
+ return reinterpret_cast<PyCodeObject*>(obj);
181
+ }
165
182
  PyObject* get() const noexcept {
166
183
  return obj;
167
184
  }
@@ -176,12 +193,8 @@ namespace {
176
193
  namespace {
177
194
  static std::unordered_map<uintptr_t, std::unordered_map<std::string, PyObjectStorage>> type_attr_dict;
178
195
  };
179
- static std::unordered_map<uintptr_t, std::unordered_map<uintptr_t, PyCodeObject*>> type_allowed_code_map;
196
+ static std::unordered_map<uintptr_t, std::unordered_map<uintptr_t, PyObjectStorage>> type_allowed_code_map;
180
197
  static std::unordered_map<uintptr_t, std::shared_ptr<std::shared_mutex>> all_type_mutex;
181
- // private_func per type, stored with RAII ownership (PyObjectStorage
182
- // INCREFs on assignment and DECREFs on destruction), so the map entry
183
- // always owns exactly one reference - no dangling-pointer risk when the
184
- // last external reference to the function disappears.
185
198
  static std::unordered_map<uintptr_t, PyObjectStorage> type_need_call;
186
199
  static std::unordered_map<uintptr_t, std::unordered_set<TwoStringTuple>> all_type_attr_set;
187
200
  namespace {
@@ -210,9 +223,34 @@ namespace {
210
223
  static std::unordered_map<uintptr_t, destructor> all_type_finalize;
211
224
 
212
225
  static std::shared_mutex all_register_new_metaclass_mutex;
213
- static std::unordered_map<uintptr_t, PyObject*> all_register_type_weak_ref;
226
+ static std::unordered_map<uintptr_t, PyObjectStorage> all_register_type_weak_ref;
214
227
  };
215
228
  };
229
+
230
+ static void
231
+ clean_all_storages() noexcept
232
+ {
233
+ AllData::cache.clear();
234
+ AllData::all_exist_name.clear();
235
+ AllData::obj_attr_keys.clear();
236
+ AllData::type_attr_dict.clear();
237
+ AllData::type_allowed_code_map.clear();
238
+ AllData::all_type_mutex.clear();
239
+ AllData::type_need_call.clear();
240
+ AllData::all_type_attr_set.clear();
241
+ AllData::all_object_attr.clear();
242
+ AllData::all_type_subclass_attr.clear();
243
+ AllData::all_object_mutex.clear();
244
+ AllData::all_type_subclass_mutex.clear();
245
+ AllData::all_type_parent_id.clear();
246
+ AllData::all_type_getattro.clear();
247
+ AllData::all_type_setattro.clear();
248
+ AllData::all_type_traverse.clear();
249
+ AllData::all_type_clear.clear();
250
+ AllData::all_type_finalize.clear();
251
+ AllData::all_register_type_weak_ref.clear();
252
+ }
253
+
216
254
  struct FinalObject
217
255
  {
218
256
  PyObjectStorage result;
@@ -1623,6 +1661,21 @@ PrivateAttr_tp_clear(PyObject* self) noexcept
1623
1661
  return 0;
1624
1662
  }
1625
1663
 
1664
+ // get_type_need_tp_* : the metaclass counterparts of get_need_tp_*.
1665
+ // get_need_tp_* -> find the original slots of *types created by* the
1666
+ // metaclasses (wrapped with the instance-level
1667
+ // PrivateAttr_tp_* / PrivateAttr_tp_getattro set).
1668
+ // get_type_need_tp_* -> find the original slots of the *metaclasses*
1669
+ // themselves (wrapped with the metaclass-level
1670
+ // PrivateAttrType_getattr / PrivateAttrType_setattr /
1671
+ // register_finalize / PrivateAttrType_tp_traverse /
1672
+ // PrivateAttrType_tp_clear).
1673
+ static getattrofunc get_type_need_tp_getattro(PyTypeObject* cls) noexcept;
1674
+ static setattrofunc get_type_need_tp_setattro(PyTypeObject* cls) noexcept;
1675
+ static traverseproc get_type_need_tp_traverse(PyTypeObject* cls) noexcept;
1676
+ static inquiry get_type_need_tp_clear(PyTypeObject* cls) noexcept;
1677
+ static destructor get_type_need_tp_finalize(PyTypeObject* cls) noexcept;
1678
+
1626
1679
  // metaclass-level (type object) traverse/clear: call original if any, and handle AllData-stored type attributes
1627
1680
  static int
1628
1681
  PrivateAttrType_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
@@ -1644,7 +1697,7 @@ PrivateAttrType_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept
1644
1697
  if (res) return res;
1645
1698
  }
1646
1699
  } else {
1647
- traverseproc orig = get_need_tp_traverse(typ);
1700
+ traverseproc orig = get_type_need_tp_traverse(typ);
1648
1701
  if (orig) {
1649
1702
  int res = orig(self, visit, arg);
1650
1703
  if (res) return res;
@@ -1731,7 +1784,7 @@ PrivateAttrType_tp_clear(PyObject* self) noexcept
1731
1784
  inquiry orig = ::AllData::all_type_clear[typ_id];
1732
1785
  if (orig) orig(self);
1733
1786
  } else {
1734
- inquiry orig = get_need_tp_clear(typ);
1787
+ inquiry orig = get_type_need_tp_clear(typ);
1735
1788
  if (orig) orig(self);
1736
1789
  }
1737
1790
 
@@ -1815,8 +1868,8 @@ static PyTypeObject PrivateAttrType = {
1815
1868
  0, // tp_as_buffer
1816
1869
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
1817
1870
  "metaclass for private attributes", // tp_doc
1818
- 0, // tp_travers
1819
- 0, // tp_clear
1871
+ (traverseproc)PrivateAttrType_tp_traverse, // tp_travers
1872
+ (inquiry)PrivateAttrType_tp_clear, // tp_clear
1820
1873
  0, // tp_richcompare
1821
1874
  0, // tp_weaklistoffset
1822
1875
  0, // tp_iter
@@ -2048,7 +2101,7 @@ try_get_attr_string(PyObject* obj, const char* name) noexcept
2048
2101
  }
2049
2102
 
2050
2103
  static void
2051
- analyse_all_code(PyObject* obj, std::unordered_map<uintptr_t, PyCodeObject*>& map, std::unordered_set<uintptr_t>& _seen) noexcept
2104
+ analyse_all_code(PyObject* obj, std::unordered_map<uintptr_t, PyObjectStorage>& map, std::unordered_set<uintptr_t>& _seen) noexcept
2052
2105
  {
2053
2106
  uintptr_t obj_id = (uintptr_t)obj;
2054
2107
  if (_seen.find(obj_id) != _seen.end()) {
@@ -2056,8 +2109,7 @@ analyse_all_code(PyObject* obj, std::unordered_map<uintptr_t, PyCodeObject*>& ma
2056
2109
  }
2057
2110
  _seen.insert(obj_id);
2058
2111
  if (PyObject_TypeCheck(obj, &PyCode_Type)) {
2059
- Py_INCREF(obj);
2060
- map[(uintptr_t)obj] = (PyCodeObject*)obj;
2112
+ map[(uintptr_t)obj] = obj;
2061
2113
  PyObject* co_contain = try_get_attr_string(obj, "co_consts");
2062
2114
  if (co_contain && PySequence_Check(co_contain)) {
2063
2115
  Py_ssize_t len = PySequence_Length(co_contain);
@@ -2218,11 +2270,11 @@ need_analyse_type(PyObject* type) noexcept
2218
2270
  }
2219
2271
  std::shared_lock lock(::AllData::all_register_new_metaclass_mutex);
2220
2272
  for (auto& [id, metaclassref]: ::AllData::all_register_type_weak_ref){
2221
- if (!PyWeakref_CheckRef(metaclassref)) {
2273
+ if (!PyWeakref_CheckRef((PyObject*)metaclassref)) {
2222
2274
  continue;
2223
2275
  }
2224
2276
  #if PY_VERSION_HEX < 0x030D0000
2225
- PyObject* metaclass = PyWeakref_GET_OBJECT(metaclassref);
2277
+ PyObject* metaclass = PyWeakref_GET_OBJECT((PyObject*)metaclassref);
2226
2278
  if (type == metaclass) continue;
2227
2279
  if (PyObject_IsInstance(type, metaclass)) {
2228
2280
  return true;
@@ -2505,21 +2557,6 @@ static traverseproc get_need_tp_traverse(PyTypeObject* cls) noexcept;
2505
2557
  static inquiry get_need_tp_clear(PyTypeObject* cls) noexcept;
2506
2558
  static destructor get_need_tp_finalize(PyTypeObject* cls) noexcept;
2507
2559
 
2508
- // get_type_need_tp_* : the metaclass counterparts of get_need_tp_*.
2509
- // get_need_tp_* -> find the original slots of *types created by* the
2510
- // metaclasses (wrapped with the instance-level
2511
- // PrivateAttr_tp_* / PrivateAttr_tp_getattro set).
2512
- // get_type_need_tp_* -> find the original slots of the *metaclasses*
2513
- // themselves (wrapped with the metaclass-level
2514
- // PrivateAttrType_getattr / PrivateAttrType_setattr /
2515
- // register_finalize / PrivateAttrType_tp_traverse /
2516
- // PrivateAttrType_tp_clear).
2517
- static getattrofunc get_type_need_tp_getattro(PyTypeObject* cls) noexcept;
2518
- static setattrofunc get_type_need_tp_setattro(PyTypeObject* cls) noexcept;
2519
- static traverseproc get_type_need_tp_traverse(PyTypeObject* cls) noexcept;
2520
- static inquiry get_type_need_tp_clear(PyTypeObject* cls) noexcept;
2521
- static destructor get_type_need_tp_finalize(PyTypeObject* cls) noexcept;
2522
-
2523
2560
  // instance-level traverse/clear for types that use private attributes
2524
2561
  static int PrivateAttr_tp_traverse(PyObject* self, visitproc visit, void* arg) noexcept;
2525
2562
  static int PrivateAttr_tp_clear(PyObject* self) noexcept;
@@ -2739,10 +2776,7 @@ PrivateAttrType_postprocess(PyObject* new_type, PrivateAttrCreationData& data) n
2739
2776
  }
2740
2777
 
2741
2778
  if (data.private_func) {
2742
- // PyObjectStorage::operator=(PyObject*) takes its own reference here;
2743
- // data's reference is released by PrivateAttrCreationData::clear()
2744
- // right after this, and the map entry's reference is released when the
2745
- // entry is erased in PrivateAttrType_finalize.
2779
+ Py_INCREF(data.private_func);
2746
2780
  ::AllData::type_need_call[type_id] = data.private_func;
2747
2781
  }
2748
2782
 
@@ -3225,13 +3259,9 @@ PrivateAttrType_finalize(PyObject* cls) noexcept
3225
3259
  ::AllData::all_type_attr_set.erase(typ_id);
3226
3260
  }
3227
3261
  if (::AllData::type_allowed_code_map.find(typ_id) != ::AllData::type_allowed_code_map.end()) {
3228
- for (auto& [id, obj] : ::AllData::type_allowed_code_map[typ_id]) {
3229
- Py_XDECREF(obj);
3230
- }
3231
3262
  ::AllData::type_allowed_code_map.erase(typ_id);
3232
3263
  }
3233
3264
  if (::AllData::type_need_call.find(typ_id) != ::AllData::type_need_call.end()) {
3234
- // erase() destroys the PyObjectStorage, which DECREFs private_func.
3235
3265
  ::AllData::type_need_call.erase(typ_id);
3236
3266
  }
3237
3267
  if (::AllData::type_attr_dict.find(typ_id) != ::AllData::type_attr_dict.end()) {
@@ -3541,7 +3571,7 @@ is_class_of_registed_type(PyObject* metaclass) noexcept
3541
3571
  continue;
3542
3572
  }
3543
3573
  #if PY_VERSION_HEX < 0x030D0000
3544
- PyObject* instance = PyWeakref_GET_OBJECT(instanceref);
3574
+ PyObject* instance = PyWeakref_GET_OBJECT((PyObject*)instanceref);
3545
3575
  if (instance == metaclass) continue;
3546
3576
  if (PyObject_IsInstance(instance, metaclass)) {
3547
3577
  return true;
@@ -4033,10 +4063,66 @@ static PyModuleDef def = {
4033
4063
  NULL
4034
4064
  };
4035
4065
 
4066
+ static PyObject*
4067
+ AtExit_clean_storage(PyObject* /*self*/, PyObject* /*obj*/) noexcept
4068
+ {
4069
+ clean_all_storages();
4070
+ Py_RETURN_NONE;
4071
+ }
4072
+
4073
+ static PyMethodDef clean_storage = {
4074
+ "clean_storage",
4075
+ (PyCFunction)AtExit_clean_storage,
4076
+ METH_NOARGS,
4077
+ "Clean all storages"
4078
+ };
4079
+
4080
+ static int
4081
+ atexit_register_clean_func(void) noexcept
4082
+ {
4083
+ PyObject* atexit = PyImport_ImportModule("atexit");
4084
+ if (!atexit) {
4085
+ return -1;
4086
+ }
4087
+ PyObject* register_func = PyObject_GetAttrString(atexit, "register");
4088
+ if (!register_func) {
4089
+ Py_DECREF(atexit);
4090
+ return -1;
4091
+ }
4092
+
4093
+ PyObject* func = PyCFunction_New(&clean_storage, NULL);
4094
+ if (!func) {
4095
+ Py_DECREF(register_func);
4096
+ Py_DECREF(atexit);
4097
+ return -1;
4098
+ }
4099
+ PyObject* args = PyTuple_New(1);
4100
+ if (!args) {
4101
+ Py_DECREF(func);
4102
+ Py_DECREF(register_func);
4103
+ Py_DECREF(atexit);
4104
+ return -1;
4105
+ }
4106
+ PyTuple_SET_ITEM(args, 0, func);
4107
+ PyObject* result = PyObject_CallObject(register_func, args);
4108
+ if (result == NULL) {
4109
+ Py_DECREF(args);
4110
+ Py_DECREF(register_func);
4111
+ Py_DECREF(atexit);
4112
+ return -1;
4113
+ }
4114
+ Py_DECREF(args);
4115
+ Py_DECREF(register_func);
4116
+ Py_DECREF(atexit);
4117
+ Py_DECREF(result);
4118
+ return 0;
4119
+ }
4120
+
4036
4121
  PyMODINIT_FUNC
4037
4122
  PyInit_private_attribute(void) noexcept
4038
4123
  {
4039
4124
  if (init_all_slots() <0 ||
4125
+ atexit_register_clean_func() < 0 ||
4040
4126
  PyType_Ready(&PrivateWrapType) < 0 ||
4041
4127
  PyType_Ready(&PrivateWrapProxyType) < 0 ||
4042
4128
  PyType_Ready(&PrivateAttrType) < 0 ||
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 2.1.0
3
+ Version: 2.1.2
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.0',
21
+ version='2.1.2',
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.',