diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b560b4..e059ae2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12", 3.13] steps: - uses: actions/checkout@v4 @@ -53,7 +53,7 @@ jobs: fail-fast: false max-parallel: 4 matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12", 3.13] steps: - uses: actions/checkout@v4 @@ -63,7 +63,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - pip install -r requirements-dev.txt + pip install --upgrade pip + pip install -e ".[dev,examples]" - name: Test with pytest run: | pytest tests --cov=pysensors --cov-report=xml @@ -71,7 +72,7 @@ jobs: uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml + files: ./coverage.xml - name: Execute feature notebook with papermill run: | pip install papermill @@ -80,6 +81,6 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de0d049..cb6801e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,4 +23,6 @@ repos: rev: 7.1.1 hooks: - id: flake8 - args: ["--config=setup.cfg"] \ No newline at end of file + args: + - --max-line-length=88 + - --extend-ignore=E203,F401 \ No newline at end of file diff --git a/README.rst b/README.rst index c54829e..17acc24 100644 --- a/README.rst +++ b/README.rst @@ -141,7 +141,7 @@ Installation Dependencies ^^^^^^^^^^^^ -The high-level dependencies for PySensors are Linux or macOS and Python 3.9-3.12. ``pip`` is also recommended as is makes managing PySensors' other dependencies much easier. You can install it by following the instructions `here `__. +The requirements for PySensors are Linux or macOS and Python 3.10-3.13. ``pip`` is recommended as it makes managing PySensors' other dependencies much easier. You can install it by following the instructions `here `__. PySensors has not been tested on Windows. @@ -174,13 +174,12 @@ Then, to install the package, run cd pysensors pip install . -If you do not have pip you can instead use +If you do not have root access, you should add the ``--user`` option to the install command above. +If you want an editable install (recommended for development), run .. code-block:: bash - python setup.py install - -If you do not have root access, you should add the ``--user`` option to the ``install`` commands above. + pip install -e . Features @@ -213,11 +212,11 @@ Documentation ------------- PySensors has a `documentation site `__ hosted by readthedocs. Examples are available `online `__, as static -`Jupyter notebooks `__ and as `interactive notebooks `__. To run the example notebooks locally you should install the dependencies in ``requirements-examples.txt``: +`Jupyter notebooks `__ and as `interactive notebooks `__. To run the example notebooks locally, you should install the examples dependencies: .. code-block:: bash - pip install -r requirements-examples.txt + pip install -e ".[examples]" Community guidelines -------------------- @@ -233,11 +232,11 @@ your work! Contributing code ^^^^^^^^^^^^^^^^^ -We welcome contributions to PySensors. To contribute a new feature please submit a pull request. To get started we recommend installing the packages in ``requirements-dev.txt`` via +We welcome contributions to PySensors. To contribute a new feature please submit a pull request. To get started, we recommend installing the optional dev packages .. code-block:: bash - pip install -r requirements-dev.txt + pip install -e ".[dev]" This will allow you to run unit tests and automatically format your code. To be accepted your code should conform to PEP8 and pass all unit tests. Code can be tested by invoking diff --git a/pyproject.toml b/pyproject.toml index c37c501..c58c67e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,67 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4", "setuptools_scm_git_archive"] -build-backend = "setuptools.build_meta:__legacy__" +requires = [ + "setuptools>=64", + "wheel", + "setuptools_scm[toml]>=8", + "setuptools_scm_git_archive", +] +build-backend = "setuptools.build_meta" -[tool.setuptools_scm] -write_to = "pysensors/version.py" +[project] +name = "python-sensors" +description = "Sparse Sensor Placement" +authors = [ + {name="Brian de Silva", email="bdesilva@uw.edu"}, + {name="Krithika Manohar", email="kmanohar@uw.edu"}, + {name="Emily Clark", email="emily.e.clark93@gmail.com"}, +] +license = {text = "MIT"} +requires-python = ">=3.10" +dynamic = ["version"] +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Topic :: Scientific/Engineering :: Mathematics", +] +readme = "README.rst" +dependencies = [ + "scikit-learn", + "numpy>=2.0", + "matplotlib>=3.10", + "pandas", + "scipy" +] + +[project.urls] +Homepage = "https://github.com/dynamicslab/pysensors" +Repository = "https://github.com/dynamicslab/pysensors" + +[project.optional-dependencies] +dev = [ + "pytest >=6.2.4, <8.0.0", + "pytest-cov", + "pytest-lazy-fixture", + "flake8-builtins-unleashed", + "codecov", + "sphinx >= 2", + "sphinxcontrib-apidoc", + "sphinx_rtd_theme", + "pre-commit", + "nbsphinx", +] +examples = [ + "jupyter", + "matplotlib", + "netCDF4", + "notebook", + "seaborn", +] [tool.black] line-length = 88 @@ -21,4 +79,21 @@ exclude = ''' filterwarnings = [ "ignore::RuntimeWarning", "ignore::UserWarning" +] + +[tool.setuptools] +packages = { find = { exclude = ["tests", "examples"] } } + +[tool.setuptools_scm] +write_to = "pysensors/version.py" + +[tool.flake8] +exclude = [ + ".git", + ".venv", + "dist", + "build", + "docs", + "examples", + "__pycache__", ] \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 20231f0..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,15 +0,0 @@ --e . --r requirements.txt --r requirements-examples.txt -pytest >=6.2.4, <8.0.0 -pytest-cov -pytest-lazy-fixture -flake8-builtins-unleashed -codecov -setuptools_scm -setuptools_scm_git_archive -sphinx >= 2 -sphinxcontrib-apidoc -sphinx_rtd_theme -pre-commit -nbsphinx \ No newline at end of file diff --git a/requirements-examples.txt b/requirements-examples.txt deleted file mode 100644 index c8a7d7c..0000000 --- a/requirements-examples.txt +++ /dev/null @@ -1,6 +0,0 @@ -jupyter -matplotlib -netCDF4 -notebook -pandas -seaborn \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9198ba0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -scikit-learn>=0.23 -matplotlib -numpy -pandas -scipy diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 135daaf..0000000 --- a/setup.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[flake8] -exclude = - .git, - .venv, - dist, - build, - __pycache__ -ignore = - # Line break before binary operator - Conflicts black - W503 - # Whitespace before ':' - Conflicts black - E203 -per-file-ignores = - __init__.py:F401,F403 -max-line-length = 88 -import-order-style = smarkets -statistics = True -count = True -verbose = 1 -# format = [%(code)s] %(text)s @ %(path)s:%(row)d:%(col)d \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index ff72e92..0000000 --- a/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -import pathlib -import sys - -from setuptools import find_packages, setup - -assert sys.version_info >= (3, 9, 0), "pysensors requires Python 3.9+" - -NAME = "python-sensors" -DESCRIPTION = "Sparse sensor placement" -URL = "https://github.com/dynamicslab/pysensors" -EMAIL = "bdesilva@uw.edu, kmanohar@uw.edu, emily.e.clark93@gmail.com" -AUTHOR = "Brian de Silva, Krithika Manohar, Emily Clark" -PYTHON = ">=3.6" -LICENSE = "MIT" -CLASSIFIERS = [ - "Programming Language :: Python", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Development Status :: 4 - Beta", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", - "Topic :: Scientific/Engineering :: Mathematics", -] - -here = pathlib.Path(__file__).parent - -with open(here / "requirements.txt", "r") as f: - REQUIRED = f.readlines() - -with open(here / "README.rst", "r") as f: - LONG_DESCRIPTION = f.read() - - -setup( - name=NAME, - use_scm_version=True, - setup_requires=["setuptools_scm", "setuptools_scm_git_archive"], - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - long_description_content_type="text/x-rst", - author=AUTHOR, - author_email=EMAIL, - url=URL, - packages=find_packages(exclude=["test", "examples"]), - install_requires=REQUIRED, - python_requires=PYTHON, - license=LICENSE, - classifiers=CLASSIFIERS, -) diff --git a/tests/utils/test_constraints.py b/tests/utils/test_constraints.py index ea1eae5..b711a8e 100644 --- a/tests/utils/test_constraints.py +++ b/tests/utils/test_constraints.py @@ -3580,11 +3580,15 @@ def test_draw_with_numpy_array_and_equation(self): constraint = UserDefinedConstraints( all_sensors=all_sensors, equation="x**2 + y**2 <= 4", data=sample_array ) - with patch( - "pysensors.utils._constraints.get_coordinates_from_indices" - ) as mock_get_coords, patch("builtins.eval") as mock_eval, patch.object( - BaseConstraint, "get_functionalConstraind_sensors_indices" - ) as mock_get_func: + with ( + patch( + "pysensors.utils._constraints.get_coordinates_from_indices" + ) as mock_get_coords, + patch("builtins.eval") as mock_eval, + patch.object( + BaseConstraint, "get_functionalConstraind_sensors_indices" + ) as mock_get_func, + ): x_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) y_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) mock_get_coords.side_effect = [ @@ -3634,13 +3638,17 @@ def test_function(x, y): all_sensors=all_sensors, file="test_constraint.py", data=sample_array ) assert constraint.functions == test_function - with patch.object( - BaseConstraint, "functional_constraints" - ) as mock_func_constr, patch.object( - BaseConstraint, "get_functionalConstraind_sensors_indices" - ) as mock_get_func, patch( - "pysensors.utils._constraints.get_coordinates_from_indices" - ) as mock_get_coords: + with ( + patch.object( + BaseConstraint, "functional_constraints" + ) as mock_func_constr, + patch.object( + BaseConstraint, "get_functionalConstraind_sensors_indices" + ) as mock_get_func, + patch( + "pysensors.utils._constraints.get_coordinates_from_indices" + ) as mock_get_coords, + ): mock_func_constr.return_value = np.array([1.5, -0.5, 0.8, -1.2, 2.3]) mock_get_func.return_value = (np.array([0, 2, 4]), np.array([0, 1, 2])) mock_get_coords.return_value = (