From 889dc0079b15a64965cd31035c65c5be45f10e08 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Tue, 19 Nov 2019 12:03:10 +1300 Subject: [PATCH 1/2] refactor(setup/ci): require and test Python 3.5, 3.6, 3.7 and 3.8 * Drop Python 2.7 from test matrix; add Python 3.8 * At this time, current fiona release does not compile with Python 3.8, but it will be resolved with the next fiona release * Remove Python 2.7 requirements file/logic for install * enum34 no longer required (only needed for Python<3.4) * Simplify setup.py, raise RuntimeError if Python version too old * Remove outdated comment (bdist_wininst is deprecated) * Fix t007_test.py for Python 3.8, which raises other warnings (currently from matplotlib, with a fix in the next release) --- .travis.yml | 12 +++++------- README.md | 2 +- autotest/t007_test.py | 6 ++++-- requirements.travis.txt | 4 ++-- requirements27.travis.txt | 13 ------------- setup.py | 21 ++++++--------------- travis/install.sh | 10 +++------- 7 files changed, 21 insertions(+), 47 deletions(-) delete mode 100644 requirements27.travis.txt mode change 100755 => 100644 setup.py diff --git a/.travis.yml b/.travis.yml index d000e14358..a8c905f27f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,21 +6,19 @@ services: - xvfb matrix: include: - - env: RUN_TYPE=test - python: 2.7 + - env: RUN_TYPE=misc + python: 3.7 - env: RUN_TYPE=test python: 3.5 - env: RUN_TYPE=test python: 3.6 - env: RUN_TYPE=test python: 3.7 - - env: RUN_TYPE=misc - python: 3.7 -# - env: RUN_TYPE=test -# python: 3.8-dev + - env: RUN_TYPE=test + python: 3.8 # allow_failures: # - env: RUN_TYPE=test -# python: 3.8-dev +# python: 3.8 cache: pip: true directories: diff --git a/README.md b/README.md index 1a69df8710..1e7a0c0e91 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ FloPy requires **Python** 3.5 (or higher). **Dependencies:** -FloPy requires **NumPy** 1.9 (or higher) and **enum34** for **Python** 3.5 (or higher). +FloPy requires **NumPy** 1.9 (or higher). **For base and Anaconda Python distributions:** diff --git a/autotest/t007_test.py b/autotest/t007_test.py index add3f9c00b..480f66dec4 100644 --- a/autotest/t007_test.py +++ b/autotest/t007_test.py @@ -926,8 +926,10 @@ def check_vertices(): model=mf, line={'line': verts}, xul=mf.dis.sr.xul, yul=mf.dis.sr.yul) - # for wn in w: - # print(str(wn)) + for wn in w: + print(str(wn)) + if len(w) > 5: + w = w[0:5] assert len(w) in (3, 5), len(w) if len(w) == 5: assert w[0].category == DeprecationWarning, w[0] diff --git a/requirements.travis.txt b/requirements.travis.txt index 1c09acc058..c99b310838 100644 --- a/requirements.travis.txt +++ b/requirements.travis.txt @@ -1,11 +1,11 @@ appdirs matplotlib netcdf4 -fiona +fiona ; python_version < '3.8' descartes pyproj pyshp pandas scipy rasterio -affine \ No newline at end of file +affine diff --git a/requirements27.travis.txt b/requirements27.travis.txt deleted file mode 100644 index 3ff9e51906..0000000000 --- a/requirements27.travis.txt +++ /dev/null @@ -1,13 +0,0 @@ -appdirs -matplotlib -netcdf4<1.2.8 -fiona -descartes -pyproj -pyshp -enum34 -pandas -requests -scipy -rasterio -affine diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 8abfb876a8..3cef1eaafa --- a/setup.py +++ b/setup.py @@ -1,27 +1,19 @@ import os import sys from setuptools import setup -# To use: -# python setup.py bdist --format=wininst from flopy import __version__, __name__, __author__ -# trap someone trying to install flopy with something other -# than python 2 or 3 -if not sys.version_info[0] in [2, 3]: - print('Sorry, Flopy not supported in your Python version') - print(' Supported versions: 2 and 3') - print(' Your version of Python: {}'.format(sys.version_info[0])) - sys.exit(1) # return non-zero value for failure - -long_description = '' +# ensure minimum version of Python is running +if sys.version_info[0:2] < (3, 5): + raise RuntimeError('Flopy requires Python >= 3.5') try: import pypandoc fpth = os.path.join('docs', 'PyPi_release.md') long_description = pypandoc.convert(fpth, 'rst') -except: - pass +except ImportError: + long_description = '' setup(name=__name__, description='FloPy is a Python package to create, run, and post-process MODFLOW-based models.', @@ -37,8 +29,7 @@ url='https://github.com/modflowpy/flopy/', license='CC0', platforms='Windows, Mac OS-X, Linux', - install_requires=['enum34;python_version<"3.4"', - 'numpy>=1.9'], + install_requires=['numpy>=1.9'], packages=['flopy', 'flopy.modflow', 'flopy.modflowlgr', 'flopy.modpath', 'flopy.mt3d', 'flopy.seawat', 'flopy.utils', 'flopy.plot', 'flopy.pest', 'flopy.export', 'flopy.discretization', diff --git a/travis/install.sh b/travis/install.sh index 8a49df110e..d70471eaf7 100755 --- a/travis/install.sh +++ b/travis/install.sh @@ -3,13 +3,9 @@ set -e echo "Installing pip for Python ${TRAVIS_PYTHON_VERSION} ${RUN_TYPE} run" pip install --upgrade pip -if [ "${TRAVIS_PYTHON_VERSION}" = "2.7" ]; then - pip install -r requirements27.travis.txt -else - pip install -r requirements.travis.txt - pip install rasterio - pip install --upgrade numpy -fi +pip install -r requirements.travis.txt +pip install rasterio +pip install --upgrade numpy if [ "${RUN_TYPE}" = "misc" ]; then pip install flake8 pylint pylint-exit pip install jupyter nbconvert From cbfc079dd2e4e655fa65871a1e013936e39b1261 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Wed, 20 Nov 2019 09:21:09 +1300 Subject: [PATCH 2/2] refactor(general): remove Python 2 logic to assume Python 3 only --- autotest/t007_test.py | 10 ++++------ autotest/t011_test.py | 4 ---- autotest/t017_test.py | 10 ++-------- autotest/t032_test.py | 2 -- autotest/t034_test.py | 5 +---- autotest/t041_test.py | 6 +----- autotest/t066_test_copy.py | 5 ----- flopy/mbase.py | 19 +++---------------- flopy/modflow/mf.py | 6 +----- flopy/modflow/mfgage.py | 5 +---- flopy/modflow/mfhyd.py | 23 +++++++---------------- flopy/modflow/mflak.py | 5 +---- flopy/modflow/mfuzf1.py | 2 -- flopy/pakbase.py | 9 +-------- flopy/plot/map.py | 11 ++++------- flopy/utils/flopy_io.py | 9 ++------- flopy/utils/mflistfile.py | 15 ++------------- flopy/utils/mfreadnam.py | 3 +-- 18 files changed, 31 insertions(+), 118 deletions(-) diff --git a/autotest/t007_test.py b/autotest/t007_test.py index 480f66dec4..a46e4c1742 100644 --- a/autotest/t007_test.py +++ b/autotest/t007_test.py @@ -1063,17 +1063,15 @@ def test_vertex_model_dot_plot(): exe_name="mf6", sim_ws=sim_path) disv_ml = disv_sim.get_model('gwf_1') - if sys.version_info[0] > 2: - ax = disv_ml.plot() - assert ax + ax = disv_ml.plot() + assert ax def test_model_dot_plot(): loadpth = os.path.join('..', 'examples', 'data', 'secp') ml = flopy.modflow.Modflow.load('secp.nam', model_ws=loadpth) - if sys.version_info[0] > 2: - ax = ml.plot() - assert ax + ax = ml.plot() + assert ax def test_get_rc_from_node_coordinates(): diff --git a/autotest/t011_test.py b/autotest/t011_test.py index 7e6851f7cb..fadc34fc6e 100644 --- a/autotest/t011_test.py +++ b/autotest/t011_test.py @@ -4,14 +4,10 @@ """ import os -import sys import flopy import numpy as np from nose.tools import raises -if sys.version_info[0] == 2: - FileNotFoundError = IOError - def test_mflistfile(): pth = os.path.join('..', 'examples', 'data', 'freyberg') diff --git a/autotest/t017_test.py b/autotest/t017_test.py index ebb030d00d..0095c03880 100644 --- a/autotest/t017_test.py +++ b/autotest/t017_test.py @@ -1,7 +1,6 @@ # Test binary and formatted data readers import os import shutil -import sys import numpy as np import flopy from nose.tools import assert_raises @@ -12,11 +11,6 @@ shutil.rmtree(cpth) os.makedirs(cpth) -if sys.version_info[0] == 2: - closed_file_error_msg = 'I/O operation on closed file' -else: - closed_file_error_msg = 'seek of closed file' - def test_formattedfile_read(): @@ -102,7 +96,7 @@ def test_binaryfile_read_context(): with assert_raises(ValueError) as e: h.get_data() - assert str(e.exception) == closed_file_error_msg, str(e.exception) + assert str(e.exception) == 'seek of closed file', str(e.exception) def test_cellbudgetfile_read_context(): @@ -116,7 +110,7 @@ def test_cellbudgetfile_read_context(): with assert_raises(ValueError) as e: v.get_data(text='DRAINS') - assert str(e.exception) == closed_file_error_msg, str(e.exception) + assert str(e.exception) == 'seek of closed file', str(e.exception) def test_cellbudgetfile_read(): diff --git a/autotest/t032_test.py b/autotest/t032_test.py index c1e2b16fb0..24e502b23b 100644 --- a/autotest/t032_test.py +++ b/autotest/t032_test.py @@ -86,8 +86,6 @@ def test_epsgreference(): if prjtxt is None: print("unable to retrieve CRS prj txt") return - if sys.version_info[0] == 2: - prjtxt = prjtxt.encode('ascii') assert isinstance(prjtxt, str),type(prjtxt) prj = ep.to_dict() assert 32614 in prj diff --git a/autotest/t034_test.py b/autotest/t034_test.py index d0e59bd77c..3a4738730a 100644 --- a/autotest/t034_test.py +++ b/autotest/t034_test.py @@ -219,10 +219,7 @@ def test_uzf_surfk(): def test_read_write_nwt_options(): - if sys.version_info[0] > 2: - from io import StringIO - else: - from cStringIO import StringIO + from io import StringIO from flopy.modflow import ModflowWel, ModflowUzf1, ModflowSfr2 from flopy.utils.optionblock import OptionBlock diff --git a/autotest/t041_test.py b/autotest/t041_test.py index 05e76ffbfe..f8fb7b574c 100644 --- a/autotest/t041_test.py +++ b/autotest/t041_test.py @@ -2,7 +2,6 @@ Test the observation process load and write """ import os -import sys import shutil import numpy as np import flopy @@ -276,10 +275,7 @@ def test_multilayerhob_prfail(): def test_multilayerhob_pr_multiline(): - if sys.version_info[0] > 2: - from io import StringIO - else: - from cStringIO import StringIO + from io import StringIO problem_hob = ["2 4 7", "1 1", diff --git a/autotest/t066_test_copy.py b/autotest/t066_test_copy.py index b747472cfc..0d981e5c06 100644 --- a/autotest/t066_test_copy.py +++ b/autotest/t066_test_copy.py @@ -1,6 +1,5 @@ """Test copying of flopy objects. """ -import sys import os import copy import inspect @@ -190,8 +189,6 @@ def list_is_copy(mflist1, mflist2): def test_mf2005_copy(): - if sys.version_info[0] < 3: - return path = '../examples/data/freyberg_multilayer_transient/freyberg.nam' model_ws, namefile = os.path.split(path) m = fm.Modflow.load(namefile, model_ws=model_ws) @@ -202,8 +199,6 @@ def test_mf2005_copy(): def test_mf6_copy(): - if sys.version_info[0] < 3: - return sim_ws = '../examples/data/mf6/test045_lake2tr' sim = mf6.MFSimulation.load('mfsim.nam', 'mf6', sim_ws=sim_ws) m = sim.get_model('lakeex2a') diff --git a/flopy/mbase.py b/flopy/mbase.py index 122b15a904..ea154fef7e 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -12,12 +12,10 @@ import shutil import threading import warnings +import queue as Queue -if sys.version_info > (3, 0): - import queue as Queue -else: - import Queue from datetime import datetime +from shutil import which from subprocess import Popen, PIPE, STDOUT import copy import numpy as np @@ -26,10 +24,6 @@ from .discretization.modeltime import ModelTime from .discretization.grid import Grid -if sys.version_info >= (3, 3): - from shutil import which -else: - from distutils.spawn import find_executable as which # Global variables iconst = 1 # Multiplier for individual array elements in integer and real arrays read by MODFLOW's U2DREL, U1DREL and U2DINT. @@ -1603,15 +1597,8 @@ def q_output(output, q): for t in cargs: argv.append(t) - if sys.version_info[0:2] == (2, 7) and sys.platform != 'win32': - # Python 2.7 workaround for non-Windows - close_fds = True - else: - close_fds = False # default - # run the model with Popen - proc = Popen(argv, stdout=PIPE, stderr=STDOUT, cwd=model_ws, - close_fds=close_fds) + proc = Popen(argv, stdout=PIPE, stderr=STDOUT, cwd=model_ws) if not use_async: while True: diff --git a/flopy/modflow/mf.py b/flopy/modflow/mf.py index 4a7eb243e8..77d7ccb73d 100644 --- a/flopy/modflow/mf.py +++ b/flopy/modflow/mf.py @@ -6,11 +6,7 @@ import os import flopy -import sys -if sys.version_info[0] == 2: - from inspect import getargspec as getfullargspec -else: - from inspect import getfullargspec +from inspect import getfullargspec from ..mbase import BaseModel from ..pakbase import Package from ..utils import mfreadnam diff --git a/flopy/modflow/mfgage.py b/flopy/modflow/mfgage.py index 184225420b..314e498604 100644 --- a/flopy/modflow/mfgage.py +++ b/flopy/modflow/mfgage.py @@ -297,10 +297,7 @@ def load(f, model, nper=None, ext_unit_dict=None): openfile = not hasattr(f, 'read') if openfile: filename = f - if sys.version_info[0] == 2: - f = open(filename, 'r') - elif sys.version_info[0] == 3: - f = open(filename, 'r', errors='replace') + f = open(filename, 'r', errors='replace') # dataset 0 -- header while True: diff --git a/flopy/modflow/mfhyd.py b/flopy/modflow/mfhyd.py index 031416218a..590beb9d99 100644 --- a/flopy/modflow/mfhyd.py +++ b/flopy/modflow/mfhyd.py @@ -228,22 +228,13 @@ def write_file(self): # write dataset 2 for idx in range(self.nhyd): - if sys.version_info[0] == 3: - f.write('{} '.format(self.obsdata['pckg'][idx].decode())) - f.write('{} '.format(self.obsdata['arr'][idx].decode())) - f.write('{} '.format(self.obsdata['intyp'][idx].decode())) - f.write('{} '.format(self.obsdata['klay'][idx] + 1)) - f.write('{} '.format(self.obsdata['xl'][idx])) - f.write('{} '.format(self.obsdata['yl'][idx])) - f.write('{} '.format(self.obsdata['hydlbl'][idx].decode())) - else: - f.write('{} '.format(self.obsdata['pckg'][idx])) - f.write('{} '.format(self.obsdata['arr'][idx])) - f.write('{} '.format(self.obsdata['intyp'][idx])) - f.write('{} '.format(self.obsdata['klay'][idx] + 1)) - f.write('{} '.format(self.obsdata['xl'][idx])) - f.write('{} '.format(self.obsdata['yl'][idx])) - f.write('{} '.format(self.obsdata['hydlbl'][idx])) + f.write('{} '.format(self.obsdata['pckg'][idx].decode())) + f.write('{} '.format(self.obsdata['arr'][idx].decode())) + f.write('{} '.format(self.obsdata['intyp'][idx].decode())) + f.write('{} '.format(self.obsdata['klay'][idx] + 1)) + f.write('{} '.format(self.obsdata['xl'][idx])) + f.write('{} '.format(self.obsdata['yl'][idx])) + f.write('{} '.format(self.obsdata['hydlbl'][idx].decode())) f.write('\n') # close hydmod file diff --git a/flopy/modflow/mflak.py b/flopy/modflow/mflak.py index 7665f8a3c5..a8ce876485 100644 --- a/flopy/modflow/mflak.py +++ b/flopy/modflow/mflak.py @@ -614,10 +614,7 @@ def load(f, model, nper=None, ext_unit_dict=None): openfile = not hasattr(f, 'read') if openfile: filename = f - if sys.version_info[0] == 2: - f = open(filename, 'r') - elif sys.version_info[0] == 3: - f = open(filename, 'r', errors='replace') + f = open(filename, 'r', errors='replace') # dataset 0 -- header while True: diff --git a/flopy/modflow/mfuzf1.py b/flopy/modflow/mfuzf1.py index e67137e85a..f206db3deb 100644 --- a/flopy/modflow/mfuzf1.py +++ b/flopy/modflow/mfuzf1.py @@ -776,9 +776,7 @@ def load(f, model, ext_unit_dict=None, check=False): # dataset 0 -- header while True: - # can't use next() because util2d uses readline() line = f.readline() - # (can't mix iteration types in python 2) if line[0] != '#': break # determine problem dimensions diff --git a/flopy/pakbase.py b/flopy/pakbase.py index e6f1d36e29..15dfc052ec 100644 --- a/flopy/pakbase.py +++ b/flopy/pakbase.py @@ -9,8 +9,6 @@ import abc import os -import sys -import platform import webbrowser as wb import numpy as np @@ -680,12 +678,7 @@ def load(f, model, pak_type, ext_unit_dict=None, **kwargs): openfile = not hasattr(f, 'read') if openfile: filename = f - if platform.system().lower() == 'windows' and \ - sys.version_info[0] < 3: - import io - f = io.open(filename, 'r') - else: - f = open(filename, 'r') + f = open(filename, 'r') elif hasattr(f, 'name'): filename = f.name else: diff --git a/flopy/plot/map.py b/flopy/plot/map.py index dd4b4668bb..5a87bcdaaf 100644 --- a/flopy/plot/map.py +++ b/flopy/plot/map.py @@ -1,5 +1,4 @@ import numpy as np -import sys from ..discretization import StructuredGrid, UnstructuredGrid from ..utils import geometry @@ -899,9 +898,8 @@ def plot_pathline(self, pl, travel_time=None, **kwargs): if 'layer' in kwargs: kon = kwargs.pop('layer') - if sys.version_info[0] > 2: - if isinstance(kon, bytes): - kon = kon.decode() + if isinstance(kon, bytes): + kon = kon.decode() if isinstance(kon, str): if kon.lower() == 'all': kon = -1 @@ -1046,9 +1044,8 @@ def plot_timeseries(self, ts, travel_time=None, **kwargs): if 'layer' in kwargs: kon = kwargs.pop('layer') - if sys.version_info[0] > 2: - if isinstance(kon, bytes): - kon = kon.decode() + if isinstance(kon, bytes): + kon = kon.decode() if isinstance(kon, str): if kon.lower() == 'all': diff --git a/flopy/utils/flopy_io.py b/flopy/utils/flopy_io.py index f97703eab4..8712803e02 100755 --- a/flopy/utils/flopy_io.py +++ b/flopy/utils/flopy_io.py @@ -306,14 +306,9 @@ def loadtxt(file, delimiter=' ', dtype=None, skiprows=0, use_pandas=True, def get_url_text(url, error_msg=None): """ - Get text from a url, using either python 3 or 2. + Get text from a url. """ - try: - # For Python 3.0 and later - from urllib.request import urlopen - except ImportError: - # Fall back to Python 2's urllib2 - from urllib2 import urlopen + from urllib.request import urlopen try: urlobj = urlopen(url) text = urlobj.read().decode() diff --git a/flopy/utils/mflistfile.py b/flopy/utils/mflistfile.py index 02c397ff24..0043c6e15d 100644 --- a/flopy/utils/mflistfile.py +++ b/flopy/utils/mflistfile.py @@ -8,16 +8,12 @@ import collections import os import re -import sys from datetime import timedelta import numpy as np import errno from ..utils.utils_def import totim_to_datetime -if sys.version_info[0] == 2: - FileNotFoundError = IOError - class ListBudget(object): """ @@ -52,10 +48,7 @@ def __init__(self, file_name, budgetkey=None, timeunit='days'): assert os.path.exists(file_name), "file_name {0} not found".format( file_name) self.file_name = file_name - if sys.version_info[0] == 2: - self.f = open(file_name, 'r') - elif sys.version_info[0] == 3: - self.f = open(file_name, 'r', encoding='ascii', errors='replace') + self.f = open(file_name, 'r', encoding='ascii', errors='replace') self.tssp_lines = 0 @@ -281,11 +274,7 @@ def get_model_runtime(self, units='seconds'): return None # reopen the file - if sys.version_info[0] == 2: - self.f = open(self.file_name, 'r') - elif sys.version_info[0] == 3: - self.f = open(self.file_name, 'r', encoding='ascii', - errors='replace') + self.f = open(self.file_name, 'r', encoding='ascii', errors='replace') units = units.lower() if not units == 'seconds' and not units == 'minutes' and not units == 'hours': raise ( diff --git a/flopy/utils/mfreadnam.py b/flopy/utils/mfreadnam.py index 23648df4a7..230d6d0f84 100644 --- a/flopy/utils/mfreadnam.py +++ b/flopy/utils/mfreadnam.py @@ -184,8 +184,7 @@ def parsenamefile(namfilename, packages, verbose=True): openmode = 'rb' else: openmode = 'r' - if sys.version_info[0] > 2: - kwargs['errors'] = 'replace' + kwargs['errors'] = 'replace' try: filehandle = open(fname, openmode, **kwargs) except IOError: