private-attribute-cpp 1.4.10__tar.gz → 2.0.0__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-1.4.10/private_attribute_cpp.egg-info → private_attribute_cpp-2.0.0}/PKG-INFO +2 -1
  2. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/README.md +1 -0
  3. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute.cpp +416 -419
  4. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute.pyi +1 -1
  5. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0/private_attribute_cpp.egg-info}/PKG-INFO +2 -1
  6. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/setup.py +1 -1
  7. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/LICENSE +0 -0
  8. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/MANIFEST.in +0 -0
  9. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/picosha2.h +0 -0
  10. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute_cpp.egg-info/SOURCES.txt +0 -0
  11. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
  12. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
  13. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/private_attribute_cpp.egg-info/top_level.txt +0 -0
  14. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/setup.cfg +0 -0
  15. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_class_private_attrs.py +0 -0
  16. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_private_abc.py +0 -0
  17. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_private_attrs.py +0 -0
  18. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_private_func.py +0 -0
  19. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_private_inheritance.py +0 -0
  20. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/tests/test_private_method.py +0 -0
  21. {private_attribute_cpp-1.4.10 → private_attribute_cpp-2.0.0}/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: 1.4.10
3
+ Version: 2.0.0
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
@@ -247,6 +247,7 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
247
247
  - All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
248
248
  - Finally the attributes' names in `__private_attrs__` will be change to a tuple with two hash.
249
249
  - Finally the `_PrivateWrap` object will be recoveried to the original object.
250
+ - Don't use a decorator which will return the `_PrivateWrap` in `PrivateWrapProxy` which will raise `TypeError`.
250
251
  - One class defined in another class cannot use another class's private attribute.
251
252
  - One parent class defined an attribute which not in `__private_attrs__` or not a `PrivateAttrType` instance, the child class shouldn't contain the attribute in its `__private_attrs__`.
252
253
  - CPython may change "tp_getattro", "tp_setattro" and so on when you change the attribute "\_\_getattribute\_\_", "\_\_setattr\_\_" and so on. If you are fear about it, you can use `ensure_type` to reset those tp slots. For the other metaclasses, you can use `ensure_metaclass` to reset those tp slots. Also, don't set those methods on these classes in your code.
@@ -228,6 +228,7 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
228
228
  - All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
229
229
  - Finally the attributes' names in `__private_attrs__` will be change to a tuple with two hash.
230
230
  - Finally the `_PrivateWrap` object will be recoveried to the original object.
231
+ - Don't use a decorator which will return the `_PrivateWrap` in `PrivateWrapProxy` which will raise `TypeError`.
231
232
  - One class defined in another class cannot use another class's private attribute.
232
233
  - One parent class defined an attribute which not in `__private_attrs__` or not a `PrivateAttrType` instance, the child class shouldn't contain the attribute in its `__private_attrs__`.
233
234
  - CPython may change "tp_getattro", "tp_setattro" and so on when you change the attribute "\_\_getattribute\_\_", "\_\_setattr\_\_" and so on. If you are fear about it, you can use `ensure_type` to reset those tp slots. For the other metaclasses, you can use `ensure_metaclass` to reset those tp slots. Also, don't set those methods on these classes in your code.
@@ -12,14 +12,12 @@
12
12
  #include <ctime>
13
13
  #include <iomanip>
14
14
  #include <sstream>
15
- #include <vector>
16
15
  #include <random>
17
16
  #include <mutex>
18
17
  #include <shared_mutex>
19
18
  #include "picosha2.h"
20
19
  #include <functional>
21
20
  #include <memory>
22
- #include <algorithm>
23
21
 
24
22
  // python under 3.13 doesn't have PyDict_ContainsString, so we implement it ourselves
25
23
  #if PY_VERSION_HEX < 0x030D0000
@@ -35,7 +33,6 @@ PyDict_ContainsString(PyObject *op, const char *key) noexcept
35
33
  return res;
36
34
  }
37
35
  #endif
38
-
39
36
  static const auto module_running_time = std::chrono::system_clock::now();
40
37
 
41
38
  static std::string
@@ -110,7 +107,7 @@ namespace std {
110
107
  return key.gethash();
111
108
  }
112
109
  };
113
- };
110
+ }
114
111
 
115
112
  namespace {
116
113
  namespace AllData {
@@ -144,12 +141,6 @@ namespace {
144
141
  };
145
142
  };
146
143
 
147
- namespace AllSlots {
148
- static getattrofunc original_getattro = nullptr;
149
- static setattrofunc original_setattro = nullptr;
150
- static destructor original_finalize = nullptr;
151
- }
152
-
153
144
  struct FinalObject
154
145
  {
155
146
  PyObject* result = NULL;
@@ -227,7 +218,7 @@ generate_private_attr_name(uintptr_t obj_id, const std::string& attr_name) noexc
227
218
 
228
219
  unsigned long long seed = std::stoul(hash_str.substr(0, 8), nullptr, 16);
229
220
 
230
- std::mt19937 rng(seed);
221
+ std::mt19937 rng(static_cast<std::mt19937::result_type>(seed));
231
222
 
232
223
  static const std::string printable_chars =
233
224
  "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
@@ -271,10 +262,7 @@ default_random_string(uintptr_t obj_id, const std::string& attr_name) noexcept
271
262
  result = it->second;
272
263
  return result;
273
264
  }
274
- while (true) {
275
- if (::AllData::all_exist_name.find(result) == ::AllData::all_exist_name.end()) {
276
- break;
277
- }
265
+ while (::AllData::all_exist_name.find(result) != ::AllData::all_exist_name.end()) {
278
266
  result = original_result + "_" + std::to_string(i);
279
267
  i++;
280
268
  }
@@ -328,12 +316,77 @@ public:
328
316
  type = value = traceback = nullptr;
329
317
  }
330
318
 
319
+ const char* what() const noexcept override {
320
+ if (!msg_.empty())
321
+ return msg_.c_str();
322
+
323
+ if (!type || !value) {
324
+ msg_ = "cannot format this exception (missing type/value)";
325
+ return msg_.c_str();
326
+ }
327
+
328
+ PyObject* tb_mod = PyImport_ImportModule("traceback");
329
+ if (!tb_mod) {
330
+ msg_ = "cannot format this exception (import traceback failed)";
331
+ return msg_.c_str();
332
+ }
333
+
334
+ PyObject* format_exc = PyObject_GetAttrString(tb_mod, "format_exception");
335
+ Py_DECREF(tb_mod);
336
+ if (!format_exc) {
337
+ msg_ = "cannot format this exception (get format_exception failed)";
338
+ return msg_.c_str();
339
+ }
340
+
341
+ PyObject* result = PyObject_CallFunctionObjArgs(
342
+ format_exc, type, value, traceback ? traceback : Py_None, nullptr);
343
+ Py_DECREF(format_exc);
344
+
345
+ if (!result) {
346
+ msg_ = "cannot format this exception (format_exception failed)";
347
+ return msg_.c_str();
348
+ }
349
+
350
+ PyObject* empty = PyUnicode_FromString("");
351
+ if (!empty) {
352
+ Py_DECREF(result);
353
+ msg_ = "cannot format this exception (create empty string failed)";
354
+ return msg_.c_str();
355
+ }
356
+
357
+ PyObject* joined = PyUnicode_Join(empty, result);
358
+ Py_DECREF(empty);
359
+ Py_DECREF(result);
360
+
361
+ if (!joined) {
362
+ msg_ = "cannot format this exception (join failed)";
363
+ return msg_.c_str();
364
+ }
365
+
366
+ const char* utf8 = PyUnicode_AsUTF8(joined);
367
+ if (utf8)
368
+ msg_ = utf8;
369
+ else
370
+ msg_ = "cannot format this exception (AsUTF8 failed)";
371
+
372
+ Py_DECREF(joined);
373
+ return msg_.c_str();
374
+ }
375
+
331
376
  private:
332
377
  PyObject* type = nullptr;
333
378
  PyObject* value = nullptr;
334
379
  PyObject* traceback = nullptr;
380
+ mutable std::string msg_;
335
381
  };
336
382
 
383
+ // In MSVC, disable warning C5272
384
+ // The custom_random_string will throw RestorePythonException, which cannot be copied and will be caught anyway.
385
+ #ifdef _MSC_VER
386
+ #pragma warning(push)
387
+ #pragma warning(disable: 5272)
388
+ #endif
389
+
337
390
  static std::string
338
391
  custom_random_string(uintptr_t obj_id, const std::string& attr_name, PyObject* func)
339
392
  {
@@ -372,10 +425,7 @@ custom_random_string(uintptr_t obj_id, const std::string& attr_name, PyObject* f
372
425
  result = it->second;
373
426
  return result;
374
427
  }
375
- while (true) {
376
- if (::AllData::all_exist_name.find(result) == ::AllData::all_exist_name.end()) {
377
- break;
378
- }
428
+ while (::AllData::all_exist_name.find(result) != ::AllData::all_exist_name.end()) {
379
429
  result = original_result + "_" + std::to_string(i);
380
430
  i++;
381
431
  }
@@ -395,6 +445,10 @@ custom_random_string(uintptr_t obj_id, const std::string& attr_name, PyObject* f
395
445
  return result;
396
446
  }
397
447
 
448
+ #ifdef _MSC_VER
449
+ #pragma warning(pop)
450
+ #endif
451
+
398
452
  static void
399
453
  clear_obj(uintptr_t obj_id) noexcept
400
454
  {
@@ -883,17 +937,11 @@ static PrivateWrapObject* PrivateWrap_New(PyObject *decorator, PyObject *func, P
883
937
  static void PrivateWrap_dealloc(PrivateWrapObject *self) noexcept;
884
938
  static PyObject* PrivateWrap_call(PrivateWrapObject *self, PyObject *args, PyObject *kw) noexcept;
885
939
 
886
- static PyObject *
887
- PrivateWrap_result(PyObject *obj, void* /*closure*/) noexcept
888
- {
889
- if (!obj) {
890
- Py_RETURN_NONE;
891
- }
892
-
893
- PyObject *res = ((PrivateWrapObject*)obj)->result;
894
- Py_INCREF(res);
895
- return res;
896
- }
940
+ static PyMemberDef PrivateWrap_members[] = {
941
+ {"result", T_OBJECT, offsetof(PrivateWrapObject, result), READONLY, "The result object"},
942
+ {"__wrapped__", T_OBJECT, offsetof(PrivateWrapObject, result), READONLY, "The result object"},
943
+ {NULL}
944
+ };
897
945
 
898
946
  static PyObject *
899
947
  PrivateWrap_funcs(PyObject *obj, void* /*closure*/) noexcept
@@ -909,7 +957,7 @@ static PyObject*
909
957
  PrivateWrap_doc(PyObject *obj, void* /*closure*/) noexcept
910
958
  {
911
959
  if (!obj) {
912
- return PyUnicode_FromString("PrivateWrap");
960
+ return PyUnicode_InternFromString("PrivateWrap");
913
961
  }
914
962
  PyObject* doc = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__doc__");
915
963
  if (!doc) {
@@ -923,26 +971,26 @@ static PyObject*
923
971
  PrivateWrap_module(PyObject *obj, void* /*closure*/) noexcept
924
972
  {
925
973
  if (!obj) {
926
- return PyUnicode_FromString("private_attribute");
974
+ return PyUnicode_InternFromString("private_attribute");
927
975
  }
928
976
  PyObject* module = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__module__");
929
977
  if (!module){
930
978
  PyErr_Clear();
931
- return PyUnicode_FromString("private_attribute");
979
+ return PyUnicode_InternFromString("private_attribute");
932
980
  }
933
981
  return module;
934
982
  }
935
983
 
936
984
  static PyObject*
937
- PrivateWarp_name(PyObject* obj, void* /*closure*/) noexcept
985
+ PrivateWrap_name(PyObject* obj, void* /*closure*/) noexcept
938
986
  {
939
987
  if (!obj) {
940
- return PyUnicode_FromString("_PrivateWrap");
988
+ return PyUnicode_InternFromString("_PrivateWrap");
941
989
  }
942
990
  PyObject* name = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__name__");
943
991
  if (!name) {
944
992
  PyErr_Clear();
945
- return PyUnicode_FromString("_PrivateWrap");
993
+ return PyUnicode_InternFromString("_PrivateWrap");
946
994
  }
947
995
  return name;
948
996
  }
@@ -951,12 +999,12 @@ static PyObject*
951
999
  PrivateWrap_qualname(PyObject* obj, void* /*closure*/) noexcept
952
1000
  {
953
1001
  if (!obj) {
954
- return PyUnicode_FromString("_PrivateWrap");
1002
+ return PyUnicode_InternFromString("_PrivateWrap");
955
1003
  }
956
1004
  PyObject* qualname = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__qualname__");
957
1005
  if (!qualname) {
958
1006
  PyErr_Clear();
959
- return PyUnicode_FromString("_PrivateWrap");
1007
+ return PyUnicode_InternFromString("_PrivateWrap");
960
1008
  }
961
1009
  return qualname;
962
1010
  }
@@ -991,34 +1039,76 @@ PrivateWrap_type_params(PyObject* obj, void* /*closure*/) noexcept
991
1039
  return type_params;
992
1040
  }
993
1041
 
994
- static const char* PrivateWrap_result_doc = "the final result of decorating";
995
1042
  static const char* PrivateWrap_funcs_doc = "the original functions";
996
1043
 
997
1044
  static PyGetSetDef PrivateWrap_getset[] = {
998
- {"result", (getter)PrivateWrap_result, NULL, PrivateWrap_result_doc, NULL},
999
1045
  {"funcs", (getter)PrivateWrap_funcs, NULL, PrivateWrap_funcs_doc, NULL},
1000
- {"__wrapped__", (getter)PrivateWrap_result, NULL, NULL, NULL},
1001
1046
  {"__doc__", (getter)PrivateWrap_doc, NULL, NULL, NULL},
1002
1047
  {"__module__", (getter)PrivateWrap_module, NULL, NULL, NULL},
1003
- {"__name__", (getter)PrivateWarp_name, NULL, NULL, NULL},
1048
+ {"__name__", (getter)PrivateWrap_name, NULL, NULL, NULL},
1004
1049
  {"__qualname__", (getter)PrivateWrap_qualname, NULL, NULL, NULL},
1005
1050
  {"__annotate__", (getter)PrivateWrap_annotate, NULL, NULL, NULL},
1006
1051
  {"__type_params__", (getter)PrivateWrap_type_params, NULL, NULL, NULL},
1007
1052
  {NULL}
1008
1053
  };
1009
1054
 
1055
+ #define PrivateWrap_getattro_dunder(name) do {\
1056
+ if (strcmp(name_str, "__" #name "__") == 0) {\
1057
+ return PrivateWrap_##name(obj, NULL);\
1058
+ }\
1059
+ } while(0)
1060
+
1010
1061
  static PyObject *
1011
1062
  PrivateWrap_getattro(PyObject *obj, PyObject *name) noexcept
1012
1063
  {
1013
- PyObject *res = PyObject_GenericGetAttr(obj, name);
1014
- if (res != NULL) {
1015
- return res;
1064
+ PrivateWrapObject *self = (PrivateWrapObject *)obj;
1065
+ const char *name_str = PyUnicode_AsUTF8(name);
1066
+ if (!name_str) {
1067
+ PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
1068
+ return NULL;
1016
1069
  }
1070
+ if (strcmp(name_str, "__wrapped__") == 0 || strcmp(name_str, "result") == 0) {
1071
+ if (!self->result) {
1072
+ PyErr_SetString(PyExc_AttributeError, "attribute 'result' is invalid");
1073
+ return NULL;
1074
+ }
1075
+ Py_INCREF(self->result);
1076
+ return self->result;
1077
+ }
1078
+ if (strcmp(name_str, "funcs") == 0) {
1079
+ return PrivateWrap_funcs(obj, NULL);
1080
+ }
1081
+ PrivateWrap_getattro_dunder(doc);
1082
+ PrivateWrap_getattro_dunder(module);
1083
+ PrivateWrap_getattro_dunder(name);
1084
+ PrivateWrap_getattro_dunder(qualname);
1085
+ PrivateWrap_getattro_dunder(annotate);
1086
+ PrivateWrap_getattro_dunder(type_params);
1087
+ return PyObject_GetAttr(self->result, name);
1088
+ }
1017
1089
 
1018
- PyErr_Clear();
1090
+ static PyObject*
1091
+ PrivateWrap_descrget(PyObject* obj, PyObject* name, PyObject* cls) noexcept
1092
+ {
1093
+ PyObject* result = ((PrivateWrapObject*)obj)->result;
1094
+ if (Py_TYPE(result)->tp_descr_get) {
1095
+ return Py_TYPE(result)->tp_descr_get(result, name, cls);
1096
+ } else {
1097
+ Py_INCREF(result);
1098
+ return result;
1099
+ }
1100
+ }
1019
1101
 
1020
- PrivateWrapObject *self = (PrivateWrapObject *)obj;
1021
- return PyObject_GetAttr(self->result, name);
1102
+ static int
1103
+ PrivateWrap_descrset(PyObject* obj, PyObject* name, PyObject* value) noexcept
1104
+ {
1105
+ PyObject* result = ((PrivateWrapObject*)obj)->result;
1106
+ if (Py_TYPE(result)->tp_descr_set) {
1107
+ return Py_TYPE(result)->tp_descr_set(result, name, value);
1108
+ } else {
1109
+ PyErr_SetString(PyExc_AttributeError, "attribute is not settable");
1110
+ return -1;
1111
+ }
1022
1112
  }
1023
1113
 
1024
1114
  static PyTypeObject PrivateWrapType = {
@@ -1042,7 +1132,7 @@ static PyTypeObject PrivateWrapType = {
1042
1132
  0, // tp_setattro
1043
1133
  0, // tp_as_buffer
1044
1134
  Py_TPFLAGS_DEFAULT, // tp_flags
1045
- "_PrivateWrap", // tp_doc
1135
+ "private_attribute._PrivateWrap", // tp_doc
1046
1136
  0, // tp_traverse
1047
1137
  0, // tp_clear
1048
1138
  0, // tp_richcompare
@@ -1050,21 +1140,29 @@ static PyTypeObject PrivateWrapType = {
1050
1140
  0, // tp_iter
1051
1141
  0, // tp_iternext
1052
1142
  0, // tp_methods
1053
- 0, // tp_members
1143
+ PrivateWrap_members, // tp_members
1054
1144
  PrivateWrap_getset, // tp_getset
1145
+ 0, // tp_base
1146
+ 0, // tp_dict
1147
+ PrivateWrap_descrget, // tp_descr_get
1148
+ PrivateWrap_descrset, // tp_descr_set
1055
1149
  };
1056
1150
 
1057
1151
  static PrivateWrapObject*
1058
1152
  PrivateWrap_New(PyObject *decorator, PyObject *func, PyObject *list) noexcept
1059
1153
  {
1060
- PrivateWrapObject *self =
1061
- PyObject_New(PrivateWrapObject, &PrivateWrapType);
1062
1154
  PyObject *wrapped = PyObject_CallFunctionObjArgs(decorator, func, NULL);
1063
1155
  if (!wrapped) {
1064
- Py_DECREF(self);
1156
+ return NULL;
1157
+ }
1158
+ if (PyObject_TypeCheck(wrapped, &PrivateWrapType)) {
1159
+ PyErr_SetString(PyExc_TypeError, "decorator returned a '_PrivateWrap' object which is not allowed");
1160
+ Py_DECREF(wrapped);
1065
1161
  return NULL;
1066
1162
  }
1067
1163
 
1164
+ PrivateWrapObject *self =
1165
+ PyObject_New(PrivateWrapObject, &PrivateWrapType);
1068
1166
  self->func_list = list;
1069
1167
  Py_INCREF(list);
1070
1168
 
@@ -1824,12 +1922,17 @@ need_analyse_type(PyObject* type) noexcept
1824
1922
  }
1825
1923
  #if PY_VERSION_HEX < 0x030D0000
1826
1924
  PyObject* metaclass = PyWeakref_GET_OBJECT(metaclassref);
1925
+ if (type == metaclass) continue;
1827
1926
  if (PyObject_IsInstance(type, metaclass)) {
1828
1927
  return true;
1829
1928
  }
1830
1929
  #else
1831
1930
  PyObject* metaclass;
1832
1931
  if (PyWeakref_GetRef(metaclassref, &metaclass) == 1) {
1932
+ if (type == metaclass) {
1933
+ Py_DECREF(metaclass);
1934
+ continue;
1935
+ }
1833
1936
  if (PyObject_IsInstance(type, metaclass)) {
1834
1937
  Py_DECREF(metaclass);
1835
1938
  return true;
@@ -1841,157 +1944,6 @@ need_analyse_type(PyObject* type) noexcept
1841
1944
  return false;
1842
1945
  }
1843
1946
 
1844
- static void
1845
- get_getattribute_and_getattr(PyTypeObject* cls, PyObject** getattribute, PyObject** getattr) noexcept
1846
- {
1847
- bool has_getattribute = false;
1848
- bool has_getattr = false;
1849
- if (PyDict_ContainsString(cls->tp_dict, "__getattribute__")) {
1850
- *getattribute = PyDict_GetItemString(cls->tp_dict, "__getattribute__");
1851
- has_getattribute = true;
1852
- }
1853
- if (PyDict_ContainsString(cls->tp_dict, "__getattr__")) {
1854
- *getattr = PyDict_GetItemString(cls->tp_dict, "__getattr__");
1855
- has_getattr = true;
1856
- }
1857
- if (has_getattribute && has_getattr) {
1858
- return;
1859
- }
1860
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1861
- if (!has_getattribute && PyDict_ContainsString(base->tp_dict, "__getattribute__")) {
1862
- *getattribute = PyDict_GetItemString(base->tp_dict, "__getattribute__");
1863
- has_getattribute = true;
1864
- }
1865
- if (!has_getattr && PyDict_ContainsString(base->tp_dict, "__getattr__")) {
1866
- *getattr = PyDict_GetItemString(base->tp_dict, "__getattr__");
1867
- has_getattr = true;
1868
- }
1869
- if (has_getattribute && has_getattr) {
1870
- return;
1871
- }
1872
- }
1873
- }
1874
-
1875
- static void
1876
- get_setattr_and_delattr(PyTypeObject* cls, PyObject** setattr, PyObject** delattr) noexcept
1877
- {
1878
- bool has_setattr = false;
1879
- bool has_delattr = false;
1880
- if (PyDict_ContainsString(cls->tp_dict, "__setattr__")) {
1881
- *setattr = PyDict_GetItemString(cls->tp_dict, "__setattr__");
1882
- has_setattr = true;
1883
- }
1884
- if (PyDict_ContainsString(cls->tp_dict, "__delattr__")) {
1885
- *delattr = PyDict_GetItemString(cls->tp_dict, "__delattr__");
1886
- has_delattr = true;
1887
- }
1888
- if (has_setattr && has_delattr) {
1889
- return;
1890
- }
1891
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1892
- if (!has_setattr && PyDict_ContainsString(base->tp_dict, "__setattr__")) {
1893
- *setattr = PyDict_GetItemString(base->tp_dict, "__setattr__");
1894
- has_setattr = true;
1895
- }
1896
- if (!has_delattr && PyDict_ContainsString(base->tp_dict, "__delattr__")) {
1897
- *delattr = PyDict_GetItemString(base->tp_dict, "__delattr__");
1898
- has_delattr = true;
1899
- }
1900
- if (has_setattr && has_delattr) {
1901
- return;
1902
- }
1903
- }
1904
- }
1905
-
1906
- static void
1907
- get_del(PyTypeObject* cls, PyObject** del) noexcept
1908
- {
1909
- if (PyDict_ContainsString(cls->tp_dict, "__del__")) {
1910
- *del = PyDict_GetItemString(cls->tp_dict, "__del__");
1911
- return;
1912
- }
1913
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1914
- if (PyDict_ContainsString(base->tp_dict, "__del__")) {
1915
- *del = PyDict_GetItemString(base->tp_dict, "__del__");
1916
- return;
1917
- }
1918
- }
1919
- }
1920
-
1921
- static PyObject*
1922
- python_original_tp_getattro(PyObject* self, PyObject* name) noexcept
1923
- {
1924
- PyObject* getattriute = NULL;
1925
- PyObject* getattr = NULL;
1926
- get_getattribute_and_getattr((PyTypeObject*)self->ob_type, &getattriute, &getattr);
1927
- PyObject* res = NULL;
1928
- if (getattriute) {
1929
- res = PyObject_CallFunctionObjArgs(getattriute, self, name, NULL);
1930
- } else {
1931
- res = PyObject_GenericGetAttr(self, name);
1932
- }
1933
- if (!res && getattr) {
1934
- // check if the exception is AttributeError
1935
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
1936
- return NULL;
1937
- }
1938
- PyErr_Clear();
1939
- res = PyObject_CallFunctionObjArgs(getattr, self, name, NULL);
1940
- }
1941
- return res;
1942
- }
1943
-
1944
- static int
1945
- python_original_tp_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept
1946
- {
1947
- PyObject* setattr = NULL;
1948
- PyObject* delattr = NULL;
1949
- get_setattr_and_delattr((PyTypeObject*)self->ob_type, &setattr, &delattr);
1950
- if (value && setattr) {
1951
- PyObject* res = PyObject_CallFunctionObjArgs(setattr, self, name, value, NULL);
1952
- if (!res) {
1953
- return -1;
1954
- }
1955
- Py_DECREF(res);
1956
- return 0;
1957
- } else if (!value && delattr) {
1958
- PyObject* res = PyObject_CallFunctionObjArgs(delattr, self, name, NULL);
1959
- if (!res) {
1960
- return -1;
1961
- }
1962
- Py_DECREF(res);
1963
- return 0;
1964
- }
1965
- return PyObject_GenericSetAttr(self, name, value);
1966
- }
1967
-
1968
- static void
1969
- python_original_tp_finalize(PyObject* self) noexcept
1970
- {
1971
- PyObject* del = NULL;
1972
- get_del((PyTypeObject*)self->ob_type, &del);
1973
- if (del) {
1974
- PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL;
1975
- PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
1976
- PyObject *result = PyObject_CallFunctionObjArgs(del, self, NULL);
1977
- if (result == NULL) {
1978
- // python < 3.13 use PyErr_WriteUnraisable, python >= 3.13 use PyErr_FormatUnraisable
1979
- # if PY_VERSION_HEX >= 0x030D0000
1980
- PyErr_FormatUnraisable("Exception ignored while "
1981
- "calling deallocator %R", del);
1982
- # else
1983
- PyErr_WriteUnraisable(del);
1984
- # endif
1985
- }
1986
- else {
1987
- Py_DECREF(result);
1988
- }
1989
- if (exc_type != NULL) {
1990
- PyErr_Restore(exc_type, exc_value, exc_tb);
1991
- }
1992
- }
1993
- }
1994
-
1995
1947
  static bool
1996
1948
  PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationData& data) noexcept
1997
1949
  {
@@ -2233,67 +2185,55 @@ ensure_tp(PyTypeObject* type_instance) noexcept
2233
2185
  uintptr_t type_id = (uintptr_t)(type_instance);
2234
2186
  {
2235
2187
  if (type_instance->tp_getattro != PrivateAttr_tp_getattro) {
2236
- if (!type_instance->tp_getattro) {
2188
+ if (!type_instance->tp_getattro && ::AllData::all_type_getattro.find(type_id) == ::AllData::all_type_getattro.end()) {
2237
2189
  ::AllData::all_type_getattro[type_id] = get_need_tp_getattro(type_instance);
2238
2190
  } else {
2239
2191
  ::AllData::all_type_getattro[type_id] = type_instance->tp_getattro;
2240
2192
  }
2241
2193
  type_instance->tp_getattro = PrivateAttr_tp_getattro;
2242
- } else {
2243
- if (PyDict_ContainsString(type_instance->tp_dict, "__getattribute__") || PyDict_ContainsString(type_instance->tp_dict, "__getattr__")) {
2244
- ::AllData::all_type_getattro[type_id] = AllSlots::original_getattro;
2245
- } else {
2246
- PyTypeObject* base = type_instance->tp_base;
2247
- uintptr_t base_id = (uintptr_t)(base);
2248
- if (::AllData::all_type_getattro.find(base_id) != ::AllData::all_type_getattro.end()) {
2249
- ::AllData::all_type_getattro[type_id] = ::AllData::all_type_getattro[base_id];
2250
- } else if (base && base->tp_getattro && base->tp_getattro != PrivateAttr_tp_getattro) {
2251
- ::AllData::all_type_getattro[type_id] = base->tp_getattro;
2252
- }
2194
+ } else if (::AllData::all_type_getattro.find(type_id) == ::AllData::all_type_getattro.end()) {
2195
+ PyTypeObject* base = type_instance->tp_base;
2196
+ uintptr_t base_id = (uintptr_t)(base);
2197
+ if (::AllData::all_type_getattro.find(base_id) != ::AllData::all_type_getattro.end()) {
2198
+ ::AllData::all_type_getattro[type_id] = ::AllData::all_type_getattro[base_id];
2199
+ } else if (base && base->tp_getattro && base->tp_getattro != PrivateAttr_tp_getattro) {
2200
+ ::AllData::all_type_getattro[type_id] = base->tp_getattro;
2253
2201
  }
2254
2202
  }
2255
2203
  }
2256
2204
  {
2257
2205
  if (type_instance->tp_setattro != PrivateAttr_tp_setattro) {
2258
- if (!type_instance->tp_setattro) {
2206
+ if (!type_instance->tp_setattro && ::AllData::all_type_setattro.find(type_id) == ::AllData::all_type_setattro.end()) {
2259
2207
  ::AllData::all_type_setattro[type_id] = get_need_tp_setattro(type_instance);
2260
2208
  } else {
2261
2209
  ::AllData::all_type_setattro[type_id] = type_instance->tp_setattro;
2262
2210
  }
2263
2211
  type_instance->tp_setattro = PrivateAttr_tp_setattro;
2264
- } else {
2265
- if (PyDict_ContainsString(type_instance->tp_dict, "__setattr__") || PyDict_ContainsString(type_instance->tp_dict, "__delattr__")) {
2266
- ::AllData::all_type_setattro[type_id] = AllSlots::original_setattro;
2267
- } else {
2268
- PyTypeObject* base = type_instance->tp_base;
2269
- uintptr_t base_id = (uintptr_t)(base);
2270
- if (::AllData::all_type_setattro.find(base_id) != ::AllData::all_type_setattro.end()) {
2271
- ::AllData::all_type_setattro[type_id] = ::AllData::all_type_setattro[base_id];
2272
- } else if (base && base->tp_setattro && base->tp_setattro != PrivateAttr_tp_setattro) {
2273
- ::AllData::all_type_setattro[type_id] = base->tp_setattro;
2274
- }
2212
+ } else if (::AllData::all_type_setattro.find(type_id) == ::AllData::all_type_setattro.end()){
2213
+ PyTypeObject* base = type_instance->tp_base;
2214
+ uintptr_t base_id = (uintptr_t)(base);
2215
+ if (::AllData::all_type_setattro.find(base_id) != ::AllData::all_type_setattro.end()) {
2216
+ ::AllData::all_type_setattro[type_id] = ::AllData::all_type_setattro[base_id];
2217
+ } else if (base && base->tp_setattro && base->tp_setattro != PrivateAttr_tp_setattro) {
2218
+ ::AllData::all_type_setattro[type_id] = base->tp_setattro;
2275
2219
  }
2276
2220
  }
2277
2221
  }
2278
2222
  {
2279
2223
  if (type_instance->tp_finalize != PrivateAttr_tp_finalize) {
2280
- if (!type_instance->tp_finalize) {
2224
+ if (!type_instance->tp_finalize && ::AllData::all_type_finalize.find(type_id) == ::AllData::all_type_finalize.end()) {
2281
2225
  ::AllData::all_type_finalize[type_id] = get_need_tp_finalize(type_instance);
2282
2226
  } else {
2283
2227
  ::AllData::all_type_finalize[type_id] = type_instance->tp_finalize;
2284
2228
  }
2285
2229
  type_instance->tp_finalize = PrivateAttr_tp_finalize;
2286
- } else {
2287
- if (PyDict_ContainsString(type_instance->tp_dict, "__del__")) {
2288
- ::AllData::all_type_finalize[type_id] = AllSlots::original_finalize;
2289
- } else {
2290
- PyTypeObject* base = type_instance->tp_base;
2291
- uintptr_t base_id = (uintptr_t)(base);
2292
- if (::AllData::all_type_finalize.find(base_id) != ::AllData::all_type_finalize.end()) {
2293
- ::AllData::all_type_finalize[type_id] = ::AllData::all_type_finalize[base_id];
2294
- } else if (base && base->tp_finalize && base->tp_finalize != PrivateAttr_tp_finalize) {
2295
- ::AllData::all_type_finalize[type_id] = base->tp_finalize;
2296
- }
2230
+ } else if (::AllData::all_type_finalize.find(type_id) == ::AllData::all_type_finalize.end()) {
2231
+ PyTypeObject* base = type_instance->tp_base;
2232
+ uintptr_t base_id = (uintptr_t)(base);
2233
+ if (::AllData::all_type_finalize.find(base_id) != ::AllData::all_type_finalize.end()) {
2234
+ ::AllData::all_type_finalize[type_id] = ::AllData::all_type_finalize[base_id];
2235
+ } else if (base && base->tp_finalize && base->tp_finalize != PrivateAttr_tp_finalize) {
2236
+ ::AllData::all_type_finalize[type_id] = base->tp_finalize;
2297
2237
  }
2298
2238
  }
2299
2239
  }
@@ -2302,40 +2242,16 @@ ensure_tp(PyTypeObject* type_instance) noexcept
2302
2242
  static void
2303
2243
  ensure_subclass_tp(PyTypeObject* type_instance) noexcept
2304
2244
  {
2305
- // type.__subclasses__
2306
- PyObject* type_subclasses = (PyObject*)type_instance->tp_subclasses;
2307
- if (!type_subclasses) {
2308
- return;
2309
- }
2310
- if (!PyDict_Check(type_subclasses)) {
2311
- return;
2312
- }
2313
- // check all values in the dict. It should be a weakref to a type. If not, continue.
2314
- PyObject* key, *value;
2315
- Py_ssize_t pos = 0;
2316
- while (PyDict_Next(type_subclasses, &pos, &key, &value)) {
2317
- if (!value || !PyWeakref_CheckRef(value)) {
2318
- continue;
2319
- }
2320
- // python<3.13 use PyWeakref_GetObject, python>=3.13 use PyWeakref_GetRef
2321
- #if PY_VERSION_HEX < 0x030D0000
2322
- PyObject* subclass = PyWeakref_GetObject(value);
2323
- if (!subclass || !PyType_Check(subclass)) {
2324
- continue;
2325
- }
2326
- ensure_tp((PyTypeObject*)subclass);
2327
- #else
2328
- PyObject* subclass;
2329
- if (PyWeakref_GetRef(value, (PyObject**)&subclass) != 1) {
2330
- continue;
2331
- }
2332
- if (!subclass || !PyType_Check(subclass)) {
2333
- continue;
2334
- }
2245
+ PyObject* subclasses = PyObject_CallMethod((PyObject*)&PyType_Type, "__subclasses__", "O", (PyObject*)type_instance);
2246
+ assert(subclasses);
2247
+ assert(PyList_Check(subclasses));
2248
+ Py_ssize_t list_len = PyList_GET_SIZE(subclasses);
2249
+ for (Py_ssize_t i = 0; i < list_len; i++) {
2250
+ PyObject* subclass = PyList_GetItem(subclasses, i);
2251
+ if (!PyType_Check(subclass)) continue;
2335
2252
  ensure_tp((PyTypeObject*)subclass);
2336
- Py_XDECREF(subclass);
2337
- #endif
2338
2253
  }
2254
+ Py_DECREF(subclasses);
2339
2255
  }
2340
2256
 
2341
2257
  static bool
@@ -2358,15 +2274,6 @@ PrivateAttrType_postprocess(PyObject* new_type, PrivateAttrCreationData& data) n
2358
2274
  }
2359
2275
 
2360
2276
  ensure_tp(type_instance);
2361
- if (PyDict_ContainsString(type_instance->tp_dict, "__getattribute__") || PyDict_ContainsString(type_instance->tp_dict, "__getattr__")) {
2362
- ::AllData::all_type_getattro[type_id] = AllSlots::original_getattro;
2363
- }
2364
- if (PyDict_ContainsString(type_instance->tp_dict, "__setattr__") || PyDict_ContainsString(type_instance->tp_dict, "__delattr__")) {
2365
- ::AllData::all_type_setattro[type_id] = AllSlots::original_setattro;
2366
- }
2367
- if (PyDict_ContainsString(type_instance->tp_dict, "__del__")) {
2368
- ::AllData::all_type_finalize[type_id] = AllSlots::original_finalize;
2369
- }
2370
2277
 
2371
2278
  ::AllData::type_attr_dict[type_id] = {};
2372
2279
  ::AllData::all_type_attr_set[type_id] = data.private_attrs_set;
@@ -2520,21 +2427,93 @@ PrivateAttrType_getattr(PyObject* cls, PyObject* name) noexcept
2520
2427
  return result;
2521
2428
  }
2522
2429
 
2430
+ static setattrofunc original_type_tp_setattro = 0;
2431
+
2432
+ static bool
2433
+ is_registed_type(PyObject* type) noexcept
2434
+ {
2435
+ uintptr_t typ_id = (uintptr_t)type;
2436
+ return (AllData::all_register_type_weak_ref.find(typ_id) != AllData::all_register_type_weak_ref.end());
2437
+ }
2438
+
2439
+ static PyObject* ensure_metaclass_tp(PyObject* /*self*/, PyObject* metaclass) noexcept;
2440
+
2441
+ static int
2442
+ new_type_tp_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept
2443
+ {
2444
+ std::vector<PyObject*> need_analyse_types_and_metaclasses;
2445
+ std::vector<PyObject*> registered_types_and_metaclasses;
2446
+ std::vector<PyObject*> all_subclasses;
2447
+ PyObject* subclasses = PyObject_CallMethod((PyObject*)&PyType_Type, "__subclasses__", "O", self);
2448
+ if (!subclasses) return -1;
2449
+ if (!PyList_Check(subclasses)) {
2450
+ PyErr_SetString(PyExc_TypeError, "subclasses of this class is not list");
2451
+ Py_DECREF(subclasses);
2452
+ return -1;
2453
+ }
2454
+ Py_ssize_t list_len = PyList_GET_SIZE(subclasses);
2455
+ for (Py_ssize_t i = 0; i < list_len; i++) {
2456
+ all_subclasses.push_back(PyList_GET_ITEM(subclasses, i));
2457
+ }
2458
+ if (need_analyse_type(self)) {
2459
+ need_analyse_types_and_metaclasses.push_back(self);
2460
+ } else if (is_registed_type(self)) {
2461
+ registered_types_and_metaclasses.push_back(self);
2462
+ }
2463
+ for (auto& i: all_subclasses) {
2464
+ if (need_analyse_type(i)) {
2465
+ need_analyse_types_and_metaclasses.push_back(i);
2466
+ } else if (is_registed_type(i)) {
2467
+ registered_types_and_metaclasses.push_back(i);
2468
+ }
2469
+ }
2470
+ int result = original_type_tp_setattro(self, name, value);
2471
+ for (auto& i: need_analyse_types_and_metaclasses) {
2472
+ ensure_tp((PyTypeObject*)i);
2473
+ }
2474
+ for (auto& i: registered_types_and_metaclasses) {
2475
+ ensure_metaclass_tp(0, i);
2476
+ }
2477
+ Py_DECREF(subclasses);
2478
+ return result;
2479
+ }
2480
+
2523
2481
  static int
2524
2482
  init_all_slots() noexcept
2525
2483
  {
2526
- AllSlots::original_getattro = python_original_tp_getattro;
2527
- AllSlots::original_setattro = python_original_tp_setattro;
2528
- AllSlots::original_finalize = python_original_tp_finalize;
2484
+ original_type_tp_setattro = PyType_Type.tp_setattro;
2485
+ PyObject* original_setattr = PyObject_GetAttrString((PyObject*)&PyType_Type, "__setattr__");
2486
+ if (!original_setattr) {return -1;}
2487
+ if (original_setattr && PyObject_IsInstance(original_setattr, (PyObject*)&PyWrapperDescr_Type)) {
2488
+ ((PyWrapperDescrObject*)original_setattr)->d_wrapped = (void*)new_type_tp_setattro;
2489
+ }
2490
+ Py_DECREF(original_setattr);
2491
+ PyObject* original_delattr = PyObject_GetAttrString((PyObject*)&PyType_Type, "__delattr__");
2492
+ if (!original_delattr) {return -1;}
2493
+ if (original_delattr && PyObject_IsInstance(original_delattr, (PyObject*)&PyWrapperDescr_Type)) {
2494
+ ((PyWrapperDescrObject*)original_delattr)->d_wrapped = (void*)new_type_tp_setattro;
2495
+ }
2496
+ Py_DECREF(original_delattr);
2497
+ PyType_Type.tp_setattro = new_type_tp_setattro;
2498
+ PyObject* all_metaclasses = PyObject_CallMethod((PyObject*)&PyType_Type, "__subclasses__", "O", (PyObject*)&PyType_Type);
2499
+ if (!all_metaclasses) {return -1;}
2500
+ if (!PyList_Check(all_metaclasses)) {
2501
+ Py_DECREF(all_metaclasses);
2502
+ return 0;
2503
+ }
2504
+ Py_ssize_t list_len = PyList_GET_SIZE(all_metaclasses);
2505
+ for (Py_ssize_t i = 0; i < list_len; i++) {
2506
+ PyObject* metaclass = PyList_GET_ITEM(all_metaclasses, i);
2507
+ if (!PyType_Check(metaclass)) {continue;}
2508
+ ((PyTypeObject*)metaclass)->tp_setattro = new_type_tp_setattro;
2509
+ }
2510
+ Py_DECREF(all_metaclasses);
2529
2511
  return 0;
2530
2512
  }
2531
2513
 
2532
2514
  static getattrofunc
2533
2515
  get_need_tp_getattro(PyTypeObject* cls) noexcept
2534
2516
  {
2535
- if (PyDict_ContainsString(cls->tp_dict, "__getattribute__") || PyDict_ContainsString(cls->tp_dict, "__getattr__")) {
2536
- return AllSlots::original_getattro;
2537
- }
2538
2517
  PyTypeObject* base = cls->tp_base;
2539
2518
  while (base) {
2540
2519
  if (base->tp_getattro != PrivateAttr_tp_getattro) {
@@ -2554,9 +2533,6 @@ get_need_tp_getattro(PyTypeObject* cls) noexcept
2554
2533
  static setattrofunc
2555
2534
  get_need_tp_setattro(PyTypeObject* cls) noexcept
2556
2535
  {
2557
- if (PyDict_ContainsString(cls->tp_dict, "__setattr__") || PyDict_ContainsString(cls->tp_dict, "__delattr__")) {
2558
- return AllSlots::original_setattro;
2559
- }
2560
2536
  PyTypeObject* base = cls->tp_base;
2561
2537
  while (base) {
2562
2538
  if (base->tp_setattro != PrivateAttr_tp_setattro) {
@@ -2576,9 +2552,6 @@ get_need_tp_setattro(PyTypeObject* cls) noexcept
2576
2552
  static destructor
2577
2553
  get_need_tp_finalize(PyTypeObject* cls) noexcept
2578
2554
  {
2579
- if (PyDict_ContainsString(cls->tp_dict, "__del__")) {
2580
- return AllSlots::original_finalize;
2581
- }
2582
2555
  PyTypeObject* base = cls->tp_base;
2583
2556
  while (base) {
2584
2557
  if (base->tp_finalize != PrivateAttr_tp_finalize) {
@@ -2595,64 +2568,6 @@ get_need_tp_finalize(PyTypeObject* cls) noexcept
2595
2568
  return NULL;
2596
2569
  }
2597
2570
 
2598
- static void
2599
- type_change_all_slots(PyTypeObject* cls, const char* name) noexcept
2600
- {
2601
- if (strcmp(name, "__getattribute__") == 0 || strcmp(name, "__getattr__") == 0) {
2602
- cls->tp_getattro = PrivateAttr_tp_getattro;
2603
- ::AllData::all_type_getattro[(uintptr_t)cls] = get_need_tp_getattro(cls);
2604
- } else if (strcmp(name, "__setattr__") == 0 || strcmp(name, "__delattr__") == 0) {
2605
- cls->tp_setattro = PrivateAttr_tp_setattro;
2606
- ::AllData::all_type_setattro[(uintptr_t)cls] = get_need_tp_setattro(cls);
2607
- } else if (strcmp(name, "__del__") == 0) {
2608
- cls->tp_finalize = PrivateAttr_tp_finalize;
2609
- ::AllData::all_type_finalize[(uintptr_t)cls] = get_need_tp_finalize(cls);
2610
- }
2611
- }
2612
-
2613
- static void
2614
- subtype_change_all_slots(PyTypeObject* cls, const char* name) noexcept
2615
- {
2616
- // tp_subclasses: {id(type): weakref.ref(type)}
2617
- PyObject* tp_subclasses = (PyObject*)cls->tp_subclasses;
2618
- if (!tp_subclasses) {
2619
- return;
2620
- }
2621
- if (!PyDict_Check(tp_subclasses)) {
2622
- return;
2623
- }
2624
- PyObject* key;
2625
- PyObject* value;
2626
- Py_ssize_t pos = 0;
2627
- while (PyDict_Next(tp_subclasses, &pos, &key, &value)) {
2628
- if (!PyWeakref_Check(value)) {
2629
- continue;
2630
- }
2631
- #if PY_VERSION_HEX < 0x030D0000
2632
- PyObject* obj = PyWeakref_GET_OBJECT(value);
2633
- if (!obj) {
2634
- continue;
2635
- }
2636
- #else
2637
- PyObject* obj;
2638
- if (PyWeakref_GetRef(value, &obj) != 1) {
2639
- continue;
2640
- }
2641
- if (!obj) {
2642
- continue;
2643
- }
2644
- #endif
2645
- if (!PyType_Check(obj)) {
2646
- continue;
2647
- }
2648
- PyTypeObject* sub_cls = (PyTypeObject*)obj;
2649
- type_change_all_slots(sub_cls, name);
2650
- #if PY_VERSION_HEX >= 0x030D0000
2651
- Py_XDECREF(obj);
2652
- #endif
2653
- }
2654
- }
2655
-
2656
2571
  static int
2657
2572
  PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value) noexcept
2658
2573
  {
@@ -2679,42 +2594,12 @@ PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value) noexcept
2679
2594
  return type_setattr(cls, name_str, value);
2680
2595
  }
2681
2596
  Py_XDECREF(now_code);
2682
- // if name in __getattribute__, __setattr__, __delattr__, __del__, just set to tp_dict
2683
- if (strcmp(name_str.c_str(), "__getattribute__") == 0 ||
2684
- strcmp(name_str.c_str(), "__getattr__") == 0 ||
2685
- strcmp(name_str.c_str(), "__setattr__") == 0 ||
2686
- strcmp(name_str.c_str(), "__delattr__") == 0 ||
2687
- strcmp(name_str.c_str(), "__del__") == 0) {
2688
- PyObject* tp_dict = ((PyTypeObject*)cls)->tp_dict;
2689
- if (!tp_dict) {
2690
- PyErr_SetString(PyExc_TypeError, "type has no tp_dict");
2691
- return -1;
2692
- }
2693
- if (!value) {
2694
- if (PyDict_DelItem(tp_dict, name) < 0) {
2695
- PyErr_Format(PyExc_AttributeError, "type object '%.100s' has no attribute '%U'", ((PyTypeObject*)cls)->tp_name, name);
2696
- return -1;
2697
- }
2698
- type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2699
- subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2700
- return 0;
2701
- }
2702
- if (PyDict_SetItem(tp_dict, name, value) < 0) {
2703
- return -1;
2704
- }
2705
- type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2706
- subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2707
- return 0;
2708
- }
2709
2597
  PyTypeObject* base = Py_TYPE(cls)->tp_base;
2710
2598
  while (base && base->tp_base && base->tp_setattro == PrivateAttrType_setattr) {
2711
2599
  base = base->tp_base;
2712
2600
  }
2713
2601
  if (!base) {
2714
- int result = PyType_Type.tp_setattro(cls, name, value);
2715
- ensure_tp((PyTypeObject*)cls);
2716
- ensure_subclass_tp((PyTypeObject*)cls);
2717
- return result;
2602
+ return new_type_tp_setattro(cls, name, value);
2718
2603
  }
2719
2604
  int result = base->tp_setattro(cls, name, value);
2720
2605
  ensure_tp((PyTypeObject*)cls);
@@ -2800,7 +2685,7 @@ static const char* PrivateAttrBase_doc = "The class to help to create private at
2800
2685
  static PyObject*
2801
2686
  create_private_attr_base_simple(void) noexcept
2802
2687
  {
2803
- PyObject* name = PyUnicode_FromString("PrivateAttrBase");
2688
+ PyObject* name = PyUnicode_InternFromString("PrivateAttrBase");
2804
2689
  if (!name) return NULL;
2805
2690
  PyObject* bases = PyTuple_New(0);
2806
2691
  if (!bases) {
@@ -2822,8 +2707,8 @@ create_private_attr_base_simple(void) noexcept
2822
2707
  }
2823
2708
  PyDict_SetItemString(dict, "__private_attrs__", private_attrs);
2824
2709
  PyDict_SetItemString(dict, "__slots__", private_attrs);
2825
- PyDict_SetItemString(dict, "__doc__", PyUnicode_FromString(PrivateAttrBase_doc));
2826
- PyDict_SetItemString(dict, "__module__", PyUnicode_FromString("private_attribute"));
2710
+ PyDict_SetItemString(dict, "__doc__", PyUnicode_InternFromString(PrivateAttrBase_doc));
2711
+ PyDict_SetItemString(dict, "__module__", PyUnicode_InternFromString("private_attribute"));
2827
2712
  PyObject *args = PyTuple_Pack(3, name, bases, dict);
2828
2713
  PyObject* base_type;
2829
2714
  if (args) {
@@ -2915,7 +2800,7 @@ PrivateTempObject_dealloc(PyObject* self) noexcept
2915
2800
 
2916
2801
  static PyTypeObject PrivateTempType = {
2917
2802
  PyVarObject_HEAD_INIT(NULL, 0)
2918
- "private_temp", // tp_name
2803
+ "private_attribute._PrivateTemp", // tp_name
2919
2804
  sizeof(PrivateTempObject), // tp_basicsize
2920
2805
  0, //tp_itemsize
2921
2806
  PrivateTempObject_dealloc, //tp_dealloc
@@ -3031,8 +2916,40 @@ static PyMethodDef weakref_callback_def = {
3031
2916
  NULL
3032
2917
  };
3033
2918
 
2919
+ static bool
2920
+ is_class_of_registed_type(PyObject* metaclass) noexcept
2921
+ {
2922
+ std::shared_lock lock(::AllData::all_register_new_metaclass_mutex);
2923
+ for (auto& [id, instanceref]: ::AllData::all_register_type_weak_ref){
2924
+ if (!PyWeakref_CheckRef(instanceref)) {
2925
+ continue;
2926
+ }
2927
+ #if PY_VERSION_HEX < 0x030D0000
2928
+ PyObject* instance = PyWeakref_GET_OBJECT(instanceref);
2929
+ if (instance == metaclass) continue;
2930
+ if (PyObject_IsInstance(instance, metaclass)) {
2931
+ return true;
2932
+ }
2933
+ #else
2934
+ PyObject* instance;
2935
+ if (PyWeakref_GetRef(instanceref, &instance) == 1) {
2936
+ if (instance == metaclass) {
2937
+ Py_DECREF(instance);
2938
+ continue;
2939
+ }
2940
+ if (PyObject_IsInstance(instance, metaclass)) {
2941
+ Py_DECREF(instance);
2942
+ return true;
2943
+ }
2944
+ Py_DECREF(instance);
2945
+ }
2946
+ #endif
2947
+ }
2948
+ return false;
2949
+ }
2950
+
3034
2951
  static PyObject*
3035
- register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
2952
+ register_metaclass_head(PyObject* metaclass) noexcept
3036
2953
  {
3037
2954
  if (!PyType_Check(metaclass)) {
3038
2955
  PyErr_SetString(PyExc_TypeError, "metaclass must be a type");
@@ -3042,6 +2959,18 @@ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3042
2959
  PyErr_SetString(PyExc_TypeError, "metaclass must be a metatype");
3043
2960
  return NULL;
3044
2961
  }
2962
+ if (metaclass == (PyObject*)&PyType_Type) {
2963
+ PyErr_SetString(PyExc_ValueError, "cannot register type itself");
2964
+ return NULL;
2965
+ }
2966
+ if (need_analyse_type(metaclass)) {
2967
+ PyErr_SetString(PyExc_ValueError, "cannot register type that is the instance of 'PrivateAttrType' or registed type");
2968
+ return NULL;
2969
+ }
2970
+ if (is_class_of_registed_type(metaclass)) {
2971
+ PyErr_SetString(PyExc_ValueError, "cannot register type that is the class of registed type");
2972
+ return NULL;
2973
+ }
3045
2974
  uintptr_t id = (uintptr_t)metaclass;
3046
2975
 
3047
2976
  std::unique_lock lock(::AllData::all_register_new_metaclass_mutex);
@@ -3072,6 +3001,27 @@ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3072
3001
  Py_RETURN_NONE;
3073
3002
  }
3074
3003
 
3004
+ static PyObject*
3005
+ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3006
+ {
3007
+ if (!register_metaclass_head(metaclass)) return NULL;
3008
+ PyObject* subclasses = PyObject_CallMethod((PyObject*)&PyType_Type, "__subclasses__", "O", metaclass);
3009
+ if (!subclasses) {
3010
+ PyErr_Clear();
3011
+ Py_RETURN_NONE;
3012
+ }
3013
+ Py_ssize_t list_len = PyList_GET_SIZE(subclasses);
3014
+ for (Py_ssize_t i = 0; i < list_len; i++) {
3015
+ PyObject* subclass = PyList_GET_ITEM(subclasses, i);
3016
+ if (!register_metaclass(0, subclass)) {
3017
+ Py_DECREF(subclasses);
3018
+ return NULL;
3019
+ }
3020
+ }
3021
+ Py_DECREF(subclasses);
3022
+ Py_RETURN_NONE;
3023
+ }
3024
+
3075
3025
  static PyObject*
3076
3026
  ensure_type_tp(PyObject* /*self*/, PyObject* type) noexcept
3077
3027
  {
@@ -3154,8 +3104,16 @@ PrivateModule_dir(PyObject* self, PyObject* /*args*/) noexcept
3154
3104
  return result;
3155
3105
  }
3156
3106
 
3107
+ #define PrivateModule_GETATTRO_CASE(name) \
3108
+ do {\
3109
+ if (strcmp(name_cstr, #name) == 0) {\
3110
+ PyObject* attr = PrivateModule_get_##name(NULL, NULL);\
3111
+ return attr;\
3112
+ }\
3113
+ } while (0)
3114
+
3157
3115
  static int
3158
- PrivateModule_setattro(PyObject* cls, PyObject* name, PyObject* value) noexcept
3116
+ PrivateModule_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept
3159
3117
  {
3160
3118
  // if name is "__class__" it do nothing and return success
3161
3119
  if (PyUnicode_Check(name)) {
@@ -3163,8 +3121,25 @@ PrivateModule_setattro(PyObject* cls, PyObject* name, PyObject* value) noexcept
3163
3121
  if (name_cstr && strcmp(name_cstr, "__class__") == 0) {
3164
3122
  return 0;
3165
3123
  }
3124
+ static const char* unsetable_attrs[] = {
3125
+ "PrivateWrapProxy",
3126
+ "PrivateAttrType",
3127
+ "PrivateAttrBase",
3128
+ "prepare",
3129
+ "postprocess",
3130
+ "register_metaclass",
3131
+ "ensure_type",
3132
+ "ensure_metaclass"
3133
+ };
3134
+ const size_t num_attrs = sizeof(unsetable_attrs) / sizeof(unsetable_attrs[0]);
3135
+ for (size_t i = 0; i < num_attrs; ++i) {
3136
+ if (strcmp(name_cstr, unsetable_attrs[i]) == 0) {
3137
+ PyErr_Format(PyExc_AttributeError, "attribute '%s' of 'private_attribute_module' objects is not writable", name_cstr);
3138
+ return -1;
3139
+ }
3140
+ }
3166
3141
  }
3167
- return PyObject_GenericSetAttr(cls, name, value);
3142
+ return PyModule_Type.tp_setattro(self, name, value);
3168
3143
  }
3169
3144
 
3170
3145
  static const char* prepare_and_postprocess_doc = R"(function for custom metaclass to create private attributes class.
@@ -3261,38 +3236,60 @@ static PyMethodDef PrivateModule_methods_def[] = {
3261
3236
  {NULL} // Sentinel
3262
3237
  };
3263
3238
 
3239
+ static PyObject*
3240
+ PrivateModule_getattro(PyObject* self, PyObject* name) noexcept
3241
+ {
3242
+ if (!PyUnicode_Check(name)) {
3243
+ PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
3244
+ return NULL;
3245
+ }
3246
+ const char* name_cstr = PyUnicode_AsUTF8(name);
3247
+ if (!name_cstr) {
3248
+ return NULL;
3249
+ }
3250
+ PrivateModule_GETATTRO_CASE(PrivateWrapProxy);
3251
+ PrivateModule_GETATTRO_CASE(PrivateAttrType);
3252
+ PrivateModule_GETATTRO_CASE(PrivateAttrBase);
3253
+ PrivateModule_GETATTRO_CASE(prepare);
3254
+ PrivateModule_GETATTRO_CASE(postprocess);
3255
+ PrivateModule_GETATTRO_CASE(register_metaclass);
3256
+ PrivateModule_GETATTRO_CASE(ensure_type);
3257
+ PrivateModule_GETATTRO_CASE(ensure_metaclass);
3258
+ return PyModule_Type.tp_getattro(self, name);
3259
+ }
3260
+
3264
3261
  static PyTypeObject PrivateModuleType = {
3265
3262
  PyVarObject_HEAD_INIT(NULL, 0)
3266
- "private_attribute_module", //tp_name
3267
- PyModule_Type.tp_basicsize + 8, //tp_basicsize size of module object + 8 bytes for basicsize to avoid changing attribute '__class__'
3268
- 0, //tp_itemsize
3269
- 0, //tp_dealloc
3270
- 0, //tp_print
3271
- 0, //tp_getattr
3272
- 0, //tp_setattr
3273
- 0, //tp_compare
3274
- 0, //tp_repr
3275
- 0, //tp_as_number
3276
- 0, //tp_as_sequence
3277
- 0, //tp_as_mapping
3278
- 0, //tp_hash
3279
- 0, //tp_call
3280
- 0, //tp_str
3281
- 0, //tp_getattro
3282
- (setattrofunc)PrivateModule_setattro, //tp_setattro
3283
- 0, //tp_as_buffer
3284
- Py_TPFLAGS_DEFAULT, //tp_flags
3285
- 0, //tp_doc
3286
- 0, //tp_traverse
3287
- 0, //tp_clear
3288
- 0, //tp_richcompare
3289
- 0, //tp_weaklistoffset
3290
- 0, //tp_iter
3291
- 0, //tp_iternext
3292
- PrivateModule_methods_def, //tp_methods
3293
- 0, //tp_members
3294
- PrivateModule_getsetters, //tp_getset
3295
- &PyModule_Type, //tp_base
3263
+ "private_attribute.private_attribute_module", //tp_name
3264
+ PyModule_Type.tp_basicsize + 8, //tp_basicsize size of module object + 8 bytes for basicsize to avoid changing attribute '__class__'
3265
+ 0, //tp_itemsize
3266
+ 0, //tp_dealloc
3267
+ 0, //tp_print
3268
+ 0, //tp_getattr
3269
+ 0, //tp_setattr
3270
+ 0, //tp_compare
3271
+ 0, //tp_repr
3272
+ 0, //tp_as_number
3273
+ 0, //tp_as_sequence
3274
+ 0, //tp_as_mapping
3275
+ 0, //tp_hash
3276
+ 0, //tp_call
3277
+ 0, //tp_str
3278
+ (getattrofunc)PrivateModule_getattro, //tp_getattro
3279
+ (setattrofunc)PrivateModule_setattro, //tp_setattro
3280
+ 0, //tp_as_buffer
3281
+ Py_TPFLAGS_DEFAULT, //tp_flags
3282
+ 0, //tp_doc
3283
+ 0, //tp_traverse
3284
+ 0, //tp_clear
3285
+ 0, //tp_richcompare
3286
+ 0, //tp_weaklistoffset
3287
+ 0, //tp_iter
3288
+ 0, //tp_iternext
3289
+ PrivateModule_methods_def, //tp_methods
3290
+ 0, //tp_members
3291
+ PrivateModule_getsetters, //tp_getset
3292
+ &PyModule_Type, //tp_base
3296
3293
  };
3297
3294
 
3298
3295
  static const char* module_doc = R"(
@@ -76,7 +76,7 @@ class PrivateWrapProxy:
76
76
  class PrivateAttrType(type):
77
77
  "metaclass for private attributes"
78
78
  def __new__(cls, name: str, bases: tuple,
79
- attrs: _PrivateAttrDict, /,
79
+ attrs: _PrivateAttrDict, /, *,
80
80
  private_func: Callable[[int, str], str]|None = None) -> PrivateAttrType: ...
81
81
 
82
82
  class PrivateAttrBase(metaclass=PrivateAttrType):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 1.4.10
3
+ Version: 2.0.0
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
@@ -247,6 +247,7 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
247
247
  - All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
248
248
  - Finally the attributes' names in `__private_attrs__` will be change to a tuple with two hash.
249
249
  - Finally the `_PrivateWrap` object will be recoveried to the original object.
250
+ - Don't use a decorator which will return the `_PrivateWrap` in `PrivateWrapProxy` which will raise `TypeError`.
250
251
  - One class defined in another class cannot use another class's private attribute.
251
252
  - One parent class defined an attribute which not in `__private_attrs__` or not a `PrivateAttrType` instance, the child class shouldn't contain the attribute in its `__private_attrs__`.
252
253
  - CPython may change "tp_getattro", "tp_setattro" and so on when you change the attribute "\_\_getattribute\_\_", "\_\_setattr\_\_" and so on. If you are fear about it, you can use `ensure_type` to reset those tp slots. For the other metaclasses, you can use `ensure_metaclass` to reset those tp slots. Also, don't set those methods on these classes in your code.
@@ -18,7 +18,7 @@ readme = open('README.md').read()
18
18
 
19
19
  setup(
20
20
  name='private_attribute_cpp',
21
- version='1.4.10',
21
+ version='2.0.0',
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.',