From e1edc14fe78ebba192965e55f5c070394ad8c1c6 Mon Sep 17 00:00:00 2001 From: Zachary Burnett Date: Wed, 20 Apr 2022 15:51:21 -0400 Subject: [PATCH] move configuration into `pyproject.toml` (#150) * move configuration into `pyproject.toml` * update test workflows * formatting * pin `stormevents` version * add `pytest-mock` * also test upon update to `pyproject.toml` --- .github/workflows/build.yml | 65 ++++----------------- .github/workflows/tests.yml | 59 +++++++++++++------ .github/workflows/tests_simple.yml | 55 ++++++++++++++++++ adcircpy/forcing/winds/best_track.py | 86 +++++++++++++--------------- adcircpy/fort15.py | 4 +- pyproject.toml | 65 ++++++++++++++++++++- setup.cfg | 58 ------------------- setup.py | 12 ---- 8 files changed, 213 insertions(+), 191 deletions(-) create mode 100644 .github/workflows/tests_simple.yml delete mode 100644 setup.cfg delete mode 100755 setup.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1fca6bc3..20bf3d87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,62 +6,19 @@ on: - published jobs: - build_wheels: - name: build wheel - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest ] - python-version: [ '3.x' ] - steps: - - name: checkout repository - uses: actions/checkout@v2 - - name: install Python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: load cached Python installation - id: cache - uses: actions/cache@v2 - with: - path: ${{ env.pythonLocation }} - key: build-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'setup.*') }} - - name: build wheel - run: pip wheel . -w dist --no-deps - - name: save wheel - uses: actions/upload-artifact@v2 - with: - name: build - path: ./dist/*.whl - build_sdist: - name: package source + publish: + name: publish package to PyPI runs-on: ubuntu-latest steps: - name: checkout repository uses: actions/checkout@v2 - - name: install Python - uses: actions/setup-python@v2 - - name: install dependencies + - name: install Poetry + uses: abatilo/actions-poetry@v2.1.3 + - name: install Dunamai run: pip install dunamai - - name: package source - run: python setup.py sdist - - name: save source package - uses: actions/upload-artifact@v2 - with: - name: build - path: ./dist/*.tar.gz - upload_pypi: - name: publish to PyPI - needs: [ build_wheels, build_sdist ] - runs-on: ubuntu-latest - steps: - - name: retrieve wheel(s) and source - uses: actions/download-artifact@v2 - with: - name: build - path: dist - - name: upload wheel(s) and source - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - user: __token__ - password: ${{ secrets.PYPI_TOKEN }} + - name: extract version from VCS + run: poetry version $(dunamai from any) + - name: build wheel and source + run: poetry build + - name: upload wheel and source + run: poetry publish --username __token__ --password ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8ad12fe6..aee1b966 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,44 +1,54 @@ name: tests -on: [ push ] +on: + push: + branches: + - main + paths: + - '**.py' + - '.github/workflows/tests.yml' + - 'pyproject.toml' + pull_request: + branches: + - main jobs: lint: name: lint runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ '3.x' ] steps: - name: clone repository uses: actions/checkout@v2 - name: install Python uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - name: load cached Python installation id: cache uses: actions/cache@v2 with: path: ${{ env.pythonLocation }} - key: lint-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'setup.*') }} - - name: install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: pip install ".[development]" + key: lint-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} + - name: install linters + run: pip install flake8 oitnb - name: lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: lint with oitnb + run: oitnb . --check test: needs: lint name: test runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: - os: [ ubuntu-latest ] - python-version: [ '3.6', '3.x' ] + os: [ ubuntu-latest, macos-latest ] + python-version: [ '3.6', '3.7', '3.8', '3.9', '3.x' ] + exclude: + - os: macos-latest + python-version: '3.x' steps: - name: clone repository uses: actions/checkout@v2 @@ -51,18 +61,33 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.pythonLocation }} - key: test-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'setup.*') }} + key: test-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} - name: install dependencies - if: steps.cache.outputs.cache-hit != 'true' run: pip install ".[testing]" - name: run tests - if: matrix.python-version != '3.x' || matrix.os != 'ubuntu-latest' run: pytest --numprocesses auto + test_with_coverage: + needs: [ lint, test ] + name: test with coverage + runs-on: ubuntu-latest + steps: + - name: clone repository + uses: actions/checkout@v2 + - name: install Python + uses: actions/setup-python@v2 + - name: load cached Python installation + id: cache + uses: actions/cache@v2 + with: + path: ${{ env.pythonLocation }} + key: test-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} + - name: install dependencies + run: pip install ".[testing]" - name: run tests with coverage - if: matrix.python-version == '3.x' && matrix.os == 'ubuntu-latest' run: pytest --numprocesses auto --cov . --cov-report xml:coverage.xml + - name: show coverage report + run: coverage report - name: upload coverage to Codecov - if: matrix.python-version == '3.x' && matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v2.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/tests_simple.yml b/.github/workflows/tests_simple.yml new file mode 100644 index 00000000..046d7430 --- /dev/null +++ b/.github/workflows/tests_simple.yml @@ -0,0 +1,55 @@ +name: tests + +on: + push: + branches-ignore: + - main + paths: + - '**.py' + - '.github/workflows/tests_simple.yml' + - 'pyproject.toml' + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + steps: + - name: clone repository + uses: actions/checkout@v2 + - name: install Python + uses: actions/setup-python@v2 + - name: load cached Python installation + id: cache + uses: actions/cache@v2 + with: + path: ${{ env.pythonLocation }} + key: lint-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} + - name: install linters + run: pip install flake8 oitnb + - name: lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: lint with oitnb + run: oitnb . --check + test: + needs: lint + name: test + runs-on: ubuntu-latest + steps: + - name: clone repository + uses: actions/checkout@v2 + - name: install Python + uses: actions/setup-python@v2 + - name: load cached Python installation + id: cache + uses: actions/cache@v2 + with: + path: ${{ env.pythonLocation }} + key: test-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} + - name: install dependencies + run: pip install ".[testing]" + - name: run tests + run: pytest --numprocesses auto diff --git a/adcircpy/forcing/winds/best_track.py b/adcircpy/forcing/winds/best_track.py index 444b3be7..6d55b3f4 100644 --- a/adcircpy/forcing/winds/best_track.py +++ b/adcircpy/forcing/winds/best_track.py @@ -23,52 +23,49 @@ class BestTrackForcing(VortexTrack, WindForcing): def __init__( - self, - storm: Union[str, PathLike, DataFrame, io.BytesIO], - nws: int = None, - interval_seconds: int = None, - start_date: datetime = None, - end_date: datetime = None, + self, + storm: Union[str, PathLike, DataFrame, io.BytesIO], + nws: int = None, + interval_seconds: int = None, + start_date: datetime = None, + end_date: datetime = None, ): if nws is None: nws = 20 valid_nws_values = [8, 19, 20] assert ( - nws in valid_nws_values + nws in valid_nws_values ), f'ATCF BestTrack can only use `nws` values in {valid_nws_values}' if interval_seconds is None: interval_seconds = 3600 VortexTrack.__init__( - self, - storm=storm, - start_date=start_date, - end_date=end_date, - file_deck='b', - advisories=['BEST'], + self, + storm=storm, + start_date=start_date, + end_date=end_date, + file_deck='b', + advisories=['BEST'], ) WindForcing.__init__(self, nws=nws, interval_seconds=interval_seconds) @classmethod def from_fort22( - cls, - fort22: PathLike, - nws: int = None, - interval_seconds: int = None, - start_date: datetime = None, - end_date: datetime = None, + cls, + fort22: PathLike, + nws: int = None, + interval_seconds: int = None, + start_date: datetime = None, + end_date: datetime = None, ) -> 'WindForcing': - instance = cls.from_file(path=fort22, start_date=start_date, - end_date=end_date) - WindForcing.__init__(instance, nws=nws, - interval_seconds=interval_seconds) + instance = cls.from_file(path=fort22, start_date=start_date, end_date=end_date) + WindForcing.__init__(instance, nws=nws, interval_seconds=interval_seconds) return instance def summary( - self, output: Union[str, os.PathLike] = None, - overwrite: bool = False, + self, output: Union[str, os.PathLike] = None, overwrite: bool = False, ): min_storm_speed = numpy.min(self.data['speed']) max_storm_speed = numpy.max(self.data['speed']) @@ -77,8 +74,7 @@ def summary( min_central_pressure = numpy.min(self.data['central_pressure']) max_wind_speed = numpy.max(self.data['max_sustained_wind_speed']) start_loc = (self.data['longitude'][0], self.data['latitude'][0]) - end_loc = ( - self.data['longitude'].iloc[-1], self.data['latitude'].iloc[-1]) + end_loc = (self.data['longitude'].iloc[-1], self.data['latitude'].iloc[-1]) f = [ f'Summary of storm: {self.nhc_code}', f'min./max. track speed: {min_storm_speed} m/s, {max_storm_speed} m/s', @@ -144,13 +140,13 @@ def clip_to_bbox(self, bbox, bbox_crs): msg = f'bbox must be a {Bbox} instance.' assert isinstance(bbox, Bbox), msg bbox_pol = Polygon( - [ - [bbox.xmin, bbox.ymin], - [bbox.xmax, bbox.ymin], - [bbox.xmax, bbox.ymax], - [bbox.xmin, bbox.ymax], - [bbox.xmin, bbox.ymin], - ] + [ + [bbox.xmin, bbox.ymin], + [bbox.xmax, bbox.ymin], + [bbox.xmax, bbox.ymax], + [bbox.xmin, bbox.ymax], + [bbox.xmin, bbox.ymin], + ] ) _switch = True unique_dates = numpy.unique(self.data['datetime']) @@ -167,8 +163,7 @@ def clip_to_bbox(self, bbox, bbox_crs): transformer = Transformer.from_crs(df_crs, utm_crs, always_xy=True) p = Point(*transformer.transform(lon, lat)) pol = p.buffer(radii) - transformer = Transformer.from_crs(utm_crs, bbox_crs, - always_xy=True) + transformer = Transformer.from_crs(utm_crs, bbox_crs, always_xy=True) pol = ops.transform(transformer.transform, pol) if _switch is True: if not pol.intersects(bbox_pol): @@ -187,16 +182,15 @@ def clip_to_bbox(self, bbox, bbox_crs): break if _found_start_date is False: - raise Exception( - f'No data within mesh bounding box for storm {self.storm_id}.') + raise Exception(f'No data within mesh bounding box for storm {self.storm_id}.') def plot_track( - self, - axis: Axis = None, - show: bool = False, - color: str = 'k', - coastline: bool = True, - **kwargs, + self, + axis: Axis = None, + show: bool = False, + color: str = 'k', + coastline: bool = True, + **kwargs, ): kwargs.update({'color': color}) if axis is None: @@ -210,7 +204,7 @@ def plot_track( axis.quiver(row['longitude'], row['latitude'], U, V, **kwargs) if i % 6 == 0: axis.annotate( - row['datetime'], (row['longitude'], row['latitude']), + row['datetime'], (row['longitude'], row['latitude']), ) if show: axis.axis('scaled') @@ -224,6 +218,6 @@ def plot_wind_swath(self, isotach: int, segments: int = 91): plot_polygons(isotachs) plot_polygons(swath) pyplot.suptitle( - f'{self.nhc_code} - isotach {isotach} kt ({self.start_date} - {self.end_date})' + f'{self.nhc_code} - isotach {isotach} kt ({self.start_date} - {self.end_date})' ) pyplot.show() diff --git a/adcircpy/fort15.py b/adcircpy/fort15.py index 7ed25d33..b0520e12 100644 --- a/adcircpy/fort15.py +++ b/adcircpy/fort15.py @@ -53,7 +53,9 @@ def within_wind_swath( if wind_speed is None: wind_speed = 34 - combined_wind_swaths = ops.unary_union(list(track.wind_swaths(wind_speed)['BEST'].values())) + combined_wind_swaths = ops.unary_union( + list(track.wind_swaths(wind_speed)['BEST'].values()) + ) return cls( station_types=station_types, diff --git a/pyproject.toml b/pyproject.toml index a9b62330..788a0451 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,65 @@ +[tool.poetry] +name = 'adcircpy' +version = '0.0.0' +description = 'Python package for working with ADCIRC input and output files' +authors = ['Zach Burnett ', 'Jaime R Calzada '] +license = 'GPL-3.0-or-later' +readme = 'README.md' +repository = 'https://github.com/noaa-ocs-modeling/adcircpy.git' +documentation = 'https://adcircpy.readthedocs.io' + [build-system] requires = [ - "dunamai", - "setuptools>=41.2", + 'poetry-core>=1.0.0', + 'poetry-dynamic-versioning', ] -build-backend = "setuptools.build_meta" +build-backend = 'poetry.core.masonry.api' + +[tool.poetry-dynamic-versioning] +enable = true + +[tool.poetry.dependencies] +python = '^3.6' +appdirs = '*' +geopandas = '*' +haversine = '*' +matplotlib = '*' +netCDF4 = '*' +numpy = '*' +pandas = '*' +paramiko = '*' +psutil = '*' +pyproj = '>=2.6' +requests = '*' +scipy = '*' +shapely = '*' +stormevents = '^1.4.0' +utm = '*' +isort = { version = '*', optional = true } +oitnb = { version = '*', optional = true } +pooch = { version = '*', optional = true } +pytest = { version = '*', optional = true } +pytest-cov = { version = '*', optional = true } +pytest-mock = { version = '*', optional = true } +pytest-socket = { version = '*', optional = true } +pytest-xdist = { version = '*', optional = true } +m2r2 = { version = '*', optional = true } +sphinx = { version = '*', optional = true } +sphinx-rtd-theme = { version = '*', optional = true } +sphinxcontrib-programoutput = { version = '*', optional = true } +sphinxcontrib-bibtex = { version = '*', optional = true } + +[tool.poetry.extras] +testing = ['pooch', 'pytest', 'pytest-cov', 'pytest-mock', 'pytest-socket', 'pytest-xdist'] +development = ['isort', 'oitnb'] +documentation = ['m2r2', 'sphinx', 'sphinx-rtd-theme', 'sphinxcontrib-programoutput', 'sphinxcontrib-bibtex'] + +[tool.poetry.scripts] +tidal_run = 'adcircpy.cmd.tidal_run:main' +best_track_run = 'adcircpy.cmd.best_track_run:main' +best_track_file = 'adcircpy.cmd.best_track_file:main' +plot_mesh = 'adcircpy.cmd.plot_mesh:main' +plot_maxele = 'adcircpy.cmd.plot_maxele:main' +plot_fort61 = 'adcircpy.cmd.plot_fort61:main' +fort63 = 'adcircpy.cmd.fort63:main' +tide_gen = 'adcircpy.cmd.tide_gen:main' diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4c41712f..00000000 --- a/setup.cfg +++ /dev/null @@ -1,58 +0,0 @@ -[metadata] -name = adcircpy -author = Zach Burnett , Jaime R Calzada -description = Python package for working with ADCIRC input and output files -long_description = file:README.md -long_description_content_type = text/markdown -license = GPL -python_requires = >=3.6 -url = https://github.com/noaa-ocs-modeling/adcircpy.git - -[options] -install_requires = - appdirs - geopandas - haversine - matplotlib - netCDF4 - numpy - pandas - paramiko - psutil - pyproj >= 2.6 - requests - scipy - shapely - stormevents >= 1.3.0 - utm - -[options.extras_require] -testing = - pooch - pytest - pytest-cov - pytest-mock - pytest-socket - pytest-xdist -development = - flake8 - isort - oitnb -documentation = - dunamai - m2r2 - sphinx - sphinx-rtd-theme - sphinxcontrib-programoutput - sphinxcontrib-bibtex - -[options.entry_points] -console_scripts = - tidal_run = adcircpy.cmd.tidal_run:main - best_track_run = adcircpy.cmd.best_track_run:main - best_track_file = adcircpy.cmd.best_track_file:main - plot_mesh = adcircpy.cmd.plot_mesh:main - plot_maxele = adcircpy.cmd.plot_maxele:main - plot_fort61 = adcircpy.cmd.plot_fort61:main - fort63 = adcircpy.cmd.fort63:main - tide_gen = adcircpy.cmd.tide_gen:main diff --git a/setup.py b/setup.py deleted file mode 100755 index f3931b6e..00000000 --- a/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -import warnings - -from dunamai import Version -from setuptools import find_packages, setup - -try: - __version__ = Version.from_any_vcs().serialize() -except RuntimeError as error: - warnings.warn(f'{error.__class__.__name__} - {error}') - __version__ = '0.0.0' - -setup(version=__version__, packages=find_packages(exclude=('tests',)), test_suite='tests')