private-attribute-cpp 2.1.2__tar.gz → 2.1.4__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.2/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.4}/PKG-INFO +5 -5
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/README.md +4 -4
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute.cpp +26 -14
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute.pyi +2 -2
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4/private_attribute_cpp.egg-info}/PKG-INFO +5 -5
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute_cpp.egg-info/SOURCES.txt +1 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/setup.py +1 -1
- private_attribute_cpp-2.1.4/tests/test_attr_classify_owner.py +137 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/LICENSE +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/MANIFEST.in +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/picosha2.h +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute_cpp.egg-info/dependency_links.txt +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute_cpp.egg-info/not-zip-safe +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/private_attribute_cpp.egg-info/top_level.txt +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/setup.cfg +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_class_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_abc.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_attrs.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_func.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_inheritance.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_method.py +0 -0
- {private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_wrap.py +0 -0
{private_attribute_cpp-2.1.2/private_attribute_cpp.egg-info → private_attribute_cpp-2.1.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: private_attribute_cpp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
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
|
|
@@ -75,7 +75,7 @@ print(obj.expensive_api_call(10)) # works with all decorators applied
|
|
|
75
75
|
| 1 | PrivateWrapProxy | Decorator wrapper for arbitrary decorators | When needed |
|
|
76
76
|
| 2 | private_func=callable | Custom hidden-name generator | Optional |
|
|
77
77
|
| 3 | Pass private_func in class definition | Same as above | Optional |
|
|
78
|
-
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private |
|
|
78
|
+
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private | Optional (changed in 2.1.4) |
|
|
79
79
|
| 5 | @PrivateWrapProxy(...) | Make any decorator compatible with private attributes | When needed |
|
|
80
80
|
| 6 | method.result.xxx chain + dummy wrap | Fix decorator order and name conflicts | When needed |
|
|
81
81
|
|
|
@@ -240,8 +240,8 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
240
240
|
|
|
241
241
|
## Notes
|
|
242
242
|
|
|
243
|
-
- All of the private attributes class must contain the `__private_attrs__` attribute.
|
|
244
|
-
- The `__private_attrs__` attribute must be a sequence of strings.
|
|
243
|
+
- ~~All of the private attributes class must contain the `__private_attrs__` attribute.~~(changed in 2.1.4)
|
|
244
|
+
- The `__private_attrs__` attribute must be a sequence of strings or just one string.
|
|
245
245
|
- You cannot define the name which in `__slots__` to `__private_attrs__`.
|
|
246
246
|
- When you define `__slots__` and `__private_attrs__` in one class, the attributes in `__private_attrs__` can also be defined in the methods, even though they are not in `__slots__`.
|
|
247
247
|
- All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
|
|
@@ -266,4 +266,4 @@ This package require the c++ module "[picosha2](https://github.com/okdshin/PicoS
|
|
|
266
266
|
|
|
267
267
|
## Support
|
|
268
268
|
|
|
269
|
-
Now it
|
|
269
|
+
Now it only support Cpython.
|
|
@@ -56,7 +56,7 @@ print(obj.expensive_api_call(10)) # works with all decorators applied
|
|
|
56
56
|
| 1 | PrivateWrapProxy | Decorator wrapper for arbitrary decorators | When needed |
|
|
57
57
|
| 2 | private_func=callable | Custom hidden-name generator | Optional |
|
|
58
58
|
| 3 | Pass private_func in class definition | Same as above | Optional |
|
|
59
|
-
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private |
|
|
59
|
+
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private | Optional (changed in 2.1.4) |
|
|
60
60
|
| 5 | @PrivateWrapProxy(...) | Make any decorator compatible with private attributes | When needed |
|
|
61
61
|
| 6 | method.result.xxx chain + dummy wrap | Fix decorator order and name conflicts | When needed |
|
|
62
62
|
|
|
@@ -221,8 +221,8 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
221
221
|
|
|
222
222
|
## Notes
|
|
223
223
|
|
|
224
|
-
- All of the private attributes class must contain the `__private_attrs__` attribute.
|
|
225
|
-
- The `__private_attrs__` attribute must be a sequence of strings.
|
|
224
|
+
- ~~All of the private attributes class must contain the `__private_attrs__` attribute.~~(changed in 2.1.4)
|
|
225
|
+
- The `__private_attrs__` attribute must be a sequence of strings or just one string.
|
|
226
226
|
- You cannot define the name which in `__slots__` to `__private_attrs__`.
|
|
227
227
|
- When you define `__slots__` and `__private_attrs__` in one class, the attributes in `__private_attrs__` can also be defined in the methods, even though they are not in `__slots__`.
|
|
228
228
|
- All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
|
|
@@ -247,4 +247,4 @@ This package require the c++ module "[picosha2](https://github.com/okdshin/PicoS
|
|
|
247
247
|
|
|
248
248
|
## Support
|
|
249
249
|
|
|
250
|
-
Now it
|
|
250
|
+
Now it only support Cpython.
|
|
@@ -317,11 +317,11 @@ attr_classify(uintptr_t typ_id, const std::string& name, PyCodeObject* code) noe
|
|
|
317
317
|
chain.insert(chain.end(), parent_ids.begin(), parent_ids.end());
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
// Find the class whose code is currently running (the code owner).
|
|
320
|
+
// Find the class whose code is currently running (the code owner, parent first).
|
|
321
321
|
uintptr_t code_class = 0;
|
|
322
|
-
for (auto
|
|
323
|
-
if (is_class_code(
|
|
324
|
-
code_class =
|
|
322
|
+
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
|
|
323
|
+
if (is_class_code(*it, code)) {
|
|
324
|
+
code_class = *it; // first hit from the base-most end
|
|
325
325
|
break;
|
|
326
326
|
}
|
|
327
327
|
}
|
|
@@ -2333,10 +2333,23 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2333
2333
|
return false;
|
|
2334
2334
|
}
|
|
2335
2335
|
|
|
2336
|
-
|
|
2337
|
-
if (
|
|
2338
|
-
|
|
2339
|
-
|
|
2336
|
+
PyObjectStorage __private_attrs__;
|
|
2337
|
+
if (PyDict_ContainsString(data.attrs, "__private_attrs__")) {
|
|
2338
|
+
__private_attrs__ = PyDict_GetItemString(data.attrs, "__private_attrs__");
|
|
2339
|
+
}
|
|
2340
|
+
else {
|
|
2341
|
+
__private_attrs__ = PyTuple_New(0);
|
|
2342
|
+
Py_DECREF(__private_attrs__);
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
if (PyUnicode_Check(__private_attrs__)) {
|
|
2346
|
+
PyObject* new___private_attrs__ = PyTuple_New(1);
|
|
2347
|
+
if (!new___private_attrs__) {
|
|
2348
|
+
return false;
|
|
2349
|
+
}
|
|
2350
|
+
PyTuple_SET_ITEM(new___private_attrs__, 0, __private_attrs__);
|
|
2351
|
+
__private_attrs__ = new___private_attrs__;
|
|
2352
|
+
Py_DECREF(__private_attrs__);
|
|
2340
2353
|
}
|
|
2341
2354
|
|
|
2342
2355
|
if (!PySequence_Check(__private_attrs__)) {
|
|
@@ -2379,8 +2392,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2379
2392
|
|
|
2380
2393
|
for (const char** p = invalid_name; *p != NULL; p++) {
|
|
2381
2394
|
if (strcmp(attr_str.c_str(), *p) == 0) {
|
|
2382
|
-
|
|
2383
|
-
PyErr_SetString(PyExc_TypeError, error_msg.c_str());
|
|
2395
|
+
PyErr_Format(PyExc_TypeError, "Invalid name '%s' in '__private_attrs__'", attr_str.c_str());
|
|
2384
2396
|
return false;
|
|
2385
2397
|
}
|
|
2386
2398
|
}
|
|
@@ -2421,7 +2433,7 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2421
2433
|
}
|
|
2422
2434
|
slot_seq = keys;
|
|
2423
2435
|
} else {
|
|
2424
|
-
slot_seq = PySequence_Fast(all_slots, "__slots__ must be a sequence");
|
|
2436
|
+
slot_seq = PySequence_Fast(all_slots, "__slots__ must be a string, dict or sequence");
|
|
2425
2437
|
}
|
|
2426
2438
|
if (!slot_seq) {
|
|
2427
2439
|
return false;
|
|
@@ -2433,9 +2445,9 @@ PrivateAttrType_preprocess(PyObject* args, PyObject* kwds, PrivateAttrCreationDa
|
|
|
2433
2445
|
PyObject* slot = PySequence_Fast_GET_ITEM(slot_seq, j);
|
|
2434
2446
|
if (PyUnicode_Check(slot)) {
|
|
2435
2447
|
const char* slot_cstr = PyUnicode_AsUTF8(slot);
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2448
|
+
std::string final_name = real_class_name(slot_cstr, data.class_name);
|
|
2449
|
+
if (data.private_attrs_vector_string.find(final_name) != data.private_attrs_vector_string.end()){
|
|
2450
|
+
PyErr_Format(PyExc_TypeError, "'__slots__' and '__private_attrs__' cannot have the same attribute name: '%s'", final_name.c_str());
|
|
2439
2451
|
Py_DECREF(slot_seq);
|
|
2440
2452
|
return false;
|
|
2441
2453
|
}
|
|
@@ -15,12 +15,12 @@ class MyClass(PrivateAttrBase):
|
|
|
15
15
|
return self._private_attr1
|
|
16
16
|
```
|
|
17
17
|
"""
|
|
18
|
-
from typing import Any, TypeVar, Callable, TypedDict, Sequence, Generic
|
|
18
|
+
from typing import Any, TypeVar, Callable, TypedDict, Sequence, Generic, Optional
|
|
19
19
|
from types import FunctionType
|
|
20
20
|
|
|
21
21
|
# define the dict that must have a key "__private_attrs__" and value must be the sequence of strings
|
|
22
22
|
class _PrivateAttrDict(TypedDict):
|
|
23
|
-
__private_attrs__: Sequence[str]
|
|
23
|
+
__private_attrs__: Optional[Sequence[str]]
|
|
24
24
|
|
|
25
25
|
_T = TypeVar('T')
|
|
26
26
|
|
{private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4/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.1.
|
|
3
|
+
Version: 2.1.4
|
|
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
|
|
@@ -75,7 +75,7 @@ print(obj.expensive_api_call(10)) # works with all decorators applied
|
|
|
75
75
|
| 1 | PrivateWrapProxy | Decorator wrapper for arbitrary decorators | When needed |
|
|
76
76
|
| 2 | private_func=callable | Custom hidden-name generator | Optional |
|
|
77
77
|
| 3 | Pass private_func in class definition | Same as above | Optional |
|
|
78
|
-
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private |
|
|
78
|
+
| 4 | \_\_private_attrs\_\_ list | Declare which attributes are private | Optional (changed in 2.1.4) |
|
|
79
79
|
| 5 | @PrivateWrapProxy(...) | Make any decorator compatible with private attributes | When needed |
|
|
80
80
|
| 6 | method.result.xxx chain + dummy wrap | Fix decorator order and name conflicts | When needed |
|
|
81
81
|
|
|
@@ -240,8 +240,8 @@ TypeError: Can't instantiate abstract class MyClass without an implementation fo
|
|
|
240
240
|
|
|
241
241
|
## Notes
|
|
242
242
|
|
|
243
|
-
- All of the private attributes class must contain the `__private_attrs__` attribute.
|
|
244
|
-
- The `__private_attrs__` attribute must be a sequence of strings.
|
|
243
|
+
- ~~All of the private attributes class must contain the `__private_attrs__` attribute.~~(changed in 2.1.4)
|
|
244
|
+
- The `__private_attrs__` attribute must be a sequence of strings or just one string.
|
|
245
245
|
- You cannot define the name which in `__slots__` to `__private_attrs__`.
|
|
246
246
|
- When you define `__slots__` and `__private_attrs__` in one class, the attributes in `__private_attrs__` can also be defined in the methods, even though they are not in `__slots__`.
|
|
247
247
|
- All of the object that is the instance of the class "PrivateAttrBase" or its subclass are default to be unable to be pickled.
|
|
@@ -266,4 +266,4 @@ This package require the c++ module "[picosha2](https://github.com/okdshin/PicoS
|
|
|
266
266
|
|
|
267
267
|
## Support
|
|
268
268
|
|
|
269
|
-
Now it
|
|
269
|
+
Now it only support Cpython.
|
|
@@ -10,6 +10,7 @@ private_attribute_cpp.egg-info/SOURCES.txt
|
|
|
10
10
|
private_attribute_cpp.egg-info/dependency_links.txt
|
|
11
11
|
private_attribute_cpp.egg-info/not-zip-safe
|
|
12
12
|
private_attribute_cpp.egg-info/top_level.txt
|
|
13
|
+
tests/test_attr_classify_owner.py
|
|
13
14
|
tests/test_class_private_attrs.py
|
|
14
15
|
tests/test_private_abc.py
|
|
15
16
|
tests/test_private_attrs.py
|
|
@@ -18,7 +18,7 @@ readme = open('README.md').read()
|
|
|
18
18
|
|
|
19
19
|
setup(
|
|
20
20
|
name='private_attribute_cpp',
|
|
21
|
-
version='2.1.
|
|
21
|
+
version='2.1.4',
|
|
22
22
|
author="HuangHaoHua",
|
|
23
23
|
author_email="13140752715@example.com",
|
|
24
24
|
description='A Python package that provides a way to define private attributes in C++ implementation.',
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""attr_classify ownership rule (v2.1.3).
|
|
2
|
+
|
|
3
|
+
When a code object is owned by several classes on the MRO chain (e.g. a
|
|
4
|
+
subclass aliases a parent method with `f = g`, or copies a parent method's
|
|
5
|
+
bytecode with `f.__code__ = g.__code__`), attr_classify must attribute the
|
|
6
|
+
code to the base-most (declaring, parent) class. Otherwise the parent code's
|
|
7
|
+
private attribute accesses get split across two storages: the tampered method
|
|
8
|
+
writes the subclass's storage while the untampered parent method reads the
|
|
9
|
+
parent's storage, corrupting the parent's behavior.
|
|
10
|
+
|
|
11
|
+
Key invariant: parent methods inc() (tampered by the child) and peek() (not
|
|
12
|
+
tampered) must ALWAYS see the same storage on a child instance.
|
|
13
|
+
"""
|
|
14
|
+
import unittest
|
|
15
|
+
|
|
16
|
+
import private_attribute
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Parent(private_attribute.PrivateAttrBase):
|
|
20
|
+
__private_attrs__ = ["_n"]
|
|
21
|
+
_n = 0
|
|
22
|
+
|
|
23
|
+
def inc(self):
|
|
24
|
+
self._n += 1
|
|
25
|
+
return self._n
|
|
26
|
+
|
|
27
|
+
def peek(self):
|
|
28
|
+
return self._n
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestAliasCodeOwnership(unittest.TestCase):
|
|
32
|
+
"""`f = g` in the class body registers the same function object with both
|
|
33
|
+
classes; the aliased and the untampered parent methods must stay
|
|
34
|
+
consistent on a child instance."""
|
|
35
|
+
|
|
36
|
+
def test_alias_method_sees_same_storage_as_untampered(self):
|
|
37
|
+
class AliasChild(Parent):
|
|
38
|
+
__private_attrs__ = ["_n"]
|
|
39
|
+
_n = 100
|
|
40
|
+
f = Parent.inc # same function object in both class dicts
|
|
41
|
+
|
|
42
|
+
c = AliasChild()
|
|
43
|
+
v1 = c.f() # parent code, double-owned
|
|
44
|
+
v2 = c.peek() # parent code, single-owned
|
|
45
|
+
self.assertEqual(v1, v2)
|
|
46
|
+
c.f()
|
|
47
|
+
self.assertEqual(c.peek(), v2 + 1)
|
|
48
|
+
self.assertEqual(Parent().peek(), 0)
|
|
49
|
+
|
|
50
|
+
def test_alias_without_shadow_works(self):
|
|
51
|
+
class AliasChild2(Parent):
|
|
52
|
+
__private_attrs__ = []
|
|
53
|
+
f = Parent.inc
|
|
54
|
+
|
|
55
|
+
c = AliasChild2()
|
|
56
|
+
v1 = c.f()
|
|
57
|
+
self.assertEqual(v1, c.peek())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestCodeSwapOwnership(unittest.TestCase):
|
|
61
|
+
"""`f.__code__ = g.__code__` inside the class body; the swapped method
|
|
62
|
+
must behave exactly like the parent's method (same storage)."""
|
|
63
|
+
|
|
64
|
+
def test_code_swap_sees_same_storage_as_untampered(self):
|
|
65
|
+
class CodeChild(Parent):
|
|
66
|
+
__private_attrs__ = ["_n"]
|
|
67
|
+
_n = 100
|
|
68
|
+
|
|
69
|
+
def f(self):
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
f.__code__ = Parent.inc.__code__
|
|
73
|
+
|
|
74
|
+
c = CodeChild()
|
|
75
|
+
v1 = c.f()
|
|
76
|
+
v2 = c.peek()
|
|
77
|
+
self.assertEqual(v1, v2)
|
|
78
|
+
c.f()
|
|
79
|
+
self.assertEqual(c.peek(), v2 + 1)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestFallbackSemantics(unittest.TestCase):
|
|
83
|
+
"""Per-subject fallback is preserved: an unwritten read resolves to the
|
|
84
|
+
subject's own class-level default, consistently for both methods."""
|
|
85
|
+
|
|
86
|
+
def test_unwritten_read_falls_back_per_subject(self):
|
|
87
|
+
class FreshChild(Parent):
|
|
88
|
+
__private_attrs__ = ["_n"]
|
|
89
|
+
_n = 100
|
|
90
|
+
f = Parent.inc
|
|
91
|
+
|
|
92
|
+
c = FreshChild()
|
|
93
|
+
self.assertEqual(c.peek(), 100)
|
|
94
|
+
v1 = c.f()
|
|
95
|
+
self.assertEqual(v1, c.peek())
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TestNormalUsageRegression(unittest.TestCase):
|
|
99
|
+
"""Without tampering, nothing changes: child's own code uses child
|
|
100
|
+
storage, parent's code keeps its per-subject view."""
|
|
101
|
+
|
|
102
|
+
def test_child_own_code_uses_child_storage(self):
|
|
103
|
+
class NormChild(Parent):
|
|
104
|
+
__private_attrs__ = ["_n"]
|
|
105
|
+
_n = 100
|
|
106
|
+
|
|
107
|
+
def own_inc(self):
|
|
108
|
+
self._n += 1
|
|
109
|
+
return self._n
|
|
110
|
+
|
|
111
|
+
c = NormChild()
|
|
112
|
+
self.assertEqual(c.own_inc(), 101)
|
|
113
|
+
self.assertEqual(c.peek(), 100)
|
|
114
|
+
self.assertEqual(Parent().peek(), 0)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class TestIsolationIntact(unittest.TestCase):
|
|
118
|
+
"""The v2.1.0 isolation rule still holds: the subclass's own code must
|
|
119
|
+
not touch a parent's private attribute."""
|
|
120
|
+
|
|
121
|
+
def test_subclass_own_code_denied(self):
|
|
122
|
+
class IsoParent(private_attribute.PrivateAttrBase):
|
|
123
|
+
__private_attrs__ = ["_secret"]
|
|
124
|
+
_secret = 1
|
|
125
|
+
|
|
126
|
+
class IsoChild(IsoParent):
|
|
127
|
+
__private_attrs__ = []
|
|
128
|
+
|
|
129
|
+
def read_secret(self):
|
|
130
|
+
return self._secret
|
|
131
|
+
|
|
132
|
+
with self.assertRaises(AttributeError):
|
|
133
|
+
IsoChild().read_secret()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
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.2 → private_attribute_cpp-2.1.4}/tests/test_class_private_attrs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{private_attribute_cpp-2.1.2 → private_attribute_cpp-2.1.4}/tests/test_private_inheritance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|