private-attribute-cpp 1.3.6__tar.gz → 1.3.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 1.3.6
3
+ Version: 1.3.7
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
@@ -128,6 +128,12 @@ namespace {
128
128
  };
129
129
  };
130
130
 
131
+ namespace AllSlots {
132
+ static getattrofunc original_getattro = nullptr;
133
+ static setattrofunc original_setattro = nullptr;
134
+ static destructor original_finalize = nullptr;
135
+ }
136
+
131
137
  struct FinalObject
132
138
  {
133
139
  PyObject* result = NULL;
@@ -1191,6 +1197,8 @@ typedef struct {
1191
1197
  static void PrivateAttr_object_init_private_dict(uintptr_t obj_id, uintptr_t type_id);
1192
1198
  static void ensure_tp(PyTypeObject* type_instance);
1193
1199
  static void ensure_subclass_tp(PyTypeObject* type_instance);
1200
+ static int PrivateAttr_tp_setattro(PyObject* self, PyObject* name, PyObject* value);
1201
+ static void PrivateAttr_tp_finalize(PyObject* self);
1194
1202
 
1195
1203
  static PyObject*
1196
1204
  PrivateAttr_tp_getattro(PyObject* self, PyObject* name)
@@ -1212,8 +1220,11 @@ PrivateAttr_tp_getattro(PyObject* self, PyObject* name)
1212
1220
  }
1213
1221
  Py_XDECREF(code);
1214
1222
  if (::AllData::all_type_getattro.find(type_id) != ::AllData::all_type_getattro.end()){
1215
- PyObject* result = ::AllData::all_type_getattro[type_id](self, name);
1216
- return result;
1223
+ if (::AllData::all_type_getattro[type_id]) {
1224
+ PyObject* result = ::AllData::all_type_getattro[type_id](self, name);
1225
+ ensure_tp(typ);
1226
+ return result;
1227
+ }
1217
1228
  }
1218
1229
  return PyObject_GenericGetAttr(self, name);
1219
1230
  }
@@ -1245,6 +1256,7 @@ PrivateAttr_tp_setattro(PyObject* self, PyObject* name, PyObject* value)
1245
1256
  Py_XDECREF(code);
1246
1257
  if (::AllData::all_type_setattro.find(typ_id) != ::AllData::all_type_setattro.end()){
1247
1258
  int result = ::AllData::all_type_setattro[typ_id](self, name, value);
1259
+ ensure_tp(typ);
1248
1260
  return result;
1249
1261
  }
1250
1262
  return PyObject_GenericSetAttr(self, name, value);
@@ -1258,7 +1270,10 @@ PrivateAttr_tp_finalize(PyObject* self)
1258
1270
  uintptr_t typ_id = (uintptr_t)typ;
1259
1271
  Py_ssize_t original_ref = Py_REFCNT(self);
1260
1272
  if (::AllData::all_type_finalize.find(typ_id) != ::AllData::all_type_finalize.end()){
1261
- ::AllData::all_type_finalize[typ_id](self);
1273
+ if (::AllData::all_type_finalize[typ_id]) {
1274
+ ::AllData::all_type_finalize[typ_id](self);
1275
+ ensure_tp(typ);
1276
+ }
1262
1277
  }
1263
1278
  if (original_ref != Py_REFCNT(self)) {
1264
1279
  return;
@@ -1743,6 +1758,21 @@ need_analyse_type(PyObject* type)
1743
1758
  return false;
1744
1759
  }
1745
1760
 
1761
+ // python under 3.13 doesn't has PyDict_ContainsString, so we implement it ourselves
1762
+ #if PY_VERSION_HEX < 0x030D0000
1763
+ static int
1764
+ PyDict_ContainsString(PyObject *op, const char *key)
1765
+ {
1766
+ PyObject *key_obj = PyUnicode_FromString(key);
1767
+ if (key_obj == NULL) {
1768
+ return -1;
1769
+ }
1770
+ int res = PyDict_Contains(op, key_obj);
1771
+ Py_DECREF(key_obj);
1772
+ return res;
1773
+ }
1774
+ #endif
1775
+
1746
1776
  static bool
1747
1777
  PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationData& data)
1748
1778
  {
@@ -1809,7 +1839,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
1809
1839
  }
1810
1840
 
1811
1841
  if (!PyUnicode_Check(attr)) {
1812
- PyErr_SetString(PyExc_TypeError, "all items in '__private_attrs__' must be strings");
1842
+ PyErr_Format(PyExc_TypeError, "all items in '__private_attrs__' must be 'str', not '%.200s'", Py_TYPE(attr)->tp_name);
1813
1843
  return false;
1814
1844
  }
1815
1845
 
@@ -1821,7 +1851,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
1821
1851
  std::string attr_str = real_class_name(attr_cstr, data.class_name);
1822
1852
 
1823
1853
  for (const char** p = invalid_name; *p != NULL; p++) {
1824
- if (attr_str == *p) {
1854
+ if (strcmp(attr_str.c_str(), *p) == 0) {
1825
1855
  std::string error_msg = "invalid attribute name: '" + std::string(*p) + "'";
1826
1856
  PyErr_SetString(PyExc_TypeError, error_msg.c_str());
1827
1857
  return false;
@@ -1842,10 +1872,10 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
1842
1872
  return false;
1843
1873
  }
1844
1874
 
1845
- PyObject* all_slots = PyDict_GetItemString(data.attrs_copy, "__slots__");
1846
- bool has_slots = (all_slots != NULL);
1875
+ int has_slots = PyDict_ContainsString(data.attrs_copy, "__slots__");
1847
1876
 
1848
1877
  if (has_slots) {
1878
+ PyObject* all_slots = PyDict_GetItemString(data.attrs_copy, "__slots__");
1849
1879
  PyObject* slot_seq = PySequence_Fast(all_slots, "__slots__ must be a sequence");
1850
1880
  if (!slot_seq) {
1851
1881
  return false;
@@ -1866,8 +1896,6 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
1866
1896
  }
1867
1897
  }
1868
1898
  Py_DECREF(slot_seq);
1869
- } else {
1870
- PyErr_Clear();
1871
1899
  }
1872
1900
 
1873
1901
  Py_ssize_t bases_len = PyTuple_GET_SIZE(data.bases);
@@ -1927,16 +1955,16 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
1927
1955
 
1928
1956
  // get "private_func" from kwds
1929
1957
  if (kwds && PyDict_Check(kwds)) {
1930
- data.private_func = PyDict_GetItemString(kwds, "private_func");
1931
- if (data.private_func) {
1958
+ bool has_private_func = PyDict_ContainsString(kwds, "private_func");
1959
+ if (has_private_func) {
1960
+ data.private_func = PyDict_GetItemString(kwds, "private_func");
1932
1961
  Py_INCREF(data.private_func);
1933
1962
  }
1934
1963
  data.base_kwds = PyDict_Copy(kwds);
1935
1964
  if (!data.base_kwds) {
1936
1965
  return false;
1937
1966
  }
1938
- PyDict_DelItemString(data.base_kwds, "private_func");
1939
- PyErr_Clear();
1967
+ if (has_private_func) PyDict_DelItemString(data.base_kwds, "private_func");
1940
1968
  }
1941
1969
 
1942
1970
  return true;
@@ -1968,13 +1996,21 @@ PrivateAttrType_create(PyTypeObject* type, PrivateAttrCreationData& data)
1968
1996
  return new_type;
1969
1997
  }
1970
1998
 
1999
+ static getattrofunc get_need_tp_getattro(PyTypeObject* cls);
2000
+ static setattrofunc get_need_tp_setattro(PyTypeObject* cls);
2001
+ static destructor get_need_tp_finalize(PyTypeObject* cls);
2002
+
1971
2003
  static void
1972
2004
  ensure_tp(PyTypeObject* type_instance)
1973
2005
  {
1974
2006
  uintptr_t type_id = (uintptr_t)(type_instance);
1975
- if (type_instance->tp_getattro) {
2007
+ {
1976
2008
  if (type_instance->tp_getattro != PrivateAttr_tp_getattro) {
1977
- ::AllData::all_type_getattro[type_id] = type_instance->tp_getattro;
2009
+ if (!type_instance->tp_getattro) {
2010
+ ::AllData::all_type_getattro[type_id] = get_need_tp_getattro(type_instance);
2011
+ } else {
2012
+ ::AllData::all_type_getattro[type_id] = type_instance->tp_getattro;
2013
+ }
1978
2014
  type_instance->tp_getattro = PrivateAttr_tp_getattro;
1979
2015
  } else {
1980
2016
  PyTypeObject* base = type_instance->tp_base;
@@ -1986,9 +2022,13 @@ ensure_tp(PyTypeObject* type_instance)
1986
2022
  }
1987
2023
  }
1988
2024
  }
1989
- if (type_instance->tp_setattro) {
2025
+ {
1990
2026
  if (type_instance->tp_setattro != PrivateAttr_tp_setattro) {
1991
- ::AllData::all_type_setattro[type_id] = type_instance->tp_setattro;
2027
+ if (!type_instance->tp_setattro) {
2028
+ ::AllData::all_type_setattro[type_id] = get_need_tp_setattro(type_instance);
2029
+ } else {
2030
+ ::AllData::all_type_setattro[type_id] = type_instance->tp_setattro;
2031
+ }
1992
2032
  type_instance->tp_setattro = PrivateAttr_tp_setattro;
1993
2033
  } else {
1994
2034
  PyTypeObject* base = type_instance->tp_base;
@@ -2000,9 +2040,13 @@ ensure_tp(PyTypeObject* type_instance)
2000
2040
  }
2001
2041
  }
2002
2042
  }
2003
- if (type_instance->tp_finalize) {
2043
+ {
2004
2044
  if (type_instance->tp_finalize != PrivateAttr_tp_finalize) {
2005
- ::AllData::all_type_finalize[type_id] = type_instance->tp_finalize;
2045
+ if (!type_instance->tp_finalize) {
2046
+ ::AllData::all_type_finalize[type_id] = get_need_tp_finalize(type_instance);
2047
+ } else {
2048
+ ::AllData::all_type_finalize[type_id] = type_instance->tp_finalize;
2049
+ }
2006
2050
  type_instance->tp_finalize = PrivateAttr_tp_finalize;
2007
2051
  } else {
2008
2052
  PyTypeObject* base = type_instance->tp_base;
@@ -2022,23 +2066,37 @@ ensure_subclass_tp(PyTypeObject* type_instance)
2022
2066
  // type.__subclasses__
2023
2067
  PyObject* type_subclasses = (PyObject*)type_instance->tp_subclasses;
2024
2068
  if (!type_subclasses) {
2025
- PyErr_Clear();
2026
2069
  return;
2027
2070
  }
2028
- if (!PyList_Check(type_subclasses)) {
2029
- Py_DECREF(type_subclasses);
2071
+ if (!PyDict_Check(type_subclasses)) {
2030
2072
  return;
2031
2073
  }
2032
- Py_ssize_t subclasses_size = PyList_GET_SIZE(type_subclasses);
2033
- for (Py_ssize_t i = 0; i < subclasses_size; i++) {
2034
- PyObject* subclass = PyList_GET_ITEM(type_subclasses, i);
2074
+ // check all values in the dict. It should be a weakref to a type. If not, continue.
2075
+ PyObject* key, *value;
2076
+ Py_ssize_t pos = 0;
2077
+ while (PyDict_Next(type_subclasses, &pos, &key, &value)) {
2078
+ if (!value || !PyWeakref_CheckRef(value)) {
2079
+ continue;
2080
+ }
2081
+ // python<3.13 use PyWeakref_GetObject, python>=3.13 use PyWeakref_GetRef
2082
+ #if PY_VERSION_HEX < 0x030D0000
2083
+ PyObject* subclass = PyWeakref_GetObject(value);
2035
2084
  if (!subclass || !PyType_Check(subclass)) {
2036
2085
  continue;
2037
2086
  }
2038
2087
  ensure_tp((PyTypeObject*)subclass);
2039
- ensure_subclass_tp((PyTypeObject*)subclass);
2088
+ #else
2089
+ PyObject* subclass;
2090
+ if (PyWeakref_GetRef(value, (PyObject**)&subclass) != 0) {
2091
+ continue;
2092
+ }
2093
+ if (!subclass || !PyType_Check(subclass)) {
2094
+ continue;
2095
+ }
2096
+ ensure_tp((PyTypeObject*)subclass);
2097
+ Py_XDECREF(subclass);
2098
+ #endif
2040
2099
  }
2041
- Py_DECREF(type_subclasses);
2042
2100
  }
2043
2101
 
2044
2102
  static bool
@@ -2205,6 +2263,178 @@ PrivateAttrType_getattr(PyObject* cls, PyObject* name)
2205
2263
  return result;
2206
2264
  }
2207
2265
 
2266
+ static int
2267
+ init_all_slots()
2268
+ {
2269
+ PyObject* class_locals = PyDict_New();
2270
+ if (!class_locals) {
2271
+ return -1;
2272
+ }
2273
+ // set "__getattribute__", "__setattr__", "__delattr__", "__del__" to class_locals
2274
+ if (PyDict_SetItemString(class_locals, "__getattribute__", Py_None) < 0) {
2275
+ Py_DECREF(class_locals);
2276
+ return -1;
2277
+ }
2278
+ if (PyDict_SetItemString(class_locals, "__setattr__", Py_None) < 0) {
2279
+ Py_DECREF(class_locals);
2280
+ return -1;
2281
+ }
2282
+ if (PyDict_SetItemString(class_locals, "__delattr__", Py_None) < 0) {
2283
+ Py_DECREF(class_locals);
2284
+ return -1;
2285
+ }
2286
+ if (PyDict_SetItemString(class_locals, "__del__", Py_None) < 0) {
2287
+ Py_DECREF(class_locals);
2288
+ return -1;
2289
+ }
2290
+ PyObject* class_name = PyUnicode_FromString("_TempClass");
2291
+ if (!class_name) {
2292
+ Py_DECREF(class_locals);
2293
+ return -1;
2294
+ }
2295
+ PyObject* args = PyTuple_Pack(3, class_name, PyTuple_New(0), class_locals);
2296
+ if (!args) {
2297
+ Py_DECREF(class_name);
2298
+ Py_DECREF(class_locals);
2299
+ return -1;
2300
+ }
2301
+ PyObject* temp_class = PyType_Type.tp_new(&PyType_Type, args, NULL);
2302
+ Py_DECREF(args);
2303
+ if (!temp_class) {
2304
+ return -1;
2305
+ }
2306
+ PyTypeObject* temp_type = (PyTypeObject*)temp_class;
2307
+ AllSlots::original_getattro = temp_type->tp_getattro;
2308
+ AllSlots::original_setattro = temp_type->tp_setattro;
2309
+ AllSlots::original_finalize = temp_type->tp_finalize;
2310
+ Py_DECREF(temp_class);
2311
+ return 0;
2312
+ }
2313
+
2314
+ static getattrofunc
2315
+ get_need_tp_getattro(PyTypeObject* cls)
2316
+ {
2317
+ if (PyDict_ContainsString(cls->tp_dict, "__getattribute__") || PyDict_ContainsString(cls->tp_dict, "__getattr__")) {
2318
+ return AllSlots::original_getattro;
2319
+ }
2320
+ PyTypeObject* base = cls->tp_base;
2321
+ while (base) {
2322
+ if (base->tp_getattro != PrivateAttr_tp_getattro) {
2323
+ return base->tp_getattro;
2324
+ } else if (::AllData::all_type_getattro.find((uintptr_t)base) != ::AllData::all_type_getattro.end()) {
2325
+ if (::AllData::all_type_getattro[(uintptr_t)base]) {
2326
+ return ::AllData::all_type_getattro[(uintptr_t)base];
2327
+ }
2328
+ base = base->tp_base;
2329
+ } else {
2330
+ base = base->tp_base;
2331
+ }
2332
+ }
2333
+ return NULL;
2334
+ }
2335
+
2336
+ static setattrofunc
2337
+ get_need_tp_setattro(PyTypeObject* cls)
2338
+ {
2339
+ if (PyDict_ContainsString(cls->tp_dict, "__setattr__") || PyDict_ContainsString(cls->tp_dict, "__delattr__")) {
2340
+ return AllSlots::original_setattro;
2341
+ }
2342
+ PyTypeObject* base = cls->tp_base;
2343
+ while (base) {
2344
+ if (base->tp_setattro != PrivateAttr_tp_setattro) {
2345
+ return base->tp_setattro;
2346
+ } else if (::AllData::all_type_setattro.find((uintptr_t)base) != ::AllData::all_type_setattro.end()) {
2347
+ if (::AllData::all_type_setattro[(uintptr_t)base]) {
2348
+ return ::AllData::all_type_setattro[(uintptr_t)base];
2349
+ }
2350
+ base = base->tp_base;
2351
+ } else {
2352
+ base = base->tp_base;
2353
+ }
2354
+ }
2355
+ return NULL;
2356
+ }
2357
+
2358
+ static destructor
2359
+ get_need_tp_finalize(PyTypeObject* cls)
2360
+ {
2361
+ if (PyDict_ContainsString(cls->tp_dict, "__del__")) {
2362
+ return AllSlots::original_finalize;
2363
+ }
2364
+ PyTypeObject* base = cls->tp_base;
2365
+ while (base) {
2366
+ if (base->tp_finalize != PrivateAttr_tp_finalize) {
2367
+ return base->tp_finalize;
2368
+ } else if (::AllData::all_type_finalize.find((uintptr_t)base) != ::AllData::all_type_finalize.end()) {
2369
+ if (::AllData::all_type_finalize[(uintptr_t)base]) {
2370
+ return ::AllData::all_type_finalize[(uintptr_t)base];
2371
+ }
2372
+ base = base->tp_base;
2373
+ } else {
2374
+ base = base->tp_base;
2375
+ }
2376
+ }
2377
+ return NULL;
2378
+ }
2379
+
2380
+ static void
2381
+ type_change_all_slots(PyTypeObject* cls, const char* name)
2382
+ {
2383
+ if (strcmp(name, "__getattribute__") == 0 || strcmp(name, "__getattr__") == 0) {
2384
+ cls->tp_getattro = PrivateAttr_tp_getattro;
2385
+ ::AllData::all_type_getattro[(uintptr_t)cls] = get_need_tp_getattro(cls);
2386
+ } else if (strcmp(name, "__setattr__") == 0 || strcmp(name, "__delattr__") == 0) {
2387
+ cls->tp_setattro = PrivateAttr_tp_setattro;
2388
+ ::AllData::all_type_setattro[(uintptr_t)cls] = get_need_tp_setattro(cls);
2389
+ } else if (strcmp(name, "__del__") == 0) {
2390
+ cls->tp_finalize = PrivateAttr_tp_finalize;
2391
+ ::AllData::all_type_finalize[(uintptr_t)cls] = get_need_tp_finalize(cls);
2392
+ }
2393
+ }
2394
+
2395
+ static void
2396
+ subtype_change_all_slots(PyTypeObject* cls, const char* name)
2397
+ {
2398
+ // tp_subclasses: {id(type): weakref.ref(type)}
2399
+ PyObject* tp_subclasses = (PyObject*)cls->tp_subclasses;
2400
+ if (!tp_subclasses) {
2401
+ return;
2402
+ }
2403
+ if (!PyDict_Check(tp_subclasses)) {
2404
+ return;
2405
+ }
2406
+ PyObject* key;
2407
+ PyObject* value;
2408
+ Py_ssize_t pos = 0;
2409
+ while (PyDict_Next(tp_subclasses, &pos, &key, &value)) {
2410
+ if (!PyWeakref_Check(value)) {
2411
+ continue;
2412
+ }
2413
+ #if PY_VERSION_HEX < 0x030D0000
2414
+ PyObject* obj = PyWeakref_GET_OBJECT(value);
2415
+ if (!obj) {
2416
+ continue;
2417
+ }
2418
+ #else
2419
+ PyObject* obj;
2420
+ if (PyWeakref_GetRef(value, &obj) != 0) {
2421
+ continue;
2422
+ }
2423
+ if (!obj) {
2424
+ continue;
2425
+ }
2426
+ #endif
2427
+ if (!PyType_Check(obj)) {
2428
+ continue;
2429
+ }
2430
+ PyTypeObject* sub_cls = (PyTypeObject*)obj;
2431
+ type_change_all_slots(sub_cls, name);
2432
+ #if PY_VERSION_HEX >= 0x030D0000
2433
+ Py_XDECREF(obj);
2434
+ #endif
2435
+ }
2436
+ }
2437
+
2208
2438
  static int
2209
2439
  PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value)
2210
2440
  {
@@ -2231,6 +2461,28 @@ PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value)
2231
2461
  return type_setattr(cls, name_str, value);
2232
2462
  }
2233
2463
  Py_XDECREF(now_code);
2464
+ // if name in __getattribute__, __setattr__, __delattr__, __del__, just set to tp_dict
2465
+ if (name_str == "__getattribute__" || name_str == "__getattr__" || name_str == "__setattr__" || name_str == "__delattr__" || name_str == "__del__") {
2466
+ PyObject* tp_dict = ((PyTypeObject*)cls)->tp_dict;
2467
+ if (!tp_dict) {
2468
+ PyErr_SetString(PyExc_TypeError, "type has no tp_dict");
2469
+ return -1;
2470
+ }
2471
+ if (!value) {
2472
+ if (PyDict_DelItem(tp_dict, name) < 0) {
2473
+ return -1;
2474
+ }
2475
+ type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2476
+ subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2477
+ return 0;
2478
+ }
2479
+ if (PyDict_SetItem(tp_dict, name, value) < 0) {
2480
+ return -1;
2481
+ }
2482
+ type_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2483
+ subtype_change_all_slots((PyTypeObject*)cls, name_str.c_str());
2484
+ return 0;
2485
+ }
2234
2486
  PyTypeObject* base = Py_TYPE(cls)->tp_base;
2235
2487
  while (base && base->tp_base && base->tp_setattro == PrivateAttrType_setattr) {
2236
2488
  base = base->tp_base;
@@ -2316,7 +2568,7 @@ static void
2316
2568
  PrivateAttrType_del(PyObject* cls)
2317
2569
  {
2318
2570
  PrivateAttrType_finalize(cls);
2319
- (Py_TYPE(cls))->tp_free(cls);
2571
+ PyType_Type.tp_dealloc(cls);
2320
2572
  }
2321
2573
 
2322
2574
  static const char* PrivateAttrBase_doc = "The class to help to create private attribute. It does not have any special behavior.";
@@ -2775,7 +3027,8 @@ static PyModuleDef def = {
2775
3027
  PyMODINIT_FUNC
2776
3028
  PyInit_private_attribute(void)
2777
3029
  {
2778
- if (PyType_Ready(&PrivateWrapType) < 0 ||
3030
+ if (init_all_slots() <0 ||
3031
+ PyType_Ready(&PrivateWrapType) < 0 ||
2779
3032
  PyType_Ready(&PrivateWrapProxyType) < 0 ||
2780
3033
  PyType_Ready(&PrivateAttrType) < 0 ||
2781
3034
  PyType_Ready(&PrivateModuleType) < 0 ||
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: private_attribute_cpp
3
- Version: 1.3.6
3
+ Version: 1.3.7
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
@@ -19,7 +19,7 @@ readme = open('README.md').read()
19
19
 
20
20
  setup(
21
21
  name='private_attribute_cpp',
22
- version='1.3.6',
22
+ version='1.3.7',
23
23
  author="HuangHaoHua",
24
24
  author_email="13140752715@example.com",
25
25
  description='A Python package that provides a way to define private attributes in C++ implementation.',