|
1
|
1 |
|
import abc |
|
2
|
1 |
|
import logging |
|
3
|
1 |
|
import os |
|
4
|
1 |
|
import json |
|
5
|
1 |
|
import shutil |
|
6
|
|
|
|
|
7
|
1 |
|
import cloudpickle as _cloudpickle |
|
8
|
|
|
|
|
9
|
|
|
|
|
10
|
1 |
|
logger = logging.getLogger(__name__) |
|
11
|
|
|
|
|
12
|
|
|
|
|
13
|
1 |
|
class QueryObject(abc.ABC): |
|
14
|
|
|
""" |
|
15
|
|
|
Derived class needs to implement the following interface: |
|
16
|
|
|
* query() -- given input, return query result |
|
17
|
|
|
* get_docstring() -- returns documentation for the Query Object |
|
18
|
|
|
""" |
|
19
|
|
|
|
|
20
|
1 |
|
def __init__(self, description=""): |
|
21
|
1 |
|
self.description = description |
|
22
|
|
|
|
|
23
|
1 |
|
def get_dependencies(self): |
|
24
|
|
|
"""All endpoints this endpoint depends on""" |
|
25
|
1 |
|
return [] |
|
26
|
|
|
|
|
27
|
1 |
|
@abc.abstractmethod |
|
28
|
1 |
|
def query(self, input): |
|
29
|
|
|
"""execute query on the provided input""" |
|
30
|
|
|
pass |
|
31
|
|
|
|
|
32
|
1 |
|
@abc.abstractmethod |
|
33
|
1 |
|
def get_docstring(self): |
|
34
|
|
|
"""Returns documentation for the query object |
|
35
|
|
|
|
|
36
|
|
|
By default, this method returns the docstring for 'query' method |
|
37
|
|
|
Derived class may overwrite this method to dynamically create docstring |
|
38
|
|
|
""" |
|
39
|
|
|
pass |
|
40
|
|
|
|
|
41
|
1 |
|
def save(self, path): |
|
42
|
|
|
""" Save query object to the given local path |
|
43
|
|
|
|
|
44
|
|
|
Parameters |
|
45
|
|
|
---------- |
|
46
|
|
|
path : str |
|
47
|
|
|
The location to save the query object to |
|
48
|
|
|
""" |
|
49
|
1 |
|
if os.path.exists(path): |
|
50
|
|
|
logger.warning( |
|
51
|
|
|
f'Overwriting existing file "{path}" when saving query object' |
|
52
|
|
|
) |
|
53
|
|
|
rm_fn = os.remove if os.path.isfile(path) else shutil.rmtree |
|
54
|
|
|
rm_fn(path) |
|
55
|
1 |
|
self._save_local(path) |
|
56
|
|
|
|
|
57
|
1 |
|
def _save_local(self, path): |
|
58
|
|
|
"""Save current query object to local path |
|
59
|
|
|
""" |
|
60
|
1 |
|
try: |
|
61
|
1 |
|
os.makedirs(path) |
|
62
|
|
|
except OSError as e: |
|
63
|
|
|
import errno |
|
64
|
|
|
|
|
65
|
|
|
if e.errno == errno.EEXIST and os.path.isdir(path): |
|
66
|
|
|
pass |
|
67
|
|
|
else: |
|
68
|
|
|
raise |
|
69
|
|
|
|
|
70
|
1 |
|
with open(os.path.join(path, "pickle_archive"), "wb") as f: |
|
71
|
1 |
|
_cloudpickle.dump(self, f) |
|
72
|
|
|
|
|
73
|
1 |
|
@classmethod |
|
74
|
1 |
|
def load(cls, path): |
|
75
|
|
|
""" Load query object from given path |
|
76
|
|
|
""" |
|
77
|
|
|
new_po = None |
|
78
|
|
|
new_po = cls._load_local(path) |
|
79
|
|
|
|
|
80
|
|
|
logger.info(f'Loaded query object "{type(new_po).__name__}" successfully') |
|
81
|
|
|
|
|
82
|
|
|
return new_po |
|
83
|
|
|
|
|
84
|
1 |
|
@classmethod |
|
85
|
1 |
|
def _load_local(cls, path): |
|
86
|
|
|
path = os.path.abspath(os.path.expanduser(path)) |
|
87
|
|
|
with open(os.path.join(path, "pickle_archive"), "rb") as f: |
|
88
|
|
|
return _cloudpickle.load(f) |
|
89
|
|
|
|
|
90
|
1 |
|
@classmethod |
|
91
|
1 |
|
def _make_serializable(cls, result): |
|
92
|
|
|
"""Convert a result from object query to python data structure that can |
|
93
|
|
|
easily serialize over network |
|
94
|
|
|
""" |
|
95
|
|
|
try: |
|
96
|
|
|
json.dumps(result) |
|
97
|
|
|
except TypeError: |
|
98
|
|
|
raise TypeError( |
|
99
|
|
|
"Result from object query is not json serializable: " f"{result}" |
|
100
|
|
|
) |
|
101
|
|
|
|
|
102
|
|
|
return result |
|
103
|
|
|
|
|
104
|
|
|
# Returns an array of dictionary that contains the methods and their |
|
105
|
|
|
# corresponding schema information. |
|
106
|
1 |
|
@abc.abstractmethod |
|
107
|
1 |
|
def get_methods(self): |
|
108
|
|
|
return None |
|
109
|
|
|
|