-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathoverrides.py
123 lines (107 loc) · 3.97 KB
/
overrides.py
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#
# Copyright 2015 Mikko Korpela
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys
import dis
__VERSION__ = '1.6'
if sys.version < '3':
def itemint(x):
return ord(x)
else:
def itemint(x):
return x
long = int
def overrides(method):
"""Decorator to indicate that the decorated method overrides a method in
superclass.
The decorator code is executed while loading class. Using this method
should have minimal runtime performance implications.
This is based on my idea about how to do this and fwc:s highly improved
algorithm for the implementation fwc:s
algorithm : http://stackoverflow.com/a/14631397/308189
my answer : http://stackoverflow.com/a/8313042/308189
How to use:
from overrides import overrides
class SuperClass(object):
def method(self):
return 2
class SubClass(SuperClass):
@overrides
def method(self):
return 1
:raises AssertionError if no match in super classes for the method name
:return method with possibly added (if the method doesn't have one)
docstring from super class
"""
for super_class in _get_base_classes(sys._getframe(2), method.__globals__):
if hasattr(super_class, method.__name__):
super_method = getattr(super_class, method.__name__)
if hasattr(super_method, "__finalized__"):
finalized = getattr(super_method, "__finalized__")
if finalized:
raise AssertionError('Method "%s" is finalized' %
method.__name__)
if not method.__doc__:
method.__doc__ = super_method.__doc__
return method
raise AssertionError('No super class method found for "%s"' %
method.__name__)
def _get_base_classes(frame, namespace):
return [_get_base_class(class_name_components, namespace) for
class_name_components in _get_base_class_names(frame)]
def _get_base_class_names(frame):
""" Get baseclass names from the code object """
co, lasti = frame.f_code, frame.f_lasti
code = co.co_code
i = 0
extended_arg = 0
extends = []
while i <= lasti:
c = code[i]
op = itemint(c)
i += 1
if op >= dis.HAVE_ARGUMENT:
oparg = itemint(code[i]) + itemint(code[i+1])*256 + extended_arg
extended_arg = 0
i += 2
if op == dis.EXTENDED_ARG:
extended_arg = oparg*long(65536)
if op in dis.hasconst:
if type(co.co_consts[oparg]) == str:
extends = []
elif op in dis.hasname:
if dis.opname[op] == 'LOAD_NAME':
extends.append(('name', co.co_names[oparg]))
if dis.opname[op] == 'LOAD_ATTR':
extends.append(('attr', co.co_names[oparg]))
if dis.opname[op] == 'LOAD_GLOBAL':
extends.append(('name', co.co_names[oparg]))
items = []
previous_item = []
for t, s in extends:
if t == 'name':
if previous_item:
items.append(previous_item)
previous_item = [s]
else:
previous_item += [s]
if previous_item:
items.append(previous_item)
return items
def _get_base_class(components, namespace):
obj = namespace[components[0]]
for component in components[1:]:
obj = getattr(obj, component)
return obj