private-attribute-cpp 1.4.11__tar.gz → 2.0.1__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.11/private_attribute_cpp.egg-info → private_attribute_cpp-2.0.1}/PKG-INFO +1 -1
  2. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute.cpp +405 -396
  3. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1/private_attribute_cpp.egg-info}/PKG-INFO +1 -1
  4. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/setup.py +1 -1
  5. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/LICENSE +0 -0
  6. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/MANIFEST.in +0 -0
  7. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/README.md +0 -0
  8. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/picosha2.h +0 -0
  9. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute.pyi +0 -0
  10. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute_cpp.egg-info/SOURCES.txt +0 -0
  11. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
  12. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
  13. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/private_attribute_cpp.egg-info/top_level.txt +0 -0
  14. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/setup.cfg +0 -0
  15. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_class_private_attrs.py +0 -0
  16. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_private_abc.py +0 -0
  17. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_private_attrs.py +0 -0
  18. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_private_func.py +0 -0
  19. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_private_inheritance.py +0 -0
  20. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/tests/test_private_method.py +0 -0
  21. {private_attribute_cpp-1.4.11 → private_attribute_cpp-2.0.1}/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.11
3
+ Version: 2.0.1
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
@@ -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!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
@@ -325,12 +316,77 @@ public:
325
316
  type = value = traceback = nullptr;
326
317
  }
327
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
+
328
376
  private:
329
377
  PyObject* type = nullptr;
330
378
  PyObject* value = nullptr;
331
379
  PyObject* traceback = nullptr;
380
+ mutable std::string msg_;
332
381
  };
333
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
+
334
390
  static std::string
335
391
  custom_random_string(uintptr_t obj_id, const std::string& attr_name, PyObject* func)
336
392
  {
@@ -389,6 +445,10 @@ custom_random_string(uintptr_t obj_id, const std::string& attr_name, PyObject* f
389
445
  return result;
390
446
  }
391
447
 
448
+ #ifdef _MSC_VER
449
+ #pragma warning(pop)
450
+ #endif
451
+
392
452
  static void
393
453
  clear_obj(uintptr_t obj_id) noexcept
394
454
  {
@@ -897,7 +957,7 @@ static PyObject*
897
957
  PrivateWrap_doc(PyObject *obj, void* /*closure*/) noexcept
898
958
  {
899
959
  if (!obj) {
900
- return PyUnicode_FromString("PrivateWrap");
960
+ return PyUnicode_InternFromString("PrivateWrap");
901
961
  }
902
962
  PyObject* doc = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__doc__");
903
963
  if (!doc) {
@@ -911,26 +971,26 @@ static PyObject*
911
971
  PrivateWrap_module(PyObject *obj, void* /*closure*/) noexcept
912
972
  {
913
973
  if (!obj) {
914
- return PyUnicode_FromString("private_attribute");
974
+ return PyUnicode_InternFromString("private_attribute");
915
975
  }
916
976
  PyObject* module = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__module__");
917
977
  if (!module){
918
978
  PyErr_Clear();
919
- return PyUnicode_FromString("private_attribute");
979
+ return PyUnicode_InternFromString("private_attribute");
920
980
  }
921
981
  return module;
922
982
  }
923
983
 
924
984
  static PyObject*
925
- PrivateWarp_name(PyObject* obj, void* /*closure*/) noexcept
985
+ PrivateWrap_name(PyObject* obj, void* /*closure*/) noexcept
926
986
  {
927
987
  if (!obj) {
928
- return PyUnicode_FromString("_PrivateWrap");
988
+ return PyUnicode_InternFromString("_PrivateWrap");
929
989
  }
930
990
  PyObject* name = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__name__");
931
991
  if (!name) {
932
992
  PyErr_Clear();
933
- return PyUnicode_FromString("_PrivateWrap");
993
+ return PyUnicode_InternFromString("_PrivateWrap");
934
994
  }
935
995
  return name;
936
996
  }
@@ -939,12 +999,12 @@ static PyObject*
939
999
  PrivateWrap_qualname(PyObject* obj, void* /*closure*/) noexcept
940
1000
  {
941
1001
  if (!obj) {
942
- return PyUnicode_FromString("_PrivateWrap");
1002
+ return PyUnicode_InternFromString("_PrivateWrap");
943
1003
  }
944
1004
  PyObject* qualname = PyObject_GetAttrString(((PrivateWrapObject*)obj)->result, "__qualname__");
945
1005
  if (!qualname) {
946
1006
  PyErr_Clear();
947
- return PyUnicode_FromString("_PrivateWrap");
1007
+ return PyUnicode_InternFromString("_PrivateWrap");
948
1008
  }
949
1009
  return qualname;
950
1010
  }
@@ -979,32 +1039,76 @@ PrivateWrap_type_params(PyObject* obj, void* /*closure*/) noexcept
979
1039
  return type_params;
980
1040
  }
981
1041
 
982
- static const char* PrivateWrap_result_doc = "the final result of decorating";
983
1042
  static const char* PrivateWrap_funcs_doc = "the original functions";
984
1043
 
985
1044
  static PyGetSetDef PrivateWrap_getset[] = {
986
1045
  {"funcs", (getter)PrivateWrap_funcs, NULL, PrivateWrap_funcs_doc, NULL},
987
1046
  {"__doc__", (getter)PrivateWrap_doc, NULL, NULL, NULL},
988
1047
  {"__module__", (getter)PrivateWrap_module, NULL, NULL, NULL},
989
- {"__name__", (getter)PrivateWarp_name, NULL, NULL, NULL},
1048
+ {"__name__", (getter)PrivateWrap_name, NULL, NULL, NULL},
990
1049
  {"__qualname__", (getter)PrivateWrap_qualname, NULL, NULL, NULL},
991
1050
  {"__annotate__", (getter)PrivateWrap_annotate, NULL, NULL, NULL},
992
1051
  {"__type_params__", (getter)PrivateWrap_type_params, NULL, NULL, NULL},
993
1052
  {NULL}
994
1053
  };
995
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
+
996
1061
  static PyObject *
997
1062
  PrivateWrap_getattro(PyObject *obj, PyObject *name) noexcept
998
1063
  {
999
- PyObject *res = PyObject_GenericGetAttr(obj, name);
1000
- if (res != NULL) {
1001
- 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;
1002
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
+ }
1003
1089
 
1004
- 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
+ }
1005
1101
 
1006
- PrivateWrapObject *self = (PrivateWrapObject *)obj;
1007
- 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
+ }
1008
1112
  }
1009
1113
 
1010
1114
  static PyTypeObject PrivateWrapType = {
@@ -1028,7 +1132,7 @@ static PyTypeObject PrivateWrapType = {
1028
1132
  0, // tp_setattro
1029
1133
  0, // tp_as_buffer
1030
1134
  Py_TPFLAGS_DEFAULT, // tp_flags
1031
- "_PrivateWrap", // tp_doc
1135
+ "private_attribute._PrivateWrap", // tp_doc
1032
1136
  0, // tp_traverse
1033
1137
  0, // tp_clear
1034
1138
  0, // tp_richcompare
@@ -1038,6 +1142,10 @@ static PyTypeObject PrivateWrapType = {
1038
1142
  0, // tp_methods
1039
1143
  PrivateWrap_members, // tp_members
1040
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
1041
1149
  };
1042
1150
 
1043
1151
  static PrivateWrapObject*
@@ -1814,12 +1922,17 @@ need_analyse_type(PyObject* type) noexcept
1814
1922
  }
1815
1923
  #if PY_VERSION_HEX < 0x030D0000
1816
1924
  PyObject* metaclass = PyWeakref_GET_OBJECT(metaclassref);
1925
+ if (type == metaclass) continue;
1817
1926
  if (PyObject_IsInstance(type, metaclass)) {
1818
1927
  return true;
1819
1928
  }
1820
1929
  #else
1821
1930
  PyObject* metaclass;
1822
1931
  if (PyWeakref_GetRef(metaclassref, &metaclass) == 1) {
1932
+ if (type == metaclass) {
1933
+ Py_DECREF(metaclass);
1934
+ continue;
1935
+ }
1823
1936
  if (PyObject_IsInstance(type, metaclass)) {
1824
1937
  Py_DECREF(metaclass);
1825
1938
  return true;
@@ -1831,157 +1944,6 @@ need_analyse_type(PyObject* type) noexcept
1831
1944
  return false;
1832
1945
  }
1833
1946
 
1834
- static void
1835
- get_getattribute_and_getattr(PyTypeObject* cls, PyObject** getattribute, PyObject** getattr) noexcept
1836
- {
1837
- bool has_getattribute = false;
1838
- bool has_getattr = false;
1839
- if (PyDict_ContainsString(cls->tp_dict, "__getattribute__")) {
1840
- *getattribute = PyDict_GetItemString(cls->tp_dict, "__getattribute__");
1841
- has_getattribute = true;
1842
- }
1843
- if (PyDict_ContainsString(cls->tp_dict, "__getattr__")) {
1844
- *getattr = PyDict_GetItemString(cls->tp_dict, "__getattr__");
1845
- has_getattr = true;
1846
- }
1847
- if (has_getattribute && has_getattr) {
1848
- return;
1849
- }
1850
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1851
- if (!has_getattribute && PyDict_ContainsString(base->tp_dict, "__getattribute__")) {
1852
- *getattribute = PyDict_GetItemString(base->tp_dict, "__getattribute__");
1853
- has_getattribute = true;
1854
- }
1855
- if (!has_getattr && PyDict_ContainsString(base->tp_dict, "__getattr__")) {
1856
- *getattr = PyDict_GetItemString(base->tp_dict, "__getattr__");
1857
- has_getattr = true;
1858
- }
1859
- if (has_getattribute && has_getattr) {
1860
- return;
1861
- }
1862
- }
1863
- }
1864
-
1865
- static void
1866
- get_setattr_and_delattr(PyTypeObject* cls, PyObject** setattr, PyObject** delattr) noexcept
1867
- {
1868
- bool has_setattr = false;
1869
- bool has_delattr = false;
1870
- if (PyDict_ContainsString(cls->tp_dict, "__setattr__")) {
1871
- *setattr = PyDict_GetItemString(cls->tp_dict, "__setattr__");
1872
- has_setattr = true;
1873
- }
1874
- if (PyDict_ContainsString(cls->tp_dict, "__delattr__")) {
1875
- *delattr = PyDict_GetItemString(cls->tp_dict, "__delattr__");
1876
- has_delattr = true;
1877
- }
1878
- if (has_setattr && has_delattr) {
1879
- return;
1880
- }
1881
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1882
- if (!has_setattr && PyDict_ContainsString(base->tp_dict, "__setattr__")) {
1883
- *setattr = PyDict_GetItemString(base->tp_dict, "__setattr__");
1884
- has_setattr = true;
1885
- }
1886
- if (!has_delattr && PyDict_ContainsString(base->tp_dict, "__delattr__")) {
1887
- *delattr = PyDict_GetItemString(base->tp_dict, "__delattr__");
1888
- has_delattr = true;
1889
- }
1890
- if (has_setattr && has_delattr) {
1891
- return;
1892
- }
1893
- }
1894
- }
1895
-
1896
- static void
1897
- get_del(PyTypeObject* cls, PyObject** del) noexcept
1898
- {
1899
- if (PyDict_ContainsString(cls->tp_dict, "__del__")) {
1900
- *del = PyDict_GetItemString(cls->tp_dict, "__del__");
1901
- return;
1902
- }
1903
- for (PyTypeObject* base = cls->tp_base; base != NULL && base != &PyBaseObject_Type; base = base->tp_base) {
1904
- if (PyDict_ContainsString(base->tp_dict, "__del__")) {
1905
- *del = PyDict_GetItemString(base->tp_dict, "__del__");
1906
- return;
1907
- }
1908
- }
1909
- }
1910
-
1911
- static PyObject*
1912
- python_original_tp_getattro(PyObject* self, PyObject* name) noexcept
1913
- {
1914
- PyObject* getattriute = NULL;
1915
- PyObject* getattr = NULL;
1916
- get_getattribute_and_getattr((PyTypeObject*)self->ob_type, &getattriute, &getattr);
1917
- PyObject* res = NULL;
1918
- if (getattriute) {
1919
- res = PyObject_CallFunctionObjArgs(getattriute, self, name, NULL);
1920
- } else {
1921
- res = PyObject_GenericGetAttr(self, name);
1922
- }
1923
- if (!res && getattr) {
1924
- // check if the exception is AttributeError
1925
- if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
1926
- return NULL;
1927
- }
1928
- PyErr_Clear();
1929
- res = PyObject_CallFunctionObjArgs(getattr, self, name, NULL);
1930
- }
1931
- return res;
1932
- }
1933
-
1934
- static int
1935
- python_original_tp_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept
1936
- {
1937
- PyObject* setattr = NULL;
1938
- PyObject* delattr = NULL;
1939
- get_setattr_and_delattr((PyTypeObject*)self->ob_type, &setattr, &delattr);
1940
- if (value && setattr) {
1941
- PyObject* res = PyObject_CallFunctionObjArgs(setattr, self, name, value, NULL);
1942
- if (!res) {
1943
- return -1;
1944
- }
1945
- Py_DECREF(res);
1946
- return 0;
1947
- } else if (!value && delattr) {
1948
- PyObject* res = PyObject_CallFunctionObjArgs(delattr, self, name, NULL);
1949
- if (!res) {
1950
- return -1;
1951
- }
1952
- Py_DECREF(res);
1953
- return 0;
1954
- }
1955
- return PyObject_GenericSetAttr(self, name, value);
1956
- }
1957
-
1958
- static void
1959
- python_original_tp_finalize(PyObject* self) noexcept
1960
- {
1961
- PyObject* del = NULL;
1962
- get_del((PyTypeObject*)self->ob_type, &del);
1963
- if (del) {
1964
- PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL;
1965
- PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
1966
- PyObject *result = PyObject_CallFunctionObjArgs(del, self, NULL);
1967
- if (result == NULL) {
1968
- // python < 3.13 use PyErr_WriteUnraisable, python >= 3.13 use PyErr_FormatUnraisable
1969
- # if PY_VERSION_HEX >= 0x030D0000
1970
- PyErr_FormatUnraisable("Exception ignored while "
1971
- "calling deallocator %R", del);
1972
- # else
1973
- PyErr_WriteUnraisable(del);
1974
- # endif
1975
- }
1976
- else {
1977
- Py_DECREF(result);
1978
- }
1979
- if (exc_type != NULL) {
1980
- PyErr_Restore(exc_type, exc_value, exc_tb);
1981
- }
1982
- }
1983
- }
1984
-
1985
1947
  static bool
1986
1948
  PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationData& data) noexcept
1987
1949
  {
@@ -2223,67 +2185,55 @@ ensure_tp(PyTypeObject* type_instance) noexcept
2223
2185
  uintptr_t type_id = (uintptr_t)(type_instance);
2224
2186
  {
2225
2187
  if (type_instance->tp_getattro != PrivateAttr_tp_getattro) {
2226
- if (!type_instance->tp_getattro) {
2188
+ if (!type_instance->tp_getattro && ::AllData::all_type_getattro.find(type_id) == ::AllData::all_type_getattro.end()) {
2227
2189
  ::AllData::all_type_getattro[type_id] = get_need_tp_getattro(type_instance);
2228
2190
  } else {
2229
2191
  ::AllData::all_type_getattro[type_id] = type_instance->tp_getattro;
2230
2192
  }
2231
2193
  type_instance->tp_getattro = PrivateAttr_tp_getattro;
2232
- } else {
2233
- if (PyDict_ContainsString(type_instance->tp_dict, "__getattribute__") || PyDict_ContainsString(type_instance->tp_dict, "__getattr__")) {
2234
- ::AllData::all_type_getattro[type_id] = AllSlots::original_getattro;
2235
- } else {
2236
- PyTypeObject* base = type_instance->tp_base;
2237
- uintptr_t base_id = (uintptr_t)(base);
2238
- if (::AllData::all_type_getattro.find(base_id) != ::AllData::all_type_getattro.end()) {
2239
- ::AllData::all_type_getattro[type_id] = ::AllData::all_type_getattro[base_id];
2240
- } else if (base && base->tp_getattro && base->tp_getattro != PrivateAttr_tp_getattro) {
2241
- ::AllData::all_type_getattro[type_id] = base->tp_getattro;
2242
- }
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;
2243
2201
  }
2244
2202
  }
2245
2203
  }
2246
2204
  {
2247
2205
  if (type_instance->tp_setattro != PrivateAttr_tp_setattro) {
2248
- if (!type_instance->tp_setattro) {
2206
+ if (!type_instance->tp_setattro && ::AllData::all_type_setattro.find(type_id) == ::AllData::all_type_setattro.end()) {
2249
2207
  ::AllData::all_type_setattro[type_id] = get_need_tp_setattro(type_instance);
2250
2208
  } else {
2251
2209
  ::AllData::all_type_setattro[type_id] = type_instance->tp_setattro;
2252
2210
  }
2253
2211
  type_instance->tp_setattro = PrivateAttr_tp_setattro;
2254
- } else {
2255
- if (PyDict_ContainsString(type_instance->tp_dict, "__setattr__") || PyDict_ContainsString(type_instance->tp_dict, "__delattr__")) {
2256
- ::AllData::all_type_setattro[type_id] = AllSlots::original_setattro;
2257
- } else {
2258
- PyTypeObject* base = type_instance->tp_base;
2259
- uintptr_t base_id = (uintptr_t)(base);
2260
- if (::AllData::all_type_setattro.find(base_id) != ::AllData::all_type_setattro.end()) {
2261
- ::AllData::all_type_setattro[type_id] = ::AllData::all_type_setattro[base_id];
2262
- } else if (base && base->tp_setattro && base->tp_setattro != PrivateAttr_tp_setattro) {
2263
- ::AllData::all_type_setattro[type_id] = base->tp_setattro;
2264
- }
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;
2265
2219
  }
2266
2220
  }
2267
2221
  }
2268
2222
  {
2269
2223
  if (type_instance->tp_finalize != PrivateAttr_tp_finalize) {
2270
- if (!type_instance->tp_finalize) {
2224
+ if (!type_instance->tp_finalize && ::AllData::all_type_finalize.find(type_id) == ::AllData::all_type_finalize.end()) {
2271
2225
  ::AllData::all_type_finalize[type_id] = get_need_tp_finalize(type_instance);
2272
2226
  } else {
2273
2227
  ::AllData::all_type_finalize[type_id] = type_instance->tp_finalize;
2274
2228
  }
2275
2229
  type_instance->tp_finalize = PrivateAttr_tp_finalize;
2276
- } else {
2277
- if (PyDict_ContainsString(type_instance->tp_dict, "__del__")) {
2278
- ::AllData::all_type_finalize[type_id] = AllSlots::original_finalize;
2279
- } else {
2280
- PyTypeObject* base = type_instance->tp_base;
2281
- uintptr_t base_id = (uintptr_t)(base);
2282
- if (::AllData::all_type_finalize.find(base_id) != ::AllData::all_type_finalize.end()) {
2283
- ::AllData::all_type_finalize[type_id] = ::AllData::all_type_finalize[base_id];
2284
- } else if (base && base->tp_finalize && base->tp_finalize != PrivateAttr_tp_finalize) {
2285
- ::AllData::all_type_finalize[type_id] = base->tp_finalize;
2286
- }
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;
2287
2237
  }
2288
2238
  }
2289
2239
  }
@@ -2292,40 +2242,16 @@ ensure_tp(PyTypeObject* type_instance) noexcept
2292
2242
  static void
2293
2243
  ensure_subclass_tp(PyTypeObject* type_instance) noexcept
2294
2244
  {
2295
- // type.__subclasses__
2296
- PyObject* type_subclasses = (PyObject*)type_instance->tp_subclasses;
2297
- if (!type_subclasses) {
2298
- return;
2299
- }
2300
- if (!PyDict_Check(type_subclasses)) {
2301
- return;
2302
- }
2303
- // check all values in the dict. It should be a weakref to a type. If not, continue.
2304
- PyObject* key, *value;
2305
- Py_ssize_t pos = 0;
2306
- while (PyDict_Next(type_subclasses, &pos, &key, &value)) {
2307
- if (!value || !PyWeakref_CheckRef(value)) {
2308
- continue;
2309
- }
2310
- // python<3.13 use PyWeakref_GetObject, python>=3.13 use PyWeakref_GetRef
2311
- #if PY_VERSION_HEX < 0x030D0000
2312
- PyObject* subclass = PyWeakref_GetObject(value);
2313
- if (!subclass || !PyType_Check(subclass)) {
2314
- continue;
2315
- }
2316
- ensure_tp((PyTypeObject*)subclass);
2317
- #else
2318
- PyObject* subclass;
2319
- if (PyWeakref_GetRef(value, (PyObject**)&subclass) != 1) {
2320
- continue;
2321
- }
2322
- if (!subclass || !PyType_Check(subclass)) {
2323
- continue;
2324
- }
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;
2325
2252
  ensure_tp((PyTypeObject*)subclass);
2326
- Py_XDECREF(subclass);
2327
- #endif
2328
2253
  }
2254
+ Py_DECREF(subclasses);
2329
2255
  }
2330
2256
 
2331
2257
  static bool
@@ -2348,15 +2274,6 @@ PrivateAttrType_postprocess(PyObject* new_type, PrivateAttrCreationData& data) n
2348
2274
  }
2349
2275
 
2350
2276
  ensure_tp(type_instance);
2351
- if (PyDict_ContainsString(type_instance->tp_dict, "__getattribute__") || PyDict_ContainsString(type_instance->tp_dict, "__getattr__")) {
2352
- ::AllData::all_type_getattro[type_id] = AllSlots::original_getattro;
2353
- }
2354
- if (PyDict_ContainsString(type_instance->tp_dict, "__setattr__") || PyDict_ContainsString(type_instance->tp_dict, "__delattr__")) {
2355
- ::AllData::all_type_setattro[type_id] = AllSlots::original_setattro;
2356
- }
2357
- if (PyDict_ContainsString(type_instance->tp_dict, "__del__")) {
2358
- ::AllData::all_type_finalize[type_id] = AllSlots::original_finalize;
2359
- }
2360
2277
 
2361
2278
  ::AllData::type_attr_dict[type_id] = {};
2362
2279
  ::AllData::all_type_attr_set[type_id] = data.private_attrs_set;
@@ -2510,21 +2427,95 @@ PrivateAttrType_getattr(PyObject* cls, PyObject* name) noexcept
2510
2427
  return result;
2511
2428
  }
2512
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
+
2513
2481
  static int
2514
2482
  init_all_slots() noexcept
2515
2483
  {
2516
- AllSlots::original_getattro = python_original_tp_getattro;
2517
- AllSlots::original_setattro = python_original_tp_setattro;
2518
- 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
+ if (((PyTypeObject*)metaclass)->tp_setattro == original_type_tp_setattro) {
2509
+ ((PyTypeObject*)metaclass)->tp_setattro = new_type_tp_setattro;
2510
+ }
2511
+ }
2512
+ Py_DECREF(all_metaclasses);
2519
2513
  return 0;
2520
2514
  }
2521
2515
 
2522
2516
  static getattrofunc
2523
2517
  get_need_tp_getattro(PyTypeObject* cls) noexcept
2524
2518
  {
2525
- if (PyDict_ContainsString(cls->tp_dict, "__getattribute__") || PyDict_ContainsString(cls->tp_dict, "__getattr__")) {
2526
- return AllSlots::original_getattro;
2527
- }
2528
2519
  PyTypeObject* base = cls->tp_base;
2529
2520
  while (base) {
2530
2521
  if (base->tp_getattro != PrivateAttr_tp_getattro) {
@@ -2544,9 +2535,6 @@ get_need_tp_getattro(PyTypeObject* cls) noexcept
2544
2535
  static setattrofunc
2545
2536
  get_need_tp_setattro(PyTypeObject* cls) noexcept
2546
2537
  {
2547
- if (PyDict_ContainsString(cls->tp_dict, "__setattr__") || PyDict_ContainsString(cls->tp_dict, "__delattr__")) {
2548
- return AllSlots::original_setattro;
2549
- }
2550
2538
  PyTypeObject* base = cls->tp_base;
2551
2539
  while (base) {
2552
2540
  if (base->tp_setattro != PrivateAttr_tp_setattro) {
@@ -2566,9 +2554,6 @@ get_need_tp_setattro(PyTypeObject* cls) noexcept
2566
2554
  static destructor
2567
2555
  get_need_tp_finalize(PyTypeObject* cls) noexcept
2568
2556
  {
2569
- if (PyDict_ContainsString(cls->tp_dict, "__del__")) {
2570
- return AllSlots::original_finalize;
2571
- }
2572
2557
  PyTypeObject* base = cls->tp_base;
2573
2558
  while (base) {
2574
2559
  if (base->tp_finalize != PrivateAttr_tp_finalize) {
@@ -2585,64 +2570,6 @@ get_need_tp_finalize(PyTypeObject* cls) noexcept
2585
2570
  return NULL;
2586
2571
  }
2587
2572
 
2588
- static void
2589
- type_change_all_slots(PyTypeObject* cls, const char* name) noexcept
2590
- {
2591
- if (strcmp(name, "__getattribute__") == 0 || strcmp(name, "__getattr__") == 0) {
2592
- cls->tp_getattro = PrivateAttr_tp_getattro;
2593
- ::AllData::all_type_getattro[(uintptr_t)cls] = get_need_tp_getattro(cls);
2594
- } else if (strcmp(name, "__setattr__") == 0 || strcmp(name, "__delattr__") == 0) {
2595
- cls->tp_setattro = PrivateAttr_tp_setattro;
2596
- ::AllData::all_type_setattro[(uintptr_t)cls] = get_need_tp_setattro(cls);
2597
- } else if (strcmp(name, "__del__") == 0) {
2598
- cls->tp_finalize = PrivateAttr_tp_finalize;
2599
- ::AllData::all_type_finalize[(uintptr_t)cls] = get_need_tp_finalize(cls);
2600
- }
2601
- }
2602
-
2603
- static void
2604
- subtype_change_all_slots(PyTypeObject* cls, const char* name) noexcept
2605
- {
2606
- // tp_subclasses: {id(type): weakref.ref(type)}
2607
- PyObject* tp_subclasses = (PyObject*)cls->tp_subclasses;
2608
- if (!tp_subclasses) {
2609
- return;
2610
- }
2611
- if (!PyDict_Check(tp_subclasses)) {
2612
- return;
2613
- }
2614
- PyObject* key;
2615
- PyObject* value;
2616
- Py_ssize_t pos = 0;
2617
- while (PyDict_Next(tp_subclasses, &pos, &key, &value)) {
2618
- if (!PyWeakref_Check(value)) {
2619
- continue;
2620
- }
2621
- #if PY_VERSION_HEX < 0x030D0000
2622
- PyObject* obj = PyWeakref_GET_OBJECT(value);
2623
- if (!obj) {
2624
- continue;
2625
- }
2626
- #else
2627
- PyObject* obj;
2628
- if (PyWeakref_GetRef(value, &obj) != 1) {
2629
- continue;
2630
- }
2631
- if (!obj) {
2632
- continue;
2633
- }
2634
- #endif
2635
- if (!PyType_Check(obj)) {
2636
- continue;
2637
- }
2638
- PyTypeObject* sub_cls = (PyTypeObject*)obj;
2639
- type_change_all_slots(sub_cls, name);
2640
- #if PY_VERSION_HEX >= 0x030D0000
2641
- Py_XDECREF(obj);
2642
- #endif
2643
- }
2644
- }
2645
-
2646
2573
  static int
2647
2574
  PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value) noexcept
2648
2575
  {
@@ -2669,42 +2596,12 @@ PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value) noexcept
2669
2596
  return type_setattr(cls, name_str, value);
2670
2597
  }
2671
2598
  Py_XDECREF(now_code);
2672
- // if name in __getattribute__, __setattr__, __delattr__, __del__, just set to tp_dict
2673
- if (strcmp(name_str.c_str(), "__getattribute__") == 0 ||
2674
- strcmp(name_str.c_str(), "__getattr__") == 0 ||
2675
- strcmp(name_str.c_str(), "__setattr__") == 0 ||
2676
- strcmp(name_str.c_str(), "__delattr__") == 0 ||
2677
- strcmp(name_str.c_str(), "__del__") == 0) {
2678
- PyObject* tp_dict = ((PyTypeObject*)cls)->tp_dict;
2679
- if (!tp_dict) {
2680
- PyErr_SetString(PyExc_TypeError, "type has no tp_dict");
2681
- return -1;
2682
- }
2683
- if (!value) {
2684
- if (PyDict_DelItem(tp_dict, name) < 0) {
2685
- PyErr_Format(PyExc_AttributeError, "type object '%.100s' has no attribute '%U'", ((PyTypeObject*)cls)->tp_name, name);
2686
- return -1;
2687
- }
2688
- type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2689
- subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2690
- return 0;
2691
- }
2692
- if (PyDict_SetItem(tp_dict, name, value) < 0) {
2693
- return -1;
2694
- }
2695
- type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2696
- subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2697
- return 0;
2698
- }
2699
2599
  PyTypeObject* base = Py_TYPE(cls)->tp_base;
2700
2600
  while (base && base->tp_base && base->tp_setattro == PrivateAttrType_setattr) {
2701
2601
  base = base->tp_base;
2702
2602
  }
2703
2603
  if (!base) {
2704
- int result = PyType_Type.tp_setattro(cls, name, value);
2705
- ensure_tp((PyTypeObject*)cls);
2706
- ensure_subclass_tp((PyTypeObject*)cls);
2707
- return result;
2604
+ return new_type_tp_setattro(cls, name, value);
2708
2605
  }
2709
2606
  int result = base->tp_setattro(cls, name, value);
2710
2607
  ensure_tp((PyTypeObject*)cls);
@@ -2790,7 +2687,7 @@ static const char* PrivateAttrBase_doc = "The class to help to create private at
2790
2687
  static PyObject*
2791
2688
  create_private_attr_base_simple(void) noexcept
2792
2689
  {
2793
- PyObject* name = PyUnicode_FromString("PrivateAttrBase");
2690
+ PyObject* name = PyUnicode_InternFromString("PrivateAttrBase");
2794
2691
  if (!name) return NULL;
2795
2692
  PyObject* bases = PyTuple_New(0);
2796
2693
  if (!bases) {
@@ -2812,8 +2709,8 @@ create_private_attr_base_simple(void) noexcept
2812
2709
  }
2813
2710
  PyDict_SetItemString(dict, "__private_attrs__", private_attrs);
2814
2711
  PyDict_SetItemString(dict, "__slots__", private_attrs);
2815
- PyDict_SetItemString(dict, "__doc__", PyUnicode_FromString(PrivateAttrBase_doc));
2816
- PyDict_SetItemString(dict, "__module__", PyUnicode_FromString("private_attribute"));
2712
+ PyDict_SetItemString(dict, "__doc__", PyUnicode_InternFromString(PrivateAttrBase_doc));
2713
+ PyDict_SetItemString(dict, "__module__", PyUnicode_InternFromString("private_attribute"));
2817
2714
  PyObject *args = PyTuple_Pack(3, name, bases, dict);
2818
2715
  PyObject* base_type;
2819
2716
  if (args) {
@@ -2905,7 +2802,7 @@ PrivateTempObject_dealloc(PyObject* self) noexcept
2905
2802
 
2906
2803
  static PyTypeObject PrivateTempType = {
2907
2804
  PyVarObject_HEAD_INIT(NULL, 0)
2908
- "private_temp", // tp_name
2805
+ "private_attribute._PrivateTemp", // tp_name
2909
2806
  sizeof(PrivateTempObject), // tp_basicsize
2910
2807
  0, //tp_itemsize
2911
2808
  PrivateTempObject_dealloc, //tp_dealloc
@@ -3021,8 +2918,40 @@ static PyMethodDef weakref_callback_def = {
3021
2918
  NULL
3022
2919
  };
3023
2920
 
2921
+ static bool
2922
+ is_class_of_registed_type(PyObject* metaclass) noexcept
2923
+ {
2924
+ std::shared_lock lock(::AllData::all_register_new_metaclass_mutex);
2925
+ for (auto& [id, instanceref]: ::AllData::all_register_type_weak_ref){
2926
+ if (!PyWeakref_CheckRef(instanceref)) {
2927
+ continue;
2928
+ }
2929
+ #if PY_VERSION_HEX < 0x030D0000
2930
+ PyObject* instance = PyWeakref_GET_OBJECT(instanceref);
2931
+ if (instance == metaclass) continue;
2932
+ if (PyObject_IsInstance(instance, metaclass)) {
2933
+ return true;
2934
+ }
2935
+ #else
2936
+ PyObject* instance;
2937
+ if (PyWeakref_GetRef(instanceref, &instance) == 1) {
2938
+ if (instance == metaclass) {
2939
+ Py_DECREF(instance);
2940
+ continue;
2941
+ }
2942
+ if (PyObject_IsInstance(instance, metaclass)) {
2943
+ Py_DECREF(instance);
2944
+ return true;
2945
+ }
2946
+ Py_DECREF(instance);
2947
+ }
2948
+ #endif
2949
+ }
2950
+ return false;
2951
+ }
2952
+
3024
2953
  static PyObject*
3025
- register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
2954
+ register_metaclass_head(PyObject* metaclass) noexcept
3026
2955
  {
3027
2956
  if (!PyType_Check(metaclass)) {
3028
2957
  PyErr_SetString(PyExc_TypeError, "metaclass must be a type");
@@ -3032,6 +2961,18 @@ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3032
2961
  PyErr_SetString(PyExc_TypeError, "metaclass must be a metatype");
3033
2962
  return NULL;
3034
2963
  }
2964
+ if (metaclass == (PyObject*)&PyType_Type) {
2965
+ PyErr_SetString(PyExc_ValueError, "cannot register type itself");
2966
+ return NULL;
2967
+ }
2968
+ if (need_analyse_type(metaclass)) {
2969
+ PyErr_SetString(PyExc_ValueError, "cannot register type that is the instance of 'PrivateAttrType' or registed type");
2970
+ return NULL;
2971
+ }
2972
+ if (is_class_of_registed_type(metaclass)) {
2973
+ PyErr_SetString(PyExc_ValueError, "cannot register type that is the class of registed type");
2974
+ return NULL;
2975
+ }
3035
2976
  uintptr_t id = (uintptr_t)metaclass;
3036
2977
 
3037
2978
  std::unique_lock lock(::AllData::all_register_new_metaclass_mutex);
@@ -3062,6 +3003,27 @@ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3062
3003
  Py_RETURN_NONE;
3063
3004
  }
3064
3005
 
3006
+ static PyObject*
3007
+ register_metaclass(PyObject* /*self*/, PyObject* metaclass) noexcept
3008
+ {
3009
+ if (!register_metaclass_head(metaclass)) return NULL;
3010
+ PyObject* subclasses = PyObject_CallMethod((PyObject*)&PyType_Type, "__subclasses__", "O", metaclass);
3011
+ if (!subclasses) {
3012
+ PyErr_Clear();
3013
+ Py_RETURN_NONE;
3014
+ }
3015
+ Py_ssize_t list_len = PyList_GET_SIZE(subclasses);
3016
+ for (Py_ssize_t i = 0; i < list_len; i++) {
3017
+ PyObject* subclass = PyList_GET_ITEM(subclasses, i);
3018
+ if (!register_metaclass(0, subclass)) {
3019
+ Py_DECREF(subclasses);
3020
+ return NULL;
3021
+ }
3022
+ }
3023
+ Py_DECREF(subclasses);
3024
+ Py_RETURN_NONE;
3025
+ }
3026
+
3065
3027
  static PyObject*
3066
3028
  ensure_type_tp(PyObject* /*self*/, PyObject* type) noexcept
3067
3029
  {
@@ -3144,8 +3106,16 @@ PrivateModule_dir(PyObject* self, PyObject* /*args*/) noexcept
3144
3106
  return result;
3145
3107
  }
3146
3108
 
3109
+ #define PrivateModule_GETATTRO_CASE(name) \
3110
+ do {\
3111
+ if (strcmp(name_cstr, #name) == 0) {\
3112
+ PyObject* attr = PrivateModule_get_##name(NULL, NULL);\
3113
+ return attr;\
3114
+ }\
3115
+ } while (0)
3116
+
3147
3117
  static int
3148
- PrivateModule_setattro(PyObject* cls, PyObject* name, PyObject* value) noexcept
3118
+ PrivateModule_setattro(PyObject* self, PyObject* name, PyObject* value) noexcept
3149
3119
  {
3150
3120
  // if name is "__class__" it do nothing and return success
3151
3121
  if (PyUnicode_Check(name)) {
@@ -3153,8 +3123,25 @@ PrivateModule_setattro(PyObject* cls, PyObject* name, PyObject* value) noexcept
3153
3123
  if (name_cstr && strcmp(name_cstr, "__class__") == 0) {
3154
3124
  return 0;
3155
3125
  }
3126
+ static const char* unsetable_attrs[] = {
3127
+ "PrivateWrapProxy",
3128
+ "PrivateAttrType",
3129
+ "PrivateAttrBase",
3130
+ "prepare",
3131
+ "postprocess",
3132
+ "register_metaclass",
3133
+ "ensure_type",
3134
+ "ensure_metaclass"
3135
+ };
3136
+ const size_t num_attrs = sizeof(unsetable_attrs) / sizeof(unsetable_attrs[0]);
3137
+ for (size_t i = 0; i < num_attrs; ++i) {
3138
+ if (strcmp(name_cstr, unsetable_attrs[i]) == 0) {
3139
+ PyErr_Format(PyExc_AttributeError, "attribute '%s' of 'private_attribute_module' objects is not writable", name_cstr);
3140
+ return -1;
3141
+ }
3142
+ }
3156
3143
  }
3157
- return PyObject_GenericSetAttr(cls, name, value);
3144
+ return PyModule_Type.tp_setattro(self, name, value);
3158
3145
  }
3159
3146
 
3160
3147
  static const char* prepare_and_postprocess_doc = R"(function for custom metaclass to create private attributes class.
@@ -3251,44 +3238,66 @@ static PyMethodDef PrivateModule_methods_def[] = {
3251
3238
  {NULL} // Sentinel
3252
3239
  };
3253
3240
 
3241
+ static PyObject*
3242
+ PrivateModule_getattro(PyObject* self, PyObject* name) noexcept
3243
+ {
3244
+ if (!PyUnicode_Check(name)) {
3245
+ PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
3246
+ return NULL;
3247
+ }
3248
+ const char* name_cstr = PyUnicode_AsUTF8(name);
3249
+ if (!name_cstr) {
3250
+ return NULL;
3251
+ }
3252
+ PrivateModule_GETATTRO_CASE(PrivateWrapProxy);
3253
+ PrivateModule_GETATTRO_CASE(PrivateAttrType);
3254
+ PrivateModule_GETATTRO_CASE(PrivateAttrBase);
3255
+ PrivateModule_GETATTRO_CASE(prepare);
3256
+ PrivateModule_GETATTRO_CASE(postprocess);
3257
+ PrivateModule_GETATTRO_CASE(register_metaclass);
3258
+ PrivateModule_GETATTRO_CASE(ensure_type);
3259
+ PrivateModule_GETATTRO_CASE(ensure_metaclass);
3260
+ return PyModule_Type.tp_getattro(self, name);
3261
+ }
3262
+
3254
3263
  static PyTypeObject PrivateModuleType = {
3255
3264
  PyVarObject_HEAD_INIT(NULL, 0)
3256
- "private_attribute_module", //tp_name
3257
- PyModule_Type.tp_basicsize + 8, //tp_basicsize size of module object + 8 bytes for basicsize to avoid changing attribute '__class__'
3258
- 0, //tp_itemsize
3259
- 0, //tp_dealloc
3260
- 0, //tp_print
3261
- 0, //tp_getattr
3262
- 0, //tp_setattr
3263
- 0, //tp_compare
3264
- 0, //tp_repr
3265
- 0, //tp_as_number
3266
- 0, //tp_as_sequence
3267
- 0, //tp_as_mapping
3268
- 0, //tp_hash
3269
- 0, //tp_call
3270
- 0, //tp_str
3271
- 0, //tp_getattro
3272
- (setattrofunc)PrivateModule_setattro, //tp_setattro
3273
- 0, //tp_as_buffer
3274
- Py_TPFLAGS_DEFAULT, //tp_flags
3275
- 0, //tp_doc
3276
- 0, //tp_traverse
3277
- 0, //tp_clear
3278
- 0, //tp_richcompare
3279
- 0, //tp_weaklistoffset
3280
- 0, //tp_iter
3281
- 0, //tp_iternext
3282
- PrivateModule_methods_def, //tp_methods
3283
- 0, //tp_members
3284
- PrivateModule_getsetters, //tp_getset
3285
- &PyModule_Type, //tp_base
3265
+ "private_attribute.private_attribute_module", //tp_name
3266
+ PyModule_Type.tp_basicsize + 8, //tp_basicsize size of module object + 8 bytes for basicsize to avoid changing attribute '__class__'
3267
+ 0, //tp_itemsize
3268
+ 0, //tp_dealloc
3269
+ 0, //tp_print
3270
+ 0, //tp_getattr
3271
+ 0, //tp_setattr
3272
+ 0, //tp_compare
3273
+ 0, //tp_repr
3274
+ 0, //tp_as_number
3275
+ 0, //tp_as_sequence
3276
+ 0, //tp_as_mapping
3277
+ 0, //tp_hash
3278
+ 0, //tp_call
3279
+ 0, //tp_str
3280
+ (getattrofunc)PrivateModule_getattro, //tp_getattro
3281
+ (setattrofunc)PrivateModule_setattro, //tp_setattro
3282
+ 0, //tp_as_buffer
3283
+ Py_TPFLAGS_DEFAULT, //tp_flags
3284
+ 0, //tp_doc
3285
+ 0, //tp_traverse
3286
+ 0, //tp_clear
3287
+ 0, //tp_richcompare
3288
+ 0, //tp_weaklistoffset
3289
+ 0, //tp_iter
3290
+ 0, //tp_iternext
3291
+ PrivateModule_methods_def, //tp_methods
3292
+ 0, //tp_members
3293
+ PrivateModule_getsetters, //tp_getset
3294
+ &PyModule_Type, //tp_base
3286
3295
  };
3287
3296
 
3288
3297
  static const char* module_doc = R"(
3289
3298
  A module that provides a metaclass for creating classes with private attributes.
3290
- Private attributes are defined in the `__private_attrs__` sequence and are only
3291
- You can use the `PrivateAttrBase` metaclass to create classes with private attributes.
3299
+ Private attributes are defined in the `__private_attrs__` sequence and are only visitable in class codes.
3300
+ You can use the `PrivateAttrBase` as the base class to create classes with private attributes.
3292
3301
  The attributes which are private are not on the instance's `__dict__` and cannot be accessed outside
3293
3302
  but in the methods defined in class it is reachable.
3294
3303
  Usage example:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 1.4.11
3
+ Version: 2.0.1
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='1.4.11',
21
+ version='2.0.1',
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.',