private-attribute-cpp 2.1.18__tar.gz → 2.1.20__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 (24) hide show
  1. {private_attribute_cpp-2.1.18/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.20}/PKG-INFO +1 -1
  2. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute.cpp +358 -83
  3. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute.pyi +13 -2
  4. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20/private_attribute_cpp.egg-info}/PKG-INFO +1 -1
  5. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/setup.py +1 -1
  6. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/LICENSE +0 -0
  7. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/MANIFEST.in +0 -0
  8. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/README.md +0 -0
  9. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/picosha2.h +0 -0
  10. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute_cpp.egg-info/SOURCES.txt +0 -0
  11. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
  12. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
  13. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/private_attribute_cpp.egg-info/top_level.txt +0 -0
  14. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/setup.cfg +0 -0
  15. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_attr_classify_owner.py +0 -0
  16. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_class_private_attrs.py +0 -0
  17. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_gc_collect.py +0 -0
  18. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_abc.py +0 -0
  19. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_attrs.py +0 -0
  20. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_func.py +0 -0
  21. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_inheritance.py +0 -0
  22. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_method.py +0 -0
  23. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_private_wrap.py +0 -0
  24. {private_attribute_cpp-2.1.18 → private_attribute_cpp-2.1.20}/tests/test_slots_cycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 2.1.18
3
+ Version: 2.1.20
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
@@ -931,6 +931,270 @@ type_delattr(PyObject* typ, const std::string& attr_name) noexcept
931
931
  return 0;
932
932
  }
933
933
 
934
+ // list proxy
935
+ // privatewrap.funcs return this type after 2.1.19
936
+
937
+ typedef struct {
938
+ PyObject_HEAD
939
+ PyObject *list;
940
+ } listproxy;
941
+
942
+ static bool
943
+ listproxy_check(listproxy *obj) noexcept
944
+ {
945
+ if (!obj->list) {
946
+ PyErr_SetString(PyExc_RuntimeError, "listproxy object has no list");
947
+ return false;
948
+ }
949
+ if (!PyList_CheckExact(obj->list)) {
950
+ PyErr_Format(PyExc_TypeError, "listproxy object can only proxy list, not '%s'", Py_TYPE(obj->list)->tp_name);
951
+ return false;
952
+ }
953
+ return true;
954
+ }
955
+
956
+ static PyObject*
957
+ listproxy_copy(PyObject* self, PyObject* /*args*/) noexcept
958
+ {
959
+ listproxy* obj = (listproxy*)self;
960
+ if (!listproxy_check(obj)) {
961
+ return NULL;
962
+ }
963
+ return PySequence_List(obj->list);
964
+ }
965
+
966
+ static PyObject*
967
+ listproxy_count(PyObject* self, PyObject* value) noexcept
968
+ {
969
+ listproxy* obj = (listproxy*)self;
970
+ if (!listproxy_check(obj)) {
971
+ return NULL;
972
+ }
973
+ Py_ssize_t m = PySequence_Count(obj->list, value);
974
+ if (m < 0) {
975
+ return NULL;
976
+ }
977
+ PyObject* result = PyLong_FromSsize_t(m);
978
+ return result;
979
+ }
980
+
981
+ static PyObject*
982
+ listproxy_index(PyObject* self, PyObject* args) noexcept
983
+ {
984
+ listproxy* obj = (listproxy*)self;
985
+ if (!listproxy_check(obj)) {
986
+ return NULL;
987
+ }
988
+ PyObject* method = PyObject_GetAttrString(obj->list, "index");
989
+ if (!method) {
990
+ return NULL;
991
+ }
992
+ PyObject* result = PyObject_Call(method, args, NULL);
993
+ Py_DECREF(method);
994
+ return result;
995
+ }
996
+
997
+ static Py_ssize_t
998
+ listproxy_length(PyObject* self) noexcept
999
+ {
1000
+ listproxy* obj = (listproxy*)self;
1001
+ if (!listproxy_check(obj)) {
1002
+ return -1;
1003
+ }
1004
+ return PyList_Size(obj->list);
1005
+ }
1006
+
1007
+ static PyObject*
1008
+ listproxy_iter(PyObject* self) noexcept
1009
+ {
1010
+ listproxy* obj = (listproxy*)self;
1011
+ if (!listproxy_check(obj)) {
1012
+ return NULL;
1013
+ }
1014
+ return PyObject_GetIter(obj->list);
1015
+ }
1016
+
1017
+ static PyObject*
1018
+ listproxy_item(PyObject* self, Py_ssize_t i) noexcept
1019
+ {
1020
+ listproxy* obj = (listproxy*)self;
1021
+ if (!listproxy_check(obj)) {
1022
+ return NULL;
1023
+ }
1024
+ Py_ssize_t size = PyList_Size(obj->list);
1025
+ if (i < 0) {
1026
+ i += size;
1027
+ }
1028
+ if (i < 0 || i >= size) {
1029
+ PyErr_SetString(PyExc_IndexError, "listproxy index out of range");
1030
+ return NULL;
1031
+ }
1032
+ PyObject* item = PyList_GetItem(obj->list, i);
1033
+ if (!item) {
1034
+ return NULL;
1035
+ }
1036
+ Py_INCREF(item);
1037
+ return item;
1038
+ }
1039
+
1040
+ static PyObject*
1041
+ listproxy_subscript(PyObject* self, PyObject* key) noexcept
1042
+ {
1043
+ listproxy* obj = (listproxy*)self;
1044
+ if (!listproxy_check(obj)) {
1045
+ return NULL;
1046
+ }
1047
+ return PyObject_GetItem(obj->list, key);
1048
+ }
1049
+
1050
+ static int
1051
+ listproxy_contains(PyObject* self, PyObject* value) noexcept
1052
+ {
1053
+ listproxy* obj = (listproxy*)self;
1054
+ if (!listproxy_check(obj)) {
1055
+ return -1;
1056
+ }
1057
+ return PySequence_Contains(obj->list, value);
1058
+ }
1059
+
1060
+ static PyObject*
1061
+ listproxy_repr(PyObject* self) noexcept
1062
+ {
1063
+ listproxy* obj = (listproxy*)self;
1064
+ if (!listproxy_check(obj)) {
1065
+ return NULL;
1066
+ }
1067
+ PyObject* inner = PyObject_Repr(obj->list);
1068
+ if (!inner) {
1069
+ return NULL;
1070
+ }
1071
+ PyObject* result = PyUnicode_FromFormat("listproxy(%U)", inner);
1072
+ Py_DECREF(inner);
1073
+ return result;
1074
+ }
1075
+
1076
+ static PyObject*
1077
+ listproxy_richcompare(PyObject* self, PyObject* other, int op) noexcept
1078
+ {
1079
+ listproxy* obj = (listproxy*)self;
1080
+ if (!listproxy_check(obj)) {
1081
+ return NULL;
1082
+ }
1083
+
1084
+ // If the counterpart is a genuine Python list, compare the underlying
1085
+ // list directly. Otherwise copy the counterpart into a real list first
1086
+ // (so that listproxy == listproxy and listproxy == tuple behave like a
1087
+ // list-to-list comparison), then compare.
1088
+ PyObject* self_list = obj->list;
1089
+ bool decref_self_list = false;
1090
+ if (!PyList_CheckExact(other)) {
1091
+ self_list = PySequence_List(obj->list);
1092
+ if (!self_list) {
1093
+ PyErr_Clear();
1094
+ Py_RETURN_NOTIMPLEMENTED;
1095
+ }
1096
+ decref_self_list = true;
1097
+ }
1098
+
1099
+ PyObject* result = PyObject_RichCompare(self_list, other, op);
1100
+ if (decref_self_list) {
1101
+ Py_DECREF(self_list);
1102
+ }
1103
+ return result;
1104
+ }
1105
+
1106
+ static void
1107
+ listproxy_dealloc(listproxy* self) noexcept
1108
+ {
1109
+ Py_XDECREF(self->list);
1110
+ Py_TYPE(self)->tp_free((PyObject*)self);
1111
+ }
1112
+
1113
+ static PySequenceMethods listproxy_as_sequence = {
1114
+ listproxy_length, // sq_length
1115
+ 0, // sq_concat
1116
+ 0, // sq_repeat
1117
+ listproxy_item, // sq_item
1118
+ 0, // was_sq_slice
1119
+ 0, // sq_ass_item
1120
+ 0, // was_sq_ass_slice
1121
+ listproxy_contains, // sq_contains
1122
+ 0, // sq_inplace_concat
1123
+ 0, // sq_inplace_repeat
1124
+ };
1125
+
1126
+ static PyMappingMethods listproxy_as_mapping = {
1127
+ listproxy_length, // mp_length
1128
+ listproxy_subscript, // mp_subscript
1129
+ 0, // mp_ass_subscript
1130
+ };
1131
+
1132
+ static PyMethodDef listproxy_methods[] = {
1133
+ {"copy", listproxy_copy, METH_NOARGS, "Return a copy of the proxied list."},
1134
+ {"count", listproxy_count, METH_O, "Return number of occurrences of value."},
1135
+ {"index", listproxy_index, METH_VARARGS, "Return first index of value."},
1136
+ {NULL, NULL, 0, NULL}
1137
+ };
1138
+
1139
+ static int
1140
+ listproxy_traverse(PyObject* self, visitproc visit, void* arg) noexcept
1141
+ {
1142
+ listproxy* obj = (listproxy*)self;
1143
+ Py_VISIT(obj->list);
1144
+ return 0;
1145
+ }
1146
+
1147
+ static int
1148
+ listproxy_clear(PyObject* self) noexcept
1149
+ {
1150
+ listproxy* obj = (listproxy*)self;
1151
+ Py_CLEAR(obj->list);
1152
+ return 0;
1153
+ }
1154
+
1155
+ static PyTypeObject listproxyType = {
1156
+ PyVarObject_HEAD_INIT(NULL, 0)
1157
+ "private_attribute.listproxy", // tp_name
1158
+ sizeof(listproxy), // tp_basicsize
1159
+ 0, // tp_itemsize
1160
+ (destructor)listproxy_dealloc, // tp_dealloc
1161
+ 0, // tp_print
1162
+ 0, // tp_getattr
1163
+ 0, // tp_setattr
1164
+ 0, // tp_reserved
1165
+ listproxy_repr, // tp_repr
1166
+ 0, // tp_as_number
1167
+ &listproxy_as_sequence, // tp_as_sequence
1168
+ &listproxy_as_mapping, // tp_as_mapping
1169
+ 0, // tp_hash
1170
+ 0, // tp_call
1171
+ 0, // tp_str
1172
+ 0, // tp_getattro
1173
+ 0, // tp_setattro
1174
+ 0, // tp_as_buffer
1175
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
1176
+ "A read-only proxy of a list.", // tp_doc
1177
+ listproxy_traverse, // tp_traverse
1178
+ listproxy_clear, // tp_clear
1179
+ listproxy_richcompare, // tp_richcompare
1180
+ 0, // tp_weaklistoffset
1181
+ listproxy_iter, // tp_iter
1182
+ 0, // tp_iternext
1183
+ listproxy_methods, // tp_methods
1184
+ };
1185
+
1186
+ static PyObject*
1187
+ listproxy_new(PyObject* list) noexcept
1188
+ {
1189
+ listproxy* obj = PyObject_GC_New(listproxy, &listproxyType);
1190
+ if (!obj) {
1191
+ return NULL;
1192
+ }
1193
+ obj->list = list;
1194
+ Py_INCREF(list);
1195
+ return (PyObject*)obj;
1196
+ }
1197
+
934
1198
  // ================================================================
935
1199
  // _PrivateWrap
936
1200
  // ================================================================
@@ -957,7 +1221,7 @@ PrivateWrap_funcs(PyObject *obj, void* /*closure*/) noexcept
957
1221
  Py_RETURN_NONE;
958
1222
  }
959
1223
 
960
- return PySequence_Tuple(((PrivateWrapObject*)obj)->func_list);
1224
+ return listproxy_new(((PrivateWrapObject*)obj)->func_list);
961
1225
  }
962
1226
 
963
1227
  static PyObject*
@@ -1148,7 +1412,7 @@ PrivateWrap_clear(PyObject* obj) noexcept
1148
1412
 
1149
1413
  static PyTypeObject PrivateWrapType = {
1150
1414
  PyVarObject_HEAD_INIT(NULL, 0)
1151
- "_PrivateWrap", // tp_name
1415
+ "private_attribute._PrivateWrap", // tp_name
1152
1416
  sizeof(PrivateWrapObject), // tp_basicsize
1153
1417
  0, // tp_itemsize
1154
1418
  (destructor)PrivateWrap_dealloc, // tp_dealloc
@@ -2100,17 +2364,12 @@ PrivateAttrType_tp_clear(PyObject* self) noexcept
2100
2364
  static void
2101
2365
  PrivateAttr_object_init_private_dict(uintptr_t obj_id, uintptr_t type_id) noexcept
2102
2366
  {
2103
- if (::AllData::all_object_mutex.find(type_id) == ::AllData::all_object_mutex.end()) {
2104
- ::AllData::all_object_mutex[type_id].clear();
2105
- }
2106
- if (::AllData::all_object_attr.find(type_id) == ::AllData::all_object_attr.end()) {
2107
- ::AllData::all_object_attr[type_id] = {};
2108
- }
2109
- if (::AllData::all_object_mutex[type_id].find(obj_id) == ::AllData::all_object_mutex[type_id].end()) {
2110
- ::AllData::all_object_mutex[type_id][obj_id] = std::make_unique<std::shared_mutex>();
2111
- }
2112
- if (::AllData::all_object_attr[type_id].find(obj_id) == ::AllData::all_object_attr[type_id].end()) {
2113
- ::AllData::all_object_attr[type_id][obj_id] = {};
2367
+ object_create_mutex(type_id, obj_id);
2368
+ {
2369
+ std::unique_lock<std::shared_mutex> lock(*::AllData::all_object_mutex[type_id][obj_id]);
2370
+ if (::AllData::all_object_attr[type_id].find(obj_id) == ::AllData::all_object_attr[type_id].end()) {
2371
+ ::AllData::all_object_attr[type_id][obj_id] = {};
2372
+ }
2114
2373
  }
2115
2374
  if (::AllData::all_type_parent_id.find(type_id) != ::AllData::all_type_parent_id.end()) {
2116
2375
  for (auto parent_id : ::AllData::all_type_parent_id[type_id]) {
@@ -2177,7 +2436,7 @@ get_string_hash_tuple(const std::string& name) noexcept
2177
2436
  std::string name1hash, name2hash;
2178
2437
  picosha2::hash256_hex_string(name1, name1hash);
2179
2438
  picosha2::hash256_hex_string(name2, name2hash);
2180
- return PyTuple_Pack(2, PyUnicode_FromString(name1hash.c_str()), PyUnicode_FromString(name2hash.c_str()));
2439
+ return Py_BuildValue("(ss)", name1hash.c_str(), name2hash.c_str());
2181
2440
  }
2182
2441
 
2183
2442
  static TwoStringTuple
@@ -2466,6 +2725,14 @@ real_class_name(const std::string& name, const std::string& class_name) noexcept
2466
2725
  return name;
2467
2726
  }
2468
2727
 
2728
+ #define PRIVATEATTR_CREATION_DATA_CLEAR(name) do \
2729
+ { \
2730
+ if (name) { \
2731
+ Py_DECREF(name); \
2732
+ name = nullptr; \
2733
+ } \
2734
+ } while (0)
2735
+
2469
2736
  struct PrivateAttrCreationData
2470
2737
  {
2471
2738
  std::string class_name;
@@ -2489,34 +2756,13 @@ struct PrivateAttrCreationData
2489
2756
  if (cleared) {
2490
2757
  return;
2491
2758
  }
2492
- if (name) {
2493
- Py_DECREF(name);
2494
- name = nullptr;
2495
- }
2496
- if (bases) {
2497
- Py_DECREF(bases);
2498
- bases = nullptr;
2499
- }
2500
- if (attrs) {
2501
- Py_DECREF(attrs);
2502
- attrs = nullptr;
2503
- }
2504
- if (attrs_copy) {
2505
- Py_DECREF(attrs_copy);
2506
- attrs_copy = nullptr;
2507
- }
2508
- if (new_hash_private_attrs) {
2509
- Py_DECREF(new_hash_private_attrs);
2510
- new_hash_private_attrs = nullptr;
2511
- }
2512
- if (private_func) {
2513
- Py_DECREF(private_func);
2514
- private_func = nullptr;
2515
- }
2516
- if (base_kwds) {
2517
- Py_DECREF(base_kwds);
2518
- base_kwds = nullptr;
2519
- }
2759
+ PRIVATEATTR_CREATION_DATA_CLEAR(name);
2760
+ PRIVATEATTR_CREATION_DATA_CLEAR(bases);
2761
+ PRIVATEATTR_CREATION_DATA_CLEAR(attrs);
2762
+ PRIVATEATTR_CREATION_DATA_CLEAR(attrs_copy);
2763
+ PRIVATEATTR_CREATION_DATA_CLEAR(new_hash_private_attrs);
2764
+ PRIVATEATTR_CREATION_DATA_CLEAR(private_func);
2765
+ PRIVATEATTR_CREATION_DATA_CLEAR(base_kwds);
2520
2766
 
2521
2767
  need_remove_itself.clear();
2522
2768
  need_remove_subclass.clear();
@@ -2747,7 +2993,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
2747
2993
 
2748
2994
  // forbidden to have "__static_attributes__" in attrs, because it exposes the private attributes name.
2749
2995
  if (PyDict_ContainsString(data.attrs_copy, "__static_attributes__")) {
2750
- PyDict_DelItemString(data.attrs_copy, "__static_attributes__");
2996
+ if (PyDict_DelItemString(data.attrs_copy, "__static_attributes__") < 0) return false;
2751
2997
  }
2752
2998
 
2753
2999
  {
@@ -2772,7 +3018,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
2772
3018
  // (PrivateAttrCreationData::clear). No manual INCREF here,
2773
3019
  // otherwise the value leaks (+1) when the class is destroyed.
2774
3020
  data.need_remove_itself[attr_name] = need_value;
2775
- PyDict_DelItem(data.attrs_copy, key);
3021
+ if (PyDict_DelItem(data.attrs_copy, key)) return false;
2776
3022
  continue;
2777
3023
  }
2778
3024
  uintptr_t need_remove_subclass_id = need_remove_to_subclass(attr_name);
@@ -2785,10 +3031,12 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
2785
3031
  }
2786
3032
  // PyObjectStorage assignment INCREFs (RAII); no manual INCREF.
2787
3033
  data.need_remove_subclass[need_remove_subclass_id][attr_name] = need_value;
2788
- PyDict_DelItem(data.attrs_copy, key);
3034
+ if (PyDict_DelItem(data.attrs_copy, key)) return false;
2789
3035
  continue;
2790
3036
  }
2791
- if (value != need_value) PyDict_SetItem(data.attrs_copy, key, need_value);
3037
+ if (value != need_value) {
3038
+ if (PyDict_SetItem(data.attrs_copy, key, need_value) < 0) return false;
3039
+ }
2792
3040
  }
2793
3041
  Py_DECREF(forward_analyse);
2794
3042
  }
@@ -2804,7 +3052,9 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
2804
3052
  if (!data.base_kwds) {
2805
3053
  return false;
2806
3054
  }
2807
- if (has_private_func) PyDict_DelItemString(data.base_kwds, "private_func");
3055
+ if (has_private_func) {
3056
+ if (PyDict_DelItemString(data.base_kwds, "private_func") < 0) return false;
3057
+ }
2808
3058
  }
2809
3059
 
2810
3060
  return true;
@@ -3610,54 +3860,78 @@ static const char* PrivateAttrBase_doc = "The class to help to create private at
3610
3860
  static PyObject*
3611
3861
  create_private_attr_base_simple(void) noexcept
3612
3862
  {
3613
- PyObject* name = PyUnicode_InternFromString("PrivateAttrBase");
3614
- if (!name) return NULL;
3615
- PyObject* bases = PyTuple_New(0);
3616
- if (!bases) {
3617
- Py_DECREF(name);
3618
- return NULL;
3619
- }
3620
- PyObject* dict = PyDict_New();
3621
- if (!dict) {
3622
- Py_DECREF(name);
3623
- Py_DECREF(bases);
3624
- return NULL;
3625
- }
3626
- PyObject *private_attrs = PyTuple_New(0);
3627
- if (!private_attrs) {
3628
- Py_DECREF(name);
3629
- Py_DECREF(bases);
3630
- Py_DECREF(dict);
3631
- return NULL;
3632
- }
3633
- PyDict_SetItemString(dict, "__private_attrs__", private_attrs);
3634
- PyDict_SetItemString(dict, "__slots__", private_attrs);
3635
- PyDict_SetItemString(dict, "__doc__", PyUnicode_InternFromString(PrivateAttrBase_doc));
3636
- PyDict_SetItemString(dict, "__module__", PyUnicode_InternFromString("private_attribute"));
3637
- PyObject *args = PyTuple_Pack(3, name, bases, dict);
3638
- PyObject* base_type;
3639
- if (args) {
3863
+ PyObject* name = NULL;
3864
+ PyObject* bases = NULL;
3865
+ PyObject* dict = NULL;
3866
+ PyObject* private_attrs = NULL;
3867
+ PyObject* doc = NULL;
3868
+ PyObject* module_name = NULL;
3869
+ PyObject* args = NULL;
3870
+ PyObject* base_type = NULL;
3871
+
3872
+ name = PyUnicode_InternFromString("PrivateAttrBase");
3873
+ if (!name) goto fail;
3874
+
3875
+ bases = PyTuple_New(0);
3876
+ if (!bases) goto fail;
3877
+
3878
+ dict = PyDict_New();
3879
+ if (!dict) goto fail;
3880
+
3881
+ private_attrs = PyTuple_New(0);
3882
+ if (!private_attrs) goto fail;
3883
+
3884
+ doc = PyUnicode_InternFromString(PrivateAttrBase_doc);
3885
+ if (!doc) goto fail;
3886
+
3887
+ module_name = PyUnicode_InternFromString("private_attribute");
3888
+ if (!module_name) goto fail;
3889
+
3890
+ if (PyDict_SetItemString(dict, "__private_attrs__", private_attrs) < 0) goto fail;
3891
+ if (PyDict_SetItemString(dict, "__slots__", private_attrs) < 0) goto fail;
3892
+ if (PyDict_SetItemString(dict, "__doc__", doc) < 0) goto fail;
3893
+ if (PyDict_SetItemString(dict, "__module__", module_name) < 0) goto fail;
3894
+
3895
+ Py_DECREF(doc);
3896
+ doc = NULL;
3897
+ Py_DECREF(module_name);
3898
+ module_name = NULL;
3899
+
3900
+ args = PyTuple_Pack(3, name, bases, dict);
3901
+ if (!args) goto fail;
3902
+
3903
+ {
3640
3904
  // RAII: the first heap type created through tp_new (PrivateAttrBase)
3641
3905
  // carries CPython's pristine subtype_traverse/subtype_clear in its
3642
3906
  // tp_traverse/tp_clear; PrivateAttrType_new captures those pointers
3643
3907
  // while this guard is active.
3644
3908
  PrivateAttrCaptureGuard capture_guard;
3645
3909
  base_type = PrivateAttrType_new((PyTypeObject*)&PrivateAttrType, args, NULL);
3646
- Py_DECREF(args);
3647
- } else {
3648
- Py_DECREF(name);
3649
- Py_DECREF(bases);
3650
- Py_DECREF(dict);
3651
- return NULL;
3652
3910
  }
3911
+ Py_DECREF(args);
3912
+ args = NULL;
3913
+
3653
3914
  Py_DECREF(name);
3915
+ name = NULL;
3654
3916
  Py_DECREF(bases);
3917
+ bases = NULL;
3655
3918
  Py_DECREF(dict);
3656
- if (!base_type) {
3657
- return NULL;
3658
- }
3919
+ dict = NULL;
3920
+
3921
+ if (!base_type) goto fail;
3922
+
3659
3923
  ((PyTypeObject*)base_type)->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
3660
3924
  return base_type;
3925
+
3926
+ fail:
3927
+ Py_XDECREF(name);
3928
+ Py_XDECREF(bases);
3929
+ Py_XDECREF(dict);
3930
+ Py_XDECREF(private_attrs);
3931
+ Py_XDECREF(doc);
3932
+ Py_XDECREF(module_name);
3933
+ Py_XDECREF(args);
3934
+ return NULL;
3661
3935
  }
3662
3936
 
3663
3937
  typedef struct {
@@ -4496,6 +4770,7 @@ PyInit_private_attribute(void) noexcept
4496
4770
  }
4497
4771
  if (init_all_slots() <0 ||
4498
4772
  atexit_register_clean_func() < 0 ||
4773
+ PyType_Ready(&listproxyType) < 0 ||
4499
4774
  PyType_Ready(&PrivateWrapType) < 0 ||
4500
4775
  PyType_Ready(&PrivateWrapProxyType) < 0 ||
4501
4776
  PyType_Ready(&PrivateAttrType) < 0 ||
@@ -15,8 +15,9 @@ class MyClass(PrivateAttrBase):
15
15
  return self._private_attr1
16
16
  ```
17
17
  """
18
- from typing import Any, TypeVar, Callable, TypedDict, Sequence, Generic, Optional, cast
18
+ from typing import Any, TypeVar, Callable, TypedDict, Sequence, Generic, Optional, cast, Iterator
19
19
  from types import FunctionType
20
+ import sys
20
21
 
21
22
  # define the dict that must have a key "__private_attrs__" and value must be the sequence of strings
22
23
  class _PrivateAttrDict(TypedDict):
@@ -24,6 +25,16 @@ class _PrivateAttrDict(TypedDict):
24
25
 
25
26
  _T = TypeVar('_T')
26
27
 
28
+ class listproxy(Generic[_T]):
29
+ "a read-only proxy of a list"
30
+ def copy(self) -> list[_T]: ...
31
+ def count(self, value: Any, /) -> int: ...
32
+ def index(self, value: Any, start: int = 0, stop: int = sys.maxsize, /) -> int: ...
33
+ def __len__(self) -> int: ...
34
+ def __iter__(self) -> Iterator[_T]: ...
35
+ def __getitem__(self, key: int | slice) -> Any: ...
36
+ def __contains__(self, value: object) -> bool: ...
37
+
27
38
  class _PrivateWrap(Generic[_T]):
28
39
  @property
29
40
  def result(self) -> _T:
@@ -31,7 +42,7 @@ class _PrivateWrap(Generic[_T]):
31
42
  ...
32
43
 
33
44
  @property
34
- def funcs(self) -> tuple[FunctionType]:
45
+ def funcs(self) -> listproxy[FunctionType]:
35
46
  "the original functions"
36
47
  ...
37
48
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 2.1.18
3
+ Version: 2.1.20
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
@@ -24,7 +24,7 @@ readme = open('README.md').read()
24
24
 
25
25
  setup(
26
26
  name='private_attribute_cpp',
27
- version='2.1.18',
27
+ version='2.1.20',
28
28
  author="HuangHaoHua",
29
29
  author_email="13140752715@example.com",
30
30
  description='A Python package that provides a way to define private attributes in C++ implementation.',