Skip to content

Commit e6ec67d

Browse files
feat: Drop Python 2.7 support (#704)
* Drop Python 2.7 support for pyhf * Add no Python 2.7 support to FAQ and link to last supported release
1 parent 16fc8e7 commit e6ec67d

File tree

12 files changed

+44
-51
lines changed

12 files changed

+44
-51
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
os: [ubuntu-latest, macos-latest]
17-
python-version: [2.7, 3.6, 3.7]
17+
python-version: [3.6, 3.7]
1818

1919
steps:
2020
- uses: actions/checkout@v1.2.0
@@ -51,7 +51,6 @@ jobs:
5151
file: ./coverage.xml
5252
flags: unittests
5353
- name: Test Contrib module with pytest
54-
if: matrix.python-version != 2.7
5554
run: |
5655
python -m pytest -r sx tests/contrib --mpl --mpl-baseline-path tests/contrib/baseline
5756
- name: Run benchmarks

docs/faq.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ Use the :code:`--backend` option for :code:`pyhf cls` to specify a tensor backen
1414
The default backend is NumPy.
1515
For more information see :code:`pyhf cls --help`.
1616

17+
Does ``pyhf`` support Python 2?
18+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19+
No.
20+
Like the rest of the Python community, as of January 2020 the latest releases of ``pyhf`` no longer support Python 2.
21+
The last release of ``pyhf`` that was compatible with Python 2.7 is `v0.3.4 <https://pypi.org/project/pyhf/0.3.4/>`__, which can be installed with
22+
23+
.. code-block:: console
24+
25+
python -m pip install pyhf~=0.3
26+
27+
I only have access to Python 2. How can I use ``pyhf``?
28+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29+
30+
It is recommended that ``pyhf`` is used as a standalone step in any analysis, and its environment need not be the same as the rest of the analysis.
31+
As Python 2 is not supported it is suggested that you setup a Python 3 runtime on whatever machine you're using.
32+
If you're using a cluster, talk with your system administrators to get their help in doing so.
33+
If you are unable to get a Python 3 runtime, versioned Docker images of ``pyhf`` are distributed through `Docker Hub <https://hub.docker.com/r/pyhf/pyhf>`__.
34+
35+
Once you have Python 3 installed, see the :ref:`installation` page to get started.
36+
1737
Troubleshooting
1838
---------------
1939

docs/governance/ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ The roadmap will be executed over mostly Quarter 3 of 2019 through Quarter 1 of
6666
- [ ] Add background model support (Issue #514) [2019-Q4 → 2020-Q1]
6767
- [ ] Develop interface for the optimizers similar to tensor/backend [2019-Q4 → 2020-Q1]
6868
- [x] Migrate to TensorFlow v2.0 (PR #541) [2019-Q4]
69-
- [ ] Drop Python 2.7 support at end of 2019 (Issue #469) [2019-Q4 (last week of December 2019)]
69+
- [x] Drop Python 2.7 support at end of 2019 (Issue #469) [2019-Q4 (last week of December 2019)]
7070
- [ ] Finalize public API [2020-Q1]
7171
- [ ] Integrate pyfitcore/Statisfactory API [2020-Q1]
7272
4. **Research**

docs/installation.rst

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _installation:
2+
13
Installation
24
============
35

@@ -8,12 +10,6 @@ To install, we suggest first setting up a `virtual environment <https://packagin
810
# Python3
911
python3 -m venv pyhf
1012
11-
12-
.. code-block:: console
13-
14-
# Python2
15-
virtualenv --python=$(which python) pyhf
16-
1713
and activating it
1814

1915
.. code-block:: console

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ build-backend = "setuptools.build_meta"
55

66
[tool.black]
77
line-length = 88
8-
py36 = false # Python 2 is still supported
8+
target-version = ['py36', 'py37', 'py38']
99
skip-string-normalization = true
10-
skip-numeric-underscore-normalization = true # Python 2 is still supported
1110
include = '\.pyi?$'
1211
exclude = '''
1312
/(

setup.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
from setuptools import setup, find_packages
22
from os import path
3-
import sys
43

54
this_directory = path.abspath(path.dirname(__file__))
6-
if sys.version_info.major < 3:
7-
from io import open
85
with open(path.join(this_directory, 'README.md'), encoding='utf-8') as readme_md:
96
long_description = readme_md.read()
107

@@ -88,16 +85,14 @@
8885
license='Apache',
8986
keywords='physics fitting numpy scipy tensorflow pytorch',
9087
classifiers=[
91-
"Programming Language :: Python :: 2",
92-
"Programming Language :: Python :: 2.7",
9388
"Programming Language :: Python :: 3",
9489
"Programming Language :: Python :: 3.6",
9590
"Programming Language :: Python :: 3.7",
9691
],
9792
package_dir={'': 'src'},
9893
packages=find_packages(where='src'),
9994
include_package_data=True,
100-
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
95+
python_requires=">=3.6",
10196
install_requires=[
10297
'scipy', # requires numpy, which is required by pyhf and tensorflow
10398
'click>=6.0', # for console scripts,

src/pyhf/cli/rootio.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
logging.basicConfig()
99
log = logging.getLogger(__name__)
1010

11-
# This is only needed for Python 2/3 compatibility
12-
def ensure_dirs(path):
13-
try:
14-
os.makedirs(path, exist_ok=True) # lgtm [py/call/wrong-named-argument]
15-
except TypeError:
16-
if not os.path.exists(path):
17-
os.makedirs(path)
18-
1911

2012
@click.group(name='rootio')
2113
def cli():
@@ -77,14 +69,14 @@ def json2xml(workspace, output_dir, specroot, dataroot, resultprefix, patch):
7769
)
7870
from .. import writexml
7971

80-
ensure_dirs(output_dir)
72+
os.makedirs(output_dir, exist_ok=True)
8173
with click.open_file(workspace, 'r') as specstream:
8274
spec = json.load(specstream)
8375
for pfile in patch:
8476
patch = json.loads(click.open_file(pfile, 'r').read())
8577
spec = jsonpatch.JsonPatch(patch).apply(spec)
86-
ensure_dirs(os.path.join(output_dir, specroot))
87-
ensure_dirs(os.path.join(output_dir, dataroot))
78+
os.makedirs(os.path.join(output_dir, specroot), exist_ok=True)
79+
os.makedirs(os.path.join(output_dir, dataroot), exist_ok=True)
8880
with click.open_file(
8981
os.path.join(output_dir, '{0:s}.xml'.format(resultprefix)), 'w'
9082
) as outstream:

src/pyhf/infer/mle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ def fixed_poi_fit(poi_val, data, pdf, init_pars=None, par_bounds=None, **kwargs)
5959
init_pars,
6060
par_bounds,
6161
[(pdf.config.poi_index, poi_val)],
62-
**kwargs
62+
**kwargs,
6363
)

src/pyhf/optimize/opt_minuit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def f(pars):
5252
errordef=1,
5353
use_array_call=True,
5454
forced_parameters=parnames,
55-
**kwargs
55+
**kwargs,
5656
)
5757
return mm
5858

src/pyhf/pdf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def __init__(self, config, mega_mods, nominal_rates, batch_size):
287287
config,
288288
mega_mods,
289289
batch_size=self.batch_size,
290-
**config.modifier_settings.get(k, {})
290+
**config.modifier_settings.get(k, {}),
291291
)
292292
for k, c in modifiers.combined.items()
293293
}

tests/test_notebooks.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ def test_xml_importexport(common_kwargs):
2525

2626

2727
def test_statisticalanalysis(common_kwargs):
28-
if sys.version_info.major > 2:
29-
# The Binder example uses specific relative paths
30-
cwd = os.getcwd()
31-
os.chdir(os.path.join(cwd, 'docs/examples/notebooks/binderexample'))
32-
pm.execute_notebook('StatisticalAnalysis.ipynb', **common_kwargs)
33-
os.chdir(cwd)
28+
# The Binder example uses specific relative paths
29+
cwd = os.getcwd()
30+
os.chdir(os.path.join(cwd, 'docs/examples/notebooks/binderexample'))
31+
pm.execute_notebook('StatisticalAnalysis.ipynb', **common_kwargs)
32+
os.chdir(cwd)
3433

3534

3635
def test_shapefactor(common_kwargs):
@@ -41,15 +40,15 @@ def test_multichannel_coupled_histos(common_kwargs):
4140
pm.execute_notebook(
4241
'docs/examples/notebooks/multichannel-coupled-histo.ipynb',
4342
parameters={'validation_datadir': 'validation/data'},
44-
**common_kwargs
43+
**common_kwargs,
4544
)
4645

4746

4847
def test_multibinpois(common_kwargs):
4948
pm.execute_notebook(
5049
'docs/examples/notebooks/multiBinPois.ipynb',
5150
parameters={'validation_datadir': 'validation/data'},
52-
**common_kwargs
51+
**common_kwargs,
5352
)
5453
nb = sb.read_notebook(common_kwargs['output_path'])
5554
assert nb.scraps['number_2d_successpoints'].data > 200

tests/test_scripts.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,11 @@ def test_import_and_export(tmpdir, script_runner):
136136
def test_patch(tmpdir, script_runner):
137137
patch = tmpdir.join('patch.json')
138138

139-
patchcontent = u'''
139+
patch.write(
140+
u'''
140141
[{"op": "replace", "path": "/channels/0/samples/0/data", "value": [5,6]}]
141142
'''
142-
patch.write(patchcontent)
143+
)
143144

144145
temp = tmpdir.join("parsed_output.json")
145146
command = 'pyhf xml2json validation/xmlimport_input/config/example.xml --basedir validation/xmlimport_input/ --output-file {0:s}'.format(
@@ -157,23 +158,15 @@ def test_patch(tmpdir, script_runner):
157158
ret = script_runner.run(*shlex.split(command))
158159
assert ret.success
159160

160-
import io
161-
162161
command = 'pyhf cls {0:s} --patch -'.format(temp.strpath, patch.strpath)
163162

164-
pipefile = io.StringIO(
165-
patchcontent
166-
) # python 2.7 pytest-files are not file-like enough
167-
ret = script_runner.run(*shlex.split(command), stdin=pipefile)
163+
ret = script_runner.run(*shlex.split(command), stdin=patch)
168164
assert ret.success
169165

170166
command = 'pyhf json2xml {0:s} --output-dir {1:s} --patch -'.format(
171167
temp.strpath, tmpdir.mkdir('output_2').strpath, patch.strpath
172168
)
173-
pipefile = io.StringIO(
174-
patchcontent
175-
) # python 2.7 pytest-files are not file-like enough
176-
ret = script_runner.run(*shlex.split(command), stdin=pipefile)
169+
ret = script_runner.run(*shlex.split(command), stdin=patch)
177170
assert ret.success
178171

179172

0 commit comments

Comments
 (0)