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
|
|
|
|