Skip to content

Commit 32ff21c

Browse files
committed
use descriptor/inheritance instead
1 parent 6bca931 commit 32ff21c

File tree

1 file changed

+59
-48
lines changed

1 file changed

+59
-48
lines changed

paam/__init__.py

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,79 @@
11
from typing import Any, Callable, List
22
from abc import ABC
33

4-
__all__ = ['GET', 'SET', 'DEL', 'Getter', 'Setter', 'Deleter', 'property_access_mod_class']
5-
6-
class _PropertyAccessModifierBase(ABC):
4+
class _PaamBaseDescriptor(ABC):
75
def __init__(self, obj: Any) -> None:
86
self.is_setter = isinstance(obj, Setter) or isinstance(self, Setter)
97
self.is_getter = isinstance(obj, Getter) or isinstance(self, Getter)
108
self.is_deleter = isinstance(obj, Deleter) or isinstance(self, Deleter)
119

12-
if isinstance(obj, (Setter, Getter, Deleter)):
13-
self.obj = obj.obj
14-
else:
15-
self.obj = obj
10+
self._obj = obj
1611

17-
def __ror__(self, other: Any) -> '_PropertyAccessModifierBase':
18-
return self.__class__(other)
1912

20-
class Setter(_PropertyAccessModifierBase):
21-
pass
13+
def __get__(self, owner_instance, owner_cls=None):
14+
raise ValueError(
15+
f"{owner_instance if owner_cls is None else owner_cls} is not gettable"
16+
)
2217

18+
def __set__(self, owner_instance, value):
19+
raise ValueError(
20+
f"{owner_instance} is not settable"
21+
)
2322

24-
class Getter(_PropertyAccessModifierBase):
25-
pass
23+
def __delete__(self, owner_instance):
24+
raise ValueError(
25+
f"{owner_instance} is not deletable"
26+
)
2627

28+
class Setter(_PaamBaseDescriptor):
29+
def __set__(self, owner_instance, value):
30+
self._obj = value
2731

28-
class Deleter(_PropertyAccessModifierBase):
29-
pass
32+
class Getter(_PaamBaseDescriptor):
33+
def __get__(self, owner_instance, owner_cls=None):
34+
return self._obj
3035

31-
class _PropertyAccessModifierFactoryBase:
36+
class Deleter(_PaamBaseDescriptor):
37+
def __delete__(self, owner_instance):
38+
del self._obj
39+
40+
class _PropertyAccessModifierFactory:
3241
def __init__(self, cls: type) -> None:
3342
self.cls = cls
3443

35-
def __ror__(self, other: Any) -> _PropertyAccessModifierBase:
44+
def __ror__(self, other: Any) -> _PaamBaseDescriptor:
45+
if issubclass(other.__class__, _PaamBaseDescriptor):
46+
class Both(self.cls, other.__class__):
47+
pass
48+
return Both(other._obj)
3649
return self.cls(other)
3750

38-
SET = _PropertyAccessModifierFactoryBase(Setter)
39-
GET = _PropertyAccessModifierFactoryBase(Getter)
40-
DEL = _PropertyAccessModifierFactoryBase(Deleter)
41-
42-
def property_access_mod_class(klass: type) -> type:
43-
old_init = klass.__init__
44-
45-
def new_init(self, *args, **kwargs):
46-
old_init(self, *args, **kwargs)
47-
for k in dir(self):
48-
v = getattr(self, k)
49-
50-
if issubclass(type(v), _PropertyAccessModifierBase):
51-
setter, getter, deleter = None, None, None
52-
setattr(self, f'_{k}', v.obj)
53-
if v.is_setter:
54-
_k = k
55-
setter = lambda obj, new: setattr(obj, f'_{_k}', new)
56-
57-
if v.is_getter:
58-
_k = k
59-
getter = lambda obj: getattr(obj, f'_{_k}')
60-
61-
if v.is_deleter:
62-
_k = k
63-
deleter = lambda obj: delattr(obj, f'_{_k}')
64-
65-
setattr(klass, k, property(getter, setter, deleter, f"Autogenerated by {__package__} for {klass}.{k}."))
66-
67-
klass.__init__ = new_init
68-
return klass
51+
SET = _PropertyAccessModifierFactory(Setter)
52+
GET = _PropertyAccessModifierFactory(Getter)
53+
DEL = _PropertyAccessModifierFactory(Deleter)
54+
55+
# read https://dev.to/mattconway1984/python-creating-instance-properties-2ej0
56+
# for more
57+
class PaamBase:
58+
def __setattr__(self, attr_name: str, value: Any) -> None:
59+
try:
60+
attr = super().__getattribute__(attr_name) # avoid recursion this way
61+
except AttributeError: # must be "normal" attribute
62+
super().__setattr__(attr_name, value)
63+
else:
64+
if issubclass(type(attr), _PaamBaseDescriptor):
65+
attr.__set__(self, value)
66+
else:
67+
super().__setattr__(attr_name, value) # if "normal" attribute
68+
69+
def __getattribute__(self, attr_name: str) -> Any:
70+
attr = super().__getattribute__(attr_name)
71+
if issubclass(type(attr), _PaamBaseDescriptor):
72+
return attr.__get__(self, self.__class__)
73+
return attr
74+
75+
def __delattr__(self, attr_name: str) -> None:
76+
attr = super().__getattribute__(attr_name)
77+
if issubclass(type(attr), _PaamBaseDescriptor):
78+
attr.__delete__(self)
79+
del attr

0 commit comments

Comments
 (0)