forked from Project-MONAI/MONAI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decorators.py
82 lines (62 loc) · 3.06 KB
/
decorators.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
# Copyright (c) MONAI Consortium
# 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.
from __future__ import annotations
from functools import wraps
__all__ = ["RestartGenerator", "MethodReplacer"]
from typing import Callable, Generator
class RestartGenerator:
"""
Wraps a generator callable which will be called whenever this class is iterated and its result returned. This is
used to create an iterator which can start iteration over the given generator multiple times.
"""
def __init__(self, create_gen: Callable[[], Generator]) -> None:
self.create_gen = create_gen
def __iter__(self) -> Generator:
return self.create_gen()
class MethodReplacer:
"""
Base class for method decorators which can be used to replace methods pass to replace_method() with wrapped versions.
"""
replace_list_name = "__replacemethods__"
def __init__(self, meth: Callable) -> None:
self.meth = meth
def replace_method(self, meth):
"""
Return a new method to replace `meth` in the instantiated object, or `meth` to do nothing.
"""
return meth
def __set_name__(self, owner, name):
"""
Add the (name,self.replace_method) pair to the list named by replace_list_name in `owner`, creating the list and
replacing the constructor of `owner` if necessary. The replaced constructor will call the old one then do the
replacing operation of substituting, for each (name,self.replace_method) pair, the named method with the returned
value from self.replace_method.
"""
entry = (name, owner, self.replace_method)
if not hasattr(owner, self.replace_list_name):
oldinit = owner.__init__
# replace the constructor with a new one which calls the old then replaces methods
@wraps(oldinit)
def newinit(_self, *args, **kwargs):
oldinit(_self, *args, **kwargs)
# replace each listed method of this newly constructed object
for m, owner, replacer in getattr(_self, self.replace_list_name):
if isinstance(_self, owner):
meth = getattr(_self, m)
newmeth = replacer(meth)
setattr(_self, m, newmeth)
owner.__init__ = newinit
setattr(owner, self.replace_list_name, [entry])
else:
namelist = getattr(owner, self.replace_list_name)
if not any(nl[0] == name for nl in namelist):
namelist.append(entry)
setattr(owner, name, self.meth)