/
coding_utils.py
116 lines (84 loc) · 3.47 KB
/
coding_utils.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
import functools
def make_registering_decorator(foreign_decorator):
"""
Returns a copy of foreign_decorator, which is identical in every
way(*), except also appends a .decorator property to the callable it
spits out.
# (*)We can be somewhat "hygienic", but new_decorator still isn't signature-preserving,
i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...
but in this case, the only argument is func, so it's not a big issue
Works on outermost decorators, based on Method 3 of https://stackoverflow.com/a/5910893/13775459
"""
def new_decorator(func):
# Call to new_decorator(method)
# Exactly like old decorator, but output keeps track of what decorated it
r = foreign_decorator(
func
) # apply foreign_decorator, like call to foreign_decorator(method) would have done
r.decorator = new_decorator # keep track of decorator
r.original = func # keep track of decorated function
return r
new_decorator.__name__ = foreign_decorator.__name__
new_decorator.__doc__ = foreign_decorator.__doc__
return new_decorator
def methods_with_decorator(cls, decorator):
"""
Returns all methods in CLS with DECORATOR as the
outermost decorator.
DECORATOR must be a "registering decorator"; one
can make any decorator "registering" via the
make_registering_decorator function.
Doesn't work for the @property decorator, but does work for the @functools.cached_property decorator.
Works on outermost decorators, based on Method 3 of https://stackoverflow.com/a/5910893/13775459
"""
for maybe_decorated in cls.__dict__.values():
if hasattr(maybe_decorated, "decorator"):
if maybe_decorated.decorator == decorator:
if hasattr(maybe_decorated, "original"):
yield maybe_decorated.original
else:
yield maybe_decorated
def rgetattr(obj, attr, *args):
"""Get chained properties.
Usage
-----
>>> class Pet:
def __init__(self):
self.favorite_color = "orange"
>>> class Person:
def __init__(self):
self.pet = Pet()
>>> p = Person()
>>> rgetattr(p, 'pet.favorite_color') # "orange"
From https://stackoverflow.com/a/31174427/13775459"""
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split("."))
def optional_arg_decorator(fn):
"""
A decorator which _optionally_ accepts arguments.
So a decorator like this:
@optional_arg_decorator
def register_something(fn, optional_arg = 'Default Value'):
...
return fn
will work in both of these usage scenarios:
@register_something('Custom Name')
def custom_name():
pass
@register_something
def default_name():
pass
Thanks to https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments#comment65959042_24617244
"""
def wrapped_decorator(*args):
if len(args) == 1 and callable(args[0]):
return fn(args[0])
else:
def real_decorator(decoratee):
return fn(decoratee, *args)
return real_decorator
return wrapped_decorator
def sort_dict(unsorted_dict: dict) -> dict:
sorted_dict = dict(sorted(unsorted_dict.items(), key=lambda item: item[0]))
return sorted_dict