Skip to content

Commit 33a49b2

Browse files
committed
More fixes and adptations for pubchem changes
1 parent 3218273 commit 33a49b2

2 files changed

Lines changed: 50 additions & 28 deletions

File tree

src/openfermion/chem/pubchem.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
12+
"""Function to extract geometry from the PubChem database."""
13+
14+
import time
15+
import urllib.error
1216

1317

1418
def geometry_from_pubchem(name: str, structure: str = None):
@@ -32,15 +36,26 @@ def geometry_from_pubchem(name: str, structure: str = None):
3236
"""
3337
import pubchempy
3438

39+
def get_compounds_with_retries(name, record_type):
40+
max_retries = 3
41+
delay = 2
42+
for attempt in range(max_retries):
43+
try:
44+
return pubchempy.get_compounds(name, 'name', record_type=record_type)
45+
except (pubchempy.PubChemHTTPError, urllib.error.URLError):
46+
if attempt == max_retries - 1:
47+
raise
48+
time.sleep(delay)
49+
3550
if structure in ['2d', '3d']:
36-
pubchempy_molecule = pubchempy.get_compounds(name, 'name', record_type=structure)
51+
pubchempy_molecule = get_compounds_with_retries(name, record_type=structure)
3752
elif structure is None:
3853
# Ideally get the 3-D geometry if available.
39-
pubchempy_molecule = pubchempy.get_compounds(name, 'name', record_type='3d')
54+
pubchempy_molecule = get_compounds_with_retries(name, record_type='3d')
4055

4156
# If the 3-D geometry isn't available, get the 2-D geometry instead.
4257
if not pubchempy_molecule:
43-
pubchempy_molecule = pubchempy.get_compounds(name, 'name', record_type='2d')
58+
pubchempy_molecule = get_compounds_with_retries(name, record_type='2d')
4459
else:
4560
raise ValueError('Incorrect value for the argument structure=%s' % structure)
4661

@@ -52,9 +67,7 @@ def geometry_from_pubchem(name: str, structure: str = None):
5267
)
5368
return None
5469

55-
pubchempy_geometry = pubchempy_molecule[0].to_dict(properties=['atoms'])['atoms']
56-
geometry = [
57-
(atom['element'], (atom['x'], atom['y'], atom.get('z', 0))) for atom in pubchempy_geometry
58-
]
70+
pubchempy_geometry = pubchempy_molecule[0].atoms
71+
geometry = [(atom.element, (atom.x, atom.y, atom.z or 0)) for atom in pubchempy_geometry]
5972

6073
return geometry

src/openfermion/chem/pubchem_test.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@
2222
from openfermion.testing.testing_utils import module_importable
2323

2424

25+
class MockAtom:
26+
def __init__(self, element, x, y, z=0):
27+
self.element = element
28+
self.x = x
29+
self.y = y
30+
self.z = z
31+
32+
2533
class MockCompound:
2634
def __init__(self, atoms):
27-
self._atoms = atoms
28-
29-
def to_dict(self, properties):
30-
return {'atoms': self._atoms}
35+
self.atoms = [MockAtom(a['element'], a['x'], a['y'], a.get('z', 0)) for a in atoms]
3136

3237

3338
def mock_get_compounds(name, searchtype, record_type='2d'):
@@ -70,20 +75,6 @@ def mock_get_compounds(name, searchtype, record_type='2d'):
7075

7176
@using_pubchempy
7277
class OpenFermionPubChemTest(unittest.TestCase):
73-
def _get_geometry_with_retries(self, name):
74-
import pubchempy
75-
import urllib.error
76-
77-
max_retries = 3
78-
delay = 2
79-
for attempt in range(max_retries):
80-
try:
81-
return geometry_from_pubchem(name)
82-
except (pubchempy.PubChemHTTPError, urllib.error.URLError):
83-
if attempt == max_retries - 1:
84-
raise
85-
time.sleep(delay)
86-
8778
@patch('pubchempy.get_compounds', mock_get_compounds)
8879
def test_water(self):
8980
water_geometry = geometry_from_pubchem('water')
@@ -161,7 +152,7 @@ def test_geometry_from_pubchem_live_api(self):
161152
except ImportError: # pragma: no cover
162153
return
163154

164-
water_geometry = self._get_geometry_with_retries('water')
155+
water_geometry = geometry_from_pubchem('water')
165156
self.assertEqual(len(water_geometry), 3)
166157

167158
def _get_mock_pubchem_error(self):
@@ -181,6 +172,24 @@ def read(self):
181172

182173
return pubchempy.PubChemHTTPError(FakeHTTPError(503, 'Server Busy'))
183174

175+
176+
def _get_mock_pubchem_error(self):
177+
import pubchempy
178+
179+
try:
180+
return pubchempy.PubChemHTTPError(503, 'Server Busy', ['busy'])
181+
except TypeError:
182+
183+
class FakeHTTPError(Exception):
184+
def __init__(self, code, msg):
185+
self.code = code
186+
self.reason = msg
187+
188+
def read(self):
189+
return b'{"Fault": {"Details": ["busy"]}}'
190+
191+
return pubchempy.PubChemHTTPError(FakeHTTPError(503, 'Server Busy'))
192+
184193
@patch('time.sleep', return_value=None)
185194
@patch('pubchempy.get_compounds')
186195
def test_geometry_from_pubchem_retry_success(self, mock_get_compounds, mock_sleep):
@@ -218,7 +227,7 @@ def test_geometry_from_pubchem_retry_success(self, mock_get_compounds, mock_slee
218227
],
219228
]
220229

221-
water_geometry = self._get_geometry_with_retries('water')
230+
water_geometry = geometry_from_pubchem('water')
222231

223232
self.assertEqual(len(water_geometry), 3)
224233
self.assertEqual(mock_get_compounds.call_count, 3)
@@ -235,7 +244,7 @@ def test_geometry_from_pubchem_retry_failure(self, mock_get_compounds, mock_slee
235244
mock_get_compounds.side_effect = self._get_mock_pubchem_error()
236245

237246
with self.assertRaises(pubchempy.PubChemHTTPError):
238-
self._get_geometry_with_retries('water')
247+
geometry_from_pubchem('water')
239248

240249
self.assertEqual(mock_get_compounds.call_count, 3)
241250
self.assertEqual(mock_sleep.call_count, 2)

0 commit comments

Comments
 (0)