Skip to content

Commit 46374fa

Browse files
committed
Speed up imports by using importlib instead of pkg_resources
Speed up imports by up to a second by replacing uses of `pkg_resources` with the new Python standard library module `importlib.resources` (or, for Python < 3.7, the backport `importlib_resources`). The old `pkg_resources` module is known to be slow because it does a lot of work on startup. See, for example, [pypa/setuptools#926](pypa/setuptools#926) and [pypa/setuptools#510](pypa/setuptools#510).
1 parent ffc0c12 commit 46374fa

32 files changed

+124
-115
lines changed

gwcelery/data/__init__.py

Whitespace-only changes.

gwcelery/data/first2years/__init__.py

Whitespace-only changes.

gwcelery/tasks/em_bright.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
from ..import app
1111
from . import gracedb, lvalert
1212
from .p_astro import _format_prob
13-
from ..util import NamedTemporaryFile, PromiseProxy, resource_pickle
13+
from ..util import NamedTemporaryFile, PromiseProxy, read_pickle
1414

1515
NS_CLASSIFIER = PromiseProxy(
16-
resource_pickle, ('ligo.data', 'knn_ns_classifier.pkl'))
16+
read_pickle, ('ligo.data', 'knn_ns_classifier.pkl'))
1717
EM_CLASSIFIER = PromiseProxy(
18-
resource_pickle, ('ligo.data', 'knn_em_classifier.pkl'))
18+
read_pickle, ('ligo.data', 'knn_em_classifier.pkl'))
1919

2020
log = get_task_logger(__name__)
2121

gwcelery/tasks/first2years.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Create mock events from the "First Two Years" paper."""
2+
from importlib import resources
23
import io
34
import random
45

@@ -9,8 +10,8 @@
910
import lal
1011
from ligo.skymap.io.events.ligolw import ContentHandler
1112
import numpy as np
12-
import pkg_resources
1313

14+
from ..data import first2years as data_first2years
1415
from ..import app
1516
from . import gracedb
1617

@@ -19,9 +20,8 @@
1920

2021
def pick_coinc():
2122
"""Pick a coincidence from the "First Two Years" paper."""
22-
filename = pkg_resources.resource_filename(
23-
__name__, '../data/first2years/2016/gstlal.xml.gz')
24-
xmldoc = utils.load_filename(filename, contenthandler=ContentHandler)
23+
with resources.open_binary(data_first2years, 'gstlal.xml.gz') as f:
24+
xmldoc, _ = utils.load_fileobj(f, contenthandler=ContentHandler)
2525
root, = xmldoc.childNodes
2626

2727
# Remove unneeded tables
@@ -139,8 +139,7 @@ def _vet_event(superevents):
139139

140140
@gracedb.task(ignore_result=True, shared=False)
141141
def _upload_psd(graceid):
142-
psd = pkg_resources.resource_string(
143-
__name__, '../data/first2years/2016/psd.xml.gz')
142+
psd = resources.read_binary(data_first2years, 'psd.xml.gz')
144143
gracedb.upload(psd, 'psd.xml.gz', graceid, 'Noise PSD', ['psd'])
145144

146145

gwcelery/tasks/p_astro.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,16 @@
1212

1313
from . import gracedb, lvalert
1414
from .. import app
15-
from ..util import PromiseProxy, resource_json
15+
from ..util import PromiseProxy, read_json
1616

1717
MEAN_VALUES_DICT = PromiseProxy(
18-
resource_json, ('ligo.data',
19-
'H1L1V1-mean_counts-1126051217-61603201.json'))
18+
read_json, ('ligo.data', 'H1L1V1-mean_counts-1126051217-61603201.json'))
2019

2120
THRESHOLDS_DICT = PromiseProxy(
22-
resource_json, ('ligo.data',
23-
'H1L1V1-pipeline-far_snr-thresholds.json'))
21+
read_json, ('ligo.data', 'H1L1V1-pipeline-far_snr-thresholds.json'))
2422

2523
P_ASTRO_LIVETIME = PromiseProxy(
26-
resource_json, ('ligo.data', 'p_astro_livetime.json'))
24+
read_json, ('ligo.data', 'p_astro_livetime.json'))
2725

2826

2927
log = get_task_logger(__name__)

gwcelery/templates/index.jinja2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@
294294
</div>
295295
<div class=card-body>
296296
<ul>
297-
{% for package in packages %}
298-
<li><a href="https://pypi.org/project/{{package.project_name}}/">{{package.project_name}}</a> {{package.version}}</li>
297+
{% for distribution in distributions %}
298+
<li><a href="https://pypi.org/project/{{distribution.metadata['Name']}}/">{{distribution.metadata['Name']}}</a> {{distribution.version}}</li>
299299
{% endfor %}
300300
</ul>
301301
</div>

gwcelery/tests/data/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/fail/L1/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/fail/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/omegascan/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/pass/H1/__init__.py

Whitespace-only changes.

gwcelery/tests/data/llhoft/pass/__init__.py

Whitespace-only changes.

gwcelery/tests/test_tasks_bayestar.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1+
from importlib import resources
12
from unittest.mock import patch
23
from xml.sax import SAXParseException
34

45
from astropy import table
56
from astropy.io import fits
67
from celery.exceptions import Ignore
78
import numpy as np
8-
import pkg_resources
99
import pytest
1010

11+
from . import data
1112
from ..tasks.bayestar import localize
1213
from ..util.tempfile import NamedTemporaryFile
1314

1415

1516
def test_localize_bad_psd():
1617
"""Test running BAYESTAR with a pad PSD file"""
1718
# Test data
18-
coinc = pkg_resources.resource_string(__name__, 'data/coinc.xml')
19+
coinc = resources.read_binary(data, 'coinc.xml')
1920
psd = b''
2021

2122
# Run function under test
@@ -37,8 +38,8 @@ def mock_bayestar(event, *args, **kwargs):
3738

3839
@pytest.fixture
3940
def coinc_psd():
40-
return (pkg_resources.resource_string(__name__, 'data/coinc.xml'),
41-
pkg_resources.resource_string(__name__, 'data/psd.xml.gz'))
41+
return (resources.read_binary(data, 'coinc.xml'),
42+
resources.read_binary(data, 'psd.xml.gz'))
4243

4344

4445
@patch('ligo.skymap.bayestar.localize', mock_bayestar)

gwcelery/tests/test_tasks_detchar.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from importlib import resources
12
from io import BytesIO
23
import logging
34
from unittest.mock import call, patch
@@ -6,29 +7,29 @@
67
from gwpy.timeseries import Bits
78
import matplotlib.pyplot as plt
89
import numpy as np
9-
from pkg_resources import resource_filename
1010
import pytest
1111

1212
from ..import app
1313
from ..import _version
1414
from ..tasks import detchar
15+
from . import data
1516

1617

1718
@pytest.fixture
1819
def llhoft_glob_pass():
1920
old = app.conf['llhoft_glob']
20-
app.conf['llhoft_glob'] = resource_filename(
21-
__name__, 'data/llhoft/pass/{detector}/*.gwf')
22-
yield
21+
with resources.path(data, '') as path:
22+
app.conf['llhoft_glob'] = str(path / 'llhoft/pass/{detector}/*.gwf')
23+
yield
2324
app.conf['llhoft_glob'] = old
2425

2526

2627
@pytest.fixture
2728
def llhoft_glob_fail():
2829
old = app.conf['llhoft_glob']
29-
app.conf['llhoft_glob'] = resource_filename(
30-
__name__, 'data/llhoft/fail/{detector}/*.gwf')
31-
yield
30+
with resources.path(data, '') as path:
31+
app.conf['llhoft_glob'] = str(path / 'llhoft/fail/{detector}/*.gwf')
32+
yield
3233
app.conf['llhoft_glob'] = old
3334

3435

@@ -86,8 +87,11 @@ def test_create_cache_old_data(mock_find, llhoft_glob_fail):
8687
mock_find.assert_called()
8788

8889

89-
@patch('gwcelery.tasks.detchar.create_cache', return_value=[resource_filename(
90-
__name__, 'data/llhoft/omegascan/scanme.gwf')])
90+
with resources.path(data, '') as expected_path:
91+
expected_path = str(expected_path / 'llhoft/omegascan/scanme.gwf')
92+
93+
94+
@patch('gwcelery.tasks.detchar.create_cache', return_value=[expected_path])
9195
def test_make_omegascan_worked(mock_create_cache, scan_strainname):
9296
durs = [1, 1, 1]
9397
t0 = 1126259463

gwcelery/tests/test_tasks_external_skymaps.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
from importlib import resources
12
from unittest.mock import patch
23

3-
import pkg_resources
44
import pytest
55

6-
from ..util import resource_json
6+
from . import data
7+
from ..util import read_json
78
from .test_tasks_skymaps import toy_fits_filecontents # noqa: F401
89
from .test_tasks_skymaps import toy_3d_fits_filecontents # noqa: F401
910
from ..tasks import external_skymaps
@@ -19,14 +20,14 @@ def mock_get_event(exttrig):
1920

2021

2122
def mock_get_superevent(graceid):
22-
return resource_json(__name__, 'data/mock_superevent_object.json')
23+
return read_json(data, 'mock_superevent_object.json')
2324

2425

2526
def mock_get_log(graceid):
2627
if graceid == 'S12345':
27-
return resource_json(__name__, 'data/gracedb_setrigger_log.json')
28+
return read_json(data, 'gracedb_setrigger_log.json')
2829
elif graceid == 'E12345':
29-
return resource_json(__name__, 'data/gracedb_externaltrigger_log.json')
30+
return read_json(data, 'gracedb_externaltrigger_log.json')
3031
else:
3132
raise ValueError
3233

@@ -41,9 +42,8 @@ def download(filename, graceid):
4142
elif (graceid == 'E12345' and
4243
filename == ('nasa.gsfc.gcn_Fermi%23GBM_Gnd_Pos_2017-08-17'
4344
+ 'T12%3A41%3A06.47_524666471_57-431.xml')):
44-
return pkg_resources.resource_string(
45-
__name__, 'data/externaltrigger_original_data.xml'
46-
)
45+
return resources.read_binary(
46+
data, 'externaltrigger_original_data.xml')
4747
else:
4848
raise ValueError
4949

gwcelery/tests/test_tasks_external_triggers.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1+
from importlib.resources import read_binary
12
from unittest.mock import patch, call
23

34
import pytest
45

5-
from pkg_resources import resource_string
6-
6+
from . import data
77
from ..tasks import external_triggers
88
from ..tasks import detchar
9-
from ..util import resource_json
9+
from ..util import read_json
1010

1111

1212
@pytest.mark.parametrize('pipeline, path',
13-
[['Fermi', 'data/fermi_grb_gcn.xml'],
14-
['INTEGRAL', 'data/integral_grb_gcn.xml'],
15-
['AGILE', 'data/agile_grb_gcn.xml']])
13+
[['Fermi', 'fermi_grb_gcn.xml'],
14+
['INTEGRAL', 'integral_grb_gcn.xml'],
15+
['AGILE', 'agile_grb_gcn.xml']])
1616
@patch('gwcelery.tasks.external_skymaps.create_upload_external_skymap')
1717
@patch('gwcelery.tasks.external_skymaps.get_upload_external_skymap.run')
1818
@patch('gwcelery.tasks.detchar.dqr_json', return_value='dqrjson')
@@ -29,7 +29,7 @@ def test_handle_create_grb_event(mock_create_event, mock_get_event,
2929
mock_get_upload_external_skymap,
3030
mock_create_upload_external_skymap,
3131
pipeline, path):
32-
text = resource_string(__name__, path)
32+
text = read_binary(data, path)
3333
external_triggers.handle_grb_gcn(payload=text)
3434
mock_create_event.assert_called_once_with(filecontents=text,
3535
search='GRB',
@@ -98,11 +98,10 @@ def test_handle_create_subthreshold_grb_event(mock_get_upload_ext_skymap,
9898
mock_create_event,
9999
mock_get_event,
100100
mock_get_events):
101-
text = resource_string(__name__,
102-
'data/fermi_subthresh_grb_lowconfidence.xml')
101+
text = read_binary(data, 'fermi_subthresh_grb_lowconfidence.xml')
103102
external_triggers.handle_grb_gcn(payload=text)
104103
mock_create_event.assert_not_called()
105-
text = resource_string(__name__, 'data/fermi_subthresh_grb_gcn.xml')
104+
text = read_binary(data, 'fermi_subthresh_grb_gcn.xml')
106105
external_triggers.handle_grb_gcn(payload=text)
107106
mock_get_events.assert_called_once_with(query=(
108107
'group: External pipeline: '
@@ -136,7 +135,7 @@ def test_handle_noise_fermi_event(mock_check_vectors,
136135
mock_get_event,
137136
mock_get_events,
138137
mock_get_upload_external_skymap):
139-
text = resource_string(__name__, 'data/fermi_noise_gcn.xml')
138+
text = read_binary(data, 'fermi_noise_gcn.xml')
140139
external_triggers.handle_grb_gcn(payload=text)
141140
mock_get_events.assert_called_once_with(query=(
142141
'group: External pipeline: '
@@ -153,8 +152,8 @@ def test_handle_noise_fermi_event(mock_check_vectors,
153152

154153

155154
@pytest.mark.parametrize('filename',
156-
['data/fermi_grb_gcn.xml',
157-
'data/fermi_noise_gcn.xml'])
155+
['fermi_grb_gcn.xml',
156+
'fermi_noise_gcn.xml'])
158157
@patch('gwcelery.tasks.external_skymaps.get_upload_external_skymap.run')
159158
@patch('gwcelery.tasks.gracedb.create_label')
160159
@patch('gwcelery.tasks.gracedb.remove_label')
@@ -175,7 +174,7 @@ def test_handle_replace_grb_event(mock_get_event, mock_get_events,
175174
mock_replace_event, mock_remove_label,
176175
mock_create_label,
177176
mock_get_upload_external_skymap, filename):
178-
text = resource_string(__name__, filename)
177+
text = read_binary(data, filename)
179178
external_triggers.handle_grb_gcn(payload=text)
180179
mock_replace_event.assert_called_once_with('E1', text)
181180
if 'grb' in filename:
@@ -278,7 +277,7 @@ def test_handle_skymap_combine(mock_create_combined_skymap):
278277
@patch('gwcelery.tasks.gracedb.create_event')
279278
def test_handle_create_snews_event(mock_create_event, mock_get_event,
280279
mock_upload, mock_json):
281-
text = resource_string(__name__, 'data/snews_gcn.xml')
280+
text = read_binary(data, 'snews_gcn.xml')
282281
external_triggers.handle_snews_gcn(payload=text)
283282
mock_create_event.assert_called_once_with(filecontents=text,
284283
search='Supernova',
@@ -310,7 +309,7 @@ def test_handle_create_snews_event(mock_create_event, mock_get_event,
310309
@patch('gwcelery.tasks.gracedb.replace_event')
311310
@patch('gwcelery.tasks.gracedb.get_events', return_value=[{'graceid': 'E1'}])
312311
def test_handle_replace_snews_event(mock_get_events, mock_replace_event):
313-
text = resource_string(__name__, 'data/snews_gcn.xml')
312+
text = read_binary(data, 'snews_gcn.xml')
314313
external_triggers.handle_snews_gcn(payload=text)
315314
mock_replace_event.assert_called_once_with('E1', text)
316315

@@ -319,7 +318,7 @@ def test_handle_replace_snews_event(mock_get_events, mock_replace_event):
319318
def test_handle_grb_exttrig_creation(mock_raven_coincidence_search):
320319
"""Test dispatch of an LVAlert message for an exttrig creation."""
321320
# Test LVAlert payload.
322-
alert = resource_json(__name__, 'data/lvalert_exttrig_creation.json')
321+
alert = read_json(data, 'lvalert_exttrig_creation.json')
323322

324323
# Run function under test
325324
external_triggers.handle_grb_lvalert(alert)
@@ -334,7 +333,7 @@ def test_handle_grb_exttrig_creation(mock_raven_coincidence_search):
334333
def test_handle_subgrb_exttrig_creation(mock_raven_coincidence_search):
335334
"""Test dispatch of an LVAlert message for an exttrig creation."""
336335
# Test LVAlert payload.
337-
alert = resource_json(__name__, 'data/lvalert_subgrb_creation.json')
336+
alert = read_json(data, 'lvalert_subgrb_creation.json')
338337

339338
# Run function under test
340339
external_triggers.handle_grb_lvalert(alert)
@@ -352,8 +351,7 @@ def test_handle_subgrb_targeted_creation(mock_raven_coincidence_search,
352351
mock_create_upload_external_skymap):
353352
"""Test dispatch of an LVAlert message for an exttrig creation."""
354353
# Test LVAlert payload.
355-
alert = resource_json(__name__,
356-
'data/lvalert_exttrig_subgrb_targeted_creation.json')
354+
alert = read_json(data, 'lvalert_exttrig_subgrb_targeted_creation.json')
357355

358356
# Run function under test
359357
external_triggers.handle_grb_lvalert(alert)
@@ -371,13 +369,13 @@ def test_handle_subgrb_targeted_creation(mock_raven_coincidence_search,
371369

372370

373371
@pytest.mark.parametrize('calls, path',
374-
[[False, 'data/lvalert_snews_test_creation.json'],
375-
[True, 'data/lvalert_snews_creation.json']])
372+
[[False, 'lvalert_snews_test_creation.json'],
373+
[True, 'lvalert_snews_creation.json']])
376374
@patch('gwcelery.tasks.raven.coincidence_search')
377375
def test_handle_sntrig_creation(mock_raven_coincidence_search, calls, path):
378376
"""Test dispatch of an LVAlert message for SNEWS alerts."""
379377
# Test LVAlert payload.
380-
alert = resource_json(__name__, path)
378+
alert = read_json(data, path)
381379

382380
# Run function under test
383381
external_triggers.handle_snews_lvalert(alert)
@@ -399,7 +397,7 @@ def test_handle_superevent_cbc_creation(mock_raven_coincidence_search,
399397
mock_get_superevent):
400398
"""Test dispatch of an LVAlert message for a CBC superevent creation."""
401399
# Test LVAlert payload.
402-
alert = resource_json(__name__, 'data/lvalert_superevent_creation.json')
400+
alert = read_json(data, 'lvalert_superevent_creation.json')
403401

404402
# Run function under test
405403
external_triggers.handle_grb_lvalert(alert)
@@ -423,7 +421,7 @@ def test_handle_superevent_burst_creation(mock_raven_coincidence_search,
423421
mock_get_superevent):
424422
"""Test dispatch of an LVAlert message for a burst superevent creation."""
425423
# Test LVAlert payload.
426-
alert = resource_json(__name__, 'data/lvalert_superevent_creation.json')
424+
alert = read_json(data, 'lvalert_superevent_creation.json')
427425

428426
# Run function under test
429427
external_triggers.handle_grb_lvalert(alert)

0 commit comments

Comments
 (0)