1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import logging
35 import re
36 import unittest
37 from rdkit import Chem
38
39 from rdkit.Chem import inchi
40 if not inchi.INCHI_AVAILABLE:
41 raise ImportError("This code requires the RDKit to be built with InChI suport")
42
43
45 mol = Chem.MolFromInchi(INCHI)
46 if not mol:
47 mol = Chem.MolFromInchi('InChI=1/{0}'.format(INCHI))
48
49 try:
50 list_chiral = Chem.FindMolChiralCenters(mol, True, True)
51 except Exception:
52 return False
53
54
55 return len(list_chiral) == 0
56
57 console = logging.StreamHandler()
58 UPD_APP = logging.getLogger('inchiinfo.application')
59
60 version_re = re.compile('(.*?)/(.*)')
61 reconnected_re = re.compile('(.*?)/r(.*)')
62 fixed_h_re = re.compile('(.*?)/f(.*)')
63 isotope_re = re.compile('(.*?)/i(.*)')
64
65 stereo_re = re.compile('.*\/t(.*?)\/.*')
66 stereo_all_re = re.compile('.*\/t([^\/]+)')
67 undef_stereo_re = re.compile('(\d+)\?')
68 all_stereo_re = re.compile('(\d+)[?+-]')
69 defined_stereo_re = re.compile('(\d+)[+-]')
70 h_layer_re = re.compile('.*\/h(.*)\/?')
71 mobile_h_group_re = re.compile('(\(H.+?\))')
72 mobile_h_atoms_re = re.compile(',(\d+)')
73
74
76
78 (version, rest) = version_re.match(inchi_str).groups()
79 reconn_match = reconnected_re.match(rest)
80
81 connection_layers = {}
82 if reconn_match:
83 (connection_layers['id_disconnected'],
84 connection_layers['id_reconnected']) = reconn_match.groups()
85 else:
86 (connection_layers['id']) = rest
87
88 fixed_h_layers = {}
89 for conn_layer in connection_layers:
90 fixed_h_layers[conn_layer] = {}
91 fixed_match = fixed_h_re.match(connection_layers[conn_layer])
92 if fixed_match:
93 (fixed_h_layers[conn_layer]['main'],
94 fixed_h_layers[conn_layer]['fixed_h']) = fixed_match.groups()
95 else:
96 fixed_h_layers[conn_layer]['main'] = connection_layers[conn_layer]
97
98 inchi = {}
99 for i0_layer in fixed_h_layers:
100 inchi[i0_layer] = {}
101 for i1_layer in fixed_h_layers[i0_layer]:
102 inchi[i0_layer][i1_layer] = {}
103 iso_match = isotope_re.match(fixed_h_layers[i0_layer][i1_layer])
104 if iso_match:
105 (inchi[i0_layer][i1_layer]['non-isotopic'],
106 inchi[i0_layer][i1_layer]['isotopic']) = iso_match.groups()
107 else:
108 inchi[i0_layer][i1_layer]['non-isotopic'] = fixed_h_layers[i0_layer][i1_layer]
109
110 self.parsed_inchi = inchi
111
113 ''' retrieve sp3 stereo information
114 return a 4-item tuple containing
115 1) Number of stereocenters detected. If 0, the remaining items of the tuple = None
116 2) Number of undefined stereocenters. Must be smaller or equal to above
117 3) True if the molecule is a meso form (with chiral centers and a plane of symmetry)
118 4) Comma-separated list of internal atom numbers with sp3 stereochemistry
119 '''
120 sp3_stereo = {}
121
122 for con_layer in self.parsed_inchi:
123 for fixed_layer in self.parsed_inchi[con_layer]:
124 sp3_stereo[fixed_layer] = {}
125 for iso_layer in self.parsed_inchi[con_layer][fixed_layer]:
126 sp3_stereo[fixed_layer][iso_layer] = {}
127 stereo_match = stereo_re.match(self.parsed_inchi[con_layer][fixed_layer][iso_layer])
128 stereo_all_match = stereo_all_re.match(self.parsed_inchi[con_layer][fixed_layer][
129 iso_layer])
130 num_stereo = 0
131 num_undef_stereo = 0
132 is_meso = False
133 stereo = ''
134 stereo_centers = []
135 undef_stereo_centers = []
136
137 if stereo_match:
138 stereo = stereo_match.group(1)
139
140 elif stereo_all_match:
141 stereo = stereo_all_match.group(1)
142 is_meso = len(defined_stereo_re.findall(stereo)) > 1
143
144 stereo_centers = all_stereo_re.findall(stereo)
145 num_stereo = len(stereo_centers)
146 undef_stereo_centers = undef_stereo_re.findall(stereo)
147 num_undef_stereo = len(undef_stereo_centers)
148
149 inchi_layer = self.parsed_inchi[con_layer][fixed_layer][iso_layer]
150 is_meso = is_meso or (num_undef_stereo > 1 and _is_achiral_by_symmetry(inchi_layer))
151 sp3_stereo[fixed_layer][iso_layer] = (num_stereo, num_undef_stereo, is_meso, stereo)
152 return sp3_stereo
153
155 ''' retrieve mobile H (tautomer) information
156 return a 2-item tuple containing
157 1) Number of mobile hydrogen groups detected. If 0, next item = ''
158 2) List of groups
159 '''
160 mobile_h = {}
161 for con_layer in self.parsed_inchi:
162 for fixed_layer in self.parsed_inchi[con_layer]:
163 mobile_h[fixed_layer] = {}
164 for iso_layer in self.parsed_inchi[con_layer][fixed_layer]:
165 num_groups = 0
166 mobile_h_groups = ''
167 h_layer_match = h_layer_re.match(self.parsed_inchi[con_layer][fixed_layer][iso_layer])
168 if h_layer_match:
169 mobile_h_matches = mobile_h_group_re.findall(h_layer_match.group(1))
170 num_groups = len(mobile_h_matches)
171 mobile_h_groups = ','.join(mobile_h_matches)
172 mobile_h[fixed_layer][iso_layer] = (num_groups, mobile_h_groups)
173 return mobile_h
174