chython 2.19__cp313-cp313-win_amd64.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.
Files changed (175) hide show
  1. chython/__init__.py +35 -0
  2. chython/_functions.py +95 -0
  3. chython/_java.py +57 -0
  4. chython/algorithms/__init__.py +18 -0
  5. chython/algorithms/_isomorphism.cp313-win_amd64.pyd +0 -0
  6. chython/algorithms/_isomorphism.pyx +189 -0
  7. chython/algorithms/_rings.cp313-win_amd64.pyd +0 -0
  8. chython/algorithms/_rings.pyx +478 -0
  9. chython/algorithms/aromatics/__init__.py +27 -0
  10. chython/algorithms/aromatics/_rules.py +124 -0
  11. chython/algorithms/aromatics/kekule.py +532 -0
  12. chython/algorithms/aromatics/test/__init__.py +18 -0
  13. chython/algorithms/aromatics/test/test_kekule.py +160 -0
  14. chython/algorithms/aromatics/test/test_thiele.py +263 -0
  15. chython/algorithms/aromatics/thiele.py +222 -0
  16. chython/algorithms/calculate2d/__init__.py +23 -0
  17. chython/algorithms/calculate2d/clean2d.js +1 -0
  18. chython/algorithms/calculate2d/molecule.py +191 -0
  19. chython/algorithms/calculate2d/reaction.py +75 -0
  20. chython/algorithms/conformers.py +106 -0
  21. chython/algorithms/depict.py +596 -0
  22. chython/algorithms/fingerprints/__init__.py +43 -0
  23. chython/algorithms/fingerprints/linear.py +202 -0
  24. chython/algorithms/fingerprints/morgan.py +132 -0
  25. chython/algorithms/fingerprints/test/__init__.py +18 -0
  26. chython/algorithms/fingerprints/test/test_linear.py +127 -0
  27. chython/algorithms/fingerprints/test/test_morgan.py +79 -0
  28. chython/algorithms/groups/__init__.py +212 -0
  29. chython/algorithms/groups/_functional.py +290 -0
  30. chython/algorithms/groups/_oxidations.py +70 -0
  31. chython/algorithms/groups/_protective.py +964 -0
  32. chython/algorithms/groups/_reactions.py +809 -0
  33. chython/algorithms/groups/_reductions.py +88 -0
  34. chython/algorithms/groups/_transformations.py +278 -0
  35. chython/algorithms/groups/test/__init__.py +18 -0
  36. chython/algorithms/groups/test/test_deprotection.py +108 -0
  37. chython/algorithms/groups/test/test_reactions.py +377 -0
  38. chython/algorithms/isomorphism.py +664 -0
  39. chython/algorithms/mapping/__init__.py +188 -0
  40. chython/algorithms/mapping/_groups.py +94 -0
  41. chython/algorithms/mapping/_reactions.py +72 -0
  42. chython/algorithms/mapping/attention.py +266 -0
  43. chython/algorithms/mapping/reconstruct.py +346 -0
  44. chython/algorithms/mcs.py +201 -0
  45. chython/algorithms/morgan.py +77 -0
  46. chython/algorithms/rings.py +178 -0
  47. chython/algorithms/smiles.py +597 -0
  48. chython/algorithms/standardize/__init__.py +30 -0
  49. chython/algorithms/standardize/_groups.py +958 -0
  50. chython/algorithms/standardize/_isomers.py +330 -0
  51. chython/algorithms/standardize/_metal_organics.py +189 -0
  52. chython/algorithms/standardize/_reagents.py +44 -0
  53. chython/algorithms/standardize/_salts.py +68 -0
  54. chython/algorithms/standardize/molecule.py +475 -0
  55. chython/algorithms/standardize/reaction.py +384 -0
  56. chython/algorithms/standardize/resonance.py +191 -0
  57. chython/algorithms/standardize/salts.py +128 -0
  58. chython/algorithms/standardize/saturation.py +373 -0
  59. chython/algorithms/standardize/test/__init__.py +18 -0
  60. chython/algorithms/standardize/test/test_canonicalize.py +66 -0
  61. chython/algorithms/standardize/test/test_groups.py +146 -0
  62. chython/algorithms/standardize/test/test_isomers.py +198 -0
  63. chython/algorithms/stereo.py +1215 -0
  64. chython/algorithms/tautomers/__init__.py +167 -0
  65. chython/algorithms/tautomers/_acid.py +59 -0
  66. chython/algorithms/tautomers/_base.py +116 -0
  67. chython/algorithms/tautomers/_keto_enol.py +107 -0
  68. chython/algorithms/tautomers/acid_base.py +208 -0
  69. chython/algorithms/tautomers/heteroarenes.py +103 -0
  70. chython/algorithms/tautomers/keto_enol.py +143 -0
  71. chython/algorithms/tautomers/test/__init__.py +18 -0
  72. chython/algorithms/tautomers/test/test_tautomers.py +80 -0
  73. chython/algorithms/test/__init__.py +18 -0
  74. chython/algorithms/test/test_isomorphism.py +58 -0
  75. chython/algorithms/test/test_smiles.py +76 -0
  76. chython/algorithms/x3dom.py +350 -0
  77. chython/containers/__init__.py +48 -0
  78. chython/containers/_pack_v2.cp313-win_amd64.pyd +0 -0
  79. chython/containers/_pack_v2.pyx +271 -0
  80. chython/containers/_unpack_v0v2.cp313-win_amd64.pyd +0 -0
  81. chython/containers/_unpack_v0v2.pyx +302 -0
  82. chython/containers/bonds.py +255 -0
  83. chython/containers/cgr.py +115 -0
  84. chython/containers/chimera.py +63 -0
  85. chython/containers/graph.py +199 -0
  86. chython/containers/molecule.py +817 -0
  87. chython/containers/query.py +71 -0
  88. chython/containers/rdkit.py +183 -0
  89. chython/containers/reaction.py +324 -0
  90. chython/containers/test/__init__.py +18 -0
  91. chython/containers/test/test_molecule.py +117 -0
  92. chython/containers/test/test_pack.py +60 -0
  93. chython/containers/test/test_rdkit.py +70 -0
  94. chython/containers/test/test_reaction.py +40 -0
  95. chython/exceptions.py +114 -0
  96. chython/files/MRVrw.py +532 -0
  97. chython/files/PDBrw.py +255 -0
  98. chython/files/RDFrw.py +309 -0
  99. chython/files/SDFrw.py +221 -0
  100. chython/files/__init__.py +30 -0
  101. chython/files/_convert.py +207 -0
  102. chython/files/_mapping.py +117 -0
  103. chython/files/_xyz.cp313-win_amd64.pyd +0 -0
  104. chython/files/_xyz.pyx +74 -0
  105. chython/files/daylight/__init__.py +24 -0
  106. chython/files/daylight/parser.py +157 -0
  107. chython/files/daylight/smarts.py +107 -0
  108. chython/files/daylight/smiles.py +260 -0
  109. chython/files/daylight/test/__init__.py +18 -0
  110. chython/files/daylight/test/test_daylight_smarts.py +289 -0
  111. chython/files/daylight/test/test_daylight_smiles.py +101 -0
  112. chython/files/daylight/test/test_parser.py +118 -0
  113. chython/files/daylight/test/test_tokenize.py +77 -0
  114. chython/files/daylight/tokenize.py +376 -0
  115. chython/files/libinchi/__init__.py +22 -0
  116. chython/files/libinchi/libinchi.dll +0 -0
  117. chython/files/libinchi/wrapper.py +554 -0
  118. chython/files/mdl/__init__.py +25 -0
  119. chython/files/mdl/emol.py +251 -0
  120. chython/files/mdl/erxn.py +67 -0
  121. chython/files/mdl/mol.py +186 -0
  122. chython/files/mdl/read.py +223 -0
  123. chython/files/mdl/rxn.py +67 -0
  124. chython/files/mdl/stereo.py +63 -0
  125. chython/files/mdl/write.py +201 -0
  126. chython/files/opsin.py +35 -0
  127. chython/files/test/__init__.py +0 -0
  128. chython/files/test/test_inchi.py +27 -0
  129. chython/files/test/test_mrv.py +47 -0
  130. chython/files/test/test_sdf_rdf.py +64 -0
  131. chython/files/test/test_xyz.py +20 -0
  132. chython/files/xyz.py +78 -0
  133. chython/periodictable/__init__.py +69 -0
  134. chython/periodictable/base/__init__.py +25 -0
  135. chython/periodictable/base/dynamic.py +149 -0
  136. chython/periodictable/base/element.py +451 -0
  137. chython/periodictable/base/groups.py +90 -0
  138. chython/periodictable/base/periods.py +46 -0
  139. chython/periodictable/base/query.py +473 -0
  140. chython/periodictable/base/vector.py +119 -0
  141. chython/periodictable/groupI.py +252 -0
  142. chython/periodictable/groupII.py +223 -0
  143. chython/periodictable/groupIII.py +1154 -0
  144. chython/periodictable/groupIV.py +210 -0
  145. chython/periodictable/groupIX.py +196 -0
  146. chython/periodictable/groupV.py +192 -0
  147. chython/periodictable/groupVI.py +183 -0
  148. chython/periodictable/groupVII.py +162 -0
  149. chython/periodictable/groupVIII.py +161 -0
  150. chython/periodictable/groupX.py +166 -0
  151. chython/periodictable/groupXI.py +164 -0
  152. chython/periodictable/groupXII.py +158 -0
  153. chython/periodictable/groupXIII.py +239 -0
  154. chython/periodictable/groupXIV.py +262 -0
  155. chython/periodictable/groupXV.py +276 -0
  156. chython/periodictable/groupXVI.py +457 -0
  157. chython/periodictable/groupXVII.py +324 -0
  158. chython/periodictable/groupXVIII.py +260 -0
  159. chython/periodictable/test/__init__.py +0 -0
  160. chython/periodictable/test/test_element.py +100 -0
  161. chython/reactor/__init__.py +24 -0
  162. chython/reactor/base.py +222 -0
  163. chython/reactor/reactor.py +177 -0
  164. chython/reactor/test/__init__.py +18 -0
  165. chython/reactor/test/test_reactor.py +40 -0
  166. chython/reactor/test/test_transformer.py +78 -0
  167. chython/reactor/transformer.py +72 -0
  168. chython/utils/__init__.py +50 -0
  169. chython/utils/free_wilson.py +114 -0
  170. chython/utils/grid.py +130 -0
  171. chython/utils/retro.py +139 -0
  172. chython-2.19.dist-info/METADATA +105 -0
  173. chython-2.19.dist-info/RECORD +175 -0
  174. chython-2.19.dist-info/WHEEL +4 -0
  175. chython-2.19.dist-info/licenses/LICENSE +165 -0
@@ -0,0 +1,376 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2022-2026 Ramil Nugmanov <nougmanoff@protonmail.com>
4
+ # This file is part of chython.
5
+ #
6
+ # chython is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation; either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ from re import compile, match, search
20
+ from ...containers.bonds import QueryBond
21
+ from ...exceptions import IncorrectSmiles, IncorrectSmarts
22
+
23
+
24
+ # -,= OR bonds supported
25
+ # !: NOT bonds supported
26
+ # ~ ANY bonds supported
27
+ # @ bond not supported
28
+ # @;!: and any other complicated combinations not supported
29
+
30
+
31
+ # tokens structure:
32
+ # (type: int, value)
33
+ # types:
34
+ # 0: atom
35
+ # 1: bond
36
+ # 2: open chain (
37
+ # 3: close chain )
38
+ # 4: dot bond .
39
+ # 5: in bracket raw data []
40
+ # 6: closure number
41
+ # 7: raw closure number
42
+ # 8: aromatic atom
43
+ # 9: up down bond
44
+
45
+ # 10: query OR bond
46
+ # 11: query NOT bond
47
+ # 12: in ring bond
48
+
49
+
50
+ iso_re = compile(r'^[0-9]+')
51
+ chg_re = compile(r'[+-][1-4+-]?')
52
+ mpp_re = compile(r':[1-9][0-9]*$')
53
+ str_re = compile(r'@[@?]?')
54
+
55
+ replace_dict = {'-': 1, '=': 2, '#': 3, ':': 4, '~': 8}
56
+ not_dict = {'-': [2, 3, 4], '=': [1, 3, 4], '#': [1, 2, 4], ':': [1, 2, 3]}
57
+ atom_re = compile(r'([1-9][0-9]{0,2})?([A-IK-PR-Zacnopsbt][a-ik-pr-vy]?)(@@|@)?(H[1-4]?)?([+-][1-4+-]?)?(:[0-9]{1,4})?')
58
+ charge_dict = {'+': 1, '+1': 1, '++': 2, '+2': 2, '+3': 3, '+++': 3, '+4': 4, '++++': 4,
59
+ '-': -1, '-1': -1, '--': -2, '-2': -2, '-3': -3, '---': -3, '-4': -4, '----': -4}
60
+
61
+
62
+ def _tokenize(smiles):
63
+ token_type = token = None
64
+ tokens = []
65
+ for s in smiles:
66
+ if token_type == 12: # -;, =;, #; or :; found.
67
+ if s == '!': # not in ring
68
+ if token: # !! case
69
+ raise IncorrectSmarts('Invalid ring bond token')
70
+ token = True
71
+ elif s == '@':
72
+ tokens.append((12, QueryBond(tokens.pop(-1)[1], not token)))
73
+ token_type = token = None # finalize [not]ring bond
74
+ else:
75
+ raise IncorrectSmarts('Invalid ring bond token')
76
+ # [atom block parser]
77
+ elif s == '[': # open complex token
78
+ if token_type == 5: # two opened [
79
+ raise IncorrectSmiles('[..[')
80
+ elif token_type in (10, 11):
81
+ raise IncorrectSmarts('Query bond invalid')
82
+ elif token_type == 7: # empty closure
83
+ raise IncorrectSmiles('invalid closure')
84
+ elif token:
85
+ tokens.append((token_type, token))
86
+ token = []
87
+ token_type = 5
88
+ elif s == ']': # close complex token
89
+ if token_type != 5:
90
+ raise IncorrectSmiles(']..]')
91
+ elif not token:
92
+ raise IncorrectSmiles('empty [] brackets')
93
+ tokens.append((5, ''.join(token)))
94
+ token = None
95
+ token_type = 0 # mark as atom
96
+ elif token_type == 5: # grow token with brackets. skip validation
97
+ token.append(s)
98
+ # closure parser
99
+ elif s.isnumeric(): # closures
100
+ if token_type in (10, 11):
101
+ raise IncorrectSmarts('Query bond invalid')
102
+ elif token_type == 2:
103
+ raise IncorrectSmiles('(1 case invalid')
104
+ elif token_type == 7: # % already found. collect number
105
+ if not token and s == '0':
106
+ raise IncorrectSmiles('number starts with 0')
107
+ token.append(s)
108
+ if len(token) == 2:
109
+ tokens.append((6, int(''.join(token))))
110
+ token = None
111
+ token_type = 6 # mark finished
112
+ else:
113
+ if s == '0':
114
+ raise IncorrectSmiles('number starts with 0')
115
+ elif token:
116
+ tokens.append((token_type, token))
117
+ token = None
118
+ token_type = 6
119
+ tokens.append((6, int(s)))
120
+ elif token_type == 7:
121
+ raise IncorrectSmiles('expected closure number')
122
+ elif s == '%':
123
+ if token_type in (10, 11):
124
+ raise IncorrectSmarts('Query bond invalid')
125
+ elif token_type == 2:
126
+ raise IncorrectSmiles('(%10 case invalid')
127
+ elif token:
128
+ tokens.append((token_type, token))
129
+ token_type = 7
130
+ token = []
131
+ # bonds parser
132
+ elif s in '=#:-~': # bonds found
133
+ if token_type == 10:
134
+ token.append(replace_dict[s])
135
+ tokens.append((10, token))
136
+ token_type = token = None # finalize token
137
+ elif token_type == 11:
138
+ token_type = None
139
+ tokens.append((10, not_dict[s]))
140
+ else:
141
+ if token:
142
+ tokens.append((token_type, token))
143
+ token = None
144
+ token_type = 1
145
+ tokens.append((1, replace_dict[s]))
146
+ elif token_type in (10, 11): # expected bond symbol
147
+ raise IncorrectSmarts('query bond invalid')
148
+ elif s in r'\/':
149
+ if token:
150
+ tokens.append((token_type, token))
151
+ token = None
152
+ token_type = 9
153
+ tokens.append((9, s == '/')) # Up is true
154
+ elif s == '.':
155
+ if token:
156
+ tokens.append((token_type, token))
157
+ token = None
158
+ token_type = 4
159
+ tokens.append((4, None))
160
+ elif s == ';': # ;@ or ;!@ - in ring or not in ring bond
161
+ if token_type is not None and token_type != 1: # start of smiles or bond, list of bonds and not bond.
162
+ raise IncorrectSmarts('Ring bond token invalid')
163
+ token_type = 12
164
+ elif s == ',': # query bond separator
165
+ if token_type != 1:
166
+ raise IncorrectSmarts('Query bond invalid')
167
+ token_type = 10
168
+ token = [tokens.pop(-1)[1]]
169
+ elif s == '!': # query not bond
170
+ if token_type not in (0, 2, 3, 6, 8): # closures, brackets or atoms expected
171
+ raise IncorrectSmarts('Query bond invalid')
172
+ elif token:
173
+ tokens.append((token_type, token))
174
+ token = None
175
+ token_type = 11
176
+ # brackets
177
+ elif s == '(':
178
+ if token_type == 2: # barely opened
179
+ raise IncorrectSmiles('((')
180
+ elif token:
181
+ tokens.append((token_type, token))
182
+ token = None
183
+ token_type = 2
184
+ tokens.append((2, None))
185
+ elif s == ')':
186
+ if token_type == 2: # barely opened
187
+ raise IncorrectSmiles('()')
188
+ elif token:
189
+ tokens.append((token_type, token))
190
+ token = None
191
+ token_type = 3
192
+ tokens.append((3, None))
193
+ # simple atoms
194
+ elif s in 'NOPSFI': # organic atoms
195
+ if token:
196
+ tokens.append((token_type, token))
197
+ token = None
198
+ token_type = 0
199
+ tokens.append((0, s))
200
+ elif s in 'cnopsb': # aromatic ring atom
201
+ if token:
202
+ tokens.append((token_type, token))
203
+ token = None
204
+ token_type = 8
205
+ tokens.append((8, s.upper()))
206
+ elif s in 'CB': # flag possible Cl or Br
207
+ if token:
208
+ tokens.append((token_type, token))
209
+ token_type = 0
210
+ token = s
211
+ elif token_type == 0:
212
+ if s == 'l':
213
+ if token == 'C':
214
+ tokens.append((0, 'Cl'))
215
+ token = None
216
+ else:
217
+ raise IncorrectSmiles('invalid element Bl')
218
+ elif s == 'r':
219
+ if token == 'B':
220
+ tokens.append((0, 'Br'))
221
+ token = None
222
+ else:
223
+ raise IncorrectSmiles('invalid smiles for Cr')
224
+ else:
225
+ raise IncorrectSmiles('invalid smiles')
226
+ else:
227
+ raise IncorrectSmiles('invalid smiles')
228
+
229
+ if token_type == 5:
230
+ raise IncorrectSmiles('atom description has not finished')
231
+ elif token_type == 7:
232
+ if token:
233
+ tokens.append((6, int(token[0])))
234
+ else:
235
+ raise IncorrectSmiles('invalid %closure')
236
+ elif token:
237
+ tokens.append((token_type, token)) # C or B
238
+ return tokens
239
+
240
+
241
+ def _atom_parse(token):
242
+ # [isotope]Element[element][@[@]][H[n]][+-charge][:mapping]
243
+ _match = atom_re.fullmatch(token)
244
+ if _match is None:
245
+ raise IncorrectSmiles(f'atom token invalid {token}')
246
+ isotope, element, stereo, hydrogen, charge, mapping = _match.groups()
247
+
248
+ if isotope:
249
+ isotope = int(isotope)
250
+
251
+ if stereo:
252
+ stereo = stereo == '@'
253
+
254
+ if hydrogen:
255
+ if len(hydrogen) > 1:
256
+ hydrogen = int(hydrogen[1:])
257
+ else:
258
+ hydrogen = 1
259
+ else:
260
+ hydrogen = 0
261
+
262
+ if charge:
263
+ try:
264
+ charge = charge_dict[charge]
265
+ except KeyError:
266
+ raise IncorrectSmiles('charge token invalid')
267
+ else:
268
+ charge = 0
269
+
270
+ if mapping:
271
+ try:
272
+ mapping = int(mapping[1:])
273
+ except ValueError:
274
+ raise IncorrectSmiles('invalid mapping token')
275
+
276
+ if element in ('c', 'n', 'o', 'p', 's', 'as', 'se', 'b', 'te'):
277
+ _type = 8
278
+ element = element.capitalize()
279
+ else:
280
+ _type = 0
281
+ return _type, {'element': element, 'isotope': isotope, 'parsed_mapping': mapping, 'charge': charge,
282
+ 'implicit_hydrogens': hydrogen, 'stereo': stereo}
283
+
284
+
285
+ def _query_parse(token):
286
+ out = {}
287
+ if isotope := match(iso_re, token):
288
+ token = token[isotope.end():] # remove isotope substring
289
+ out['isotope'] = int(isotope.group())
290
+ if charge := search(chg_re, token):
291
+ token = token[:charge.start()] + token[charge.end():] # remove charge substring
292
+ out['charge'] = charge_dict[charge.group()]
293
+
294
+ if mapping := search(mpp_re, token):
295
+ token = token[:mapping.start()]
296
+ out['parsed_mapping'] = int(mapping.group()[1:])
297
+
298
+ if stereo := search(str_re, token): # drop stereo mark. unsupported
299
+ token = token[:stereo.start()] + token[stereo.end():]
300
+ out['stereo'] = stereo.group() == '@'
301
+
302
+ # supported only <;> and <,> logic. <&> and silent <&> not supported!
303
+ primitives = token.split(';')
304
+ if element := primitives[0]:
305
+ element = [int(x[1:]) if x.startswith('#') else x for x in element.split(',')]
306
+ if len(element) == 1:
307
+ element = element[0]
308
+ else:
309
+ raise IncorrectSmarts('Empty element')
310
+ out['element'] = element
311
+
312
+ for p in primitives[1:]: # parse hydrogens (h), neighbors (D), rings_sizes (r or !R), hybridization == 4 (a)
313
+ if not p:
314
+ continue
315
+ elif p == 'a': # aromatic atom
316
+ out['hybridization'] = 4
317
+ elif p == 'A': # ignore aliphatic mark. Ad-Hoc for Marwin.
318
+ continue
319
+ elif p == '!R':
320
+ out['ring_sizes'] = 0
321
+ elif p == 'M':
322
+ out['masked'] = True
323
+ else:
324
+ p = p.split(',')
325
+ if len(p) != 1 and len({x[0] for x in p}) > 1:
326
+ raise IncorrectSmarts('Unsupported OR statement')
327
+ elif (t := p[0][0]) not in ('D', 'h', 'r', 'x', 'z'):
328
+ raise IncorrectSmarts('Unsupported SMARTS primitive. Use only D, h, r, !R and a.')
329
+ # z and x private chython marks for hybridization and heteroatoms count
330
+ try:
331
+ p = [int(x[1:]) for x in p]
332
+ except ValueError:
333
+ raise IncorrectSmarts('Unsupported SMARTS primitive')
334
+
335
+ if t == 'D':
336
+ out['neighbors'] = p
337
+ elif t == 'h':
338
+ out['implicit_hydrogens'] = p
339
+ elif t == 'r': # r
340
+ out['ring_sizes'] = p
341
+ elif t == 'x':
342
+ out['heteroatoms'] = p
343
+ else: # z
344
+ out['hybridization'] = p
345
+ return 0, out
346
+
347
+
348
+ def smiles_tokenize(smi):
349
+ tokens = _tokenize(smi)
350
+ out = []
351
+ for token_type, token in tokens:
352
+ if token_type in (0, 8): # simple atom
353
+ out.append((token_type, {'element': token}))
354
+ elif token_type == 5:
355
+ out.append(_atom_parse(token))
356
+ elif token_type == 10:
357
+ raise IncorrectSmiles('SMARTS detected')
358
+ else:
359
+ out.append((token_type, token))
360
+ return out
361
+
362
+
363
+ def smarts_tokenize(smi):
364
+ tokens = _tokenize(smi)
365
+ out = []
366
+ for token_type, token in tokens:
367
+ if token_type in (0, 8): # simple atom
368
+ out.append((0, {'element': token}))
369
+ elif token_type == 5:
370
+ out.append(_query_parse(token))
371
+ else:
372
+ out.append((token_type, token))
373
+ return out
374
+
375
+
376
+ __all__ = ['smiles_tokenize', 'smarts_tokenize']
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2023-2026 Ramil Nugmanov <nougmanoff@protonmail.com>
4
+ # This file is part of chython.
5
+ #
6
+ # chython is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation; either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program; if not, see <https://www.gnu.org/licenses/>.
18
+ #
19
+ from .wrapper import *
20
+
21
+
22
+ __all__ = ['inchi']
Binary file