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
|
File without changes
|
locator_parser/acl.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from locator_parser.common import Entity
|
|
2
|
+
from locator_parser.actions import DoNothing
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Allow(Entity):
|
|
6
|
+
TAG = "ALLOW"
|
|
7
|
+
SINGLE = True
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DB(Entity):
|
|
11
|
+
TAG = "DB"
|
|
12
|
+
HAS_PROPERTIES = True
|
|
13
|
+
CHILDREN = [Allow]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DBs(Entity):
|
|
17
|
+
TAG = "DATABASES"
|
|
18
|
+
CHILDREN = [DB]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class User(Entity):
|
|
22
|
+
TAG = "USER"
|
|
23
|
+
SINGLE = True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Role(Entity):
|
|
27
|
+
TAG = "ROLE"
|
|
28
|
+
HAS_PROPERTIES = True
|
|
29
|
+
CHILDREN = [User]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Roles(Entity):
|
|
33
|
+
TAG = "ROLES"
|
|
34
|
+
CHILDREN = [Role]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class EP(Entity):
|
|
38
|
+
TAG = "EP"
|
|
39
|
+
HAS_PROPERTIES = True
|
|
40
|
+
CHILDREN = [Allow]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class EPs(Entity):
|
|
44
|
+
TAG = "EVENT_PROCESSORS"
|
|
45
|
+
CHILDREN = [EP]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_acl(reader, writer, action=DoNothing()):
|
|
49
|
+
writer.refresh()
|
|
50
|
+
reader.set_writer(writer)
|
|
51
|
+
|
|
52
|
+
roles_p = Roles()
|
|
53
|
+
databases_p = DBs()
|
|
54
|
+
eps_p = EPs()
|
|
55
|
+
|
|
56
|
+
for _ in reader:
|
|
57
|
+
roles_p(reader, writer, action)
|
|
58
|
+
databases_p(reader, writer, action)
|
|
59
|
+
eps_p(reader, writer, action)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ----------------------------------------- #
|
|
63
|
+
# set parent
|
|
64
|
+
for _, cls in list(globals().items()):
|
|
65
|
+
try:
|
|
66
|
+
if issubclass(cls, Entity) and cls != Entity:
|
|
67
|
+
for child in cls.CHILDREN:
|
|
68
|
+
child.PARENT = Entity
|
|
69
|
+
|
|
70
|
+
if not cls.HAS_PROPERTIES and not cls.SINGLE:
|
|
71
|
+
child.PARENT = cls
|
|
72
|
+
except Exception:
|
|
73
|
+
continue
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from locator_parser.common import Action, Entity
|
|
2
|
+
from collections import OrderedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Add(Action):
|
|
6
|
+
"""
|
|
7
|
+
Add entity.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
raw_db = Add(RawDB(id='PRIMARY'))
|
|
11
|
+
raw_db.add_where(DB, id='DB_2')
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, obj):
|
|
15
|
+
self.what_to_add = obj.to_lines()
|
|
16
|
+
|
|
17
|
+
super().__init__()
|
|
18
|
+
|
|
19
|
+
if obj.__class__.PARENT is not Entity:
|
|
20
|
+
self._add_conditions[(obj.__class__.PARENT.TAG.lower(), None, None)] = False
|
|
21
|
+
|
|
22
|
+
def do(self, obj, all_lines, properties, writer):
|
|
23
|
+
writer.add(self.what_to_add)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Modify(Action):
|
|
27
|
+
"""
|
|
28
|
+
Modify entity properties.
|
|
29
|
+
If property doesn't exist, then it will be added.
|
|
30
|
+
If property is set to None, then it will be removed.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
action = Modify(symbology='TDEQ', db_archive_tmp_dir=None)
|
|
34
|
+
action.add_where(DB, id="DB_1")
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kwargs):
|
|
38
|
+
if len(kwargs) > 1:
|
|
39
|
+
raise ValueError("It is not supported to set multiple keys yet!")
|
|
40
|
+
|
|
41
|
+
field, value = "", ""
|
|
42
|
+
delete = False
|
|
43
|
+
|
|
44
|
+
for k, v in kwargs.items():
|
|
45
|
+
if v is None:
|
|
46
|
+
delete = True
|
|
47
|
+
|
|
48
|
+
field, value = k, str(v)
|
|
49
|
+
|
|
50
|
+
self.init(field, value, delete)
|
|
51
|
+
|
|
52
|
+
super().__init__()
|
|
53
|
+
|
|
54
|
+
def init(self, field, value, delete):
|
|
55
|
+
self.field = field.lower()
|
|
56
|
+
self.value = '"' + value + '"'
|
|
57
|
+
self.delete = delete
|
|
58
|
+
|
|
59
|
+
def do(self, obj, all_lines, properties_lines, writer):
|
|
60
|
+
if not obj.__class__.HAS_PROPERTIES:
|
|
61
|
+
raise AttributeError("Entity with tag '" + obj.__class__.TAG + "' does not expect to have properties!")
|
|
62
|
+
|
|
63
|
+
new_properties = list(properties_lines)
|
|
64
|
+
|
|
65
|
+
if not hasattr(obj, self.field):
|
|
66
|
+
for line in new_properties:
|
|
67
|
+
end_pos = line.find(">")
|
|
68
|
+
|
|
69
|
+
if end_pos != -1:
|
|
70
|
+
if line.find("/>") != -1 and line.find("/>") < end_pos:
|
|
71
|
+
end_pos = line.find("/>")
|
|
72
|
+
|
|
73
|
+
line = line[:end_pos] + " %s=%s" % (self.field, self.value) + line[end_pos:]
|
|
74
|
+
new_properties[-1] = line
|
|
75
|
+
break
|
|
76
|
+
else:
|
|
77
|
+
for inx, line in enumerate(new_properties):
|
|
78
|
+
pos = line.lower().find(self.field + "=")
|
|
79
|
+
|
|
80
|
+
if pos != -1:
|
|
81
|
+
# modify property
|
|
82
|
+
t_line = line[pos + len(self.field):].split()[0].split("/>")[0].split(">")[0].split("=")[1]
|
|
83
|
+
|
|
84
|
+
if self.delete:
|
|
85
|
+
line = line.replace(line[pos: pos + len(self.field) + 1] + t_line, "")
|
|
86
|
+
else:
|
|
87
|
+
line = line.replace(
|
|
88
|
+
line[pos: pos + len(self.field) + 1] + t_line,
|
|
89
|
+
line[pos: pos + len(self.field) + 1] + self.value,
|
|
90
|
+
)
|
|
91
|
+
new_properties[inx] = line
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
writer.modify(len(all_lines), properties_lines, new_properties)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Delete(Action):
|
|
98
|
+
"""
|
|
99
|
+
Delete entity
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
action = Delete()
|
|
103
|
+
action.add_where(DB, id="DB_1")
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def do(self, obj, all_lines, properites_lines, writer):
|
|
107
|
+
writer.delete(all_lines)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Get(Action):
|
|
111
|
+
"""
|
|
112
|
+
Populate self.result object with properites of found object
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
action = Get()
|
|
116
|
+
action.add_where(DB, id='LB')
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def do(self, obj, all_lines, properties_lines, writer):
|
|
120
|
+
self.lines = all_lines
|
|
121
|
+
|
|
122
|
+
# self.result = obj.__class__()
|
|
123
|
+
properties_dict = {}
|
|
124
|
+
|
|
125
|
+
properties_lines = " ".join(properties_lines)
|
|
126
|
+
|
|
127
|
+
# variables to concat space separated
|
|
128
|
+
# values
|
|
129
|
+
res_token = ""
|
|
130
|
+
double_quotes_count = 0
|
|
131
|
+
single_quotes_count = 0
|
|
132
|
+
|
|
133
|
+
for token in filter(lambda x: x not in ("", "<", "/>", ">"), properties_lines.split()[1:]):
|
|
134
|
+
# ---------------------
|
|
135
|
+
# Logic to combine values in quotes are separated
|
|
136
|
+
# by space
|
|
137
|
+
if '"' in token:
|
|
138
|
+
double_quotes_count += token.count('"')
|
|
139
|
+
if "'" in token:
|
|
140
|
+
single_quotes_count += token.count("'")
|
|
141
|
+
|
|
142
|
+
if double_quotes_count > 0 or single_quotes_count > 0:
|
|
143
|
+
if (double_quotes_count + single_quotes_count) % 2 == 0:
|
|
144
|
+
res_token += token
|
|
145
|
+
double_quotes_count = 0
|
|
146
|
+
single_quotes_count = 0
|
|
147
|
+
else:
|
|
148
|
+
res_token += token
|
|
149
|
+
continue
|
|
150
|
+
else:
|
|
151
|
+
res_token = token
|
|
152
|
+
# --------------------
|
|
153
|
+
|
|
154
|
+
t_list = res_token.split("=")
|
|
155
|
+
|
|
156
|
+
# refresh to do not concat
|
|
157
|
+
# already ready token
|
|
158
|
+
res_token = ""
|
|
159
|
+
double_quotes_count = 0
|
|
160
|
+
single_quotes_count = 0
|
|
161
|
+
|
|
162
|
+
key, value = t_list[0], t_list[1]
|
|
163
|
+
|
|
164
|
+
if value.endswith("/>"):
|
|
165
|
+
value = value[:-2]
|
|
166
|
+
|
|
167
|
+
if value.endswith(">") or value.endswith("/"):
|
|
168
|
+
value = value[:-1]
|
|
169
|
+
|
|
170
|
+
properties_dict[key.lower()] = value.replace('"', "")
|
|
171
|
+
# setattr(self.result, key.lower(), value.replace('"', ''))
|
|
172
|
+
|
|
173
|
+
self.result = obj.__class__(**properties_dict)
|
|
174
|
+
|
|
175
|
+
for key, value in properties_dict.items():
|
|
176
|
+
setattr(self.result, key, value)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class GetAll(Action):
|
|
180
|
+
|
|
181
|
+
def __init__(self):
|
|
182
|
+
super().__init__()
|
|
183
|
+
self.result = []
|
|
184
|
+
|
|
185
|
+
def do(self, obj, all_lines, properties_lines, writer):
|
|
186
|
+
get = Get()
|
|
187
|
+
get.do(obj, all_lines, properties_lines, writer)
|
|
188
|
+
|
|
189
|
+
self.result.append(get.result)
|
|
190
|
+
|
|
191
|
+
def apply(self, tag, entity):
|
|
192
|
+
if self.executed:
|
|
193
|
+
self.just_executed = False
|
|
194
|
+
self.executed = False
|
|
195
|
+
|
|
196
|
+
# Base is one that we are looking for; this entity has only
|
|
197
|
+
# tag specified.
|
|
198
|
+
# Here is logic to refresh the base, because we have to go
|
|
199
|
+
# through the subtree.
|
|
200
|
+
is_base = False
|
|
201
|
+
for key, value in reversed(self.conditions.items()):
|
|
202
|
+
t, f, v = key
|
|
203
|
+
|
|
204
|
+
if f is None and v is None:
|
|
205
|
+
is_base = True
|
|
206
|
+
|
|
207
|
+
if is_base:
|
|
208
|
+
self.conditions[key] = False
|
|
209
|
+
# leave, because we support only the last open-ended base
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
# first time it updates and then does nothing
|
|
213
|
+
self.conditions.update(self._add_conditions)
|
|
214
|
+
self._add_conditions = OrderedDict()
|
|
215
|
+
|
|
216
|
+
tag = tag.lower()
|
|
217
|
+
|
|
218
|
+
for key, value in self.conditions.items():
|
|
219
|
+
t, f, v = key
|
|
220
|
+
|
|
221
|
+
if tag == t:
|
|
222
|
+
if f:
|
|
223
|
+
if hasattr(entity, f) and getattr(entity, f) == '"' + v + '"':
|
|
224
|
+
self.conditions[(t, f, v)] = True
|
|
225
|
+
else:
|
|
226
|
+
# if we move to the same similar section, but with another key,
|
|
227
|
+
# then set it to False, because in the previous section it might be
|
|
228
|
+
# set to True
|
|
229
|
+
self.conditions[(t, f, v)] = False
|
|
230
|
+
else:
|
|
231
|
+
self.conditions[(t, f, v)] = True
|
|
232
|
+
else:
|
|
233
|
+
if value:
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
if not self.conditions[(t, f, v)]:
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
self.executed = self.is_result()
|
|
240
|
+
self.just_executed = self.executed
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class Show(Action):
|
|
244
|
+
"""
|
|
245
|
+
Print object and all children objects
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
action = Show()
|
|
249
|
+
action.add_where(DB, id='S_ORDERS_LB')
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
def do(self, obj, all_lines, properties_lines, writer):
|
|
253
|
+
print("\n".join(all_lines))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class DoNothing(Action):
|
|
257
|
+
"""
|
|
258
|
+
Default action that does nothing
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def do(self, *args, **kwargs):
|
|
262
|
+
pass
|
locator_parser/common.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from collections import OrderedDict
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import List, Type
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_properties(declaration):
|
|
8
|
+
"""
|
|
9
|
+
Construct dictionary from properties in format
|
|
10
|
+
<prop_1=value_1 prop_2=value_2 ... >
|
|
11
|
+
or
|
|
12
|
+
<prop_1=value_1 prop_2=value_2 ... />
|
|
13
|
+
and return it back
|
|
14
|
+
"""
|
|
15
|
+
tokens = declaration.split()
|
|
16
|
+
|
|
17
|
+
properties = {
|
|
18
|
+
token.split("=")[0].lower(): token.split("=")[1].replace("/>", "").replace(">", "")
|
|
19
|
+
for token in tokens[1:]
|
|
20
|
+
if "=" in token
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return properties
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Generator:
|
|
27
|
+
|
|
28
|
+
def __iter__(self):
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
def __next__(self):
|
|
32
|
+
self.current = next(self.generator)
|
|
33
|
+
return self.current
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Action(ABC):
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.conditions = OrderedDict()
|
|
40
|
+
# additional conditions that we can deduce
|
|
41
|
+
# it's applicable for the sections without properties
|
|
42
|
+
#
|
|
43
|
+
# For example, if you add DB, then you can forget that
|
|
44
|
+
# it's situated in the single <databases> section; that
|
|
45
|
+
# information we retrieve from the entities inheritance
|
|
46
|
+
# put here and use along with conditions dict
|
|
47
|
+
self._add_conditions = OrderedDict()
|
|
48
|
+
|
|
49
|
+
self.executed = False
|
|
50
|
+
self.just_executed = False
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def do(self):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def clone(self, *args, **kwargs):
|
|
57
|
+
obj = self.__class__(*args, **kwargs)
|
|
58
|
+
obj.conditions = dict(self.conditions)
|
|
59
|
+
|
|
60
|
+
for key, _ in obj.conditions.items():
|
|
61
|
+
obj.conditions[key] = False
|
|
62
|
+
|
|
63
|
+
return obj
|
|
64
|
+
|
|
65
|
+
def apply(self, tag, entity):
|
|
66
|
+
if self.executed:
|
|
67
|
+
self.just_executed = False
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
# first time it updates and then does nothing
|
|
71
|
+
self.conditions.update(self._add_conditions)
|
|
72
|
+
self._add_conditions = OrderedDict()
|
|
73
|
+
|
|
74
|
+
tag = tag.lower()
|
|
75
|
+
|
|
76
|
+
for key, value in self.conditions.items():
|
|
77
|
+
t, f, v = key
|
|
78
|
+
|
|
79
|
+
if tag == t:
|
|
80
|
+
if f:
|
|
81
|
+
if hasattr(entity, f) and getattr(entity, f) == '"' + v + '"':
|
|
82
|
+
self.conditions[(t, f, v)] = True
|
|
83
|
+
else:
|
|
84
|
+
# if we move to the same similar section, but with another key,
|
|
85
|
+
# then set it to False, because in the previous section it might be
|
|
86
|
+
# set to True
|
|
87
|
+
self.conditions[(t, f, v)] = False
|
|
88
|
+
else:
|
|
89
|
+
self.conditions[(t, f, v)] = True
|
|
90
|
+
else:
|
|
91
|
+
if value:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
if not self.conditions[(t, f, v)]:
|
|
95
|
+
break
|
|
96
|
+
|
|
97
|
+
self.executed = self.is_result()
|
|
98
|
+
self.just_executed = self.executed
|
|
99
|
+
|
|
100
|
+
def is_result(self):
|
|
101
|
+
for v in self.conditions.values():
|
|
102
|
+
if not v:
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
def add_where(self, tag, **kwargs):
|
|
108
|
+
if not isinstance(tag, str):
|
|
109
|
+
# for case when it's class name
|
|
110
|
+
tag = tag.TAG
|
|
111
|
+
|
|
112
|
+
if len(kwargs) > 1:
|
|
113
|
+
raise ValueError("It is not support to set multiple keys yet!")
|
|
114
|
+
|
|
115
|
+
if len(kwargs) == 0:
|
|
116
|
+
self.conditions[(tag.lower(), None, None)] = False
|
|
117
|
+
else:
|
|
118
|
+
self.conditions[(tag.lower(), list(kwargs.keys())[0].lower(), list(kwargs.values())[0])] = False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def apply_actions(func, reader, writer, actions=None, flush=False):
|
|
122
|
+
from locator_parser.io import LinesReader
|
|
123
|
+
from locator_parser.actions import DoNothing
|
|
124
|
+
|
|
125
|
+
if actions is None:
|
|
126
|
+
actions = []
|
|
127
|
+
|
|
128
|
+
if len(actions) == 0:
|
|
129
|
+
actions.append(DoNothing())
|
|
130
|
+
|
|
131
|
+
func(reader, writer, actions[0])
|
|
132
|
+
|
|
133
|
+
for action in actions[1:]:
|
|
134
|
+
lines = "\n".join(map(lambda x: x.replace("\n", ""), writer.lines))
|
|
135
|
+
new_reader = LinesReader(lines)
|
|
136
|
+
|
|
137
|
+
func(new_reader, writer, action)
|
|
138
|
+
|
|
139
|
+
if flush:
|
|
140
|
+
writer.flush()
|
|
141
|
+
|
|
142
|
+
for action in actions:
|
|
143
|
+
if not action.executed:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class Writer(ABC):
|
|
150
|
+
|
|
151
|
+
NOTHING_MSG = 0
|
|
152
|
+
NEW_MSG = 1
|
|
153
|
+
MODIFY_MSG = 2
|
|
154
|
+
DELETE_MSG = 3
|
|
155
|
+
|
|
156
|
+
def __init__(self):
|
|
157
|
+
self.refresh()
|
|
158
|
+
|
|
159
|
+
def refresh(self):
|
|
160
|
+
self.__gen = self.__generator__()
|
|
161
|
+
# init generator to send further messages
|
|
162
|
+
next(self.__gen)
|
|
163
|
+
|
|
164
|
+
self.lines = []
|
|
165
|
+
|
|
166
|
+
def __generator__(self):
|
|
167
|
+
while True:
|
|
168
|
+
msg_type = yield
|
|
169
|
+
|
|
170
|
+
if msg_type == Writer.NOTHING_MSG:
|
|
171
|
+
# since second value is a just next line
|
|
172
|
+
line = yield
|
|
173
|
+
self.lines.append(line)
|
|
174
|
+
elif msg_type == Writer.NEW_MSG:
|
|
175
|
+
lines = yield
|
|
176
|
+
|
|
177
|
+
assert isinstance(lines, list)
|
|
178
|
+
|
|
179
|
+
self.lines = self.lines[:-1] + lines + self.lines[-1:]
|
|
180
|
+
elif msg_type == Writer.MODIFY_MSG:
|
|
181
|
+
pos, orig_lines, new_lines = yield
|
|
182
|
+
|
|
183
|
+
for inx, pair in enumerate(zip(orig_lines, new_lines)):
|
|
184
|
+
orig_line, new_line = pair
|
|
185
|
+
|
|
186
|
+
self.lines[-pos + inx] = self.lines[-pos + inx].replace(orig_line, new_line)
|
|
187
|
+
elif msg_type == Writer.DELETE_MSG:
|
|
188
|
+
lines_to_delete = yield
|
|
189
|
+
|
|
190
|
+
for _ in range(len(lines_to_delete)):
|
|
191
|
+
self.lines.pop()
|
|
192
|
+
|
|
193
|
+
def __next__(self):
|
|
194
|
+
return self.__send(None)
|
|
195
|
+
|
|
196
|
+
def __send(self, arg):
|
|
197
|
+
"""private method to not allow user to send something directly"""
|
|
198
|
+
self.__gen.send(arg)
|
|
199
|
+
|
|
200
|
+
def put_as_is(self, line):
|
|
201
|
+
self.__send(Writer.NOTHING_MSG)
|
|
202
|
+
self.__send(line)
|
|
203
|
+
|
|
204
|
+
def add(self, lines):
|
|
205
|
+
self.__send(Writer.NEW_MSG)
|
|
206
|
+
self.__send(lines)
|
|
207
|
+
|
|
208
|
+
def modify(self, pos, orig_lines, new_lines):
|
|
209
|
+
self.__send(Writer.MODIFY_MSG)
|
|
210
|
+
self.__send((pos, orig_lines, new_lines))
|
|
211
|
+
|
|
212
|
+
def delete(self, lines):
|
|
213
|
+
self.__send(Writer.DELETE_MSG)
|
|
214
|
+
self.__send(lines)
|
|
215
|
+
|
|
216
|
+
@abstractmethod
|
|
217
|
+
def flush(self):
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class Reader(Generator):
|
|
222
|
+
|
|
223
|
+
def __init__(self):
|
|
224
|
+
self.generator = self.__generator__()
|
|
225
|
+
self.writer = None
|
|
226
|
+
self.current = None
|
|
227
|
+
|
|
228
|
+
def set_writer(self, writer):
|
|
229
|
+
self.__writer = writer
|
|
230
|
+
|
|
231
|
+
def __generator__(self):
|
|
232
|
+
for line in self.iterable_object:
|
|
233
|
+
# put just coming line as is into the writer
|
|
234
|
+
self.__writer.put_as_is(line)
|
|
235
|
+
|
|
236
|
+
line = line.strip()
|
|
237
|
+
|
|
238
|
+
if line == "" or line[0:1] == "#":
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
yield line
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class Entity(Generator, ABC):
|
|
245
|
+
|
|
246
|
+
# Tag to identify entity
|
|
247
|
+
TAG = ""
|
|
248
|
+
# Whether entity has properties
|
|
249
|
+
HAS_PROPERTIES = False
|
|
250
|
+
# Whether it is single line properties,
|
|
251
|
+
# it means that entity ends with '/>' instead of '</TAG>'
|
|
252
|
+
SINGLE = False
|
|
253
|
+
# children entities
|
|
254
|
+
CHILDREN: List[Type["Entity"]] = []
|
|
255
|
+
|
|
256
|
+
def __init__(self, **kwargs):
|
|
257
|
+
if self.__class__.SINGLE:
|
|
258
|
+
self.__class__.HAS_PROPERTIES = True
|
|
259
|
+
|
|
260
|
+
self.__tag = self.__class__.TAG.lower()
|
|
261
|
+
|
|
262
|
+
# define condition to identify section
|
|
263
|
+
pattern = "<\\s*" + self.__tag
|
|
264
|
+
if self.__class__.HAS_PROPERTIES:
|
|
265
|
+
pattern += "\\s+"
|
|
266
|
+
else:
|
|
267
|
+
pattern += "\\s*>"
|
|
268
|
+
|
|
269
|
+
self.__cond = lambda x: re.match(pattern, x.lower())
|
|
270
|
+
self.__lines = []
|
|
271
|
+
self.__properties_lines = []
|
|
272
|
+
self.__do = False
|
|
273
|
+
self.__custom_properties = kwargs
|
|
274
|
+
|
|
275
|
+
def to_lines(self):
|
|
276
|
+
result = []
|
|
277
|
+
line = "<" + self.__class__.TAG + " "
|
|
278
|
+
|
|
279
|
+
for key, value in self.__custom_properties.items():
|
|
280
|
+
line += key + '="' + str(value) + '" '
|
|
281
|
+
|
|
282
|
+
if self.__class__.SINGLE:
|
|
283
|
+
line += "/>"
|
|
284
|
+
result.append(line)
|
|
285
|
+
else:
|
|
286
|
+
line += ">"
|
|
287
|
+
result.append(line)
|
|
288
|
+
|
|
289
|
+
offset = " "
|
|
290
|
+
for child in self.__class__.CHILDREN:
|
|
291
|
+
if not child.SINGLE and not child.HAS_PROPERTIES:
|
|
292
|
+
result.append(offset + "<" + child.TAG + ">")
|
|
293
|
+
result.append(offset + "</" + child.TAG + ">")
|
|
294
|
+
|
|
295
|
+
result.append("</" + self.__class__.TAG + ">")
|
|
296
|
+
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
def __process(self, reader, writer, action):
|
|
300
|
+
line = reader.current
|
|
301
|
+
pattern = "</\\s*" + self.__tag + "\\s*>"
|
|
302
|
+
|
|
303
|
+
while True:
|
|
304
|
+
self.__lines.append(line)
|
|
305
|
+
|
|
306
|
+
if self.__class__.SINGLE:
|
|
307
|
+
if line.find("/>") != -1:
|
|
308
|
+
# the final line for single and we have to return it
|
|
309
|
+
yield line
|
|
310
|
+
|
|
311
|
+
if action.just_executed:
|
|
312
|
+
self.finish(writer, action)
|
|
313
|
+
|
|
314
|
+
return
|
|
315
|
+
else:
|
|
316
|
+
if re.match(pattern, line.lower()):
|
|
317
|
+
self.finish(writer, action)
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
line = next(reader)
|
|
322
|
+
yield line
|
|
323
|
+
except StopIteration:
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
def finish(self, writer, action):
|
|
327
|
+
if self.__do:
|
|
328
|
+
action.do(self, self.__lines, self.__properties_lines, writer)
|
|
329
|
+
|
|
330
|
+
def __read_properties__(self):
|
|
331
|
+
line = self.current
|
|
332
|
+
self.__properties_lines.append(line)
|
|
333
|
+
declare = line
|
|
334
|
+
|
|
335
|
+
while line.find(">") == -1:
|
|
336
|
+
line = next(self)
|
|
337
|
+
self.__properties_lines.append(line)
|
|
338
|
+
declare += " " + line
|
|
339
|
+
|
|
340
|
+
properties = get_properties(declare)
|
|
341
|
+
self.__dict__.update(properties)
|
|
342
|
+
|
|
343
|
+
def __process_children__(self, reader, writer, action):
|
|
344
|
+
for _ in self:
|
|
345
|
+
for child_p in self.__class__.CHILDREN:
|
|
346
|
+
child_p()(self, writer, action)
|
|
347
|
+
|
|
348
|
+
def __call__(self, reader, writer, action):
|
|
349
|
+
if self.__cond(reader.current):
|
|
350
|
+
self.generator = self.__process(reader, writer, action)
|
|
351
|
+
self.current = reader.current
|
|
352
|
+
|
|
353
|
+
self.__read_properties__()
|
|
354
|
+
action.apply(self.__class__.TAG, self)
|
|
355
|
+
|
|
356
|
+
if action.just_executed:
|
|
357
|
+
self.__do = True
|
|
358
|
+
|
|
359
|
+
self.parse(self, writer, action)
|
|
360
|
+
|
|
361
|
+
self.__process_children__(self, writer, action)
|
|
362
|
+
|
|
363
|
+
return True
|
|
364
|
+
|
|
365
|
+
return False
|
|
366
|
+
|
|
367
|
+
def parse(self, reader, writer, action):
|
|
368
|
+
pass
|