onetick.query-webapi 20250727.0.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.
- onetick/README.query_webapi.md +16 -0
- onetick/eps_generator_webapi/__main__.py +77 -0
- onetick/eps_generator_webapi/eps_generator.py +428 -0
- onetick/eps_generator_webapi/eps_list.otq +17 -0
- onetick/eps_generator_webapi/eps_list_doc.otq +19 -0
- onetick/eps_generator_webapi/plugin.py +74 -0
- onetick/eps_generator_webapi/utils.py +184 -0
- onetick/license.query_webapi.txt +21 -0
- onetick/query_webapi/__init__.py +23 -0
- onetick/query_webapi/_eps_factory.py +544 -0
- onetick/query_webapi/_internal_utils.py +318 -0
- onetick/query_webapi/config.py +18 -0
- onetick/query_webapi/configuration.py +115 -0
- onetick/query_webapi/ep/__init__.py +2 -0
- onetick/query_webapi/ep/eps.py +60859 -0
- onetick/query_webapi/exception.py +19 -0
- onetick/query_webapi/graph_components.py +919 -0
- onetick/query_webapi/open_id.py +135 -0
- onetick/query_webapi/plugin/__init__.py +32 -0
- onetick/query_webapi/query.py +3337 -0
- onetick/query_webapi/render.py +33 -0
- onetick/query_webapi/symbol.py +23 -0
- onetick/query_webapi/utils.py +416 -0
- onetick.query_webapi-20250727.0.0.dist-info/METADATA +38 -0
- onetick.query_webapi-20250727.0.0.dist-info/RECORD +28 -0
- onetick.query_webapi-20250727.0.0.dist-info/WHEEL +5 -0
- onetick.query_webapi-20250727.0.0.dist-info/top_level.txt +1 -0
- onetick.query_webapi-20250727.0.0.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# onetick.query-webapi
|
|
2
|
+
|
|
3
|
+
`onetick.query-webapi` is a Python package that provides APIs to construct OneTick's graph and query objects and then using execute them using only REST APIs. It is a lightweight, secure and easy to use package that comes with MIT license.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- Provides classes to contruct OneTick graphs, save and query them
|
|
8
|
+
- Has secure oAuth2 authentication support
|
|
9
|
+
- Supports various return data types (pyarrow, numpy, pandas, polars)
|
|
10
|
+
- It is possible to execute real-time CEP queries
|
|
11
|
+
- Compatible with Python 3.8+
|
|
12
|
+
|
|
13
|
+
## 🚀 Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install onetick.query-webapi
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import requests as _requests
|
|
3
|
+
from . import eps_generator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_access_token(url, client_id, client_secret):
|
|
7
|
+
response = _requests.post(
|
|
8
|
+
url,
|
|
9
|
+
data={"grant_type": "client_credentials"},
|
|
10
|
+
auth=(client_id, client_secret),
|
|
11
|
+
)
|
|
12
|
+
if response.status_code == 200:
|
|
13
|
+
return response.json()["access_token"]
|
|
14
|
+
else:
|
|
15
|
+
raise Exception(f"Error retrieving access token: {response.status_code} - {response.text}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
CONTEXT_SETTINGS = dict(
|
|
19
|
+
help_option_names=['--help'],
|
|
20
|
+
terminal_width=97,
|
|
21
|
+
max_content_width=97,
|
|
22
|
+
color=True,
|
|
23
|
+
)
|
|
24
|
+
CMD_SETTINGS = dict(
|
|
25
|
+
ignore_unknown_options=True,
|
|
26
|
+
allow_extra_args=True,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.command(context_settings=CONTEXT_SETTINGS)
|
|
31
|
+
@click.option("-n", "--plugin_name", default=None, required=False, help="Plugin name")
|
|
32
|
+
@click.option("-o", "--output_dir", default=None, required=False, help="Directory path where to generate whl file")
|
|
33
|
+
@click.option("-a", "--http_address", required=True, help="Address of the onetick server")
|
|
34
|
+
@click.option("-u", "--http_username", default=None, required=False, help="Username for an http authentication if needed")
|
|
35
|
+
@click.option("-p", "--http_password", default=None, required=False, help="Password for an http authentication if needed")
|
|
36
|
+
@click.option("-i", "--client_id", default=None, required=False, help="Client id of a user")
|
|
37
|
+
@click.option("-s", "--client_secret", default=None, required=False, help="Client secret of a user")
|
|
38
|
+
@click.option("-ur", "--url", default=None, required=False, help="Identity provider url")
|
|
39
|
+
@click.option("-n", "--namespace", default=None, required=False, help="Namespace names of EPs to generate python code for")
|
|
40
|
+
@click.option("-d", "--generate_documentation", default=True, required=False, help="If thue then we also generate the documentation for EPs")
|
|
41
|
+
def console(plugin_name, output_dir, http_address, http_username, http_password, client_id, client_secret, url, namespace, generate_documentation):
|
|
42
|
+
if plugin_name:
|
|
43
|
+
if not output_dir:
|
|
44
|
+
raise Exception('output_dir must be specified, when plugin_name is specified')
|
|
45
|
+
if not namespace:
|
|
46
|
+
raise Exception('namespace must be specified, when plugin_name is specified')
|
|
47
|
+
else:
|
|
48
|
+
output_dir = None
|
|
49
|
+
namespace = None
|
|
50
|
+
if not http_address:
|
|
51
|
+
raise Exception('http_address not specified')
|
|
52
|
+
|
|
53
|
+
print("EPs generation started")
|
|
54
|
+
if plugin_name:
|
|
55
|
+
print(f"Creating a plugin for {namespace} namespace EPs. Plugin name is {plugin_name}")
|
|
56
|
+
else:
|
|
57
|
+
print("Regenerating all EPs")
|
|
58
|
+
if client_id and client_secret and url:
|
|
59
|
+
access_token = get_access_token(url, client_id, client_secret)
|
|
60
|
+
elif not client_id and not client_secret and not url:
|
|
61
|
+
access_token = None
|
|
62
|
+
else:
|
|
63
|
+
raise Exception('One of url, client_id, client_secret is not specified. '
|
|
64
|
+
'These parameters must all be specified or none of them should be specified')
|
|
65
|
+
eps_generator.generate_plugin(plugin_name=plugin_name,
|
|
66
|
+
output_dir=output_dir,
|
|
67
|
+
http_address=http_address,
|
|
68
|
+
http_username=http_username,
|
|
69
|
+
http_password=http_password,
|
|
70
|
+
access_token=access_token,
|
|
71
|
+
generate_documentation=generate_documentation,
|
|
72
|
+
namespace=namespace)
|
|
73
|
+
print("Done")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == '__main__':
|
|
77
|
+
console()
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import onetick.query_webapi as otq
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from .utils import *
|
|
5
|
+
from . import plugin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_ep_infos(eps_list_otq, eps_documentation_otq, http_address, generate_documentation,
|
|
9
|
+
http_username, http_password, access_token, namespace):
|
|
10
|
+
def is_number(s):
|
|
11
|
+
try:
|
|
12
|
+
int(s)
|
|
13
|
+
return True
|
|
14
|
+
except ValueError:
|
|
15
|
+
return False
|
|
16
|
+
|
|
17
|
+
def is_boolean_param(type_name, default_value, choices, param_name):
|
|
18
|
+
if param_name == "GO_BACK_TO_FIRST_TICK":
|
|
19
|
+
return False
|
|
20
|
+
if type_name.upper() == "BOOL" and (len(choices) == 2 or len(choices) == 0):
|
|
21
|
+
return True
|
|
22
|
+
is_boolean = (default_value and default_value.lower() in ['true', 'false']) or len(choices) > 0
|
|
23
|
+
if is_boolean:
|
|
24
|
+
for choice in choices:
|
|
25
|
+
if choice.lower() not in ['true', 'false']:
|
|
26
|
+
return False
|
|
27
|
+
return is_boolean
|
|
28
|
+
|
|
29
|
+
def is_integer_param(type_name, default_value, choices):
|
|
30
|
+
if type_name.upper() == "INTEGER" or type_name.upper() == "LONG":
|
|
31
|
+
return True
|
|
32
|
+
is_integer = default_value and is_number(default_value)
|
|
33
|
+
if is_integer:
|
|
34
|
+
for choice in choices:
|
|
35
|
+
if not is_number(choice):
|
|
36
|
+
return False
|
|
37
|
+
return is_integer
|
|
38
|
+
|
|
39
|
+
def is_enum_param(type_name, default_value, choices, param_name):
|
|
40
|
+
if type_name.upper() == "ENUM" and len(choices) > 1:
|
|
41
|
+
return True
|
|
42
|
+
if default_value and default_value not in choices:
|
|
43
|
+
return False
|
|
44
|
+
is_enum = len(choices) > 1 and not (is_boolean_param(type_name, default_value, choices, param_name) or
|
|
45
|
+
is_integer_param(type_name, default_value, choices))
|
|
46
|
+
return is_enum
|
|
47
|
+
|
|
48
|
+
def detect_param_type(type_name, default_value, choices, param_name):
|
|
49
|
+
choices = list(filter(None, choices))
|
|
50
|
+
if is_boolean_param(type_name, default_value, choices, param_name):
|
|
51
|
+
return ParamInfo.ParamType.PARAM_TYPE_BOOLEAN
|
|
52
|
+
if is_integer_param(type_name, default_value, choices):
|
|
53
|
+
return ParamInfo.ParamType.PARAM_TYPE_INTEGER
|
|
54
|
+
if is_enum_param(type_name, default_value, choices, param_name):
|
|
55
|
+
return ParamInfo.ParamType.PARAM_TYPE_ENUM
|
|
56
|
+
return ParamInfo.ParamType.PARAM_TYPE_STRING
|
|
57
|
+
|
|
58
|
+
def create_param_info(ep_name_param, param_name, value_type, default_value, choices):
|
|
59
|
+
special_ep_params = [("FIND_DB_SYMBOLS", "CEP_METHOD"),
|
|
60
|
+
("OM/STRATEGY_DB_MANAGER", "PERMISSION")]
|
|
61
|
+
if (ep_name_param, param_name) in special_ep_params:
|
|
62
|
+
choices.insert(0, "EMPTY")
|
|
63
|
+
default_value = "EMPTY"
|
|
64
|
+
return ParamInfo(param_name,
|
|
65
|
+
detect_param_type(value_type, default_value, choices, param_name),
|
|
66
|
+
default_value,
|
|
67
|
+
choices)
|
|
68
|
+
|
|
69
|
+
data = otq.run(eps_list_otq, http_address, http_username=http_username, http_password=http_password,
|
|
70
|
+
access_token=access_token)["LOCAL::"]
|
|
71
|
+
ep_infos = dict()
|
|
72
|
+
cur_ep_info = EpInfo("", [])
|
|
73
|
+
for idx, ep_name in enumerate(data['MY_EVENT_PROCESSOR_NAME']):
|
|
74
|
+
ep_name = ep_name
|
|
75
|
+
if namespace and not ep_name.startswith(namespace):
|
|
76
|
+
continue
|
|
77
|
+
if ep_name == cur_ep_info.name:
|
|
78
|
+
if data['MY_PARAMETER_NAME'][idx]:
|
|
79
|
+
cur_ep_info.params.append(
|
|
80
|
+
create_param_info(ep_name,
|
|
81
|
+
data['MY_PARAMETER_NAME'][idx],
|
|
82
|
+
data['MY_TYPE_EXPR'][idx],
|
|
83
|
+
data['MY_PARAMETER_DEFAULT_VALUE'][idx],
|
|
84
|
+
data['MY_PARAMETER_CHOICES'][idx].split(',')))
|
|
85
|
+
else:
|
|
86
|
+
cur_ep_info = EpInfo(ep_name, [])
|
|
87
|
+
if data['MY_PARAMETER_NAME'][idx]:
|
|
88
|
+
cur_ep_info.params.append(
|
|
89
|
+
create_param_info(ep_name,
|
|
90
|
+
data['MY_PARAMETER_NAME'][idx],
|
|
91
|
+
data['MY_TYPE_EXPR'][idx],
|
|
92
|
+
data['MY_PARAMETER_DEFAULT_VALUE'][idx],
|
|
93
|
+
data['MY_PARAMETER_CHOICES'][idx].split(',')))
|
|
94
|
+
ep_infos[ep_name] = cur_ep_info
|
|
95
|
+
|
|
96
|
+
if generate_documentation:
|
|
97
|
+
data = otq.run(eps_documentation_otq, http_address, http_username=http_username, http_password=http_password,
|
|
98
|
+
access_token=access_token, compression="none")["LOCAL::"]
|
|
99
|
+
for idx, ep_name in enumerate(data['MY_EVENT_PROCESSOR_NAME']):
|
|
100
|
+
if ep_name not in ep_infos:
|
|
101
|
+
continue
|
|
102
|
+
ep_infos[ep_name].documentation = str(data['MY_DOCUMENTATION'][idx])
|
|
103
|
+
return ep_infos
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def generate_ep_module(ep_infos, generate_documentation, plugin_name, output_dir):
|
|
107
|
+
temp_dir = ""
|
|
108
|
+
if plugin_name:
|
|
109
|
+
temp_dir = tempfile.mkdtemp()
|
|
110
|
+
plugin_dir = temp_dir + "/onetick/query_webapi/plugin/" + plugin_name
|
|
111
|
+
os.makedirs(plugin_dir + "/ep")
|
|
112
|
+
with open(plugin_dir + "/__init__.py", "w") as f:
|
|
113
|
+
f.write('from .ep import *\n')
|
|
114
|
+
else:
|
|
115
|
+
cur_dir = os.path.dirname(os.path.realpath(__file__))
|
|
116
|
+
plugin_dir = cur_dir + "/../query_webapi"
|
|
117
|
+
|
|
118
|
+
with open(plugin_dir + "/ep/__init__.py", "w") as f:
|
|
119
|
+
f.write('from .eps import *\n')
|
|
120
|
+
|
|
121
|
+
out = open(plugin_dir + "/ep/eps.py", "w")
|
|
122
|
+
if plugin_name:
|
|
123
|
+
print_line(out, 'import onetick.query_webapi.graph_components as _graph_components')
|
|
124
|
+
print_line(out, 'import onetick.query_webapi._internal_utils as _internal_utils')
|
|
125
|
+
print_line(out, 'import onetick.query_webapi.config as _config')
|
|
126
|
+
else:
|
|
127
|
+
print_line(out, 'from .. import graph_components as _graph_components')
|
|
128
|
+
print_line(out, 'from .. import _internal_utils')
|
|
129
|
+
print_line(out, 'from .. import config as _config')
|
|
130
|
+
print_line(out, 'from .. import utils as _utils')
|
|
131
|
+
print_line(out, '')
|
|
132
|
+
print_line(out, '')
|
|
133
|
+
for ep_name in ep_infos:
|
|
134
|
+
generate_ep_class(ep_infos[ep_name], out, generate_documentation)
|
|
135
|
+
out.close()
|
|
136
|
+
|
|
137
|
+
out = open(plugin_dir + "/_eps_factory.py", "w")
|
|
138
|
+
_indent = 0
|
|
139
|
+
if not plugin_name:
|
|
140
|
+
print_line(out, 'from . import eps_dict')
|
|
141
|
+
print_line(out, 'from . import ep')
|
|
142
|
+
print_line(out, 'from inspect import getframeinfo, currentframe')
|
|
143
|
+
print_line(out, "")
|
|
144
|
+
print_line(out, "")
|
|
145
|
+
print_line(out, "def create_ep(ep_name):")
|
|
146
|
+
for ep_name in ep_infos:
|
|
147
|
+
print_line(out, "if ep_name == '" + ep_name + "':", _indent + 1)
|
|
148
|
+
print_line(out, "return ep." + to_camel_case(ep_infos[ep_name].name) + "()", _indent + 2)
|
|
149
|
+
if not plugin_name:
|
|
150
|
+
print_line(out, "if ep_name in eps_dict:", _indent + 1)
|
|
151
|
+
print_line(out, "return eps_dict[ep_name]()", _indent + 2)
|
|
152
|
+
print_line(out, "")
|
|
153
|
+
print_line(out,
|
|
154
|
+
"raise Exception('wrong ep name: {}.'.format(ep_name), getframeinfo(currentframe()).filename, getframeinfo(currentframe()).lineno)",
|
|
155
|
+
_indent + 1)
|
|
156
|
+
out.close()
|
|
157
|
+
if plugin_name:
|
|
158
|
+
plugin.build(temp_dir, output_dir, plugin_name)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def generate_ep_class(ep_info: EpInfo, out, generate_documentation, _indent=0):
|
|
162
|
+
ep_class_name, have_postfix = get_ep_class_name(ep_info)
|
|
163
|
+
print_line(out, "class " + ep_class_name + "(_graph_components.EpBase):", _indent)
|
|
164
|
+
if generate_documentation:
|
|
165
|
+
print_line(out, "\"\"\"", _indent + 1)
|
|
166
|
+
print_line(out, html2text(ep_info.documentation), _indent + 2)
|
|
167
|
+
print_line(out, "\"\"\"", _indent + 1)
|
|
168
|
+
generate_available_parameters_aliases(ep_info.params, out, _indent + 1)
|
|
169
|
+
generate_enum_params_choices_wrapper_classes(ep_info.params, out, _indent + 1)
|
|
170
|
+
generate_ep_constructor(ep_info, out, _indent + 1)
|
|
171
|
+
generate_parameter_setters(ep_info.params, out, _indent + 1)
|
|
172
|
+
generate_ep_to_string(ep_info, out, _indent + 1)
|
|
173
|
+
generate_ep_destructor(out, _indent + 1)
|
|
174
|
+
if have_postfix:
|
|
175
|
+
print_line(out, "", _indent)
|
|
176
|
+
print_line(out, to_camel_case(ep_info.name) + " = " + ep_class_name, _indent)
|
|
177
|
+
print_line(out, "", _indent)
|
|
178
|
+
|
|
179
|
+
special_ep_aliases = dict(
|
|
180
|
+
[("NumTicks", "Count"), ("High", "Max"), ("Low", "Min"), ("Vwap", "WAvg"), ("Average", "Avg")]
|
|
181
|
+
)
|
|
182
|
+
if ep_class_name in special_ep_aliases:
|
|
183
|
+
print_line(out, "", _indent)
|
|
184
|
+
print_line(out, special_ep_aliases[ep_class_name] + " = " + ep_class_name, _indent)
|
|
185
|
+
print_line(out, "", _indent)
|
|
186
|
+
print_line(out, "")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def generate_available_parameters_aliases(ep_params, out, _indent):
|
|
190
|
+
print_line(out, "class Parameters:", _indent)
|
|
191
|
+
params_name_list = "["
|
|
192
|
+
params_name_list_with_private_members = "["
|
|
193
|
+
last_param_name = ""
|
|
194
|
+
if ep_params:
|
|
195
|
+
last_param_name = ep_params[-1].name
|
|
196
|
+
for ep_param in ep_params:
|
|
197
|
+
param_field_name = get_param_pythonic_name(ep_param.name)
|
|
198
|
+
param_server_name = ep_param.name
|
|
199
|
+
|
|
200
|
+
print_line(out, param_field_name + " = \"" + param_server_name + "\"", _indent + 1)
|
|
201
|
+
params_name_list += "\"" + param_field_name + "\""
|
|
202
|
+
params_name_list_with_private_members += "\"" + param_field_name + "\", "
|
|
203
|
+
params_name_list_with_private_members += "\"_default_" + param_field_name + "\""
|
|
204
|
+
if ep_param.name != last_param_name:
|
|
205
|
+
params_name_list += ", "
|
|
206
|
+
params_name_list_with_private_members += ", "
|
|
207
|
+
|
|
208
|
+
print_line(out, "stack_info = \"STACK_INFO\"", _indent + 1)
|
|
209
|
+
parameters_name_list_with_stack_info = params_name_list_with_private_members
|
|
210
|
+
if ep_params:
|
|
211
|
+
parameters_name_list_with_stack_info += ", "
|
|
212
|
+
parameters_name_list_with_stack_info += "\"stack_info\", \"_used_strings\"]"
|
|
213
|
+
|
|
214
|
+
# Construct list_parameters() method
|
|
215
|
+
params_name_list += ']'
|
|
216
|
+
print_line(out, "")
|
|
217
|
+
print_multi_line_string_with_indent(
|
|
218
|
+
out,
|
|
219
|
+
"@staticmethod\n" +
|
|
220
|
+
"def list_parameters():",
|
|
221
|
+
_indent + 1)
|
|
222
|
+
print_line(out, "list_val = " + params_name_list, _indent + 2)
|
|
223
|
+
print_multi_line_string_with_indent(
|
|
224
|
+
out,
|
|
225
|
+
"if _config.API_CONFIG[\'SHOW_STACK_INFO\'] == 1:\n" +
|
|
226
|
+
" list_val.append(\"stack_info\")\n" +
|
|
227
|
+
"return list_val",
|
|
228
|
+
_indent + 2)
|
|
229
|
+
print_line(out, "")
|
|
230
|
+
print_line(out, "__slots__ = " + parameters_name_list_with_stack_info, _indent)
|
|
231
|
+
print_line(out, "")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def generate_enum_params_choices_wrapper_classes(ep_params, out, _indent):
|
|
235
|
+
for ep_param in ep_params:
|
|
236
|
+
if ep_param.value_type == ParamInfo.ParamType.PARAM_TYPE_ENUM:
|
|
237
|
+
generate_enum_choices_wrapper_class(ep_param, out, _indent)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def generate_enum_choices_wrapper_class(ep_param: ParamInfo, out, _indent):
|
|
241
|
+
choices = ep_param.choices
|
|
242
|
+
param_wrapper_class_name = to_camel_case(ep_param.name)
|
|
243
|
+
|
|
244
|
+
print_line(out, "class " + param_wrapper_class_name + ":", _indent)
|
|
245
|
+
if len(ep_param.default_value) == 0:
|
|
246
|
+
print_line(out, "EMPTY = \"\"", _indent + 1)
|
|
247
|
+
for choice in choices:
|
|
248
|
+
if len(choice) == 0:
|
|
249
|
+
continue
|
|
250
|
+
choice_name = replace_special_chars_with_readable_words(choice).upper()
|
|
251
|
+
if choice_name == "EMPTY":
|
|
252
|
+
print_line(out, "EMPTY = \"\"", _indent + 1)
|
|
253
|
+
else:
|
|
254
|
+
print_line(out, choice_name + " = \"" + choice + "\"", _indent + 1)
|
|
255
|
+
print_line(out, "")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def generate_ep_constructor(ep_info: EpInfo, out, _indent):
|
|
259
|
+
is_in = False
|
|
260
|
+
is_out = False
|
|
261
|
+
class_name_line = "def __init__(self"
|
|
262
|
+
for ep_param in ep_info.params:
|
|
263
|
+
param_default = get_param_pythonic_default_value(ep_param)
|
|
264
|
+
param_name = get_param_pythonic_name(ep_param.name)
|
|
265
|
+
class_name_line += ", " + param_name + "=" + param_default
|
|
266
|
+
if param_name == "input_field_name":
|
|
267
|
+
is_in = True
|
|
268
|
+
if param_name == "output_field_name":
|
|
269
|
+
is_out = True
|
|
270
|
+
if is_in:
|
|
271
|
+
class_name_line += ", In = \"\""
|
|
272
|
+
if is_out:
|
|
273
|
+
class_name_line += ", Out = \"\""
|
|
274
|
+
class_name_line += "):"
|
|
275
|
+
print_line(out, class_name_line, _indent)
|
|
276
|
+
|
|
277
|
+
print_line(out, "_graph_components.EpBase.__init__(self, \"" + ep_info.name + "\")", _indent + 1)
|
|
278
|
+
for ep_param in ep_info.params:
|
|
279
|
+
ep_pythonic_param_name = get_param_pythonic_name(ep_param.name)
|
|
280
|
+
param_default = get_param_pythonic_default_value(ep_param, True)
|
|
281
|
+
print_multi_line_string_with_indent(
|
|
282
|
+
out,
|
|
283
|
+
"self._default_" + ep_pythonic_param_name + " = " + param_default + "\n" +
|
|
284
|
+
"self." + ep_pythonic_param_name + " = " + ep_pythonic_param_name,
|
|
285
|
+
_indent + 1)
|
|
286
|
+
|
|
287
|
+
if is_in:
|
|
288
|
+
print_multi_line_string_with_indent(
|
|
289
|
+
out,
|
|
290
|
+
"if In != \"\":\n" +
|
|
291
|
+
" self.input_field_name=In",
|
|
292
|
+
_indent + 1)
|
|
293
|
+
if is_out:
|
|
294
|
+
print_multi_line_string_with_indent(
|
|
295
|
+
out,
|
|
296
|
+
"if Out != \"\":\n" +
|
|
297
|
+
" self.output_field_name=Out",
|
|
298
|
+
_indent + 1)
|
|
299
|
+
print_multi_line_string_with_indent(
|
|
300
|
+
out,
|
|
301
|
+
"self._used_strings = {}\n" +
|
|
302
|
+
"for param_name in type(self).__dict__:\n" +
|
|
303
|
+
" param_val = getattr(self, param_name, '')\n" +
|
|
304
|
+
" if isinstance(param_val, str) and _internal_utils.get_reference_counted_prefix() in param_val and not (param_val in self._used_strings):\n" +
|
|
305
|
+
" _internal_utils.inc_ref_count(param_val)\n" +
|
|
306
|
+
" self._used_strings[param_val] = 1",
|
|
307
|
+
_indent + 1)
|
|
308
|
+
# Retrieve caller filename and line number in python
|
|
309
|
+
print_line(out, "import sys", _indent + 1)
|
|
310
|
+
print_line(out, "frame = sys._getframe(1)", _indent + 1)
|
|
311
|
+
print_line(out, "self.stack_info = frame.f_code.co_filename + \":\" + str(frame.f_lineno)", _indent + 1)
|
|
312
|
+
print_line(out, "")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def get_param_pythonic_default_value(ep_param: ParamInfo, prepend_class_name: bool = False):
|
|
316
|
+
param_default_value = ep_param.default_value
|
|
317
|
+
if ep_param.value_type == ParamInfo.ParamType.PARAM_TYPE_STRING:
|
|
318
|
+
return "\"" + escape_quotes(param_default_value) + "\""
|
|
319
|
+
elif ep_param.value_type == ParamInfo.ParamType.PARAM_TYPE_BOOLEAN:
|
|
320
|
+
if param_default_value.lower() == "true":
|
|
321
|
+
return "True"
|
|
322
|
+
else:
|
|
323
|
+
return "False"
|
|
324
|
+
elif ep_param.value_type == ParamInfo.ParamType.PARAM_TYPE_ENUM:
|
|
325
|
+
choices_wrapper_class_name = to_camel_case(ep_param.name)
|
|
326
|
+
choice_name = "EMPTY" if len(param_default_value) == 0 \
|
|
327
|
+
else replace_special_chars_with_readable_words(param_default_value).upper()
|
|
328
|
+
pref = "type(self)." if prepend_class_name else ""
|
|
329
|
+
return pref + choices_wrapper_class_name + "." + choice_name
|
|
330
|
+
if not param_default_value:
|
|
331
|
+
return "\"\""
|
|
332
|
+
return param_default_value
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def generate_parameter_setters(ep_params, out, _indent):
|
|
336
|
+
for ep_param in ep_params:
|
|
337
|
+
param_name = get_param_pythonic_name(ep_param.name)
|
|
338
|
+
print_line(out, "def set_" + param_name + "(self, value):", _indent)
|
|
339
|
+
print_line(out, "self." + param_name + " = value", _indent + 1)
|
|
340
|
+
print_line(out, "return self", _indent + 1)
|
|
341
|
+
print_line(out, "")
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def generate_ep_to_string(ep_info: EpInfo, out, _indent):
|
|
345
|
+
ep_name = ep_info.name
|
|
346
|
+
print_line(out, "@staticmethod", _indent)
|
|
347
|
+
print_line(out, "def _get_name():", _indent)
|
|
348
|
+
print_line(out, "return \"" + ep_name + "\"\n", _indent + 1)
|
|
349
|
+
|
|
350
|
+
print_line(out, "def _to_string(self, for_repr=False):", _indent)
|
|
351
|
+
print_line(out, "name = self._get_name()", _indent + 1)
|
|
352
|
+
print_line(out, "desc = name + \"(\"", _indent + 1)
|
|
353
|
+
print_line(out, "py_to_str = _utils.onetick_repr if for_repr else str", _indent + 1)
|
|
354
|
+
|
|
355
|
+
for param in ep_info.params:
|
|
356
|
+
pythonic_param_default = get_param_pythonic_default_value(param)
|
|
357
|
+
pythonic_param_name = get_param_pythonic_name(param.name)
|
|
358
|
+
pythonic_param_name_upper = pythonic_param_name.upper()
|
|
359
|
+
|
|
360
|
+
if pythonic_param_name_upper == "TICK_TYPE_FIELD" and ep_name == "FIND_DB_SYMBOLS":
|
|
361
|
+
pythonic_param_name_upper = "TICK_TYPE"
|
|
362
|
+
needed_self = ""
|
|
363
|
+
if param.value_type == ParamInfo.ParamType.PARAM_TYPE_ENUM:
|
|
364
|
+
needed_self = "self."
|
|
365
|
+
|
|
366
|
+
print_line(out, "if self." + pythonic_param_name + " != " + needed_self + pythonic_param_default + ": ",
|
|
367
|
+
_indent + 1)
|
|
368
|
+
print_line(out, "desc += \"" + pythonic_param_name_upper + "=\" + py_to_str(self." + pythonic_param_name +
|
|
369
|
+
") + \",\"", _indent + 2)
|
|
370
|
+
print_line(out, "if _config.API_CONFIG[\'SHOW_STACK_INFO\'] == 1:", _indent + 1)
|
|
371
|
+
print_line(out, "desc += \"STACK_INFO=\" + py_to_str(self.stack_info) + \",\"", _indent + 2)
|
|
372
|
+
print_line(out, "desc = desc[:-1]", _indent + 1)
|
|
373
|
+
print_line(out, "if desc != name:", _indent + 1)
|
|
374
|
+
print_line(out, "desc += \")\"", _indent + 2)
|
|
375
|
+
print_line(out, "if for_repr:", _indent + 1)
|
|
376
|
+
print_line(out, "return desc + '()' if desc == name else desc", _indent + 2)
|
|
377
|
+
print_line(out, "desc += \"\\n\"", _indent + 1)
|
|
378
|
+
print_multi_line_string_with_indent(
|
|
379
|
+
out,
|
|
380
|
+
"if len(self._get_symbol_strings()) > 0:\n" +
|
|
381
|
+
" desc += \"SYMBOLS=[\" + \", \".join(self._get_symbol_strings()) + \"]\\n\"\n" +
|
|
382
|
+
"if len(self.tick_types_) > 0:\n" +
|
|
383
|
+
" desc += \"TICK_TYPES=[\" + \', \'.join(self.tick_types_) + \"]\\n\"\n" +
|
|
384
|
+
"if self.process_node_locally_:\n" +
|
|
385
|
+
" desc += \"PROCESS_NODE_LOCALLY=True\\n\"\n" +
|
|
386
|
+
"if self.node_name_ != \"\":\n" +
|
|
387
|
+
" desc += \"NODE_NAME=\" + self.node_name_ + \"\\n\"\n" +
|
|
388
|
+
"return desc",
|
|
389
|
+
_indent + 1)
|
|
390
|
+
print_multi_line_string_with_indent(
|
|
391
|
+
out,
|
|
392
|
+
"\n" +
|
|
393
|
+
"def __repr__(self):\n" +
|
|
394
|
+
" return self._to_string(for_repr=True)\n" +
|
|
395
|
+
"\n" +
|
|
396
|
+
"def __str__(self):\n" +
|
|
397
|
+
" return self._to_string()",
|
|
398
|
+
_indent)
|
|
399
|
+
print_line(out, "")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def generate_ep_destructor(out, _indent):
|
|
403
|
+
print_multi_line_string_with_indent(
|
|
404
|
+
out,
|
|
405
|
+
"def __del__(self):\n" +
|
|
406
|
+
" for param_name in getattr(self, '_used_strings', []):\n" +
|
|
407
|
+
" _internal_utils.dec_ref_count(param_name)\n" +
|
|
408
|
+
" if _internal_utils.get_ref_count(param_name) == 0:\n" +
|
|
409
|
+
" _internal_utils.remove_from_memory(param_name)",
|
|
410
|
+
_indent)
|
|
411
|
+
print_line(out, "")
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def generate_plugin(plugin_name, output_dir, http_address, http_username=None, http_password=None, access_token=None,
|
|
415
|
+
generate_documentation=True, namespace=None):
|
|
416
|
+
cur_dir = os.path.dirname(os.path.realpath(__file__))
|
|
417
|
+
generate_ep_module(
|
|
418
|
+
generate_ep_infos(cur_dir + "/eps_list.otq",
|
|
419
|
+
cur_dir + "/eps_list_doc.otq",
|
|
420
|
+
http_address,
|
|
421
|
+
generate_documentation,
|
|
422
|
+
http_username,
|
|
423
|
+
http_password,
|
|
424
|
+
access_token,
|
|
425
|
+
namespace),
|
|
426
|
+
generate_documentation,
|
|
427
|
+
plugin_name,
|
|
428
|
+
output_dir)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[show_eps_list]
|
|
2
|
+
CPU_NUMBER = 1
|
|
3
|
+
NODE_4 = SHOW_EP_LIST(ALLOW_API_ONLY_EPS="TRUE")
|
|
4
|
+
NODE_8 = ADD_FIELDS(FIELDS="varstring MY_EVENT_PROCESSOR_NAME=EVENT_PROCESSOR_NAME,varstring MY_PARAMETER_NAME=PARAMETER_NAME,varstring MY_PARAMETER_DEFAULT_VALUE=PARAMETER_DEFAULT_VALUE,varstring MY_PARAMETER_CHOICES=PARAMETER_CHOICES,varstring MY_PARAMETER_DESCRIPTION=PARAMETER_DESCRIPTION,varstring MY_TYPE_EXPR=TYPE_EXPR")
|
|
5
|
+
NODE_8_SOURCE = NODE_4
|
|
6
|
+
ROOT = PASSTHROUGH(FIELDS="MY_EVENT_PROCESSOR_NAME,MY_PARAMETER_NAME,MY_PARAMETER_DEFAULT_VALUE,MY_PARAMETER_CHOICES,MY_PARAMETER_DESCRIPTION,MY_TYPE_EXPR")
|
|
7
|
+
ROOT_SOURCE = NODE_8
|
|
8
|
+
SECURITY = LOCAL:: 0
|
|
9
|
+
TYPE = GRAPH
|
|
10
|
+
|
|
11
|
+
[_meta]
|
|
12
|
+
ApplyTimesDaily = 0
|
|
13
|
+
end = 20170710125009000
|
|
14
|
+
file_version = 1.0
|
|
15
|
+
RunningQuery = 0
|
|
16
|
+
start = 20010710125008000
|
|
17
|
+
TZ = GMT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
[show_eps_list]
|
|
3
|
+
CPU_NUMBER = 1
|
|
4
|
+
NODE_2 = ADD_FIELDS(FIELDS="varstring MY_EVENT_PROCESSOR_NAME=EVENT_PROCESSOR_NAME,varstring MY_DOCUMENTATION=DOCUMENTATION")
|
|
5
|
+
NODE_2_SOURCE = NODE_3
|
|
6
|
+
NODE_3 = SHOW_EP_LIST(LEVEL_OF_DETAIL=PER_EP,SHOW_DOCUMENTATION=TRUE,ALLOW_API_ONLY_EPS=TRUE)
|
|
7
|
+
NODE_3_TICK_TYPE = TRD
|
|
8
|
+
ROOT = PASSTHROUGH(FIELDS="MY_EVENT_PROCESSOR_NAME,MY_DOCUMENTATION")
|
|
9
|
+
ROOT_SOURCE = NODE_2
|
|
10
|
+
SECURITY = LOCAL:: 0
|
|
11
|
+
TYPE = GRAPH
|
|
12
|
+
|
|
13
|
+
[_meta]
|
|
14
|
+
ApplyTimesDaily = 0
|
|
15
|
+
end = 20170710125009000
|
|
16
|
+
file_version = 1.0
|
|
17
|
+
RunningQuery = 0
|
|
18
|
+
start = 20010710125008000
|
|
19
|
+
TZ = GMT
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import glob
|
|
5
|
+
from subprocess import run
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build(temp_dir, output_dir, plugin_name, plugin_version="0.0.1"):
|
|
9
|
+
create_setup_file(temp_dir, plugin_name, plugin_version)
|
|
10
|
+
compile_plugin(temp_dir, output_dir)
|
|
11
|
+
print('Done.')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##########################################################################
|
|
15
|
+
|
|
16
|
+
def create_setup_file(temp_dir, plugin_name, plugin_version):
|
|
17
|
+
setup_file_content = """
|
|
18
|
+
from setuptools import setup
|
|
19
|
+
from setuptools.dist import Distribution
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
PYTHON_VERSION = (3,8)
|
|
23
|
+
class BinaryDistribution(Distribution):
|
|
24
|
+
#Distribution which always forces a binary package with platform name
|
|
25
|
+
def has_ext_modules(foo):
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
options = dict(
|
|
30
|
+
name='onetick.query_webapi.{plugin_name}',
|
|
31
|
+
packages=[
|
|
32
|
+
'onetick.query_webapi.plugin.{plugin_name}',
|
|
33
|
+
'onetick.query_webapi.plugin.{plugin_name}.ep'
|
|
34
|
+
],
|
|
35
|
+
|
|
36
|
+
version='{version}',
|
|
37
|
+
description='OneTick client package',
|
|
38
|
+
long_description=__doc__,
|
|
39
|
+
author='OneTick',
|
|
40
|
+
author_email='support@onetick.com',
|
|
41
|
+
url='https://www.onetick.com',
|
|
42
|
+
license='commercial',
|
|
43
|
+
|
|
44
|
+
zip_safe=True,
|
|
45
|
+
include_package_data=True,
|
|
46
|
+
|
|
47
|
+
entry_points={{
|
|
48
|
+
'onetick.query_webapi.plugin': 'onetick.query_webapi.{plugin_name} = onetick.query_webapi.plugin.{plugin_name}'
|
|
49
|
+
}},
|
|
50
|
+
install_requires=[
|
|
51
|
+
'onetick.query_webapi',
|
|
52
|
+
],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
#########################
|
|
56
|
+
if __name__ == '__main__':
|
|
57
|
+
setup(**options)
|
|
58
|
+
""".format(plugin_name=plugin_name, version=plugin_version)
|
|
59
|
+
|
|
60
|
+
setup_file = temp_dir + '/setup.py'
|
|
61
|
+
f = open(setup_file, "x")
|
|
62
|
+
f.write(setup_file_content)
|
|
63
|
+
f.close()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def compile_plugin(temp_dir, output_dir):
|
|
67
|
+
print('create Wheel binary distribution...')
|
|
68
|
+
|
|
69
|
+
os.chdir(temp_dir)
|
|
70
|
+
cmd = f'{sys.executable} setup.py bdist_wheel'
|
|
71
|
+
run(cmd.split())
|
|
72
|
+
whl_file = temp_dir + '/dist/*.whl'
|
|
73
|
+
for file in glob.glob(whl_file):
|
|
74
|
+
shutil.copy(file, output_dir)
|