diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e051f467..565b221e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,7 @@ jobs: - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true - python-version: 3.9 + python-version: 3.12 channels: conda-forge channel-priority: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7c719fae..5ac6b4ab 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.12 - name: Lint run: | diff --git a/.github/workflows/test-cli.yml b/.github/workflows/test-cli.yml new file mode 100644 index 00000000..cb3a1f76 --- /dev/null +++ b/.github/workflows/test-cli.yml @@ -0,0 +1,43 @@ +name: Test CLI + +on: [push, pull_request] + +jobs: + test-cli: + # We want to run on external PRs, but not on our own internal PRs as they'll be run + # by the push to the branch. Without this if check, checks are duplicated since + # internal PRs match both the push and pull_request events. + if: + github.event_name == 'push' || github.event.pull_request.head.repo.full_name != + github.repository + + runs-on: ${{ matrix.os }} + + defaults: + run: + shell: bash -l {0} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ["3.10", "3.11", "3.12"] + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: python -m pip install nox tomli + + - name: Run the tests + run: nox --session test test-cli --python ${{ matrix.python-version }} --verbose + + - name: Coveralls + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9' + uses: AndreMiras/coveralls-python-action@v20201129 diff --git a/.github/workflows/build-test-ci.yml b/.github/workflows/test-langs.yml similarity index 71% rename from .github/workflows/build-test-ci.yml rename to .github/workflows/test-langs.yml index a52b5c9e..2b32dd0e 100644 --- a/.github/workflows/build-test-ci.yml +++ b/.github/workflows/test-langs.yml @@ -1,9 +1,9 @@ -name: Build/Test CI +name: Test languages on: [push, pull_request] jobs: - build-and-test: + test-langs: # We want to run on external PRs, but not on our own internal PRs as they'll be run # by the push to the branch. Without this if check, checks are duplicated since # internal PRs match both the push and pull_request events. @@ -18,18 +18,16 @@ jobs: shell: bash -l {0} strategy: + fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10"] + python-version: ["3.10", "3.11", "3.12"] + language: ["c", "cxx", "python", "fortran"] steps: - uses: actions/checkout@v3 with: submodules: true - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - uses: conda-incubator/setup-miniconda@v2 with: @@ -43,13 +41,10 @@ jobs: run: python -m pip install nox tomli - name: Install compilers - run: mamba install c-compiler cxx-compiler fortran-compiler cmake - - - name: Run the tests - run: nox --session test test-cli --python ${{ matrix.python-version }} --verbose + run: mamba install c-compiler cxx-compiler fortran-compiler - name: Run the language tests - run: nox --non-interactive --error-on-missing-interpreter --session test-langs --python ${{ matrix.python-version }} --verbose + run: nox -s "test-langs-${{ matrix.python-version }}(lang='${{ matrix.language }}')" --python ${{ matrix.python-version }} --verbose - name: Coveralls if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cd8f9928..170e3ef5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 24.2.0 hooks: - id: black name: black @@ -25,7 +25,7 @@ repos: exclude: ^babelizer/data - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: @@ -35,21 +35,21 @@ repos: exclude: ^babelizer/data - repo: https://github.com/asottile/pyupgrade - rev: v2.34.0 + rev: v3.15.1 hooks: - id: pyupgrade - args: [--py38-plus] + args: [--py310-plus] exclude: ^babelizer/data - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.13.2 hooks: - id: isort files: \.py$ exclude: ^babelizer/data - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.5.0 hooks: - id: check-builtin-literals exclude: ^babelizer/data @@ -66,7 +66,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/PyCQA/pydocstyle - rev: 6.1.1 + rev: 6.3.0 hooks: - id: pydocstyle files: babelizer/.*\.py$ @@ -77,7 +77,7 @@ repos: additional_dependencies: [".[toml]"] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 + rev: v1.8.0 hooks: - id: mypy additional_dependencies: [types-all] diff --git a/babelizer/__init__.py b/babelizer/__init__.py index 78120efc..546781d0 100644 --- a/babelizer/__init__.py +++ b/babelizer/__init__.py @@ -1,4 +1,5 @@ """The *babelizer*.""" + from ._version import __version__ __all__ = ["__version__"] diff --git a/babelizer/cli.py b/babelizer/cli.py index a85bd3dd..fadddf65 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -1,13 +1,20 @@ """The command line interface to the babelizer.""" + import fnmatch import os import pathlib +import sys import tempfile from functools import partial import click import git -import pkg_resources + +if sys.version_info >= (3, 12): # pragma: no cover (PY12+) + import importlib.resources as importlib_resources +else: # pragma: no cover (= (3, 12): # pragma: no cover (PY12+) + import importlib.resources as importlib_resources +else: # pragma: no cover (= (3, 12): # pragma: no cover (PY12+) + import importlib.resources as importlib_resources +else: # pragma: no cover (` this file +and create the new environment with: + +.. code:: bash + + $ conda env create --file=environment-fortran.yml + +When this command completes, +activate the environment +(on Linux and macOS, you may have to use ``source`` instead of ``conda``): + +.. code:: bash + + $ conda activate wrap + +The *wrap* environment now contains all the dependencies needed +to build, install, and wrap the *heat* model. + + +Build the *heat* model from source +---------------------------------- + +Clone the `bmi-example-fortran`_ repository from GitHub: + +.. code:: bash + + $ git clone https://github.com/csdms/bmi-example-fortran + +There are general `instructions`_ in the repository for building and installing +this package on Linux, macOS, and Windows. +We'll augment those instructions +with the note that we're installing into the *wrap* conda environment, +so the ``CONDA_PREFIX`` environment variable +should be used to specify the install path. + +Note that if you build the model with the +`Fortran Package Manager `_ +(fpm), you will end up with a static library (`.a` on Unix, `.lib` on +Windows) instead of the dynamic library (`.so` on Unix, `.dll` on +Windows) that the CMake build creates. We are aware of +issues linking to the compiler runtime libraries from this static +library, and for this reason we recommend using the CMake build +routine, as detailed below. + + +Linux and macOS +............... + +On Linux and macOS, +use these commands to build and install the *heat* model: + +.. code:: bash + + $ cd bmi-example-fortran + $ mkdir _build && cd _build + $ cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX + $ make install + +Verify the install by testing for the existence of the module +file of the library containing the compiled *heat* model: + +.. code:: bash + + $ test -f $CONDA_PREFIX/include/bmiheatf.mod ; echo $? + +A return of zero indicates success. + +Windows +....... + +Building the *heat* model on Windows requires either: + +* A Unix-like system, such as `Cygwin `_ or + `Windows Subsystem for Linux `_, + in which case you can follow the above Linux and macOS instructions. +* Microsoft Visual Studio 2017 or later, or Microsoft Build Tools for + Visual Studio 2017 or later, in which case the following instructions should be followed. + +Open the `Developer Command Prompt`_ and run: + +.. code:: + + > cd bmi-example-fortran + > mkdir _build + > cd _build + > cmake .. ^ + -G "NMake Makefiles" ^ + -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX% ^ + -DCMAKE_BUILD_TYPE=Release + > cmake --build . --target install --config Release + +Verify the install by testing for the existence of the module +file of the library containing the compiled *heat* model: + +.. code:: + + > if exist %CONDA_PREFIX%\include\bmiheatf.mod echo File exists + +Note that on Windows systems, the conda package we specified +called `fortran-compiler` installs a fairly old version of the Flang +compiler, and there are few options for more modern compilers +available via conda (as opposed to for Unix, where modern versions +of `GFortran are available `_). +This is fine for the example *heat* model, but code bases leveraging +newer features of Fortran may need a more modern compiler. In this +case, it might be necessary to install a Fortran compiler separately +to conda, for example using the binaries provided by +`equation.com `_. +The `BMI bindings `_ (installed +here via conda) should be compiled with the same compiler as the model +that uses them, to avoid incompatibility issues, and so if you choose +a different compiler than provided by `fortran-compiler`, you will +likely have to compile the BMI bindings with this compiler as well. + + +Create the *babelizer* input file +--------------------------------- + +The *babelizer* input file provides information to the *babelizer* +about the model to be wrapped. +The input file is created with the ``babelize generate`` subcommand. + +Return to our initial ``build`` directory and call ``babelize generate`` with: + +.. code:: bash + + $ cd ../.. + $ babelize generate \ + --package=pymt_heatf \ + --summary="PyMT plugin for the Fortran heat model" \ + --language=fortran \ + --library=bmiheatf \ + --entry-point=bmi_heat \ + --name=HeatModel \ + --requirement="" > babel_heatf.toml + +In this call, +the *babelizer* will also fill in default values; +e.g., author name, author email, GitHub username, and license. + +The resulting file, :download:`babel_heatf.toml`, +will look something like this: + +.. include:: babel_heatf.toml + :literal: + +For more information on the entries and sections of the *babelizer* input file, +see `Input file <./readme.html#input-file>`_. + + +Wrap the model with the *babelizer* +----------------------------------- + +Generate Python bindings for the model with the ``babelize init`` subcommand: + +.. code:: bash + + $ babelize init babel_heatf.toml + +The results are placed in a new directory, ``pymt_heatf``, +under the current directory. + +.. code:: bash + + $ ls -aF pymt_heatf + ./ .gitignore recipe/ + ../ LICENSE requirements-build.txt + babel.toml Makefile requirements-library.txt + CHANGES.rst MANIFEST.in requirements-testing.txt + CREDITS.rst meta/ requirements.txt + docs/ pymt_heatf/ setup.cfg + .git/ pyproject.toml setup.py + .github/ README.rst + +Before we can build the Python bindings, +we must ensure that the dependencies required by the toolchain, +as well as any required by the model, +as specified in the *babelizer* input file (none in this case), +are satisfied. + +Change to the ``pymt_heatf`` directory and install dependencies +into the conda environment: + +.. code:: bash + + $ cd pymt_heatf + $ conda install -c conda-forge \ + --file=requirements-build.txt \ + --file=requirements-testing.txt \ + --file=requirements-library.txt \ + --file=requirements.txt + +Now build the Python bindings with: + +.. code:: bash + + $ make install + +This command sets off a long list of messages, +at the end of which you'll hopefully see: + +.. code:: bash + + Successfully installed pymt-heatf-0.1 + +Internally, this uses `pip` to install the Python +package in editable mode. + +Pause a moment to see what we've done. +Change back to the initial ``build`` directory, +make a new ``test`` directory, +and change to it: + +.. code:: bash + + $ cd .. + $ mkdir test && cd test + +Start a Python session (e.g. run ``python``) and try the following commands: + +.. code:: python + + >>> from pymt_heatf import HeatModel + >>> m = HeatModel() + >>> m.get_component_name() + 'The 2D Heat Equation' + +We've imported the *heat* model, +written in Fortran, +into Python! +Exit the Python session (e.g. type ``exit()``) + +At this point, +it's a good idea to run the *bmi-tester* (`GitHub repo `_) +over the model. +The *bmi-tester* exercises each BMI method exposed through Python, +ensuring it works correctly. +However, before running the *bmi-tester*, +one last piece of information is needed. +Like all models equipped with a BMI, +*heat* uses a :term:`configuration file` to specify initial parameter values. +Create a configuration file for *heat* at the command line with: + +.. code:: bash + + $ echo "1.5, 8.0, 6, 5" > config.txt + +or download the file :download:`config.txt ` +(making sure to place it in the ``test`` directory). + +Run the *bmi-tester*: + +.. code:: bash + + $ bmi-test pymt_heatf:HeatModel --config-file=config.txt --root-dir=. -vvv + +This command sets off a long list of messages, +ending with + +.. code:: bash + + 🎉 All tests passed! + +if everything has been built correctly. + + +Add metadata to make a *pymt* component +--------------------------------------- + +The final step in wrapping the *heat* model +is to add metadata used by the `Python Modeling Tool`_, *pymt*. +CSDMS develops a set of standards, +the `CSDMS Model Metadata`_, +that provides a detailed and formalized description of a model. +The metadata allow *heat* to be run and be :term:`coupled ` +with other models that expose a BMI and have been similarly wrapped +with the *babelizer*. + +Recall the *babelizer* outputs the wrapped *heat* model +to the directory ``pymt_heatf``. +Under this directory, +the *babelizer* created a directory for *heat* model metadata, +``meta/HeatModel``. +Change back to the ``pymt_heatf`` directory +and view the current metadata: + +.. code:: bash + + $ cd ../pymt_heatf + $ ls meta/HeatModel/ + api.yaml + +The file ``api.yaml`` is automatically generated by the *babelizer*. +To complete the description of the *heat* model, +other metadata files are needed, including: + +* :download:`info.yaml ` +* :download:`run.yaml ` +* a :download:`templated model configuration file ` +* :download:`parameters.yaml ` + +`Descriptions`_ of these files and their roles +are given in the CSDMS Model Metadata repository. +Download each of the files using the links in the list above +and place them in the ``pymt_heatf/meta/HeatModel`` directory +alongside ``api.yaml``. + +Next, install *pymt*: + +.. code:: bash + + $ conda install -c conda-forge pymt + +Then start a Python session and show that the *heat* model +can be called through *pymt*: + +.. code:: python + + >>> from pymt.models import HeatModel + >>> m = HeatModel() + >>> m.name + 'The 2D Heat Equation' + +A longer example, +:download:`pymt_heatc_ex.py `, +is included in the documentation. +For easy viewing, it's reproduced here verbatim: + +.. include:: examples/pymt_heatc_ex.py + :literal: + +:download:`Download ` this Python script, +make sure we're still in the `test` directory we just created, +then run it with: + +.. code:: bash + + $ python pymt_heatc_ex.py + +Note that here we are actually running the Python script that +was developed for the :doc:`C example `, not Fortran. +That is one of the powerful things about wrapping your +BMI-enabled model and accessing it via PyMT - it provides a +standardised interface, regardless of the underlying model +and the language it was written in. + + +Summary +------- + +Using the *babelizer*, we wrapped the *heat* model, which is written in Fortran. +It can now be called as a *pymt* component in Python. + +The steps for wrapping a model with the *babelizer* outlined in this example +can also be applied to models written in C (:doc:`see the example `) +and C++. + + +.. + Links + +.. _bmi-example-fortran: https://github.com/csdms/bmi-example-fortran +.. _instructions: https://github.com/csdms/bmi-example-c/blob/master/README.md +.. _Developer Command Prompt: https://docs.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs +.. _bmi-tester: https://github.com/csdms/bmi-tester +.. _Python Modeling Tool: https://pymt.readthedocs.io +.. _CSDMS Model Metadata: https://github.com/csdms/model_metadata +.. _Descriptions: https://github.com/csdms/model_metadata/blob/develop/README.rst diff --git a/docs/source/examples/fortran/heat.txt b/docs/source/examples/fortran/heat.txt new file mode 100644 index 00000000..7ea565ae --- /dev/null +++ b/docs/source/examples/fortran/heat.txt @@ -0,0 +1 @@ +{{thermal_diffusivity}}, {{run_duration}}, {{number_of_columns}}, {{number_of_rows}} diff --git a/docs/source/examples/fortran/info.yaml b/docs/source/examples/fortran/info.yaml new file mode 100644 index 00000000..fae21955 --- /dev/null +++ b/docs/source/examples/fortran/info.yaml @@ -0,0 +1,8 @@ +summary: | + This model solves the two-dimensional heat equation on a uniform + rectilinear grid. +url: https://github.com/csdms/bmi-example-fortran +author: CSDMS +email: csdms@colorado.edu +version: 0.2 +license: MIT diff --git a/docs/source/examples/fortran/parameters.yaml b/docs/source/examples/fortran/parameters.yaml new file mode 100644 index 00000000..3c1c5f2b --- /dev/null +++ b/docs/source/examples/fortran/parameters.yaml @@ -0,0 +1,42 @@ +run_duration: + description: Simulation run time + value: + type: float + default: 1.0 + units: s + range: + min: 0.0 + max: 1000000.0 + +thermal_diffusivity: + name: Thermal diffusivity + description: Thermal diffusivity + value: + type: float + default: 1.0 + range: + min: 0.0 + max: 10.0 + units: 'm2 s-1' + +number_of_rows: + name: Number of rows + description: Number of grid rows + value: + type: int + default: 10 + range: + min: 0 + max: 10000 + units: '1' + +number_of_columns: + name: Number of columns + description: Number of grid columns + value: + type: int + default: 10 + range: + min: 0 + max: 10000 + units: '1' diff --git a/docs/source/examples/fortran/run.yaml b/docs/source/examples/fortran/run.yaml new file mode 100644 index 00000000..3b0457ff --- /dev/null +++ b/docs/source/examples/fortran/run.yaml @@ -0,0 +1 @@ +config_file: heat.txt diff --git a/docs/source/examples/heatc_ex.py b/docs/source/examples/heatc_ex.py index 28ca05c9..d6c31f23 100644 --- a/docs/source/examples/heatc_ex.py +++ b/docs/source/examples/heatc_ex.py @@ -1,4 +1,5 @@ """An example of running the heatc model through its BMI.""" + import numpy as np from pymt_heatc import HeatModel @@ -30,10 +31,10 @@ print(" - shape:", grid_shape) grid_size = m.get_grid_size(grid_id) print(" - size:", grid_size) -grid_spacing = np.empty(grid_rank, dtype=np.float) +grid_spacing = np.empty(grid_rank, dtype=np.float64) m.get_grid_spacing(grid_id, grid_spacing) print(" - spacing:", grid_spacing) -grid_origin = np.empty(grid_rank, dtype=np.float) +grid_origin = np.empty(grid_rank, dtype=np.float64) m.get_grid_origin(grid_id, grid_origin) print(" - origin:", grid_origin) print(" - variable type:", m.get_var_type(var_name)) @@ -42,7 +43,7 @@ print(" - nbytes:", m.get_var_nbytes(var_name)) # Get the initial temperature values. -val = np.empty(grid_shape, dtype=np.float) +val = np.empty(grid_shape, dtype=np.float64) m.get_value(var_name, val) print(" - initial values (gridded):") print(val.reshape(np.roll(grid_shape, 1))) diff --git a/docs/source/examples/pymt_heatc_ex.py b/docs/source/examples/pymt_heatc_ex.py old mode 100644 new mode 100755 index 44d79546..85f27694 --- a/docs/source/examples/pymt_heatc_ex.py +++ b/docs/source/examples/pymt_heatc_ex.py @@ -1,4 +1,5 @@ """Run the heat model in pymt.""" + from pymt.models import HeatModel # Instantiate the component and get its name. diff --git a/docs/source/index.rst b/docs/source/index.rst index 758e9e33..dedebede 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -41,6 +41,7 @@ User Guide readme cli example + example-fortran glossary diff --git a/external/bmi-example-python b/external/bmi-example-python index 1329f473..b0a471c2 160000 --- a/external/bmi-example-python +++ b/external/bmi-example-python @@ -1 +1 @@ -Subproject commit 1329f473ec5130ae37547f509137dd63bb957e73 +Subproject commit b0a471c2f364a66f167b3209203a6991f39d97f2 diff --git a/news/85.misc b/news/85.misc new file mode 100644 index 00000000..f020da11 --- /dev/null +++ b/news/85.misc @@ -0,0 +1,2 @@ + +Changed the default license from "MIT" to "MIT License". diff --git a/news/86.doc b/news/86.doc new file mode 100644 index 00000000..b859ea4f --- /dev/null +++ b/news/86.doc @@ -0,0 +1,3 @@ + +Added an example to the docs that shows how to use the *babelizer* to wrap +a Fortran model. diff --git a/news/88.misc b/news/88.misc new file mode 100644 index 00000000..37f8fac2 --- /dev/null +++ b/news/88.misc @@ -0,0 +1,2 @@ + +Added support for Python 3.12 and dropped support for Python less than 3.10. diff --git a/noxfile.py b/noxfile.py index 331681dc..662698e6 100644 --- a/noxfile.py +++ b/noxfile.py @@ -9,7 +9,7 @@ PROJECT = "babelizer" ROOT = pathlib.Path(__file__).parent ALL_LANGS = {"c", "cxx", "fortran", "python"} -PYTHON_VERSIONS = ["3.9", "3.10", "3.11"] +PYTHON_VERSIONS = ["3.10", "3.11", "3.12"] @nox.session(python=PYTHON_VERSIONS) @@ -42,7 +42,7 @@ def test_langs(session: nox.session, lang) -> None: build_examples(session, lang) - session.conda_install("pip", "bmi-tester>=0.5.4") + session.conda_install("pip", "bmi-tester>=0.5.4", "cmake") session.install(".[testing]") with session.chdir(tmpdir): diff --git a/pyproject.toml b/pyproject.toml index a08dda59..98e928f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,112 +1,160 @@ -[build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta" - [project] name = "babelizer" +requires-python = ">=3.10" description = "Wrap BMI libraries with Python bindings" +keywords = [ + "bmi", + "component modeling", + "geosciences", + "interoperability", + "model coupling", + "numerical modeling", + "pymt", +] authors = [ - {name = "Eric Hutton", email = "huttone@colorado.edu"}, + { name = "Eric Hutton", email = "huttone@colorado.edu" }, ] maintainers = [ - {name = "Eric Hutton", email = "huttone@colorado.edu"}, - {name = "Mark Piper", email = "mark.piper@colorado.edu"}, + { name = "Eric Hutton", email = "huttone@colorado.edu" }, + { name = "Mark Piper", email = "mark.piper@colorado.edu" }, ] -keywords = [ - "bmi", - "component modeling", - "geosciences", - "interoperability", - "model coupling", - "numerical modeling", - "pymt", -] -license = {text = "MIT License"} classifiers = [ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Topic :: Software Development :: Code Generators", -] -requires-python = ">=3.8" + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Code Generators", +] dependencies = [ - "black", - "click", - "gitpython", - "pyyaml", - "tomlkit", - "isort>=5", - "cookiecutter", - "logoizer @ git+https://github.com/mcflugen/logoizer", + "black", + "click", + "cookiecutter", + "gitpython", + "importlib-resources; python_version < '3.12'", + "isort>=5", + "logoizer @ git+https://github.com/mcflugen/logoizer", + "pyyaml", + "tomlkit", ] -dynamic = ["readme", "version"] +dynamic = [ + "readme", + "version", +] + +[project.license] +text = "MIT" [project.urls] -homepage = "https://babelizer.readthedocs.io/" +changelog = "https://github.com/csdms/babelizer/blob/master/CHANGES.rst" documentation = "https://babelizer.readthedocs.io/" +homepage = "https://babelizer.readthedocs.io/" repository = "https://github.com/csdms/babelizer" -changelog = "https://github.com/csdms/babelizer/blob/master/CHANGES.rst" [project.optional-dependencies] dev = [ - "black", - "flake8", - "flake8-bugbear", - "isort", - "nox", - "pre-commit", - "towncrier", + "black", + "flake8", + "flake8-bugbear", + "isort", + "nox", + "pre-commit", + "towncrier", ] docs = [ - "sphinx>=4", - "sphinx-click", - "sphinx-copybutton", - "sphinx-inline-tabs", - "sphinxcontrib.towncrier", - "pygments>=2.4", - "sphinx-inline-tabs", - "furo", + "furo", + "pygments>=2.4", + "sphinx-click", + "sphinx-copybutton", + "sphinx-inline-tabs", + "sphinx-inline-tabs", + "sphinx>=4", + "sphinxcontrib.towncrier", ] testing = [ - "pytest", - "pytest-cov", - "pytest-datadir", - "pytest-xdist", - "coverage[toml]", - "coveralls", + "coverage[toml]", + "coveralls", + "pytest", + "pytest-cov", + "pytest-datadir", + "pytest-xdist", ] [project.scripts] babelize = "babelizer.cli:babelize" +[build-system] +requires = [ + "setuptools", + "wheel", +] +build-backend = "setuptools.build_meta" + [tool.setuptools] include-package-data = true +[tool.setuptools.dynamic.readme] +file = "README.rst" +content-type = "text/x-rst" + +[tool.setuptools.dynamic.version] +attr = "babelizer._version.__version__" + [tool.setuptools.package-data] babelizer = [ - "data/*", + "data/*", ] [tool.setuptools.packages.find] -where = ["."] -include = ["babelizer*"] +where = [ + ".", +] +include = [ + "babelizer*", +] -[tool.setuptools.dynamic] -readme = {file = ["README.rst", "CREDITS.rst", "CHANGES.rst", "LICENSE.rst"]} -version = {attr = "babelizer._version.__version__"} +[tool.black] +line-length = 88 +exclude = """ +( + babelizer/data + | external +) +""" + +[tool.coverage.run] +omit = [ + "*/babelizer/data/*", +] +relative_files = true + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +combine_as_imports = true +line_length = 88 +skip = [ + "babelizer/data", +] [tool.pytest.ini_options] minversion = "6.0" -testpaths = ["babelizer", "tests"] -norecursedirs = [".*", "*.egg*", "build", "dist"] -# usefixtures = suppress_resource_warning +testpaths = [ + "babelizer", + "tests", +] +norecursedirs = [ + ".*", + "*.egg*", + "build", + "dist", +] addopts = """ --ignore setup.py --ignore babelizer/data @@ -119,26 +167,9 @@ addopts = """ doctest_optionflags = [ "NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL", - "ALLOW_UNICODE" + "ALLOW_UNICODE", ] -[tool.isort] -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -combine_as_imports = true -line_length = 88 -skip = ["babelizer/data"] - -[tool.black] -line-length = 88 -exclude = ''' -( - babelizer/data - | external -) -''' - [tool.towncrier] directory = "news" package = "babelizer" @@ -148,3 +179,7 @@ underlines = "-^\"" issue_format = "`#{issue} `_" title_format = "{version} ({project_date})" wrap = true + +[tool.zest-releaser] +tag-format = "v{version}" +python-file-with-version = "babelizer/_version.py" diff --git a/requirements.py b/requirements.py index dbe42cd7..f7b8d501 100755 --- a/requirements.py +++ b/requirements.py @@ -22,9 +22,9 @@ def requirements(extras): if extras: optional_dependencies = project.get("optional-dependencies", {}) for extra in extras: - dependencies[ - f"[project.optional-dependencies.{extra}]" - ] = optional_dependencies[extra] + dependencies[f"[project.optional-dependencies.{extra}]"] = ( + optional_dependencies[extra] + ) else: dependencies["[project.dependencies]"] = project["dependencies"] diff --git a/setup.cfg b/setup.cfg index 911da7e0..23f551ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,11 +5,3 @@ ignore = E501 W503 max-line-length = 88 - -[zest.releaser] -tag-format = v{version} -python-file-with-version = babelizer/_version.py - -[coverage:run] -omit = */babelizer/data/* -relative_files = True diff --git a/tests/test_cli.py b/tests/test_cli.py index ba9d3512..eec00562 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,4 +1,5 @@ """Test the babelizer command-line interface""" + from click.testing import CliRunner from babelizer.cli import babelize