onetick-py 1.177.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- locator_parser/__init__.py +0 -0
- locator_parser/acl.py +73 -0
- locator_parser/actions.py +262 -0
- locator_parser/common.py +368 -0
- locator_parser/io.py +43 -0
- locator_parser/locator.py +150 -0
- onetick/__init__.py +101 -0
- onetick/doc_utilities/__init__.py +3 -0
- onetick/doc_utilities/napoleon.py +40 -0
- onetick/doc_utilities/ot_doctest.py +140 -0
- onetick/doc_utilities/snippets.py +279 -0
- onetick/lib/__init__.py +4 -0
- onetick/lib/instance.py +141 -0
- onetick/py/__init__.py +293 -0
- onetick/py/_stack_info.py +89 -0
- onetick/py/_version.py +2 -0
- onetick/py/aggregations/__init__.py +11 -0
- onetick/py/aggregations/_base.py +648 -0
- onetick/py/aggregations/_docs.py +948 -0
- onetick/py/aggregations/compute.py +286 -0
- onetick/py/aggregations/functions.py +2216 -0
- onetick/py/aggregations/generic.py +104 -0
- onetick/py/aggregations/high_low.py +80 -0
- onetick/py/aggregations/num_distinct.py +83 -0
- onetick/py/aggregations/order_book.py +501 -0
- onetick/py/aggregations/other.py +1014 -0
- onetick/py/backports.py +26 -0
- onetick/py/cache.py +374 -0
- onetick/py/callback/__init__.py +5 -0
- onetick/py/callback/callback.py +276 -0
- onetick/py/callback/callbacks.py +131 -0
- onetick/py/compatibility.py +798 -0
- onetick/py/configuration.py +771 -0
- onetick/py/core/__init__.py +0 -0
- onetick/py/core/_csv_inspector.py +93 -0
- onetick/py/core/_internal/__init__.py +0 -0
- onetick/py/core/_internal/_manually_bound_value.py +6 -0
- onetick/py/core/_internal/_nodes_history.py +250 -0
- onetick/py/core/_internal/_op_utils/__init__.py +0 -0
- onetick/py/core/_internal/_op_utils/every_operand.py +9 -0
- onetick/py/core/_internal/_op_utils/is_const.py +10 -0
- onetick/py/core/_internal/_per_tick_scripts/tick_list_sort_template.script +121 -0
- onetick/py/core/_internal/_proxy_node.py +140 -0
- onetick/py/core/_internal/_state_objects.py +2312 -0
- onetick/py/core/_internal/_state_vars.py +93 -0
- onetick/py/core/_source/__init__.py +0 -0
- onetick/py/core/_source/_symbol_param.py +95 -0
- onetick/py/core/_source/schema.py +97 -0
- onetick/py/core/_source/source_methods/__init__.py +0 -0
- onetick/py/core/_source/source_methods/aggregations.py +809 -0
- onetick/py/core/_source/source_methods/applyers.py +296 -0
- onetick/py/core/_source/source_methods/columns.py +141 -0
- onetick/py/core/_source/source_methods/data_quality.py +301 -0
- onetick/py/core/_source/source_methods/debugs.py +272 -0
- onetick/py/core/_source/source_methods/drops.py +120 -0
- onetick/py/core/_source/source_methods/fields.py +619 -0
- onetick/py/core/_source/source_methods/filters.py +1002 -0
- onetick/py/core/_source/source_methods/joins.py +1413 -0
- onetick/py/core/_source/source_methods/merges.py +605 -0
- onetick/py/core/_source/source_methods/misc.py +1455 -0
- onetick/py/core/_source/source_methods/pandases.py +155 -0
- onetick/py/core/_source/source_methods/renames.py +356 -0
- onetick/py/core/_source/source_methods/sorts.py +183 -0
- onetick/py/core/_source/source_methods/switches.py +142 -0
- onetick/py/core/_source/source_methods/symbols.py +117 -0
- onetick/py/core/_source/source_methods/times.py +627 -0
- onetick/py/core/_source/source_methods/writes.py +986 -0
- onetick/py/core/_source/symbol.py +205 -0
- onetick/py/core/_source/tmp_otq.py +222 -0
- onetick/py/core/column.py +209 -0
- onetick/py/core/column_operations/__init__.py +0 -0
- onetick/py/core/column_operations/_methods/__init__.py +4 -0
- onetick/py/core/column_operations/_methods/_internal.py +28 -0
- onetick/py/core/column_operations/_methods/conversions.py +216 -0
- onetick/py/core/column_operations/_methods/methods.py +292 -0
- onetick/py/core/column_operations/_methods/op_types.py +160 -0
- onetick/py/core/column_operations/accessors/__init__.py +0 -0
- onetick/py/core/column_operations/accessors/_accessor.py +28 -0
- onetick/py/core/column_operations/accessors/decimal_accessor.py +104 -0
- onetick/py/core/column_operations/accessors/dt_accessor.py +537 -0
- onetick/py/core/column_operations/accessors/float_accessor.py +184 -0
- onetick/py/core/column_operations/accessors/str_accessor.py +1367 -0
- onetick/py/core/column_operations/base.py +1121 -0
- onetick/py/core/cut_builder.py +150 -0
- onetick/py/core/db_constants.py +20 -0
- onetick/py/core/eval_query.py +245 -0
- onetick/py/core/lambda_object.py +441 -0
- onetick/py/core/multi_output_source.py +232 -0
- onetick/py/core/per_tick_script.py +2256 -0
- onetick/py/core/query_inspector.py +464 -0
- onetick/py/core/source.py +1744 -0
- onetick/py/db/__init__.py +2 -0
- onetick/py/db/_inspection.py +1128 -0
- onetick/py/db/db.py +1327 -0
- onetick/py/db/utils.py +64 -0
- onetick/py/docs/__init__.py +0 -0
- onetick/py/docs/docstring_parser.py +112 -0
- onetick/py/docs/utils.py +81 -0
- onetick/py/functions.py +2398 -0
- onetick/py/license.py +190 -0
- onetick/py/log.py +88 -0
- onetick/py/math.py +935 -0
- onetick/py/misc.py +470 -0
- onetick/py/oqd/__init__.py +22 -0
- onetick/py/oqd/eps.py +1195 -0
- onetick/py/oqd/sources.py +325 -0
- onetick/py/otq.py +216 -0
- onetick/py/pyomd_mock.py +47 -0
- onetick/py/run.py +916 -0
- onetick/py/servers.py +173 -0
- onetick/py/session.py +1347 -0
- onetick/py/sources/__init__.py +19 -0
- onetick/py/sources/cache.py +167 -0
- onetick/py/sources/common.py +128 -0
- onetick/py/sources/csv.py +642 -0
- onetick/py/sources/custom.py +85 -0
- onetick/py/sources/data_file.py +305 -0
- onetick/py/sources/data_source.py +1045 -0
- onetick/py/sources/empty.py +94 -0
- onetick/py/sources/odbc.py +337 -0
- onetick/py/sources/order_book.py +271 -0
- onetick/py/sources/parquet.py +168 -0
- onetick/py/sources/pit.py +191 -0
- onetick/py/sources/query.py +495 -0
- onetick/py/sources/snapshots.py +419 -0
- onetick/py/sources/split_query_output_by_symbol.py +198 -0
- onetick/py/sources/symbology_mapping.py +123 -0
- onetick/py/sources/symbols.py +374 -0
- onetick/py/sources/ticks.py +825 -0
- onetick/py/sql.py +70 -0
- onetick/py/state.py +251 -0
- onetick/py/types.py +2131 -0
- onetick/py/utils/__init__.py +70 -0
- onetick/py/utils/acl.py +93 -0
- onetick/py/utils/config.py +186 -0
- onetick/py/utils/default.py +49 -0
- onetick/py/utils/file.py +38 -0
- onetick/py/utils/helpers.py +76 -0
- onetick/py/utils/locator.py +94 -0
- onetick/py/utils/perf.py +498 -0
- onetick/py/utils/query.py +49 -0
- onetick/py/utils/render.py +1374 -0
- onetick/py/utils/script.py +244 -0
- onetick/py/utils/temp.py +471 -0
- onetick/py/utils/types.py +120 -0
- onetick/py/utils/tz.py +84 -0
- onetick_py-1.177.0.dist-info/METADATA +137 -0
- onetick_py-1.177.0.dist-info/RECORD +152 -0
- onetick_py-1.177.0.dist-info/WHEEL +5 -0
- onetick_py-1.177.0.dist-info/entry_points.txt +2 -0
- onetick_py-1.177.0.dist-info/licenses/LICENSE +21 -0
- onetick_py-1.177.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import io
|
|
4
|
+
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
|
|
7
|
+
from onetick.py.utils import abspath_to_query_by_name
|
|
8
|
+
|
|
9
|
+
MULTIPLE_NAMESAKE_PARAMETERS_ALLOWED = frozenset(
|
|
10
|
+
[
|
|
11
|
+
"BIND_SECURITY",
|
|
12
|
+
# "PARAMETER" isn't on the list as it's handled separately
|
|
13
|
+
]
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class QueryNotFoundError(FileNotFoundError):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UncertainQueryName(ValueError):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_queries(otq_path):
|
|
26
|
+
result = []
|
|
27
|
+
with open(otq_path, "r") as fin:
|
|
28
|
+
for line in fin:
|
|
29
|
+
line = line.strip()
|
|
30
|
+
|
|
31
|
+
res = re.match(r"^\[(.*)\]$", line)
|
|
32
|
+
if res:
|
|
33
|
+
name = res.group(1)
|
|
34
|
+
if name != "_meta":
|
|
35
|
+
result.append(name)
|
|
36
|
+
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_commented_out(node):
|
|
41
|
+
return hasattr(node, "COMMENTED_OUT") and (node.COMMENTED_OUT == "1")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_query_info(otq_path, query_name="", inspected_graphs=None):
|
|
45
|
+
"""
|
|
46
|
+
Collects some meta information about an .otq-based query.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
otq_path: str
|
|
51
|
+
Absolute path to an OTQ file.
|
|
52
|
+
query_name: str
|
|
53
|
+
Name of the query to analyze; if it's not specified, the .otq file should have only one query.
|
|
54
|
+
inspected_graphs: None or Dict[tuple of (str, str) : Graph]
|
|
55
|
+
Usually should be kept as the default value (None).
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
|
|
60
|
+
Graph - Instance of an internal class. Contains the following fields:
|
|
61
|
+
nodes: list of Node
|
|
62
|
+
Nodes in the query.
|
|
63
|
+
sources: list of Node
|
|
64
|
+
Nodes without incoming links.
|
|
65
|
+
sinks: list of Node
|
|
66
|
+
Nodes without outcoming links.
|
|
67
|
+
nested_inputs: list of Node
|
|
68
|
+
Nodes with nested inputs.
|
|
69
|
+
nested_outputs: list of Node
|
|
70
|
+
Nodes with nested outputs.
|
|
71
|
+
has_unbound_sources: bool
|
|
72
|
+
Whether the query needs an unbound security list to be provided.
|
|
73
|
+
"""
|
|
74
|
+
if inspected_graphs is None:
|
|
75
|
+
inspected_graphs = {}
|
|
76
|
+
|
|
77
|
+
if (otq_path, query_name) in inspected_graphs:
|
|
78
|
+
return inspected_graphs[(otq_path, query_name)]
|
|
79
|
+
|
|
80
|
+
parsed = False
|
|
81
|
+
found = False
|
|
82
|
+
|
|
83
|
+
class Node:
|
|
84
|
+
def __init__(self):
|
|
85
|
+
self.SINK = []
|
|
86
|
+
self.SOURCE = []
|
|
87
|
+
|
|
88
|
+
def _get_ep_name_and_stack_info(self):
|
|
89
|
+
if not hasattr(self, 'EP'):
|
|
90
|
+
return None, None
|
|
91
|
+
# self.EP can be multiline, so using re.DOTALL for dot to match newline too
|
|
92
|
+
res = re.match(r"([A-Z_]+?)(\((.+)\))?$", self.EP, flags=re.DOTALL)
|
|
93
|
+
if not res:
|
|
94
|
+
return None, None
|
|
95
|
+
ep_name = res.group(1)
|
|
96
|
+
ep_parameters = res.group(3)
|
|
97
|
+
stack_info = None
|
|
98
|
+
if ep_parameters and 'STACK_INFO' in ep_parameters:
|
|
99
|
+
res = re.match(r'.*STACK_INFO="?(-?\d+)', ep_parameters)
|
|
100
|
+
if res:
|
|
101
|
+
stack_info = res.group(1)
|
|
102
|
+
return ep_name, stack_info
|
|
103
|
+
|
|
104
|
+
graph = defaultdict(Node)
|
|
105
|
+
|
|
106
|
+
if not os.path.exists(otq_path):
|
|
107
|
+
raise FileNotFoundError(f'otq "{otq_path}" is not found')
|
|
108
|
+
|
|
109
|
+
if not otq_path.endswith('.py'):
|
|
110
|
+
|
|
111
|
+
multiline_ep = False
|
|
112
|
+
multiline_value = ''
|
|
113
|
+
|
|
114
|
+
with open(otq_path, "r") as fin:
|
|
115
|
+
for line in fin:
|
|
116
|
+
line = line.strip()
|
|
117
|
+
|
|
118
|
+
if line.startswith("[") and line.endswith("]"):
|
|
119
|
+
if ("[" + query_name + "]") == line:
|
|
120
|
+
found = True
|
|
121
|
+
continue
|
|
122
|
+
else:
|
|
123
|
+
if line != "[_meta]" and query_name == "":
|
|
124
|
+
if found is True:
|
|
125
|
+
raise UncertainQueryName(
|
|
126
|
+
"No query name was passed, implying the file has only one query; it has more"
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
found = True
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
if (found and not parsed) or multiline_ep:
|
|
133
|
+
if line.startswith("TYPE = "):
|
|
134
|
+
parsed = True
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
if not multiline_ep:
|
|
138
|
+
res = re.match(r"NODE_?(\d+)_?(.*?)\s?=\s?(.*)", line) # NOSONAR
|
|
139
|
+
|
|
140
|
+
if res:
|
|
141
|
+
num = int(res.group(1))
|
|
142
|
+
param = res.group(2)
|
|
143
|
+
value = res.group(3)
|
|
144
|
+
else:
|
|
145
|
+
res = re.match(r"ROOT_?(.*?)\s?=\s?(.*)", line)
|
|
146
|
+
|
|
147
|
+
if res:
|
|
148
|
+
num = 0
|
|
149
|
+
param = res.group(1)
|
|
150
|
+
value = res.group(2)
|
|
151
|
+
|
|
152
|
+
if res and line.endswith('\\'):
|
|
153
|
+
# first line of multiline
|
|
154
|
+
multiline_ep = True
|
|
155
|
+
multiline_value += value.replace('\\', '\n')
|
|
156
|
+
continue
|
|
157
|
+
else:
|
|
158
|
+
# next line of multiline
|
|
159
|
+
multiline_value += line.replace('\\', '\n')
|
|
160
|
+
if line.endswith('\\'):
|
|
161
|
+
continue
|
|
162
|
+
# last line of multiline
|
|
163
|
+
value = multiline_value
|
|
164
|
+
multiline_ep = False
|
|
165
|
+
multiline_value = ''
|
|
166
|
+
|
|
167
|
+
if res:
|
|
168
|
+
param, value = param.strip(), value.strip()
|
|
169
|
+
|
|
170
|
+
if param == "PARAMETER":
|
|
171
|
+
if not hasattr(graph[num], "PARAMETERS"):
|
|
172
|
+
setattr(graph[num], "PARAMETERS", {})
|
|
173
|
+
bits = value.split(" ")
|
|
174
|
+
inner_param_name = bits[0]
|
|
175
|
+
inner_param_value = " ".join(bits[1:])
|
|
176
|
+
graph[num].PARAMETERS[inner_param_name] = inner_param_value
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
if param in ("SOURCE", "SINK"):
|
|
180
|
+
sources = []
|
|
181
|
+
|
|
182
|
+
for v in value.split():
|
|
183
|
+
if v == "ROOT":
|
|
184
|
+
sources.append(0)
|
|
185
|
+
else:
|
|
186
|
+
res = re.match(r"NODE_?(\d+)(\.(.*))*", v)
|
|
187
|
+
sources.append(int(res.group(1)))
|
|
188
|
+
|
|
189
|
+
value = sources
|
|
190
|
+
elif param == "":
|
|
191
|
+
param = "EP"
|
|
192
|
+
|
|
193
|
+
if param in MULTIPLE_NAMESAKE_PARAMETERS_ALLOWED:
|
|
194
|
+
# A node can have several parameters with the same name
|
|
195
|
+
if not hasattr(graph[num], param):
|
|
196
|
+
setattr(graph[num], param, [])
|
|
197
|
+
|
|
198
|
+
getattr(graph[num], param).append(value)
|
|
199
|
+
else:
|
|
200
|
+
setattr(graph[num], param, value)
|
|
201
|
+
|
|
202
|
+
if not found:
|
|
203
|
+
raise QueryNotFoundError(f'Query "{query_name}" is not found in the {otq_path}')
|
|
204
|
+
|
|
205
|
+
for num, node in graph.items():
|
|
206
|
+
setattr(node, "NUM", num)
|
|
207
|
+
setattr(node, "IS_NESTED", False)
|
|
208
|
+
|
|
209
|
+
if hasattr(node, "NESTED_INPUT"):
|
|
210
|
+
node.NESTED_INPUT = node.NESTED_INPUT.split()[0]
|
|
211
|
+
|
|
212
|
+
if hasattr(node, "NESTED_OUTPUT"):
|
|
213
|
+
node.NESTED_OUTPUT = node.NESTED_OUTPUT.split()[0]
|
|
214
|
+
|
|
215
|
+
for src in node.SOURCE:
|
|
216
|
+
if num not in graph[src].SINK:
|
|
217
|
+
graph[src].SINK.append(num)
|
|
218
|
+
|
|
219
|
+
for src in node.SINK:
|
|
220
|
+
if num not in graph[src].SOURCE:
|
|
221
|
+
graph[src].SOURCE.append(num)
|
|
222
|
+
|
|
223
|
+
if not is_commented_out(node):
|
|
224
|
+
# Commented nodes can affect sources and sinks, but cannot anything else
|
|
225
|
+
if node.EP.startswith("NESTED_OTQ"):
|
|
226
|
+
setattr(node, "IS_NESTED", True)
|
|
227
|
+
if node.EP.strip() == "NESTED_OTQ":
|
|
228
|
+
if "OTQ_PATH" in node.PARAMETERS:
|
|
229
|
+
address = node.PARAMETERS["OTQ_PATH"]
|
|
230
|
+
elif "OTQ_NODE" in node.PARAMETERS:
|
|
231
|
+
address = node.PARAMETERS["OTQ_NODE"]
|
|
232
|
+
elif hasattr(node, "PH_PATH"):
|
|
233
|
+
address = node.PH_PATH
|
|
234
|
+
else:
|
|
235
|
+
raise ValueError("Can't get NESTED_OTQ address")
|
|
236
|
+
|
|
237
|
+
node.EP = "NESTED_OTQ " + address
|
|
238
|
+
|
|
239
|
+
setattr(node, "NESTED_GRAPH", _load_nested_query(otq_path, node.EP, inspected_graphs))
|
|
240
|
+
|
|
241
|
+
class Graph:
|
|
242
|
+
def __init__(self, nodes):
|
|
243
|
+
self.nodes = nodes
|
|
244
|
+
self.sources = [node for _, node in graph.items() if len(node.SOURCE) == 0]
|
|
245
|
+
self.sinks = [node for _, node in graph.items() if len(node.SINK) == 0]
|
|
246
|
+
|
|
247
|
+
self.nested_inputs = [node for node in self.sources if hasattr(node, "NESTED_INPUT")]
|
|
248
|
+
self.nested_outputs = [node for node in self.sinks if hasattr(node, "NESTED_OUTPUT")]
|
|
249
|
+
|
|
250
|
+
inspected_graphs[(otq_path, query_name)] = self
|
|
251
|
+
|
|
252
|
+
self.has_unbound_sources = self._check_for_unbound_sources()
|
|
253
|
+
|
|
254
|
+
def _check_for_unbound_sources(self, bound_symbol_info=None):
|
|
255
|
+
if bound_symbol_info is None:
|
|
256
|
+
bound_symbol_info = {}
|
|
257
|
+
|
|
258
|
+
for node in self.sources:
|
|
259
|
+
self._search_for_bound_sink(node.NUM, bound_symbol_info)
|
|
260
|
+
|
|
261
|
+
has_unbound_sources = any((not bound_symbol_info[node.NUM]) for node in self.sources)
|
|
262
|
+
return has_unbound_sources
|
|
263
|
+
|
|
264
|
+
def _search_for_bound_sink(self, root, bound_symbol_info):
|
|
265
|
+
if root in bound_symbol_info:
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
if not is_commented_out(self.nodes[root]):
|
|
269
|
+
# A commented node cannot have its own bound symbols, but can have a bound sink
|
|
270
|
+
if hasattr(self.nodes[root], "BIND_SECURITY"):
|
|
271
|
+
for bound_security in self.nodes[root].BIND_SECURITY:
|
|
272
|
+
if (
|
|
273
|
+
("_SYMBOL_NAME" not in bound_security)
|
|
274
|
+
and ("_SYMBOL_PARAM" not in bound_security)
|
|
275
|
+
and (not bound_security.endswith("No"))
|
|
276
|
+
):
|
|
277
|
+
# The first two define dependency on lower-bound symbol;
|
|
278
|
+
# The last is whether the bound security is not unchecked in the list
|
|
279
|
+
|
|
280
|
+
bound_symbol_info[root] = True
|
|
281
|
+
break
|
|
282
|
+
|
|
283
|
+
if self.nodes[root].IS_NESTED:
|
|
284
|
+
nested = self.nodes[root].NESTED_GRAPH
|
|
285
|
+
if not nested.has_unbound_sources:
|
|
286
|
+
bound_symbol_info[root] = True
|
|
287
|
+
|
|
288
|
+
if root not in bound_symbol_info:
|
|
289
|
+
for node in self.nodes[root].SINK:
|
|
290
|
+
if node not in bound_symbol_info:
|
|
291
|
+
self._search_for_bound_sink(node, bound_symbol_info)
|
|
292
|
+
|
|
293
|
+
if bound_symbol_info.get(node, None) is True:
|
|
294
|
+
bound_symbol_info[root] = True
|
|
295
|
+
break
|
|
296
|
+
|
|
297
|
+
if root not in bound_symbol_info:
|
|
298
|
+
bound_symbol_info[root] = False
|
|
299
|
+
|
|
300
|
+
def has_unbound_if_pinned(self, in_pins):
|
|
301
|
+
"""
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
in_pins: dict(str: bool)
|
|
305
|
+
mapping of pins to whether the respective source needs unbound symbols
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
True if query with pins bound to graphs with described boundness state would need unbound symbols.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
if not self.has_unbound_sources:
|
|
313
|
+
# All sources are bound inside the query graph.
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
in_pins_to_nodes = {node.NESTED_INPUT: node for node in self.nested_inputs}
|
|
317
|
+
|
|
318
|
+
bound_symbol_info = {}
|
|
319
|
+
|
|
320
|
+
for pin, needs_unbound in in_pins.items():
|
|
321
|
+
if not needs_unbound:
|
|
322
|
+
bound_symbol_info[in_pins_to_nodes[pin].NUM] = True
|
|
323
|
+
|
|
324
|
+
return self._check_for_unbound_sources(bound_symbol_info)
|
|
325
|
+
|
|
326
|
+
return Graph(graph)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _load_nested_query(nesting_file, nested_address, inspected_graphs):
|
|
330
|
+
nested_address = nested_address.split(" ")[1]
|
|
331
|
+
address_bits = nested_address.split("::")
|
|
332
|
+
|
|
333
|
+
if len(address_bits) == 1:
|
|
334
|
+
# Only the path is given, no query name
|
|
335
|
+
# E.g. utils/FindSymbols.otq
|
|
336
|
+
query_file = address_bits[0]
|
|
337
|
+
query = ""
|
|
338
|
+
elif address_bits[-1].endswith(".otq"):
|
|
339
|
+
# remote://DB::utils/FindSymbols.otq
|
|
340
|
+
query_file = address_bits[-1]
|
|
341
|
+
query = ""
|
|
342
|
+
else:
|
|
343
|
+
# remote://DB::utils/FindSymbols.otq::Find
|
|
344
|
+
query_file, query = address_bits[-2:]
|
|
345
|
+
|
|
346
|
+
if query_file == "___ME___":
|
|
347
|
+
otq_path = nesting_file
|
|
348
|
+
else:
|
|
349
|
+
otq_path = abspath_to_query_by_name(query_file)
|
|
350
|
+
|
|
351
|
+
nested_graph = get_query_info(otq_path, query, inspected_graphs)
|
|
352
|
+
return nested_graph
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def add_pins(otq_path, query_name, specification):
|
|
356
|
+
"""
|
|
357
|
+
The function takes a query and adds pins to the query according to the
|
|
358
|
+
specification, and saves it back to the original file.
|
|
359
|
+
|
|
360
|
+
Parameters
|
|
361
|
+
----------
|
|
362
|
+
otq_path: str
|
|
363
|
+
Absolute path to an OTQ file.
|
|
364
|
+
query_name: str
|
|
365
|
+
Name of the query to analyze
|
|
366
|
+
specification: List[tuple]
|
|
367
|
+
List of 3-values tuples, where the first value node from the `get_query_info` function,
|
|
368
|
+
the second one is marker, the third one is name.
|
|
369
|
+
A marker values: 1 for input, and 0 for output.
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
Nothing
|
|
374
|
+
|
|
375
|
+
Raises
|
|
376
|
+
------
|
|
377
|
+
FileNotFoundError
|
|
378
|
+
"""
|
|
379
|
+
if not os.path.exists(otq_path):
|
|
380
|
+
raise FileNotFoundError(f'otq "{otq_path}" is not found')
|
|
381
|
+
|
|
382
|
+
found = False
|
|
383
|
+
out = io.StringIO()
|
|
384
|
+
|
|
385
|
+
with open(otq_path, "r") as fin:
|
|
386
|
+
|
|
387
|
+
multiline_ep = False
|
|
388
|
+
|
|
389
|
+
for line in fin:
|
|
390
|
+
line = line.strip()
|
|
391
|
+
|
|
392
|
+
if line.startswith("[") and line.endswith("]"):
|
|
393
|
+
if ("[" + query_name + "]") == line:
|
|
394
|
+
found = True
|
|
395
|
+
|
|
396
|
+
out.write(line + "\n")
|
|
397
|
+
|
|
398
|
+
if found or multiline_ep:
|
|
399
|
+
if not multiline_ep:
|
|
400
|
+
res = re.match(r"NODE_?(\d+)_?(.*?)\s?=\s?(.*)", line) # NOSONAR
|
|
401
|
+
|
|
402
|
+
if res:
|
|
403
|
+
num = int(res.group(1))
|
|
404
|
+
else:
|
|
405
|
+
res = re.match(r"ROOT_?(.*?)\s?=\s?(.*)", line)
|
|
406
|
+
|
|
407
|
+
if res:
|
|
408
|
+
num = 0
|
|
409
|
+
|
|
410
|
+
if res and line.endswith('\\'):
|
|
411
|
+
# first line of multiline
|
|
412
|
+
multiline_ep = True
|
|
413
|
+
continue
|
|
414
|
+
else:
|
|
415
|
+
if line.endswith('\\'):
|
|
416
|
+
continue
|
|
417
|
+
multiline_ep = False
|
|
418
|
+
|
|
419
|
+
if res:
|
|
420
|
+
for inx, v in enumerate(specification):
|
|
421
|
+
node, pin_flag, pin_name = v
|
|
422
|
+
if node.NUM == num: # noqa
|
|
423
|
+
if pin_flag is None:
|
|
424
|
+
continue
|
|
425
|
+
node_name = "NODE_" + str(num)
|
|
426
|
+
if num == 0:
|
|
427
|
+
node_name = "ROOT"
|
|
428
|
+
|
|
429
|
+
pin_id = "NESTED_INPUT" if pin_flag == 1 else "NESTED_OUTPUT"
|
|
430
|
+
|
|
431
|
+
out.write(node_name + "_" + pin_id + " = " + pin_name + "\n")
|
|
432
|
+
|
|
433
|
+
# don't use this specification item anymore
|
|
434
|
+
specification[inx] = (node, None, None)
|
|
435
|
+
|
|
436
|
+
with open(otq_path, "w") as fout:
|
|
437
|
+
fout.write(out.getvalue())
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def get_query_parameter_list(otq_path, query_name):
|
|
441
|
+
"""Returns a list of query parameter names; can be used for nesting to pass the parameters to the nested query"""
|
|
442
|
+
if not os.path.exists(otq_path):
|
|
443
|
+
raise FileNotFoundError(f'otq "{otq_path}" is not found')
|
|
444
|
+
|
|
445
|
+
found = False
|
|
446
|
+
param_list = []
|
|
447
|
+
|
|
448
|
+
with open(otq_path, "r") as fin:
|
|
449
|
+
for line in fin:
|
|
450
|
+
line = line.strip()
|
|
451
|
+
|
|
452
|
+
if line.startswith("[") and line.endswith("]"):
|
|
453
|
+
if ("[" + query_name + "]") == line:
|
|
454
|
+
found = True
|
|
455
|
+
else:
|
|
456
|
+
found = False
|
|
457
|
+
|
|
458
|
+
if found:
|
|
459
|
+
params = re.findall(r'\$(\w+|(\{(\w+)\}))', line)
|
|
460
|
+
if params:
|
|
461
|
+
param_list.extend([p[2] if p[2] != '' else p[0] for p in params])
|
|
462
|
+
|
|
463
|
+
param_list.sort()
|
|
464
|
+
return param_list
|