-
Notifications
You must be signed in to change notification settings - Fork 78
/
common.py
141 lines (101 loc) · 3.19 KB
/
common.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import copy
import importlib
import pandas # noqa: F401
try:
from patsy import PatsyError
except ImportError:
PatsyError = Exception
RTOL = 0.00001
ATOL = 1e-7
MISSINGVALUE = None
####################
# Decorators/Utils #
####################
# import numba.jit OR create mimic decorator and set existence flag
try:
from numba import jit
HAS_JIT = True
except ImportError:
def jit(function=None, **kwargs): # noqa: ARG001
"""Mimic numba.jit() with synthetic wrapper."""
if function is not None:
def wrapped(*original_args, **original_kw):
"""Case 1 - structure of a standard decorator
i.e., jit(function)(*args, **kwargs).
"""
return function(*original_args, **original_kw)
return wrapped
else:
def partial_inner(func):
"""Case 2 - returns Case 1
i.e., jit()(function)(*args, **kwargs).
"""
return jit(func)
return partial_inner
HAS_JIT = False
def simport(modname):
"""Safely import a module without raising an error.
Parameters
----------
modname : str
Module name needed to import.
Returns
-------
_simport : tuple
Either (True, Module) or (False, None) depending
on whether the import succeeded.
Notes
-----
Wrapping this function around an iterative context or a with
context would allow the module to be used without necessarily
attaching it permanently in the global namespace:
for t,mod in simport('pandas'):
if t:
mod.DataFrame()
else:
#do alternative behavior here
del mod #or don't del, your call
instead of:
t, mod = simport('pandas')
if t:
mod.DataFrame()
else:
#do alternative behavior here
The first idiom makes it work kind of a like a with statement.
"""
try:
_simport = True, importlib.import_module(modname)
except (ImportError, ModuleNotFoundError):
_simport = False, None
return _simport
def requires(*args, **kwargs):
"""Decorator to wrap functions with extra dependencies.
Parameters
----------
args : list
Modules names as strings to import.
verbose : bool
Set as ``True`` to print a warning message on import failure.
Returns
-------
inner : func
The original function if all arg in args are importable.
passer : func
A function that passes if ``inner`` fails.
"""
v = kwargs.pop("verbose", True)
wanted = copy.deepcopy(args)
def inner(function):
available = [simport(arg)[0] for arg in args]
if all(available):
return function
else:
def passer(*args, **kwargs): # noqa: ARG001
if v:
missing = [arg for i, arg in enumerate(wanted) if not available[i]]
print(f"missing dependencies: {missing}")
print(f"not running {function.__name__}")
else:
pass
return passer
return inner