-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathprototype.py
More file actions
102 lines (77 loc) · 3.27 KB
/
prototype.py
File metadata and controls
102 lines (77 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
class PrototypeException(Exception):
pass
class PrototypeSwitcher(object):
def __init__(self, delegate, extend=False):
self._delegate = delegate
self._replacement = None
self._extend = extend
def _delegate_call(self, key):
def delegator(eat_self, *args, **kwargs):
bound_fn = getattr(self._delegate, key)
if bound_fn:
return bound_fn(*args, **kwargs)
return delegator
def _decorate(self, cls):
old_getattr = getattr(cls, '__getattr__', None)
delegate_bind = self._delegate #for speed
def __getattr__(self, name):
if old_getattr:
try:
return old_getattr(self, name)
except AttributeError:
pass
return getattr(delegate_bind, name)
cls.__getattr__ = __getattr__
cls.__call__ = self._delegate_call('__call__')
return cls
@property
def new(self):
if not self._replacement:
@self._decorate
class PrototypeChild(object):
pass
self._replacement = PrototypeChild()
return self._replacement
def __call__(self, cls):
if getattr(self._delegate, '__call__', False) and not self._extend:
raise PrototypeException("You attempted to use a prototype as a decorator, but the object you're extending already has a __call__ method. You will need to explicitly use the .extend syntax like this: @prototype(obj, True) in this case. You will see this error on multi-level prototypes")
return self._decorate(cls)
def __getattr__(self, name):
if getattr(self._delegate, 'new', False):
raise PrototypeException("You are using a raw prototype as a clone, but the parent defined a new attribute. You must explicitly instantiate using @prototype(obj).new in this case. You will see this error on multi-level prototypes.")
return getattr(self.new, name)
def prototype(delegate, extend=False):
"""
Implement javascript prototypal inheritance in python. This
implementation will allow you to create new prototypes on the fly,
or set prototypes for classes.
Usage:
You can use it to make lightweight children from an existing object.
obj = Parent()
obj_child = prototype(obj)
You can use it to make static prototypes for classes.
obj = Parent()
@prototype(obj)
class Child(object):
pass
obj2 = Child()
In some rare cases you must explicitly force a clone or a
decorator to be created. If the object contains a `new` property
you must explicitly call .new to force a clone to be made.
obj = Parent()
obj.new = 1
obj_child = prototype(obj).new
If the object contains a `__call__` property, you must explicitly
create a decorator with the extend=True keyword argument.
obj = Parent()
obj.__call__ = 1
@prototype(obj, extend=True)
class Child(object):
pass
Prototypes can extend prototypes as many levels as you want. One note,
prototypes extending prototypes must use .new and extend=True.
obj = Parent()
child = prototype(obj)
grandchild = prototype(child).new
"""
return PrototypeSwitcher(delegate, extend)