chemql-python 0.1.0__tar.gz → 0.2.0__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.
Files changed (24) hide show
  1. {chemql_python-0.1.0/src/chemql_python.egg-info → chemql_python-0.2.0}/PKG-INFO +15 -2
  2. {chemql_python-0.1.0 → chemql_python-0.2.0}/README.md +14 -1
  3. {chemql_python-0.1.0 → chemql_python-0.2.0}/pyproject.toml +1 -1
  4. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/__init__.py +198 -4
  5. {chemql_python-0.1.0 → chemql_python-0.2.0/src/chemql_python.egg-info}/PKG-INFO +15 -2
  6. {chemql_python-0.1.0 → chemql_python-0.2.0}/tests/test_public_api.py +2 -1
  7. {chemql_python-0.1.0 → chemql_python-0.2.0}/tests/test_reactions.py +39 -0
  8. {chemql_python-0.1.0 → chemql_python-0.2.0}/LICENSE +0 -0
  9. {chemql_python-0.1.0 → chemql_python-0.2.0}/setup.cfg +0 -0
  10. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/__main__.py +0 -0
  11. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/BondEnergies.json +0 -0
  12. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/MoleculeBonds.json +0 -0
  13. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/Molecules.json +0 -0
  14. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/PeriodicTableJSON.json +0 -0
  15. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/Reactions.json +0 -0
  16. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/THIRD_PARTY_LICENSES/PeriodicTableJSON.txt +0 -0
  17. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/data/THIRD_PARTY_LICENSES/RHEA.txt +0 -0
  18. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql/html/periodic_table.html +0 -0
  19. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql_python.egg-info/SOURCES.txt +0 -0
  20. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql_python.egg-info/dependency_links.txt +0 -0
  21. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql_python.egg-info/entry_points.txt +0 -0
  22. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql_python.egg-info/requires.txt +0 -0
  23. {chemql_python-0.1.0 → chemql_python-0.2.0}/src/chemql_python.egg-info/top_level.txt +0 -0
  24. {chemql_python-0.1.0 → chemql_python-0.2.0}/tests/test_external_molecule_lookup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chemql-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A chemistry-focused query language and REPL
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,8 @@ Dynamic: license-file
12
12
 
13
13
  # ChemQL
14
14
 
15
+ ![ChemQL logo](logo.png)
16
+
15
17
  ChemQL is a chemistry-focused query language and REPL for exploring element, molecule, and reaction datasets. It lets you search the built-in chemistry data, filter results with expressions, set reaction conditions, and combine queries with small Python snippets when needed.
16
18
 
17
19
  ## What ChemQL does
@@ -80,9 +82,11 @@ The Python package is `chemql`. Its query engine, chemistry types, and bundled
80
82
  datasets can be imported directly:
81
83
 
82
84
  ```python
83
- from chemql import execute_query_text
85
+ from chemql import balance_stoichiometry, execute_query_text
84
86
 
85
87
  result = execute_query_text('search elements name = "Hydrogen"')
88
+ reaction = balance_stoichiometry(["O2", "H2"], ["H2O"], reversible=True)
89
+ print(reaction)
86
90
  ```
87
91
 
88
92
  ## Core query syntax
@@ -159,10 +163,19 @@ add catalyst "Vanadium oxide"
159
163
  remove catalyst "Vanadium oxide"
160
164
  conditions
161
165
  react N2 3H2
166
+ balance O2 + H2 -> H2O //reversible
167
+ balance O2 + H2 <-> H2O //irreversible
162
168
  ```
163
169
 
164
170
  Temperature accepts `C`, `F`, or `K`; pressure accepts `Pa`, `N/m^2`, `Nm^-2`, `bar`, or `atm`. The keywords `standard` and `room` are also accepted.
165
171
 
172
+ `balance` accepts chemical formulas on each side, separated by `+`. It returns
173
+ a `Reaction` with the smallest positive integer stoichiometric coefficients.
174
+ The optional `//reversible` and `//irreversible` annotations set the returned
175
+ reaction direction; without an annotation, `->` is irreversible and `<->` is
176
+ reversible. The function API is
177
+ `balance_stoichiometry(reactants, products, reversible=None)`.
178
+
166
179
  ## Operators
167
180
 
168
181
  ChemQL supports these comparison operators:
@@ -1,5 +1,7 @@
1
1
  # ChemQL
2
2
 
3
+ ![ChemQL logo](logo.png)
4
+
3
5
  ChemQL is a chemistry-focused query language and REPL for exploring element, molecule, and reaction datasets. It lets you search the built-in chemistry data, filter results with expressions, set reaction conditions, and combine queries with small Python snippets when needed.
4
6
 
5
7
  ## What ChemQL does
@@ -68,9 +70,11 @@ The Python package is `chemql`. Its query engine, chemistry types, and bundled
68
70
  datasets can be imported directly:
69
71
 
70
72
  ```python
71
- from chemql import execute_query_text
73
+ from chemql import balance_stoichiometry, execute_query_text
72
74
 
73
75
  result = execute_query_text('search elements name = "Hydrogen"')
76
+ reaction = balance_stoichiometry(["O2", "H2"], ["H2O"], reversible=True)
77
+ print(reaction)
74
78
  ```
75
79
 
76
80
  ## Core query syntax
@@ -147,10 +151,19 @@ add catalyst "Vanadium oxide"
147
151
  remove catalyst "Vanadium oxide"
148
152
  conditions
149
153
  react N2 3H2
154
+ balance O2 + H2 -> H2O //reversible
155
+ balance O2 + H2 <-> H2O //irreversible
150
156
  ```
151
157
 
152
158
  Temperature accepts `C`, `F`, or `K`; pressure accepts `Pa`, `N/m^2`, `Nm^-2`, `bar`, or `atm`. The keywords `standard` and `room` are also accepted.
153
159
 
160
+ `balance` accepts chemical formulas on each side, separated by `+`. It returns
161
+ a `Reaction` with the smallest positive integer stoichiometric coefficients.
162
+ The optional `//reversible` and `//irreversible` annotations set the returned
163
+ reaction direction; without an annotation, `->` is irreversible and `<->` is
164
+ reversible. The function API is
165
+ `balance_stoichiometry(reactants, products, reversible=None)`.
166
+
154
167
  ## Operators
155
168
 
156
169
  ChemQL supports these comparison operators:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "chemql-python"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "A chemistry-focused query language and REPL"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -13,6 +13,9 @@ from prompt_toolkit.completion import Completer, Completion
13
13
  import urllib.parse
14
14
  import urllib.request
15
15
  import urllib.error
16
+ from fractions import Fraction
17
+ from functools import reduce
18
+ from math import gcd
16
19
 
17
20
 
18
21
  __all__ = [
@@ -22,6 +25,7 @@ __all__ = [
22
25
  "Reaction",
23
26
  "ReturnTable",
24
27
  "Unknown",
28
+ "balance_stoichiometry",
25
29
  "execute_query",
26
30
  "execute_query_text",
27
31
  ]
@@ -918,7 +922,177 @@ reactionKeys = ("id", "name", "equation", "reaction_type", "reversible", "reacta
918
922
  del elements
919
923
  del reactions
920
924
  del molecules
921
- del MOLECULES_BY_FORMULA
925
+
926
+
927
+ def _formula_counts(formula):
928
+ if not isinstance(formula, str) or not formula:
929
+ raise ValueError("Chemical formulas must be non-empty strings")
930
+ symbols = {element.symbol for element in ELEMENTS}
931
+
932
+ def parse_group(index, nested=False):
933
+ counts = {}
934
+ while index < len(formula):
935
+ char = formula[index]
936
+ if char == ")":
937
+ if not nested:
938
+ raise ValueError(f"Unmatched closing parenthesis in formula `{formula}`")
939
+ return counts, index
940
+ if char == "(":
941
+ group, index = parse_group(index + 1, True)
942
+ if index >= len(formula) or formula[index] != ")":
943
+ raise ValueError(f"Unclosed parenthesis in formula `{formula}`")
944
+ index += 1
945
+ match = re.match(r"\d+", formula[index:])
946
+ multiplier = int(match.group()) if match else 1
947
+ if multiplier <= 0:
948
+ raise ValueError(f"Invalid atom count in formula `{formula}`")
949
+ if match:
950
+ index += len(match.group())
951
+ for symbol, amount in group.items():
952
+ counts[symbol] = counts.get(symbol, 0) + amount * multiplier
953
+ continue
954
+ if not char.isupper():
955
+ raise ValueError(f"Invalid chemical formula `{formula}`")
956
+ end = index + 1
957
+ if end < len(formula) and formula[end].islower():
958
+ end += 1
959
+ symbol = formula[index:end]
960
+ if symbol not in symbols:
961
+ raise ValueError(f"Unknown element `{symbol}` in formula `{formula}`")
962
+ index = end
963
+ match = re.match(r"\d+", formula[index:])
964
+ amount = int(match.group()) if match else 1
965
+ if amount <= 0:
966
+ raise ValueError(f"Invalid atom count in formula `{formula}`")
967
+ if match:
968
+ index += len(match.group())
969
+ counts[symbol] = counts.get(symbol, 0) + amount
970
+ if nested:
971
+ raise ValueError(f"Unclosed parenthesis in formula `{formula}`")
972
+ return counts, index
973
+
974
+ counts, end = parse_group(0)
975
+ if end != len(formula) or not counts:
976
+ raise ValueError(f"Invalid chemical formula `{formula}`")
977
+ return counts
978
+
979
+
980
+ def _stoichiometric_species(side):
981
+ if isinstance(side, str):
982
+ species = [item.strip() for item in side.split("+")]
983
+ else:
984
+ species = [str(item).strip() for item in side]
985
+ if not species or any(not item for item in species):
986
+ raise ValueError("Both sides of a reaction must contain molecules")
987
+ return species
988
+
989
+
990
+ def balance_stoichiometry(reactants, products, reversible=None):
991
+ """Balance a reaction and return it as a :class:`Reaction`."""
992
+ reactant_formulas = _stoichiometric_species(reactants)
993
+ product_formulas = _stoichiometric_species(products)
994
+ formulas = reactant_formulas + product_formulas
995
+ formulas_by_element = [_formula_counts(formula) for formula in formulas]
996
+ elements = list(dict.fromkeys(
997
+ element for counts in formulas_by_element for element in counts
998
+ ))
999
+ matrix = [
1000
+ [
1001
+ Fraction(counts.get(element, 0) * (1 if index < len(reactant_formulas) else -1))
1002
+ for index, counts in enumerate(formulas_by_element)
1003
+ ]
1004
+ for element in elements
1005
+ ]
1006
+
1007
+ pivot_columns = []
1008
+ pivot_row = 0
1009
+ for column in range(len(formulas)):
1010
+ selected = next(
1011
+ (row for row in range(pivot_row, len(matrix)) if matrix[row][column]),
1012
+ None,
1013
+ )
1014
+ if selected is None:
1015
+ continue
1016
+ matrix[pivot_row], matrix[selected] = matrix[selected], matrix[pivot_row]
1017
+ divisor = matrix[pivot_row][column]
1018
+ matrix[pivot_row] = [value / divisor for value in matrix[pivot_row]]
1019
+ for row in range(len(matrix)):
1020
+ if row == pivot_row or not matrix[row][column]:
1021
+ continue
1022
+ factor = matrix[row][column]
1023
+ matrix[row] = [
1024
+ value - factor * pivot
1025
+ for value, pivot in zip(matrix[row], matrix[pivot_row])
1026
+ ]
1027
+ pivot_columns.append(column)
1028
+ pivot_row += 1
1029
+ if pivot_row == len(matrix):
1030
+ break
1031
+
1032
+ free_columns = [
1033
+ column for column in range(len(formulas)) if column not in pivot_columns
1034
+ ]
1035
+ if len(free_columns) != 1:
1036
+ raise ValueError("Reaction does not have a unique stoichiometric balance")
1037
+ coefficients = [Fraction(0) for _ in formulas]
1038
+ free_column = free_columns[0]
1039
+ coefficients[free_column] = Fraction(1)
1040
+ for row, column in enumerate(pivot_columns):
1041
+ coefficients[column] = -matrix[row][free_column]
1042
+ if all(value < 0 for value in coefficients):
1043
+ coefficients = [-value for value in coefficients]
1044
+ if any(value <= 0 for value in coefficients):
1045
+ raise ValueError("Reaction cannot be balanced with positive coefficients")
1046
+
1047
+ denominator = 1
1048
+ for coefficient in coefficients:
1049
+ denominator = denominator * coefficient.denominator // gcd(
1050
+ denominator, coefficient.denominator
1051
+ )
1052
+ integer_coefficients = [
1053
+ int(coefficient * denominator) for coefficient in coefficients
1054
+ ]
1055
+ common_divisor = reduce(gcd, integer_coefficients)
1056
+ integer_coefficients = [value // common_divisor for value in integer_coefficients]
1057
+ participants = [
1058
+ {
1059
+ "molecule": formula,
1060
+ "stoichiometric_coefficient": coefficient,
1061
+ }
1062
+ for formula, coefficient in zip(formulas, integer_coefficients)
1063
+ ]
1064
+ split_at = len(reactant_formulas)
1065
+ return Reaction(
1066
+ "BALANCED",
1067
+ "Balanced reaction",
1068
+ "balanced",
1069
+ reversible,
1070
+ participants[:split_at],
1071
+ participants[split_at:],
1072
+ {},
1073
+ "",
1074
+ )
1075
+
1076
+
1077
+ def _execute_balance_command(text):
1078
+ match = re.fullmatch(
1079
+ r"\s*balance\s+(.+?)\s*(<->|->)\s*(.+?)"
1080
+ r"(?:\s*//\s*(reversible|irreversible)\s*)?",
1081
+ text,
1082
+ re.IGNORECASE,
1083
+ )
1084
+ if not match:
1085
+ raise ValueError(
1086
+ "Usage: balance <reactants> ->|<-> <products> "
1087
+ "[//reversible|//irreversible]"
1088
+ )
1089
+ reactants, arrow, products, direction = match.groups()
1090
+ reversible = (
1091
+ direction.lower() == "reversible"
1092
+ if direction
1093
+ else arrow == "<->"
1094
+ )
1095
+ return balance_stoichiometry(reactants, products, reversible)
922
1096
 
923
1097
 
924
1098
 
@@ -1549,6 +1723,13 @@ def find_endblock(lines, start):
1549
1723
  raise SyntaxError("Missing `[endblock]`")
1550
1724
 
1551
1725
  def remove_comments(line):
1726
+ if re.fullmatch(
1727
+ r"\s*balance\b.*//\s*(?:reversible|irreversible)\s*",
1728
+ line,
1729
+ re.IGNORECASE,
1730
+ ):
1731
+ return line
1732
+
1552
1733
  in_quote = None
1553
1734
  escaped = False
1554
1735
 
@@ -2134,6 +2315,14 @@ def execute(arguments, query_text=None, positions=None):
2134
2315
  if not arguments:
2135
2316
  return
2136
2317
 
2318
+ if arguments[0].lower() == "balance":
2319
+ try:
2320
+ return _execute_balance_command(
2321
+ query_text if query_text is not None else shlex.join(arguments)
2322
+ )
2323
+ except ValueError as exc:
2324
+ return format_error(str(exc), query_text)
2325
+
2137
2326
  if arguments[0].lower() in {"help", "?"}:
2138
2327
  return HELP
2139
2328
 
@@ -2562,11 +2751,15 @@ HELP = """\033[1;36mChemql - Python Chemistry Query Language\033[0m
2562
2751
  set catalysts
2563
2752
  conditions
2564
2753
  react N2 3H2
2754
+ balance O2 + H2 -> H2O //reversible
2755
+ balance O2 + H2 <-> H2O //irreversible
2565
2756
 
2566
2757
  Temperature accepts C, F, or K. Pressure accepts Pa, N/m^2, Nm^-2, or bar.
2567
2758
  `standard` means 0 C and 100000 Pa; `room` means 25 C and 101325 Pa.
2568
2759
  Unset values appear as `Not Set` in `conditions`.
2569
2760
  Reaction matching allows 5 K temperature and 5% pressure tolerance.
2761
+ `balance` returns a Reaction with the smallest positive integer coefficients.
2762
+ Direction annotations override the arrow: `//reversible` or `//irreversible`.
2570
2763
 
2571
2764
  \033[1;33mRESULTS\033[0m
2572
2765
  \033[1mfirst\033[0m
@@ -2718,7 +2911,7 @@ class ChemqlLexer(Lexer):
2718
2911
  first_word = words[0] if words else ""
2719
2912
 
2720
2913
  # search / source / list / return / clear / exit
2721
- if first_word in {"search", "source", "list", "clear", "exit", "findel", "findmol", "findre", "set", "add", "remove", "react", "conditions", "view", "help", "dump"}:
2914
+ if first_word in {"search", "source", "list", "clear", "exit", "findel", "findmol", "findre", "set", "add", "remove", "react", "balance", "conditions", "view", "help", "dump"}:
2722
2915
  command_start = line.find(first_word)
2723
2916
  command_end = command_start + len(first_word)
2724
2917
 
@@ -2857,6 +3050,7 @@ class ChemqlLexer(Lexer):
2857
3050
  "add",
2858
3051
  "remove",
2859
3052
  "react",
3053
+ "balance",
2860
3054
  "conditions",
2861
3055
  "view",
2862
3056
  "help",
@@ -2936,7 +3130,7 @@ class ChemqlCompleter(Completer):
2936
3130
  commands = (
2937
3131
  "search", "source", "list", "clear", "return", "exit",
2938
3132
  "findel", "findmol", "findre", "set", "add", "remove",
2939
- "react", "conditions",
3133
+ "react", "balance", "conditions",
2940
3134
  )
2941
3135
 
2942
3136
  if not command_words:
@@ -3019,7 +3213,7 @@ def main():
3019
3213
  "-v",
3020
3214
  "--version",
3021
3215
  action="version",
3022
- version="0.1.0",
3216
+ version="0.2.0",
3023
3217
  )
3024
3218
 
3025
3219
  args = parser.parse_args()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chemql-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A chemistry-focused query language and REPL
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,8 @@ Dynamic: license-file
12
12
 
13
13
  # ChemQL
14
14
 
15
+ ![ChemQL logo](logo.png)
16
+
15
17
  ChemQL is a chemistry-focused query language and REPL for exploring element, molecule, and reaction datasets. It lets you search the built-in chemistry data, filter results with expressions, set reaction conditions, and combine queries with small Python snippets when needed.
16
18
 
17
19
  ## What ChemQL does
@@ -80,9 +82,11 @@ The Python package is `chemql`. Its query engine, chemistry types, and bundled
80
82
  datasets can be imported directly:
81
83
 
82
84
  ```python
83
- from chemql import execute_query_text
85
+ from chemql import balance_stoichiometry, execute_query_text
84
86
 
85
87
  result = execute_query_text('search elements name = "Hydrogen"')
88
+ reaction = balance_stoichiometry(["O2", "H2"], ["H2O"], reversible=True)
89
+ print(reaction)
86
90
  ```
87
91
 
88
92
  ## Core query syntax
@@ -159,10 +163,19 @@ add catalyst "Vanadium oxide"
159
163
  remove catalyst "Vanadium oxide"
160
164
  conditions
161
165
  react N2 3H2
166
+ balance O2 + H2 -> H2O //reversible
167
+ balance O2 + H2 <-> H2O //irreversible
162
168
  ```
163
169
 
164
170
  Temperature accepts `C`, `F`, or `K`; pressure accepts `Pa`, `N/m^2`, `Nm^-2`, `bar`, or `atm`. The keywords `standard` and `room` are also accepted.
165
171
 
172
+ `balance` accepts chemical formulas on each side, separated by `+`. It returns
173
+ a `Reaction` with the smallest positive integer stoichiometric coefficients.
174
+ The optional `//reversible` and `//irreversible` annotations set the returned
175
+ reaction direction; without an annotation, `->` is irreversible and `<->` is
176
+ reversible. The function API is
177
+ `balance_stoichiometry(reactants, products, reversible=None)`.
178
+
166
179
  ## Operators
167
180
 
168
181
  ChemQL supports these comparison operators:
@@ -10,6 +10,7 @@ def test_all_exports_only_public_query_api_and_result_types():
10
10
  "Reaction",
11
11
  "ReturnTable",
12
12
  "Unknown",
13
+ "balance_stoichiometry",
13
14
  "execute_query",
14
15
  "execute_query_text",
15
16
  }
@@ -24,4 +25,4 @@ def test_cli_version_option_prints_version(monkeypatch, capsys, option):
24
25
  chemql.main()
25
26
 
26
27
  assert exception.value.code == 0
27
- assert capsys.readouterr().out == "0.1.0\n"
28
+ assert capsys.readouterr().out == "0.2.0\n"
@@ -2,6 +2,45 @@ import chemql
2
2
  from prompt_toolkit.document import Document
3
3
 
4
4
 
5
+ def test_balance_stoichiometry_returns_minimal_reaction_coefficients():
6
+ reaction = chemql.balance_stoichiometry(["O2", "H2"], ["H2O"], True)
7
+
8
+ assert isinstance(reaction, chemql.Reaction)
9
+ assert [part["stoichiometric_coefficient"] for part in reaction.reactants] == [1, 2]
10
+ assert [part["stoichiometric_coefficient"] for part in reaction.products] == [2]
11
+ assert reaction.reversible is True
12
+
13
+ grouped = chemql.balance_stoichiometry(
14
+ ["Ca(OH)2", "H3PO4"],
15
+ ["Ca3(PO4)2", "H2O"],
16
+ )
17
+ assert [
18
+ part["stoichiometric_coefficient"]
19
+ for part in grouped.reactants + grouped.products
20
+ ] == [3, 2, 1, 6]
21
+
22
+
23
+ def test_balance_syntax_preserves_direction_annotation(capsys):
24
+ chemql.process_lines(["balance O2 + H2 -> H2O //reversible"])
25
+ assert "1 O₂ + 2 H₂ ⇌ 2 H₂O" in capsys.readouterr().out
26
+
27
+ reaction = chemql.execute_query_text(
28
+ "balance O2 + H2 <-> H2O //irreversible"
29
+ )
30
+ assert isinstance(reaction, chemql.Reaction)
31
+ assert reaction.reversible is False
32
+ assert "1 O₂ + 2 H₂ → 2 H₂O" in str(reaction)
33
+
34
+
35
+ def test_balance_stoichiometry_rejects_invalid_formula():
36
+ try:
37
+ chemql.balance_stoichiometry(["Xx2"], ["H2"])
38
+ except ValueError as exc:
39
+ assert "Unknown element" in str(exc)
40
+ else:
41
+ raise AssertionError("Unknown elements must be rejected")
42
+
43
+
5
44
  def test_findre_searches_reactions_by_id_and_projects_attributes():
6
45
  result = chemql.execute([
7
46
  "findre", "RHEA:10000", "return", "id", "name", "source"
File without changes
File without changes