private-attribute-cpp 2.1.24__tar.gz → 2.2.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.
- {private_attribute_cpp-2.1.24/private_attribute_cpp.egg-info → private_attribute_cpp-2.2.0}/PKG-INFO +4 -2
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/README.md +3 -1
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute.cpp +213 -162
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute.pyi +9 -1
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0/private_attribute_cpp.egg-info}/PKG-INFO +4 -2
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute_cpp.egg-info/SOURCES.txt +3 -1
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/setup.py +1 -1
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_attr_classify_owner.py +6 -6
- private_attribute_cpp-2.2.0/tests/test_init_subclass.py +85 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_inheritance.py +42 -20
- private_attribute_cpp-2.2.0/tests/test_use_parents.py +129 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/LICENSE +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/MANIFEST.in +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/picosha2.h +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/private_attribute_cpp.egg-info/top_level.txt +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/setup.cfg +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_class_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_gc_collect.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_abc.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_func.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_method.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_wrap.py +0 -0
- {private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_slots_cycle.py +0 -0
{private_attribute_cpp-2.1.24/private_attribute_cpp.egg-info → private_attribute_cpp-2.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: private_attribute_cpp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -253,7 +253,9 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
253
253
|
- One class defined in another class cannot use another class's private attribute.
|
|
254
254
|
- 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__`.
|
|
255
255
|
- Since 2.1.0: the code of a subclass can no longer access the private attributes of its parent classes - a parent's private attribute is only reachable from the parent's own code, or from a class that declares the same name in its own `__private_attrs__`.
|
|
256
|
-
- Since 2.1.0: if a subclass
|
|
256
|
+
- Since 2.1.0: if a subclass declares a name in its own `__private_attrs__` that is also private to a parent, the two are stored separately (instance attributes per declaring class, class-level attributes per class). Class-level resolution is per-subject through the parent's code: reading the name on a subclass subject returns the subclass's own value, and the parent's own value is untouched.
|
|
257
|
+
- Since 2.2.0: a subclass must NOT define an attribute in its namespace (class body) whose name is private to a parent class, unless that name is declared in its own `__private_attrs__` or the class opts in with `use_parents=True`.
|
|
258
|
+
- Since 2.2.0: pass `use_parents=True` in the class definition to inherit all parent classes' private attribute names into the subclass's own private-attribute set. The subclass's code can then read/write those names, but the values are stored in the subclass's own storage (they never overwrite the parent's own values). The Python-level `__private_attrs__` tuple is left unchanged; only the internal private-attribute name set is merged. Default is `False`.
|
|
257
259
|
- 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.
|
|
258
260
|
- `private_attribute.register_metaclass` must be called with the metaclass which supports weakref.
|
|
259
261
|
- Don't set `__static_attributes__` in private attribute class, or it will be removed.
|
|
@@ -232,7 +232,9 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
232
232
|
- One class defined in another class cannot use another class's private attribute.
|
|
233
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__`.
|
|
234
234
|
- Since 2.1.0: the code of a subclass can no longer access the private attributes of its parent classes - a parent's private attribute is only reachable from the parent's own code, or from a class that declares the same name in its own `__private_attrs__`.
|
|
235
|
-
- Since 2.1.0: if a subclass
|
|
235
|
+
- Since 2.1.0: if a subclass declares a name in its own `__private_attrs__` that is also private to a parent, the two are stored separately (instance attributes per declaring class, class-level attributes per class). Class-level resolution is per-subject through the parent's code: reading the name on a subclass subject returns the subclass's own value, and the parent's own value is untouched.
|
|
236
|
+
- Since 2.2.0: a subclass must NOT define an attribute in its namespace (class body) whose name is private to a parent class, unless that name is declared in its own `__private_attrs__` or the class opts in with `use_parents=True`.
|
|
237
|
+
- Since 2.2.0: pass `use_parents=True` in the class definition to inherit all parent classes' private attribute names into the subclass's own private-attribute set. The subclass's code can then read/write those names, but the values are stored in the subclass's own storage (they never overwrite the parent's own values). The Python-level `__private_attrs__` tuple is left unchanged; only the internal private-attribute name set is merged. Default is `False`.
|
|
236
238
|
- 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.
|
|
237
239
|
- `private_attribute.register_metaclass` must be called with the metaclass which supports weakref.
|
|
238
240
|
- Don't set `__static_attributes__` in private attribute class, or it will be removed.
|
|
@@ -328,9 +328,10 @@ struct FinalObject
|
|
|
328
328
|
|
|
329
329
|
static TwoStringTuple get_string_hash_tuple2(const std::string& name) noexcept;
|
|
330
330
|
static PyCodeObject* get_now_code() noexcept;
|
|
331
|
-
static
|
|
331
|
+
static bool need_analyse_type(PyObject* type) noexcept;
|
|
332
|
+
static const std::vector<uintptr_t>& type_get_parent_ids(uintptr_t type_id) noexcept;
|
|
332
333
|
static bool type_private_attr(uintptr_t type, const std::string& name) noexcept;
|
|
333
|
-
static FinalObject type_get_final_attr(uintptr_t
|
|
334
|
+
static FinalObject type_get_final_attr(uintptr_t main_type, uintptr_t target_type, const std::string& name) noexcept;
|
|
334
335
|
|
|
335
336
|
// Result of classifying an attribute access.
|
|
336
337
|
// - type_id: the node on the MRO chain (a class) whose code is currently running
|
|
@@ -377,8 +378,8 @@ attr_classify(uintptr_t typ_id, const std::string& name, PyCodeObject* code) noe
|
|
|
377
378
|
// Build the MRO chain: the actual class first, then its parents.
|
|
378
379
|
std::vector<uintptr_t> chain;
|
|
379
380
|
chain.push_back(typ_id);
|
|
380
|
-
|
|
381
|
-
auto& parent_ids =
|
|
381
|
+
{
|
|
382
|
+
auto& parent_ids = type_get_parent_ids(typ_id);
|
|
382
383
|
chain.insert(chain.end(), parent_ids.begin(), parent_ids.end());
|
|
383
384
|
}
|
|
384
385
|
|
|
@@ -603,7 +604,7 @@ id_getattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
603
604
|
// Class-level fallback resolves per-subject (the instance's type): a
|
|
604
605
|
// subclass's separately-stored shadow of a parent's private name is
|
|
605
606
|
// visible through the per-subject walk in type_get_final_attr.
|
|
606
|
-
FinalObject final_object = type_get_final_attr((uintptr_t)typ, attr_name);
|
|
607
|
+
FinalObject final_object = type_get_final_attr(final_id, (uintptr_t)typ, attr_name);
|
|
607
608
|
if (final_object.status == -2) {
|
|
608
609
|
return NULL;
|
|
609
610
|
}
|
|
@@ -693,10 +694,10 @@ id_getattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
693
694
|
}
|
|
694
695
|
|
|
695
696
|
static PyObject*
|
|
696
|
-
type_getattr(PyObject* typ, const std::string& attr_name) noexcept
|
|
697
|
+
type_getattr(uintptr_t main_type, PyObject* typ, const std::string& attr_name) noexcept
|
|
697
698
|
{
|
|
698
699
|
uintptr_t typ_id = (uintptr_t)typ;
|
|
699
|
-
FinalObject final_object = type_get_final_attr(typ_id, attr_name);
|
|
700
|
+
FinalObject final_object = type_get_final_attr(main_type, typ_id, attr_name);
|
|
700
701
|
if (final_object.status == -2) {
|
|
701
702
|
return NULL;
|
|
702
703
|
}
|
|
@@ -730,7 +731,7 @@ id_setattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
730
731
|
{
|
|
731
732
|
uintptr_t obj_id = (uintptr_t) obj;
|
|
732
733
|
// Class-level fallback resolves per-subject (the instance's type).
|
|
733
|
-
FinalObject final_object = type_get_final_attr((uintptr_t)typ, attr_name);
|
|
734
|
+
FinalObject final_object = type_get_final_attr(final_id, (uintptr_t)typ, attr_name);
|
|
734
735
|
if (final_object.status == -2) {
|
|
735
736
|
return -1;
|
|
736
737
|
}
|
|
@@ -780,50 +781,45 @@ id_setattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
780
781
|
return 0;
|
|
781
782
|
}
|
|
782
783
|
|
|
783
|
-
static int type_delattr(PyObject* typ, const std::string& attr_name) noexcept;
|
|
784
|
+
static int type_delattr(uintptr_t main_type, PyObject* typ, const std::string& attr_name) noexcept;
|
|
784
785
|
|
|
785
786
|
static int
|
|
786
|
-
type_setattr(PyObject* typ, const std::string& attr_name, PyObject* value) noexcept
|
|
787
|
+
type_setattr(uintptr_t main_type, PyObject* typ, const std::string& attr_name, PyObject* value) noexcept
|
|
787
788
|
{
|
|
788
789
|
if (!value) {
|
|
789
|
-
return type_delattr(typ, attr_name);
|
|
790
|
+
return type_delattr(main_type, typ, attr_name);
|
|
790
791
|
}
|
|
791
|
-
uintptr_t
|
|
792
|
-
uintptr_t final_id = type_set_attr_long_long_guidance(typ_id, attr_name);
|
|
792
|
+
uintptr_t target_type = (uintptr_t) typ;
|
|
793
793
|
std::string final_key;
|
|
794
794
|
PyObject* type_need_call;
|
|
795
|
-
if (::AllData::type_need_call.find(
|
|
796
|
-
type_need_call = ::AllData::type_need_call[
|
|
795
|
+
if (::AllData::type_need_call.find(target_type) != ::AllData::type_need_call.end()) {
|
|
796
|
+
type_need_call = ::AllData::type_need_call[target_type];
|
|
797
797
|
} else {
|
|
798
798
|
type_need_call = NULL;
|
|
799
799
|
}
|
|
800
800
|
if (type_need_call) {
|
|
801
|
-
auto private_name_result = custom_random_string(
|
|
801
|
+
auto private_name_result = custom_random_string(target_type, attr_name, type_need_call);
|
|
802
802
|
if (!private_name_result.ok) {
|
|
803
803
|
return -1;
|
|
804
804
|
}
|
|
805
805
|
final_key = private_name_result.value;
|
|
806
806
|
} else {
|
|
807
|
-
final_key = default_random_string(
|
|
808
|
-
}
|
|
809
|
-
if (final_id == 0) {
|
|
810
|
-
PyErr_SetString(PyExc_TypeError, "type not found");
|
|
811
|
-
return -1;
|
|
807
|
+
final_key = default_random_string(target_type, attr_name);
|
|
812
808
|
}
|
|
813
|
-
if (
|
|
814
|
-
type_create_mutex(
|
|
809
|
+
if (main_type == target_type) {
|
|
810
|
+
type_create_mutex(target_type);
|
|
815
811
|
{
|
|
816
|
-
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[
|
|
817
|
-
::AllData::type_attr_dict[
|
|
812
|
+
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[target_type]);
|
|
813
|
+
::AllData::type_attr_dict[target_type][final_key] = value;
|
|
818
814
|
}
|
|
819
815
|
return 0;
|
|
820
816
|
} else {
|
|
821
817
|
// The accessed class is a subclass that stores its own value for a
|
|
822
|
-
// parent's private name separately: all_type_subclass_attr[
|
|
823
|
-
type_parenttype_create_mutex(
|
|
818
|
+
// parent's private name separately: all_type_subclass_attr[main_type][target_type].
|
|
819
|
+
type_parenttype_create_mutex(main_type, target_type);
|
|
824
820
|
{
|
|
825
|
-
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[
|
|
826
|
-
::AllData::all_type_subclass_attr[
|
|
821
|
+
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[main_type][target_type]);
|
|
822
|
+
::AllData::all_type_subclass_attr[main_type][target_type][final_key] = value;
|
|
827
823
|
return 0;
|
|
828
824
|
}
|
|
829
825
|
}
|
|
@@ -834,7 +830,7 @@ id_delattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
834
830
|
{
|
|
835
831
|
uintptr_t obj_id = (uintptr_t) obj;
|
|
836
832
|
// Class-level fallback resolves per-subject (the instance's type).
|
|
837
|
-
FinalObject final_object = type_get_final_attr((uintptr_t)typ, attr_name);
|
|
833
|
+
FinalObject final_object = type_get_final_attr(final_id, (uintptr_t)typ, attr_name);
|
|
838
834
|
if (final_object.status == -2) {
|
|
839
835
|
return -1;
|
|
840
836
|
}
|
|
@@ -892,34 +888,29 @@ id_delattr(uintptr_t final_id, const std::string& attr_name, PyObject* obj, PyOb
|
|
|
892
888
|
}
|
|
893
889
|
|
|
894
890
|
static int
|
|
895
|
-
type_delattr(PyObject* typ, const std::string& attr_name) noexcept
|
|
891
|
+
type_delattr(uintptr_t main_type, PyObject* typ, const std::string& attr_name) noexcept
|
|
896
892
|
{
|
|
897
|
-
uintptr_t
|
|
898
|
-
uintptr_t final_id = type_set_attr_long_long_guidance(typ_id, attr_name);
|
|
893
|
+
uintptr_t target_type = (uintptr_t) typ;
|
|
899
894
|
std::string final_key;
|
|
900
895
|
PyObject* type_need_call;
|
|
901
|
-
if (::AllData::type_need_call.find(
|
|
902
|
-
type_need_call = ::AllData::type_need_call[
|
|
896
|
+
if (::AllData::type_need_call.find(target_type) != ::AllData::type_need_call.end()) {
|
|
897
|
+
type_need_call = ::AllData::type_need_call[target_type];
|
|
903
898
|
} else {
|
|
904
899
|
type_need_call = NULL;
|
|
905
900
|
}
|
|
906
901
|
if (type_need_call) {
|
|
907
|
-
auto private_name_result = custom_random_string(
|
|
902
|
+
auto private_name_result = custom_random_string(target_type, attr_name, type_need_call);
|
|
908
903
|
if (!private_name_result.ok) {
|
|
909
904
|
return -1;
|
|
910
905
|
}
|
|
911
906
|
final_key = private_name_result.value;
|
|
912
907
|
} else {
|
|
913
|
-
final_key = default_random_string(
|
|
914
|
-
}
|
|
915
|
-
if (final_id == 0) {
|
|
916
|
-
PyErr_SetString(PyExc_TypeError, "type not found");
|
|
917
|
-
return -1;
|
|
908
|
+
final_key = default_random_string(target_type, attr_name);
|
|
918
909
|
}
|
|
919
|
-
if (
|
|
920
|
-
type_create_mutex(
|
|
921
|
-
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[
|
|
922
|
-
if (::AllData::type_attr_dict[
|
|
910
|
+
if (main_type == target_type) {
|
|
911
|
+
type_create_mutex(target_type);
|
|
912
|
+
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[target_type]);
|
|
913
|
+
if (::AllData::type_attr_dict[target_type].find(final_key) == ::AllData::type_attr_dict[target_type].end()) {
|
|
923
914
|
const char* type_name = get_name_from_tp_name((PyTypeObject*)typ);
|
|
924
915
|
if (type_name == NULL) {
|
|
925
916
|
return -1;
|
|
@@ -927,11 +918,11 @@ type_delattr(PyObject* typ, const std::string& attr_name) noexcept
|
|
|
927
918
|
PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '%s'", type_name, attr_name.c_str());
|
|
928
919
|
return -1;
|
|
929
920
|
}
|
|
930
|
-
::AllData::type_attr_dict[
|
|
921
|
+
::AllData::type_attr_dict[target_type].erase(final_key);
|
|
931
922
|
} else {
|
|
932
|
-
type_parenttype_create_mutex(
|
|
933
|
-
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[
|
|
934
|
-
if (::AllData::all_type_subclass_attr[
|
|
923
|
+
type_parenttype_create_mutex(main_type, target_type);
|
|
924
|
+
std::unique_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[main_type][target_type]);
|
|
925
|
+
if (::AllData::all_type_subclass_attr[main_type][target_type].find(final_key) == ::AllData::all_type_subclass_attr[main_type][target_type].end()) {
|
|
935
926
|
const char* type_name = get_name_from_tp_name((PyTypeObject*)typ);
|
|
936
927
|
if (type_name == NULL) {
|
|
937
928
|
return -1;
|
|
@@ -939,7 +930,7 @@ type_delattr(PyObject* typ, const std::string& attr_name) noexcept
|
|
|
939
930
|
PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '%s'", type_name, attr_name.c_str());
|
|
940
931
|
return -1;
|
|
941
932
|
}
|
|
942
|
-
::AllData::all_type_subclass_attr[
|
|
933
|
+
::AllData::all_type_subclass_attr[main_type][target_type].erase(final_key);
|
|
943
934
|
}
|
|
944
935
|
return 0;
|
|
945
936
|
}
|
|
@@ -2470,123 +2461,122 @@ get_string_hash_tuple2(const std::string& name) noexcept
|
|
|
2470
2461
|
}
|
|
2471
2462
|
|
|
2472
2463
|
static FinalObject
|
|
2473
|
-
type_get_final_attr(uintptr_t
|
|
2464
|
+
type_get_final_attr(uintptr_t main_type, uintptr_t target_type, const std::string& name) noexcept
|
|
2474
2465
|
{
|
|
2475
|
-
//
|
|
2476
|
-
//
|
|
2477
|
-
//
|
|
2478
|
-
//
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2466
|
+
// main_type is the code owner (the class declaring the attribute private);
|
|
2467
|
+
// target_type is the object's type whose attribute is being accessed. Only
|
|
2468
|
+
// type_attr_dict[main_type] and all_type_subclass_attr[main_type][*] are
|
|
2469
|
+
// analysed. Each visited type's own type_need_call drives its key.
|
|
2470
|
+
std::vector<uintptr_t> chain;
|
|
2471
|
+
chain.push_back(target_type);
|
|
2472
|
+
if (::AllData::all_type_parent_id.find(target_type) != ::AllData::all_type_parent_id.end()) {
|
|
2473
|
+
auto& parent_ids = ::AllData::all_type_parent_id[target_type];
|
|
2474
|
+
chain.insert(chain.end(), parent_ids.begin(), parent_ids.end());
|
|
2475
|
+
}
|
|
2476
|
+
for (auto& type : chain) {
|
|
2477
|
+
std::string key;
|
|
2478
|
+
PyObject* need_call = NULL;
|
|
2479
|
+
if (::AllData::type_need_call.find(type) != ::AllData::type_need_call.end()) {
|
|
2480
|
+
need_call = ::AllData::type_need_call[type];
|
|
2481
|
+
}
|
|
2482
|
+
if (need_call != NULL) {
|
|
2483
|
+
auto private_name_result = custom_random_string(type, name, need_call);
|
|
2484
|
+
if (!private_name_result.ok) {
|
|
2485
|
+
return -2; // -2 means exception
|
|
2495
2486
|
}
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2487
|
+
key = private_name_result.value;
|
|
2488
|
+
} else {
|
|
2489
|
+
key = default_random_string(type, name);
|
|
2490
|
+
}
|
|
2491
|
+
if (type == main_type) {
|
|
2492
|
+
type_create_mutex(main_type);
|
|
2493
|
+
std::shared_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[main_type]);
|
|
2494
|
+
auto& item_set = ::AllData::type_attr_dict[main_type];
|
|
2499
2495
|
if (item_set.find(key) != item_set.end()) {
|
|
2500
2496
|
PyObject* obj = item_set[key];
|
|
2501
2497
|
return obj;
|
|
2502
2498
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
auto& now_mro_dict = ::AllData::all_type_subclass_attr[parent_id];
|
|
2514
|
-
for (auto& now_visited_id: now_visited) {
|
|
2515
|
-
if (now_mro_dict.find(now_visited_id) != now_mro_dict.end()) {
|
|
2516
|
-
std::string key;
|
|
2517
|
-
if (::AllData::type_need_call.find(now_visited_id) != ::AllData::type_need_call.end()) {
|
|
2518
|
-
PyObject* func = ::AllData::type_need_call[now_visited_id];
|
|
2519
|
-
if (func != NULL) {
|
|
2520
|
-
auto private_name_result = custom_random_string(now_visited_id, name, func);
|
|
2521
|
-
if (!private_name_result.ok) {
|
|
2522
|
-
return -2; // -2 means exception
|
|
2523
|
-
}
|
|
2524
|
-
key = private_name_result.value;
|
|
2525
|
-
} else {
|
|
2526
|
-
key = default_random_string(now_visited_id, name);
|
|
2527
|
-
}
|
|
2528
|
-
} else {
|
|
2529
|
-
key = default_random_string(now_visited_id, name);
|
|
2530
|
-
}
|
|
2531
|
-
type_parenttype_create_mutex(parent_id, now_visited_id);
|
|
2532
|
-
std::shared_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[parent_id][now_visited_id]);
|
|
2533
|
-
if (now_mro_dict[now_visited_id].find(key) != now_mro_dict[now_visited_id].end()) {
|
|
2534
|
-
PyObject* obj = now_mro_dict[now_visited_id][key];
|
|
2535
|
-
return obj;
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
std::string key;
|
|
2541
|
-
if (::AllData::type_need_call.find(parent_id) != ::AllData::type_need_call.end()) {
|
|
2542
|
-
PyObject* func = ::AllData::type_need_call[parent_id];
|
|
2543
|
-
if (func != NULL) {
|
|
2544
|
-
auto private_name_result = custom_random_string(parent_id, name, func);
|
|
2545
|
-
if (!private_name_result.ok) {
|
|
2546
|
-
return -2; // -2 means exception
|
|
2547
|
-
}
|
|
2548
|
-
key = private_name_result.value;
|
|
2549
|
-
} else {
|
|
2550
|
-
key = default_random_string(parent_id, name);
|
|
2551
|
-
}
|
|
2552
|
-
} else {
|
|
2553
|
-
key = default_random_string(parent_id, name);
|
|
2554
|
-
}
|
|
2555
|
-
type_create_mutex(parent_id);
|
|
2556
|
-
auto& item_set = ::AllData::type_attr_dict[parent_id];
|
|
2557
|
-
std::shared_lock<std::shared_mutex> lock(*::AllData::all_type_mutex[parent_id]);
|
|
2558
|
-
if (item_set.find(key) != item_set.end()) {
|
|
2559
|
-
PyObject* obj = item_set[key];
|
|
2560
|
-
return obj;
|
|
2561
|
-
}
|
|
2499
|
+
return -1; // main_type has no such attribute -> not found
|
|
2500
|
+
}
|
|
2501
|
+
if (::AllData::all_type_subclass_attr.find(main_type) != ::AllData::all_type_subclass_attr.end()) {
|
|
2502
|
+
auto& now_mro_dict = ::AllData::all_type_subclass_attr[main_type];
|
|
2503
|
+
if (now_mro_dict.find(type) != now_mro_dict.end()) {
|
|
2504
|
+
type_parenttype_create_mutex(main_type, type);
|
|
2505
|
+
std::shared_lock<std::shared_mutex> lock(*::AllData::all_type_subclass_mutex[main_type][type]);
|
|
2506
|
+
if (now_mro_dict[type].find(key) != now_mro_dict[type].end()) {
|
|
2507
|
+
PyObject* obj = now_mro_dict[type][key];
|
|
2508
|
+
return obj;
|
|
2562
2509
|
}
|
|
2563
2510
|
}
|
|
2564
|
-
now_visited.push_back(parent_id);
|
|
2565
2511
|
}
|
|
2566
2512
|
}
|
|
2567
2513
|
return -1; // -1 means not found
|
|
2568
2514
|
}
|
|
2569
2515
|
|
|
2570
|
-
static
|
|
2571
|
-
|
|
2516
|
+
static bool
|
|
2517
|
+
need_analyse_type(PyObject* type) noexcept;
|
|
2518
|
+
|
|
2519
|
+
static bool
|
|
2520
|
+
type_private_attr_declared(uintptr_t type_id, const std::string& name) noexcept
|
|
2572
2521
|
{
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2522
|
+
// Fallback used while a type is still being created (e.g. inside
|
|
2523
|
+
// __init_subclass__), when all_type_attr_set[type_id] has not been set yet.
|
|
2524
|
+
// Read '__private_attrs__' from the type's tp_dict and check membership
|
|
2525
|
+
// via Python tuple equality.
|
|
2526
|
+
KeepPythonException _;
|
|
2527
|
+
PyTypeObject* tp = (PyTypeObject*)type_id;
|
|
2528
|
+
if (!PyType_Check(tp)) {
|
|
2529
|
+
return false;
|
|
2530
|
+
}
|
|
2531
|
+
PyObject* private_attrs = PyDict_GetItemString(tp->tp_dict, "__private_attrs__");
|
|
2532
|
+
if (!private_attrs || !PySequence_Check(private_attrs)) {
|
|
2533
|
+
return false;
|
|
2534
|
+
}
|
|
2535
|
+
PyObjectStorage hash_tuple = PyObjectStorage::from_new_ref(get_string_hash_tuple(name));
|
|
2536
|
+
if (!hash_tuple) {
|
|
2537
|
+
return false;
|
|
2538
|
+
}
|
|
2539
|
+
Py_ssize_t len = PySequence_Length(private_attrs);
|
|
2540
|
+
for (Py_ssize_t i = 0; i < len; i++) {
|
|
2541
|
+
PyObjectStorage item = PyObjectStorage::from_new_ref(PySequence_GetItem(private_attrs, i));
|
|
2542
|
+
if (!item) {
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
int eq = PyObject_RichCompareBool(item, hash_tuple, Py_EQ);
|
|
2546
|
+
if (eq < 0) {
|
|
2547
|
+
PyErr_Clear();
|
|
2548
|
+
return false;
|
|
2549
|
+
}
|
|
2550
|
+
if (eq) {
|
|
2551
|
+
return true;
|
|
2578
2552
|
}
|
|
2579
2553
|
}
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2554
|
+
return false;
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
static const std::vector<uintptr_t>&
|
|
2558
|
+
type_get_parent_ids(uintptr_t type_id) noexcept
|
|
2559
|
+
{
|
|
2560
|
+
auto it = ::AllData::all_type_parent_id.find(type_id);
|
|
2561
|
+
if (it != ::AllData::all_type_parent_id.end()) {
|
|
2562
|
+
return it->second;
|
|
2563
|
+
}
|
|
2564
|
+
// The type has not been fully registered yet (e.g. inside __init_subclass__):
|
|
2565
|
+
// build the MRO chain on the fly from tp_mro.
|
|
2566
|
+
std::vector<uintptr_t> mro_vector;
|
|
2567
|
+
PyTypeObject* tp = (PyTypeObject*)type_id;
|
|
2568
|
+
if (PyType_Check(tp) && tp->tp_mro) {
|
|
2569
|
+
Py_ssize_t mro_size = PyTuple_GET_SIZE(tp->tp_mro);
|
|
2570
|
+
for (Py_ssize_t i = 1; i < mro_size; i++) {
|
|
2571
|
+
PyObject* item = PyTuple_GET_ITEM(tp->tp_mro, i);
|
|
2572
|
+
if (!item || !PyType_Check(item) || !need_analyse_type(item)) {
|
|
2573
|
+
continue;
|
|
2586
2574
|
}
|
|
2575
|
+
mro_vector.push_back((uintptr_t)item);
|
|
2587
2576
|
}
|
|
2588
2577
|
}
|
|
2589
|
-
|
|
2578
|
+
::AllData::all_type_parent_id[type_id] = mro_vector;
|
|
2579
|
+
return ::AllData::all_type_parent_id[type_id];
|
|
2590
2580
|
}
|
|
2591
2581
|
|
|
2592
2582
|
static bool
|
|
@@ -2598,14 +2588,18 @@ type_private_attr(uintptr_t type_id, const std::string& name) noexcept
|
|
|
2598
2588
|
if (item_set.find(hash_tuple) != item_set.end()) {
|
|
2599
2589
|
return true;
|
|
2600
2590
|
}
|
|
2591
|
+
} else if (type_private_attr_declared(type_id, name)) {
|
|
2592
|
+
return true;
|
|
2601
2593
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2594
|
+
auto& parent_id_list = type_get_parent_ids(type_id);
|
|
2595
|
+
for (auto& parent_id: parent_id_list) {
|
|
2596
|
+
if (::AllData::all_type_attr_set.find(parent_id) != ::AllData::all_type_attr_set.end()) {
|
|
2605
2597
|
auto& item_set = ::AllData::all_type_attr_set[parent_id];
|
|
2606
2598
|
if (item_set.find(hash_tuple) != item_set.end()) {
|
|
2607
2599
|
return true;
|
|
2608
2600
|
}
|
|
2601
|
+
} else if (type_private_attr_declared(parent_id, name)) {
|
|
2602
|
+
return true;
|
|
2609
2603
|
}
|
|
2610
2604
|
}
|
|
2611
2605
|
return false;
|
|
@@ -2758,6 +2752,7 @@ struct PrivateAttrCreationData
|
|
|
2758
2752
|
PyObject* name = nullptr;
|
|
2759
2753
|
PyObject* bases = nullptr;
|
|
2760
2754
|
PyObject* attrs = nullptr;
|
|
2755
|
+
bool use_parents = false;
|
|
2761
2756
|
bool cleared = false;
|
|
2762
2757
|
|
|
2763
2758
|
void clear() noexcept {
|
|
@@ -2857,6 +2852,21 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2857
2852
|
return false;
|
|
2858
2853
|
}
|
|
2859
2854
|
|
|
2855
|
+
if (kwds && PyDict_Check(kwds)) {
|
|
2856
|
+
int has_use_parents = PyDict_ContainsString(kwds, "use_parents");
|
|
2857
|
+
if (has_use_parents < 0) {
|
|
2858
|
+
return false;
|
|
2859
|
+
}
|
|
2860
|
+
if (has_use_parents) {
|
|
2861
|
+
PyObject* use_parents_obj = PyDict_GetItemString(kwds, "use_parents");
|
|
2862
|
+
int truth = PyObject_IsTrue(use_parents_obj);
|
|
2863
|
+
if (truth < 0) {
|
|
2864
|
+
return false;
|
|
2865
|
+
}
|
|
2866
|
+
data.use_parents = (truth == 1);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2860
2870
|
PyObjectStorage __private_attrs__;
|
|
2861
2871
|
if (PyDict_ContainsString(data.attrs, "__private_attrs__")) {
|
|
2862
2872
|
__private_attrs__ = PyDict_GetItemString(data.attrs, "__private_attrs__");
|
|
@@ -2865,6 +2875,10 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2865
2875
|
__private_attrs__ = PyObjectStorage::from_new_ref(PyTuple_New(0));
|
|
2866
2876
|
}
|
|
2867
2877
|
|
|
2878
|
+
if (!__private_attrs__) {
|
|
2879
|
+
return false;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2868
2882
|
if (PyUnicode_Check(__private_attrs__)) {
|
|
2869
2883
|
PyObject* new___private_attrs__ = PyTuple_New(1);
|
|
2870
2884
|
if (!new___private_attrs__) {
|
|
@@ -2985,10 +2999,31 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2985
2999
|
data.all_need_analyse_base.push_back((uintptr_t)base);
|
|
2986
3000
|
}
|
|
2987
3001
|
|
|
3002
|
+
if (data.use_parents) {
|
|
3003
|
+
// 2.2.0: when use_parents=True, merge every ancestor's private attribute
|
|
3004
|
+
// names into this class's own private-attr set (the TwoStringTuple set).
|
|
3005
|
+
// The Python-level '__private_attrs__' tuple is left untouched; only
|
|
3006
|
+
// AllData::all_type_attr_set[this] gains the inherited names.
|
|
3007
|
+
std::vector<uintptr_t> all_ancestors;
|
|
3008
|
+
for (auto& base : data.all_need_analyse_base) {
|
|
3009
|
+
all_ancestors.push_back(base);
|
|
3010
|
+
if (::AllData::all_type_parent_id.find(base) != ::AllData::all_type_parent_id.end()) {
|
|
3011
|
+
auto& ancestor_ids = ::AllData::all_type_parent_id[base];
|
|
3012
|
+
all_ancestors.insert(all_ancestors.end(), ancestor_ids.begin(), ancestor_ids.end());
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
for (auto& ancestor : all_ancestors) {
|
|
3016
|
+
if (::AllData::all_type_attr_set.find(ancestor) != ::AllData::all_type_attr_set.end()) {
|
|
3017
|
+
auto& ancestor_set = ::AllData::all_type_attr_set[ancestor];
|
|
3018
|
+
data.private_attrs_set.insert(ancestor_set.begin(), ancestor_set.end());
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
|
|
2988
3023
|
std::function<uintptr_t(std::string)> need_remove_to_subclass = [&data](std::string attr_name){
|
|
2989
3024
|
for (auto& base: data.all_need_analyse_base) {
|
|
2990
3025
|
if (type_private_attr(base, attr_name)) {
|
|
2991
|
-
return
|
|
3026
|
+
return base;
|
|
2992
3027
|
}
|
|
2993
3028
|
}
|
|
2994
3029
|
return (uintptr_t)0;
|
|
@@ -3027,13 +3062,19 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
3027
3062
|
uintptr_t need_remove_subclass_id = need_remove_to_subclass(attr_name);
|
|
3028
3063
|
if (need_remove_subclass_id) {
|
|
3029
3064
|
// The class body defines an attribute whose name is private to a
|
|
3030
|
-
// parent class
|
|
3031
|
-
//
|
|
3032
|
-
if (data.
|
|
3033
|
-
|
|
3065
|
+
// parent class and is not declared in this class's own
|
|
3066
|
+
// '__private_attrs__'.
|
|
3067
|
+
if (!data.use_parents) {
|
|
3068
|
+
// 2.2.0: forbidden unless the parent private attribute is
|
|
3069
|
+
// explicitly inherited via use_parents=True.
|
|
3070
|
+
PyErr_Format(PyExc_TypeError,
|
|
3071
|
+
"cannot define '%s' in class '%s': it is private to a parent class and not declared in this class's '__private_attrs__' (set use_parents=True to inherit parent private attributes)",
|
|
3072
|
+
attr_name.c_str(), data.class_name.c_str());
|
|
3073
|
+
return false;
|
|
3034
3074
|
}
|
|
3035
|
-
//
|
|
3036
|
-
|
|
3075
|
+
// use_parents=True: store the value into this class's own private
|
|
3076
|
+
// attribute storage (type_attr_dict[this]) instead of the parent's.
|
|
3077
|
+
data.need_remove_itself[attr_name] = need_value;
|
|
3037
3078
|
if (PyDict_DelItem(data.attrs_copy, key)) return false;
|
|
3038
3079
|
continue;
|
|
3039
3080
|
}
|
|
@@ -3057,6 +3098,9 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
3057
3098
|
if (has_private_func) {
|
|
3058
3099
|
if (PyDict_DelItemString(data.base_kwds, "private_func") < 0) return false;
|
|
3059
3100
|
}
|
|
3101
|
+
if (PyDict_ContainsString(kwds, "use_parents")) {
|
|
3102
|
+
if (PyDict_DelItemString(data.base_kwds, "use_parents") < 0) return false;
|
|
3103
|
+
}
|
|
3060
3104
|
}
|
|
3061
3105
|
|
|
3062
3106
|
return true;
|
|
@@ -3269,8 +3313,15 @@ PrivateAttrType_postprocess(PyObject* new_type, PrivateAttrCreationData& data) n
|
|
|
3269
3313
|
if (::AllData::all_type_subclass_mutex.find(i) == ::AllData::all_type_subclass_mutex.end()) {
|
|
3270
3314
|
::AllData::all_type_subclass_mutex[i].clear();
|
|
3271
3315
|
}
|
|
3272
|
-
|
|
3273
|
-
|
|
3316
|
+
// Do not wipe a parent's shadow already written for this class during
|
|
3317
|
+
// __init_subclass__ (e.g. `cls._value = ...` inside the parent's
|
|
3318
|
+
// __init_subclass__ runs inside tp_new, before postprocess).
|
|
3319
|
+
if (::AllData::all_type_subclass_attr[i].find(type_id) == ::AllData::all_type_subclass_attr[i].end()) {
|
|
3320
|
+
::AllData::all_type_subclass_attr[i][type_id] = {};
|
|
3321
|
+
}
|
|
3322
|
+
if (::AllData::all_type_subclass_mutex[i].find(type_id) == ::AllData::all_type_subclass_mutex[i].end()) {
|
|
3323
|
+
::AllData::all_type_subclass_mutex[i][type_id] = std::make_unique<std::shared_mutex>();
|
|
3324
|
+
}
|
|
3274
3325
|
}
|
|
3275
3326
|
|
|
3276
3327
|
for (auto& [key, value]: data.need_remove_itself) {
|
|
@@ -3418,7 +3469,7 @@ PrivateAttrType_getattr(PyObject* cls, PyObject* name) noexcept
|
|
|
3418
3469
|
return NULL;
|
|
3419
3470
|
}
|
|
3420
3471
|
if (classify.type_id != 0) {
|
|
3421
|
-
return type_getattr(cls, name_str);
|
|
3472
|
+
return type_getattr(classify.type_id, cls, name_str);
|
|
3422
3473
|
}
|
|
3423
3474
|
}
|
|
3424
3475
|
}
|
|
@@ -3766,7 +3817,7 @@ PrivateAttrType_setattr(PyObject* cls, PyObject* name, PyObject* value) noexcept
|
|
|
3766
3817
|
return -1;
|
|
3767
3818
|
}
|
|
3768
3819
|
if (classify.type_id != 0) {
|
|
3769
|
-
return type_setattr(cls, name_str, value);
|
|
3820
|
+
return type_setattr(classify.type_id, cls, name_str, value);
|
|
3770
3821
|
}
|
|
3771
3822
|
// owner is 0: not private for this access context -> normal attribute
|
|
3772
3823
|
}
|
|
@@ -99,7 +99,8 @@ class PrivateAttrType(type):
|
|
|
99
99
|
"metaclass for private attributes"
|
|
100
100
|
def __new__(cls, name: str, bases: tuple,
|
|
101
101
|
attrs: _PrivateAttrDict, /, *,
|
|
102
|
-
private_func: Callable[[int, str], str]|None = None
|
|
102
|
+
private_func: Callable[[int, str], str]|None = None,
|
|
103
|
+
use_parents: bool = False) -> PrivateAttrType: ...
|
|
103
104
|
|
|
104
105
|
class PrivateAttrBase(metaclass=PrivateAttrType):
|
|
105
106
|
"The class to help creating private attribute. It does not have any special behavior."
|
|
@@ -146,6 +147,13 @@ def prepare(name: str, bases: tuple, attrs: _PrivateAttrDict, /, **kwds) -> _Pri
|
|
|
146
147
|
|
|
147
148
|
private_attribute.register_metaclass(PrivateAbcMeta)
|
|
148
149
|
```
|
|
150
|
+
|
|
151
|
+
Optional keyword arguments:
|
|
152
|
+
- private_func: custom hidden-name generator `(obj_id, attr_name) -> str`.
|
|
153
|
+
- use_parents: when True, merge every parent class's private attribute names
|
|
154
|
+
into this class's own private-attribute set, so the subclass's code may
|
|
155
|
+
read/write those names (stored in the subclass's own storage). Default
|
|
156
|
+
False.
|
|
149
157
|
"""
|
|
150
158
|
...
|
|
151
159
|
|
{private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0/private_attribute_cpp.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: private_attribute_cpp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -253,7 +253,9 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
253
253
|
- One class defined in another class cannot use another class's private attribute.
|
|
254
254
|
- 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__`.
|
|
255
255
|
- Since 2.1.0: the code of a subclass can no longer access the private attributes of its parent classes - a parent's private attribute is only reachable from the parent's own code, or from a class that declares the same name in its own `__private_attrs__`.
|
|
256
|
-
- Since 2.1.0: if a subclass
|
|
256
|
+
- Since 2.1.0: if a subclass declares a name in its own `__private_attrs__` that is also private to a parent, the two are stored separately (instance attributes per declaring class, class-level attributes per class). Class-level resolution is per-subject through the parent's code: reading the name on a subclass subject returns the subclass's own value, and the parent's own value is untouched.
|
|
257
|
+
- Since 2.2.0: a subclass must NOT define an attribute in its namespace (class body) whose name is private to a parent class, unless that name is declared in its own `__private_attrs__` or the class opts in with `use_parents=True`.
|
|
258
|
+
- Since 2.2.0: pass `use_parents=True` in the class definition to inherit all parent classes' private attribute names into the subclass's own private-attribute set. The subclass's code can then read/write those names, but the values are stored in the subclass's own storage (they never overwrite the parent's own values). The Python-level `__private_attrs__` tuple is left unchanged; only the internal private-attribute name set is merged. Default is `False`.
|
|
257
259
|
- 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.
|
|
258
260
|
- `private_attribute.register_metaclass` must be called with the metaclass which supports weakref.
|
|
259
261
|
- Don't set `__static_attributes__` in private attribute class, or it will be removed.
|
|
@@ -13,10 +13,12 @@ private_attribute_cpp.egg-info/top_level.txt
|
|
|
13
13
|
tests/test_attr_classify_owner.py
|
|
14
14
|
tests/test_class_private_attrs.py
|
|
15
15
|
tests/test_gc_collect.py
|
|
16
|
+
tests/test_init_subclass.py
|
|
16
17
|
tests/test_private_abc.py
|
|
17
18
|
tests/test_private_attrs.py
|
|
18
19
|
tests/test_private_func.py
|
|
19
20
|
tests/test_private_inheritance.py
|
|
20
21
|
tests/test_private_method.py
|
|
21
22
|
tests/test_private_wrap.py
|
|
22
|
-
tests/test_slots_cycle.py
|
|
23
|
+
tests/test_slots_cycle.py
|
|
24
|
+
tests/test_use_parents.py
|
|
@@ -24,7 +24,7 @@ readme = open('README.md').read()
|
|
|
24
24
|
|
|
25
25
|
setup(
|
|
26
26
|
name='private_attribute_cpp',
|
|
27
|
-
version='2.
|
|
27
|
+
version='2.2.0',
|
|
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.',
|
{private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_attr_classify_owner.py
RENAMED
|
@@ -80,24 +80,24 @@ class TestCodeSwapOwnership(unittest.TestCase):
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
class TestFallbackSemantics(unittest.TestCase):
|
|
83
|
-
"""
|
|
84
|
-
|
|
83
|
+
"""Parent code always reads the parent's own storage, even when a subclass
|
|
84
|
+
shadows the name in its own class-level storage."""
|
|
85
85
|
|
|
86
|
-
def
|
|
86
|
+
def test_unwritten_read_uses_parent_storage(self):
|
|
87
87
|
class FreshChild(Parent):
|
|
88
88
|
__private_attrs__ = ["_n"]
|
|
89
89
|
_n = 100
|
|
90
90
|
f = Parent.inc
|
|
91
91
|
|
|
92
92
|
c = FreshChild()
|
|
93
|
-
self.assertEqual(c.peek(),
|
|
93
|
+
self.assertEqual(c.peek(), 0)
|
|
94
94
|
v1 = c.f()
|
|
95
95
|
self.assertEqual(v1, c.peek())
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
class TestNormalUsageRegression(unittest.TestCase):
|
|
99
99
|
"""Without tampering, nothing changes: child's own code uses child
|
|
100
|
-
storage, parent's code
|
|
100
|
+
storage, parent's code reads the parent's own storage (0)."""
|
|
101
101
|
|
|
102
102
|
def test_child_own_code_uses_child_storage(self):
|
|
103
103
|
class NormChild(Parent):
|
|
@@ -110,7 +110,7 @@ class TestNormalUsageRegression(unittest.TestCase):
|
|
|
110
110
|
|
|
111
111
|
c = NormChild()
|
|
112
112
|
self.assertEqual(c.own_inc(), 101)
|
|
113
|
-
self.assertEqual(c.peek(),
|
|
113
|
+
self.assertEqual(c.peek(), 0)
|
|
114
114
|
self.assertEqual(Parent().peek(), 0)
|
|
115
115
|
|
|
116
116
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import private_attribute
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestInitSubclassWrite(unittest.TestCase):
|
|
6
|
+
"""A parent may write a private attribute into a subclass (e.g. via
|
|
7
|
+
__init_subclass__); later, the parent's own code reading the subclass (or
|
|
8
|
+
an instance of it) must see the parent-stored value, while the subclass's
|
|
9
|
+
own same-name private storage stays independent."""
|
|
10
|
+
|
|
11
|
+
def test_init_subclass_write_visible_to_parent_code(self):
|
|
12
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
13
|
+
__private_attrs__ = ["_value"]
|
|
14
|
+
_value = "parent_own"
|
|
15
|
+
|
|
16
|
+
def __init_subclass__(cls, **kw):
|
|
17
|
+
super().__init_subclass__(**kw)
|
|
18
|
+
cls._value = "from_parent"
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def get_cls_value(cls):
|
|
22
|
+
return cls._value
|
|
23
|
+
|
|
24
|
+
def get_value(self):
|
|
25
|
+
return self._value
|
|
26
|
+
|
|
27
|
+
class Child(Parent):
|
|
28
|
+
__private_attrs__ = ["_value"]
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
super().__init__()
|
|
32
|
+
self._value = "child_own"
|
|
33
|
+
|
|
34
|
+
def get_own(self):
|
|
35
|
+
return self._value
|
|
36
|
+
|
|
37
|
+
# parent code reading the subclass class / instance sees the
|
|
38
|
+
# parent-stored value
|
|
39
|
+
self.assertEqual(Child.get_cls_value(), "from_parent")
|
|
40
|
+
self.assertEqual(Child().get_value(), "from_parent")
|
|
41
|
+
# the subclass's own code reads/writes its own independent storage
|
|
42
|
+
self.assertEqual(Child().get_own(), "child_own")
|
|
43
|
+
# the parent's own storage is untouched
|
|
44
|
+
self.assertEqual(Parent().get_value(), "parent_own")
|
|
45
|
+
# private attribute stays hidden from outside
|
|
46
|
+
with self.assertRaises(AttributeError):
|
|
47
|
+
Child()._value
|
|
48
|
+
with self.assertRaises(AttributeError):
|
|
49
|
+
Child._value
|
|
50
|
+
|
|
51
|
+
def test_parent_writes_child_after_creation(self):
|
|
52
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
53
|
+
__private_attrs__ = ["_value"]
|
|
54
|
+
_value = "parent_own"
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def write_child(cls, child_cls, v):
|
|
58
|
+
child_cls._value = v
|
|
59
|
+
|
|
60
|
+
def get_value(self):
|
|
61
|
+
return self._value
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def get_cls_value(cls):
|
|
65
|
+
return cls._value
|
|
66
|
+
|
|
67
|
+
class Child(Parent):
|
|
68
|
+
__private_attrs__ = ["_value"]
|
|
69
|
+
|
|
70
|
+
def __init__(self):
|
|
71
|
+
super().__init__()
|
|
72
|
+
self._value = "child_own"
|
|
73
|
+
|
|
74
|
+
def get_own(self):
|
|
75
|
+
return self._value
|
|
76
|
+
|
|
77
|
+
Parent.write_child(Child, "from_parent")
|
|
78
|
+
self.assertEqual(Child.get_cls_value(), "from_parent")
|
|
79
|
+
self.assertEqual(Child().get_value(), "from_parent")
|
|
80
|
+
self.assertEqual(Child().get_own(), "child_own")
|
|
81
|
+
self.assertEqual(Parent().get_value(), "parent_own")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
unittest.main()
|
{private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_private_inheritance.py
RENAMED
|
@@ -88,11 +88,9 @@ class TestPrivateInheritance(unittest.TestCase):
|
|
|
88
88
|
with self.assertRaises(AttributeError):
|
|
89
89
|
obj._value
|
|
90
90
|
|
|
91
|
-
def
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# per-subject through the PARENT's code. It does NOT grant the
|
|
95
|
-
# subclass's own code any access to the name.
|
|
91
|
+
def test_same_name_class_attrs_require_use_parents(self):
|
|
92
|
+
# 2.2.0: a class body may not define a name that is private to a parent
|
|
93
|
+
# unless it declares the name itself or opts in via use_parents=True.
|
|
96
94
|
class Parent(private_attribute.PrivateAttrBase):
|
|
97
95
|
__private_attrs__ = ["_value"]
|
|
98
96
|
_value = 10
|
|
@@ -100,7 +98,24 @@ class TestPrivateInheritance(unittest.TestCase):
|
|
|
100
98
|
def get_value(self):
|
|
101
99
|
return self._value
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
with self.assertRaises(TypeError):
|
|
102
|
+
class Child(Parent):
|
|
103
|
+
__private_attrs__ = []
|
|
104
|
+
_value = 20
|
|
105
|
+
|
|
106
|
+
def test_use_parents_class_attrs(self):
|
|
107
|
+
# 2.2.0: use_parents=True lets a subclass define a parent's private
|
|
108
|
+
# name in its own body; the value is stored in the subclass's own
|
|
109
|
+
# storage and the parent's value is untouched. Parent code always
|
|
110
|
+
# reads the parent's own storage.
|
|
111
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
112
|
+
__private_attrs__ = ["_value"]
|
|
113
|
+
_value = 10
|
|
114
|
+
|
|
115
|
+
def get_value(self):
|
|
116
|
+
return self._value
|
|
117
|
+
|
|
118
|
+
class Child(Parent, use_parents=True):
|
|
104
119
|
__private_attrs__ = []
|
|
105
120
|
_value = 20
|
|
106
121
|
|
|
@@ -109,19 +124,20 @@ class TestPrivateInheritance(unittest.TestCase):
|
|
|
109
124
|
|
|
110
125
|
self.assertEqual(Parent().get_value(), 10)
|
|
111
126
|
obj = Child()
|
|
112
|
-
# parent's code
|
|
113
|
-
self.assertEqual(obj.get_value(),
|
|
114
|
-
# the subclass's own code
|
|
115
|
-
|
|
116
|
-
obj.get_child_value()
|
|
127
|
+
# parent's code reads the parent's own storage, not the child's shadow
|
|
128
|
+
self.assertEqual(obj.get_value(), 10)
|
|
129
|
+
# the subclass's own code reads its own value
|
|
130
|
+
self.assertEqual(obj.get_child_value(), 20)
|
|
117
131
|
with self.assertRaises(AttributeError):
|
|
118
132
|
obj._value
|
|
119
133
|
with self.assertRaises(AttributeError):
|
|
120
134
|
Child._value
|
|
121
135
|
|
|
122
|
-
def
|
|
123
|
-
#
|
|
124
|
-
# the
|
|
136
|
+
def test_use_parents_class_attrs_write_separately(self):
|
|
137
|
+
# 2.2.0: with use_parents=True, parent code (set_value/get_value) writes
|
|
138
|
+
# and reads the parent's own shadow of the child's storage
|
|
139
|
+
# (all_type_subclass_attr[Parent][Child]); the subclass's own code
|
|
140
|
+
# operates on the child's own storage (type_attr_dict[Child]).
|
|
125
141
|
class Parent(private_attribute.PrivateAttrBase):
|
|
126
142
|
__private_attrs__ = ["_value"]
|
|
127
143
|
_value = 1
|
|
@@ -134,7 +150,7 @@ class TestPrivateInheritance(unittest.TestCase):
|
|
|
134
150
|
def get_value(cls):
|
|
135
151
|
return cls._value
|
|
136
152
|
|
|
137
|
-
class Child(Parent):
|
|
153
|
+
class Child(Parent, use_parents=True):
|
|
138
154
|
__private_attrs__ = []
|
|
139
155
|
_value = 2
|
|
140
156
|
|
|
@@ -142,16 +158,22 @@ class TestPrivateInheritance(unittest.TestCase):
|
|
|
142
158
|
def set_own(cls, v):
|
|
143
159
|
cls._value = v
|
|
144
160
|
|
|
161
|
+
@classmethod
|
|
162
|
+
def get_own(cls):
|
|
163
|
+
return cls._value
|
|
164
|
+
|
|
145
165
|
self.assertEqual(Parent.get_value(), 1)
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
# parent code reads the parent's shadow (empty -> falls back to parent storage)
|
|
167
|
+
self.assertEqual(Child.get_value(), 1)
|
|
168
|
+
# parent's classmethod called on the child writes the parent's shadow
|
|
148
169
|
Child.set_value(99)
|
|
149
170
|
self.assertEqual(Child.get_value(), 99)
|
|
150
171
|
self.assertEqual(Parent.get_value(), 1)
|
|
151
|
-
# the subclass's own code
|
|
152
|
-
|
|
153
|
-
|
|
172
|
+
# the subclass's own code writes its own storage
|
|
173
|
+
Child.set_own(7)
|
|
174
|
+
self.assertEqual(Child.get_own(), 7)
|
|
154
175
|
self.assertEqual(Child.get_value(), 99)
|
|
176
|
+
self.assertEqual(Parent.get_value(), 1)
|
|
155
177
|
# parent's own storage still writable through the parent
|
|
156
178
|
Parent.set_value(5)
|
|
157
179
|
self.assertEqual(Parent.get_value(), 5)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import private_attribute
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestUseParents(unittest.TestCase):
|
|
6
|
+
def test_forbidden_parent_private_in_namespace(self):
|
|
7
|
+
# 2.2.0: a subclass must not write a parent-private attribute into its
|
|
8
|
+
# own namespace unless it is declared in __private_attrs__ or
|
|
9
|
+
# use_parents=True is set.
|
|
10
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
11
|
+
__private_attrs__ = ["_secret"]
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self._secret = 1
|
|
15
|
+
|
|
16
|
+
with self.assertRaises(TypeError):
|
|
17
|
+
class Child(Parent):
|
|
18
|
+
__private_attrs__ = []
|
|
19
|
+
_secret = 999
|
|
20
|
+
|
|
21
|
+
def test_use_parents_instance_storage(self):
|
|
22
|
+
# 2.2.0: use_parents=True lets the subclass code read/write the
|
|
23
|
+
# parent's private name, but the value lives in the subclass's own
|
|
24
|
+
# storage; the parent's own storage is untouched.
|
|
25
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
26
|
+
__private_attrs__ = ["_secret"]
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self._secret = 1
|
|
30
|
+
|
|
31
|
+
def get_secret(self):
|
|
32
|
+
return self._secret
|
|
33
|
+
|
|
34
|
+
class Child(Parent, use_parents=True):
|
|
35
|
+
__private_attrs__ = []
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
super().__init__()
|
|
39
|
+
self._secret = 99
|
|
40
|
+
|
|
41
|
+
def read_own(self):
|
|
42
|
+
return self._secret
|
|
43
|
+
|
|
44
|
+
def write_own(self, v):
|
|
45
|
+
self._secret = v
|
|
46
|
+
|
|
47
|
+
obj = Child()
|
|
48
|
+
# parent's code sees the parent's own value
|
|
49
|
+
self.assertEqual(obj.get_secret(), 1)
|
|
50
|
+
# child's code sees its own value
|
|
51
|
+
self.assertEqual(obj.read_own(), 99)
|
|
52
|
+
# child writes into its own storage only
|
|
53
|
+
obj.write_own(7)
|
|
54
|
+
self.assertEqual(obj.read_own(), 7)
|
|
55
|
+
self.assertEqual(obj.get_secret(), 1)
|
|
56
|
+
with self.assertRaises(AttributeError):
|
|
57
|
+
obj._secret
|
|
58
|
+
|
|
59
|
+
def test_use_parents_class_body_value(self):
|
|
60
|
+
# 2.2.0: with use_parents=True a subclass may define a parent-private
|
|
61
|
+
# name in its own body; it is stored in the subclass's own storage.
|
|
62
|
+
# Parent code always reads the parent's own storage.
|
|
63
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
64
|
+
__private_attrs__ = ["_value"]
|
|
65
|
+
_value = 10
|
|
66
|
+
|
|
67
|
+
def get_value(self):
|
|
68
|
+
return self._value
|
|
69
|
+
|
|
70
|
+
class Child(Parent, use_parents=True):
|
|
71
|
+
__private_attrs__ = []
|
|
72
|
+
_value = 20
|
|
73
|
+
|
|
74
|
+
def get_own(self):
|
|
75
|
+
return self._value
|
|
76
|
+
|
|
77
|
+
self.assertEqual(Parent().get_value(), 10)
|
|
78
|
+
obj = Child()
|
|
79
|
+
self.assertEqual(obj.get_value(), 10)
|
|
80
|
+
self.assertEqual(obj.get_own(), 20)
|
|
81
|
+
with self.assertRaises(AttributeError):
|
|
82
|
+
obj._value
|
|
83
|
+
|
|
84
|
+
def test_use_parents_not_inherited_by_grandchild(self):
|
|
85
|
+
# 2.2.0: use_parents merges the ancestor sets only into the class that
|
|
86
|
+
# opts in; a grandchild without use_parents neither sees nor may
|
|
87
|
+
# define the grandparent's private name.
|
|
88
|
+
class GrandParent(private_attribute.PrivateAttrBase):
|
|
89
|
+
__private_attrs__ = ["_g"]
|
|
90
|
+
|
|
91
|
+
class Parent(GrandParent, use_parents=True):
|
|
92
|
+
__private_attrs__ = []
|
|
93
|
+
_g = 1
|
|
94
|
+
|
|
95
|
+
# Child (no use_parents) cannot define the grandparent-private name.
|
|
96
|
+
with self.assertRaises(TypeError):
|
|
97
|
+
class Child(Parent):
|
|
98
|
+
__private_attrs__ = []
|
|
99
|
+
_g = 2
|
|
100
|
+
|
|
101
|
+
class Child2(Parent):
|
|
102
|
+
__private_attrs__ = []
|
|
103
|
+
|
|
104
|
+
def read_g(self):
|
|
105
|
+
return self._g
|
|
106
|
+
|
|
107
|
+
c = Child2()
|
|
108
|
+
with self.assertRaises(AttributeError):
|
|
109
|
+
c.read_g()
|
|
110
|
+
|
|
111
|
+
def test_use_parents_false_is_default(self):
|
|
112
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
113
|
+
__private_attrs__ = ["_secret"]
|
|
114
|
+
|
|
115
|
+
# default (no kwarg) behaves like use_parents=False
|
|
116
|
+
with self.assertRaises(TypeError):
|
|
117
|
+
class Child(Parent):
|
|
118
|
+
__private_attrs__ = []
|
|
119
|
+
_secret = 1
|
|
120
|
+
|
|
121
|
+
# explicit False behaves the same
|
|
122
|
+
with self.assertRaises(TypeError):
|
|
123
|
+
class Child2(Parent, use_parents=False):
|
|
124
|
+
__private_attrs__ = []
|
|
125
|
+
_secret = 1
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{private_attribute_cpp-2.1.24 → private_attribute_cpp-2.2.0}/tests/test_class_private_attrs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|