chemql-python 1.2.2__tar.gz → 1.2.3__tar.gz
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.
- {chemql_python-1.2.2/src/chemql_python.egg-info → chemql_python-1.2.3}/PKG-INFO +6 -6
- {chemql_python-1.2.2 → chemql_python-1.2.3}/README.md +5 -5
- {chemql_python-1.2.2 → chemql_python-1.2.3}/pyproject.toml +1 -1
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/__init__.py +130 -37
- {chemql_python-1.2.2 → chemql_python-1.2.3/src/chemql_python.egg-info}/PKG-INFO +6 -6
- {chemql_python-1.2.2 → chemql_python-1.2.3}/LICENSE +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/setup.cfg +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/__main__.py +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/BondEnergies.json +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/MoleculeBonds.json +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/Molecules.json +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/PeriodicTableJSON.json +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/Reactions.json +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/THIRD_PARTY_LICENSES/PeriodicTableJSON.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/data/THIRD_PARTY_LICENSES/RHEA.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql/html/periodic_table.html +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql_python.egg-info/SOURCES.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql_python.egg-info/dependency_links.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql_python.egg-info/entry_points.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql_python.egg-info/requires.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/src/chemql_python.egg-info/top_level.txt +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/tests/test_external_molecule_lookup.py +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/tests/test_public_api.py +0 -0
- {chemql_python-1.2.2 → chemql_python-1.2.3}/tests/test_reactions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chemql-python
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: A chemistry-focused query language and REPL
|
|
5
5
|
Author-email: Mohammad Rakibul Islam <rakibulofficial1017@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -17,15 +17,15 @@ Dynamic: license-file
|
|
|
17
17
|
shell commands in one interactive environment.**
|
|
18
18
|
|
|
19
19
|
ChemQL is a command-line chemistry query language for exploring chemical
|
|
20
|
-
elements, molecules, and reactions. Version 1.2.
|
|
20
|
+
elements, molecules, and reactions. Version 1.2.3 combines database-style
|
|
21
21
|
chemistry queries, reaction tools, Python interoperability, reusable functions
|
|
22
22
|
and macros, and a persistent shell in one scriptable environment. It combines a small SQL-inspired
|
|
23
23
|
query syntax with a persistent Python environment, reaction utilities,
|
|
24
24
|
optional external molecule lookup, and tools for running repeatable
|
|
25
25
|
scripts.
|
|
26
26
|
|
|
27
|
-
> **Version covered by this README:** 1.2.
|
|
28
|
-
> The examples below describe the ChemQL 1.2.
|
|
27
|
+
> **Version covered by this README:** 1.2.3\
|
|
28
|
+
> The examples below describe the ChemQL 1.2.3 source. Dataset
|
|
29
29
|
> contents and external-service availability can affect individual
|
|
30
30
|
> lookup results.
|
|
31
31
|
|
|
@@ -538,7 +538,7 @@ queried or inspected from Python.
|
|
|
538
538
|
|
|
539
539
|
## Preprocessor, functions, and macros
|
|
540
540
|
|
|
541
|
-
ChemQL 1.2.
|
|
541
|
+
ChemQL 1.2.3 adds three ways to reuse values and logic. These declarations
|
|
542
542
|
are processed in source order: definitions affect later lines, not earlier
|
|
543
543
|
ones. They persist during the current ChemQL process/session; they are not
|
|
544
544
|
saved automatically between separate launches.
|
|
@@ -833,7 +833,7 @@ redirect a file into the command.
|
|
|
833
833
|
``` bash
|
|
834
834
|
chemql --version
|
|
835
835
|
chemql --upgrade
|
|
836
|
-
chemql --upgrade 1.2.
|
|
836
|
+
chemql --upgrade 1.2.3
|
|
837
837
|
```
|
|
838
838
|
|
|
839
839
|
`--upgrade` installs the latest published `chemql-python` release; supplying a
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
shell commands in one interactive environment.**
|
|
5
5
|
|
|
6
6
|
ChemQL is a command-line chemistry query language for exploring chemical
|
|
7
|
-
elements, molecules, and reactions. Version 1.2.
|
|
7
|
+
elements, molecules, and reactions. Version 1.2.3 combines database-style
|
|
8
8
|
chemistry queries, reaction tools, Python interoperability, reusable functions
|
|
9
9
|
and macros, and a persistent shell in one scriptable environment. It combines a small SQL-inspired
|
|
10
10
|
query syntax with a persistent Python environment, reaction utilities,
|
|
11
11
|
optional external molecule lookup, and tools for running repeatable
|
|
12
12
|
scripts.
|
|
13
13
|
|
|
14
|
-
> **Version covered by this README:** 1.2.
|
|
15
|
-
> The examples below describe the ChemQL 1.2.
|
|
14
|
+
> **Version covered by this README:** 1.2.3\
|
|
15
|
+
> The examples below describe the ChemQL 1.2.3 source. Dataset
|
|
16
16
|
> contents and external-service availability can affect individual
|
|
17
17
|
> lookup results.
|
|
18
18
|
|
|
@@ -525,7 +525,7 @@ queried or inspected from Python.
|
|
|
525
525
|
|
|
526
526
|
## Preprocessor, functions, and macros
|
|
527
527
|
|
|
528
|
-
ChemQL 1.2.
|
|
528
|
+
ChemQL 1.2.3 adds three ways to reuse values and logic. These declarations
|
|
529
529
|
are processed in source order: definitions affect later lines, not earlier
|
|
530
530
|
ones. They persist during the current ChemQL process/session; they are not
|
|
531
531
|
saved automatically between separate launches.
|
|
@@ -820,7 +820,7 @@ redirect a file into the command.
|
|
|
820
820
|
``` bash
|
|
821
821
|
chemql --version
|
|
822
822
|
chemql --upgrade
|
|
823
|
-
chemql --upgrade 1.2.
|
|
823
|
+
chemql --upgrade 1.2.3
|
|
824
824
|
```
|
|
825
825
|
|
|
826
826
|
`--upgrade` installs the latest published `chemql-python` release; supplying a
|
|
@@ -47,7 +47,7 @@ __all__ = [
|
|
|
47
47
|
"__version__"
|
|
48
48
|
]
|
|
49
49
|
|
|
50
|
-
__version__ = "1.2.
|
|
50
|
+
__version__ = "1.2.3"
|
|
51
51
|
|
|
52
52
|
# Persistent preprocessor/function state for the current ChemQL session.
|
|
53
53
|
DEFINITIONS = {}
|
|
@@ -55,6 +55,7 @@ FUNCTIONS = {}
|
|
|
55
55
|
MACROS = {}
|
|
56
56
|
EXECUTION_HISTORY = []
|
|
57
57
|
_DEFINITION_NAME = r"[A-Za-z_][A-Za-z0-9_]*"
|
|
58
|
+
_NO_RETURN = object()
|
|
58
59
|
|
|
59
60
|
def _expand_definitions(line):
|
|
60
61
|
"""Replace @NAME literally, including inside strings, using current definitions."""
|
|
@@ -109,111 +110,203 @@ def _split_function_arguments(source):
|
|
|
109
110
|
return args
|
|
110
111
|
|
|
111
112
|
def _call_function(name, arguments):
|
|
112
|
-
"""Call a ChemQL function, including
|
|
113
|
+
"""Call a ChemQL function, including let assignments and block syntax."""
|
|
113
114
|
if name not in FUNCTIONS:
|
|
114
115
|
raise NameError(f"Unknown ChemQL function `{name}`")
|
|
115
116
|
|
|
116
117
|
values = [evaluate_python(arg) for arg in arguments]
|
|
117
118
|
definition = FUNCTIONS[name]
|
|
119
|
+
|
|
118
120
|
saved_globals = python_globals.copy()
|
|
119
121
|
saved_chemql_variables = CHEMQL_VARIABLES.copy()
|
|
122
|
+
|
|
120
123
|
had_arguments = "arguments" in python_globals
|
|
121
124
|
saved_arguments = python_globals.get("arguments")
|
|
122
|
-
|
|
125
|
+
|
|
126
|
+
# Expose only the supplied arguments.
|
|
123
127
|
python_globals["arguments"] = list(values)
|
|
124
128
|
|
|
125
129
|
def substitute_parameters(source):
|
|
126
|
-
|
|
130
|
+
"""Replace \\1, \\2, etc. without corrupting \\10 or longer indices."""
|
|
127
131
|
return re.sub(
|
|
128
132
|
r"\\(\d+)",
|
|
129
|
-
lambda match:
|
|
130
|
-
|
|
133
|
+
lambda match: (
|
|
134
|
+
repr(values[int(match.group(1)) - 1])
|
|
135
|
+
if 0 < int(match.group(1)) <= len(values)
|
|
136
|
+
else match.group(0)
|
|
137
|
+
),
|
|
131
138
|
source,
|
|
132
139
|
)
|
|
133
140
|
|
|
134
141
|
def normalize_let_line(source):
|
|
135
|
-
"""Translate
|
|
136
|
-
match = re.fullmatch(
|
|
142
|
+
"""Translate let name = expression into a Python assignment."""
|
|
143
|
+
match = re.fullmatch(
|
|
144
|
+
r"\s*let\s+([A-Za-z_]\w*)\s*=\s*(.*?)\s*",
|
|
145
|
+
source,
|
|
146
|
+
)
|
|
147
|
+
|
|
137
148
|
if not match:
|
|
138
149
|
return source
|
|
150
|
+
|
|
139
151
|
variable, expression = match.groups()
|
|
140
152
|
expression = substitute_parameters(expression)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
#
|
|
153
|
+
expression = _expand_definitions(expression)
|
|
154
|
+
|
|
155
|
+
# Translate simple ChemQL [variable] expressions into (variable).
|
|
144
156
|
expression = re.sub(
|
|
145
|
-
r"\[\s*([A-Za-z_]
|
|
157
|
+
r"\[\s*([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*\]",
|
|
146
158
|
r"(\1)",
|
|
147
159
|
expression,
|
|
148
160
|
)
|
|
161
|
+
|
|
149
162
|
return f"[{variable} = {expression}]"
|
|
150
163
|
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
def execute_function_lines(lines):
|
|
165
|
+
"""Execute function statements and propagate return values."""
|
|
153
166
|
i = 0
|
|
154
|
-
|
|
155
|
-
|
|
167
|
+
|
|
168
|
+
while i < len(lines):
|
|
169
|
+
line = lines[i].strip()
|
|
170
|
+
|
|
156
171
|
if not line:
|
|
157
172
|
i += 1
|
|
158
173
|
continue
|
|
159
174
|
|
|
160
|
-
#
|
|
175
|
+
# Multiline Python block.
|
|
161
176
|
if line == "[[[":
|
|
162
177
|
block = []
|
|
163
178
|
i += 1
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
|
|
180
|
+
while i < len(lines) and lines[i].strip() != "]]]":
|
|
181
|
+
source_line = substitute_parameters(lines[i])
|
|
182
|
+
source_line = _expand_definitions(source_line)
|
|
183
|
+
block.append(source_line)
|
|
166
184
|
i += 1
|
|
167
|
-
|
|
168
|
-
|
|
185
|
+
|
|
186
|
+
if i >= len(lines):
|
|
187
|
+
raise SyntaxError(
|
|
188
|
+
f"Missing `]]]` in function `{name}`"
|
|
189
|
+
)
|
|
190
|
+
|
|
169
191
|
evaluate_python("\n".join(block))
|
|
170
192
|
i += 1
|
|
171
193
|
continue
|
|
172
194
|
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
|
|
195
|
+
# Recognize block headers generically, while excluding
|
|
196
|
+
# closing and branch markers from being treated as headers.
|
|
197
|
+
generic_header = re.match(
|
|
198
|
+
r"^\[\s*(?!endblock\b|else\b|elif\b)"
|
|
199
|
+
r"[A-Za-z_]\w*\b.*\]\s*$",
|
|
200
|
+
line,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if is_python_block_header(line) or generic_header:
|
|
204
|
+
end = find_endblock(lines, i + 1)
|
|
205
|
+
|
|
177
206
|
block_body = [
|
|
178
207
|
normalize_let_line(substitute_parameters(source_line))
|
|
179
|
-
for source_line in
|
|
208
|
+
for source_line in lines[i + 1:end]
|
|
180
209
|
]
|
|
210
|
+
|
|
181
211
|
code = line[1:-1].strip()
|
|
212
|
+
|
|
182
213
|
if code.startswith("for "):
|
|
183
214
|
execute_for_block(line, block_body)
|
|
215
|
+
|
|
184
216
|
elif code.startswith("while "):
|
|
185
217
|
execute_while_block(line, block_body)
|
|
218
|
+
|
|
186
219
|
elif code.startswith("if "):
|
|
187
|
-
|
|
220
|
+
# Expand parameters and definitions before evaluating.
|
|
221
|
+
condition = code[3:].strip()
|
|
222
|
+
condition = substitute_parameters(condition)
|
|
223
|
+
condition = _expand_definitions(condition)
|
|
224
|
+
|
|
225
|
+
# Find the top-level else branch.
|
|
226
|
+
else_index = next(
|
|
227
|
+
(
|
|
228
|
+
j
|
|
229
|
+
for j, source_line in enumerate(block_body)
|
|
230
|
+
if source_line.strip() == "[else]"
|
|
231
|
+
),
|
|
232
|
+
None,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
if evaluate_python(condition):
|
|
236
|
+
selected_body = (
|
|
237
|
+
block_body
|
|
238
|
+
if else_index is None
|
|
239
|
+
else block_body[:else_index]
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
selected_body = (
|
|
243
|
+
[]
|
|
244
|
+
if else_index is None
|
|
245
|
+
else block_body[else_index + 1:]
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Execute only the selected branch. A return exits
|
|
249
|
+
# this function immediately.
|
|
250
|
+
result = execute_function_lines(selected_body)
|
|
251
|
+
|
|
252
|
+
if result is not _NO_RETURN:
|
|
253
|
+
return result
|
|
254
|
+
|
|
188
255
|
else:
|
|
189
|
-
raise SyntaxError(
|
|
256
|
+
raise SyntaxError(
|
|
257
|
+
f"Unsupported function block `{code}`"
|
|
258
|
+
)
|
|
259
|
+
|
|
190
260
|
i = end + 1
|
|
191
261
|
continue
|
|
192
262
|
|
|
193
263
|
if line == "[endblock]":
|
|
194
|
-
raise SyntaxError(
|
|
264
|
+
raise SyntaxError(
|
|
265
|
+
f"Unexpected `[endblock]` in function `{name}`"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Handle return statements inside the function.
|
|
269
|
+
if re.match(r"^return\b", line):
|
|
270
|
+
expression = re.sub(r"^return\b", "", line, count=1).strip()
|
|
195
271
|
|
|
196
|
-
if line.startswith("return"):
|
|
197
|
-
expression = line[len("return"):].strip()
|
|
198
272
|
if expression.startswith("[") and expression.endswith("]"):
|
|
199
|
-
expression = expression[1:-1]
|
|
273
|
+
expression = expression[1:-1].strip()
|
|
274
|
+
|
|
200
275
|
expression = substitute_parameters(expression)
|
|
276
|
+
expression = _expand_definitions(expression)
|
|
277
|
+
|
|
201
278
|
return evaluate_python(expression) if expression else None
|
|
202
279
|
|
|
203
280
|
normalized = normalize_let_line(line)
|
|
281
|
+
|
|
204
282
|
if normalized != line:
|
|
205
|
-
# The wrapper is ChemQL syntax; pass the assignment itself to Python.
|
|
206
283
|
evaluate_python(normalized[1:-1])
|
|
284
|
+
elif line.startswith("[") and line.endswith("]"):
|
|
285
|
+
expression = line[1:-1].strip()
|
|
286
|
+
expression = substitute_parameters(expression)
|
|
287
|
+
expression = _expand_definitions(expression)
|
|
288
|
+
evaluate_python(expression)
|
|
207
289
|
else:
|
|
208
|
-
|
|
290
|
+
expression = substitute_parameters(line)
|
|
291
|
+
expression = _expand_definitions(expression)
|
|
292
|
+
evaluate_python(expression)
|
|
293
|
+
|
|
209
294
|
i += 1
|
|
210
|
-
|
|
295
|
+
|
|
296
|
+
return _NO_RETURN
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
result = execute_function_lines(definition["body"])
|
|
300
|
+
return None if result is _NO_RETURN else result
|
|
301
|
+
|
|
211
302
|
finally:
|
|
212
|
-
#
|
|
303
|
+
# Restore the caller's namespaces.
|
|
213
304
|
python_globals.clear()
|
|
214
305
|
python_globals.update(saved_globals)
|
|
306
|
+
|
|
215
307
|
CHEMQL_VARIABLES.clear()
|
|
216
308
|
CHEMQL_VARIABLES.update(saved_chemql_variables)
|
|
309
|
+
|
|
217
310
|
if had_arguments:
|
|
218
311
|
python_globals["arguments"] = saved_arguments
|
|
219
312
|
else:
|
|
@@ -2593,7 +2686,7 @@ def execute_if_block(header, body):
|
|
|
2593
2686
|
)
|
|
2594
2687
|
|
|
2595
2688
|
for section_header, section_body in sections:
|
|
2596
|
-
code = section_header[1:-1].strip()
|
|
2689
|
+
code = _expand_definitions(section_header[1:-1].strip())
|
|
2597
2690
|
|
|
2598
2691
|
if code.startswith("if "):
|
|
2599
2692
|
condition = code[3:].strip()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chemql-python
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: A chemistry-focused query language and REPL
|
|
5
5
|
Author-email: Mohammad Rakibul Islam <rakibulofficial1017@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -17,15 +17,15 @@ Dynamic: license-file
|
|
|
17
17
|
shell commands in one interactive environment.**
|
|
18
18
|
|
|
19
19
|
ChemQL is a command-line chemistry query language for exploring chemical
|
|
20
|
-
elements, molecules, and reactions. Version 1.2.
|
|
20
|
+
elements, molecules, and reactions. Version 1.2.3 combines database-style
|
|
21
21
|
chemistry queries, reaction tools, Python interoperability, reusable functions
|
|
22
22
|
and macros, and a persistent shell in one scriptable environment. It combines a small SQL-inspired
|
|
23
23
|
query syntax with a persistent Python environment, reaction utilities,
|
|
24
24
|
optional external molecule lookup, and tools for running repeatable
|
|
25
25
|
scripts.
|
|
26
26
|
|
|
27
|
-
> **Version covered by this README:** 1.2.
|
|
28
|
-
> The examples below describe the ChemQL 1.2.
|
|
27
|
+
> **Version covered by this README:** 1.2.3\
|
|
28
|
+
> The examples below describe the ChemQL 1.2.3 source. Dataset
|
|
29
29
|
> contents and external-service availability can affect individual
|
|
30
30
|
> lookup results.
|
|
31
31
|
|
|
@@ -538,7 +538,7 @@ queried or inspected from Python.
|
|
|
538
538
|
|
|
539
539
|
## Preprocessor, functions, and macros
|
|
540
540
|
|
|
541
|
-
ChemQL 1.2.
|
|
541
|
+
ChemQL 1.2.3 adds three ways to reuse values and logic. These declarations
|
|
542
542
|
are processed in source order: definitions affect later lines, not earlier
|
|
543
543
|
ones. They persist during the current ChemQL process/session; they are not
|
|
544
544
|
saved automatically between separate launches.
|
|
@@ -833,7 +833,7 @@ redirect a file into the command.
|
|
|
833
833
|
``` bash
|
|
834
834
|
chemql --version
|
|
835
835
|
chemql --upgrade
|
|
836
|
-
chemql --upgrade 1.2.
|
|
836
|
+
chemql --upgrade 1.2.3
|
|
837
837
|
```
|
|
838
838
|
|
|
839
839
|
`--upgrade` installs the latest published `chemql-python` release; supplying a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|