diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a8565fbd..d61df9c38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort @@ -11,7 +11,7 @@ repos: language: python_venv language_version: python39 - - repo: https://gitlab.com/pycqa/flake8 + - repo: https://github.com/pycqa/flake8 rev: 5.0.4 hooks: - id: flake8 @@ -34,11 +34,11 @@ repos: exclude: condabuild/meta.yaml - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.981 + rev: v1.2.0 hooks: - id: mypy files: src|sphinx|test language: python_venv language_version: python39 additional_dependencies: - - numpy~=1.22 + - numpy~=1.24 diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 7564403b9..0c18f0e32 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -23,16 +23,43 @@ Release Notes correctly. +*pytools* 2.1 +------------- + +2.1.0 +~~~~~ + +- API: new decorator :obj:`.fitted_only` to mark methods that may only be + called after their associated object has been fitted using :meth:`.FittableMixin.fit`. +- API: remove method ``ensure_fitted`` from :class:`.FittableMixin`, which is no longer + needed due to the new decorator :obj:`.fitted_only`. +- API: new Sphinx callback :class:`.TrackCurrentClass` to keep track of the current + class being processed by *autodoc*. +- API: new Sphinx callback :class:`.RenamePrivateArguments` to rename private + “positional-only” arguments in a function's signature (with two leading underscores) + back to their original names in the source code, so that *autodoc* can pick them up + correctly. +- API: :class:`.Expression` objects support plain text and HTML output in Jupyter + notebooks + + *pytools* 2.0 ------------- -*pytools* 2.0 features enhanced visualisations together with additional API improvements, +*pytools* 2 introduces enhanced visualisations along with additional API improvements, and is now subject to static type checking with |mypy|. + +2.0.6 +~~~~~ + +- BUILD: add support for :mod:`pandas` |nbsp| 2.0 and above + + 2.0.5 ~~~~~ -- API: de-dent docstrings before processing them with the :obj:``.subsdoc`` decorator +- API: de-dent docstrings before processing them with the :obj:`.subsdoc` decorator - FIX: in method :meth:`.AllTracker.resolve_forward_references`, unwrap functions before accessing their ``__globals__`` attribute diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5f64d094b..eb87d04f6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -54,7 +54,7 @@ stages: versionSpec: '3.9' displayName: 'use Python 3.9' - script: | - python -m pip install isort~=5.10 + python -m pip install isort~=5.12 python -m isort --check --diff . displayName: 'Run isort' - job: @@ -87,7 +87,7 @@ stages: versionSpec: '3.9' displayName: 'use Python 3.9' - script: | - python -m pip install mypy~=0.981 numpy~=1.22 + python -m pip install mypy~=1.2.0 numpy~=1.24 python -m mypy src displayName: 'Run mypy' @@ -101,17 +101,20 @@ stages: displayName: 'detect changes' steps: - checkout: self + fetchDepth: 2 - task: Bash@3 name: diff inputs: targetType: 'inline' script: | + set -eux + echo Repo: $(Build.DefinitionName) cd $(System.DefaultWorkingDirectory) - files_changed=$(git diff $(Build.SourceVersion)^! --name-only) + files_changed=$(git diff $(Build.SourceVersion)^ --name-only) echo "Files changed since last commit: ${files_changed}" - n_files_changed=$(git diff $(Build.SourceVersion)^! --name-only | grep -i -E 'meta\.yaml|pyproject\.toml|azure-pipelines\.yml|tox\.ini|make\.py' | wc -l | xargs) + n_files_changed=$(git diff $(Build.SourceVersion)^ --name-only | grep -i -E 'meta\.yaml|pyproject\.toml|azure-pipelines\.yml|tox\.ini|make\.py' | wc -l | xargs) if [ ${n_files_changed} -gt 0 ] then build_changed=1 @@ -141,8 +144,8 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' - displayName: 'use Python 3.8' + versionSpec: '3.9' + displayName: 'use Python 3.9' - checkout: self @@ -153,10 +156,15 @@ stages: targetType: 'inline' script: | set -eux - eval "$(conda shell.bash hook)" - conda env create - conda activate $(project_name)-develop + # install micromamba + curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba + export MAMBA_ROOT_PREFIX=~/micromamba + eval "$(./bin/micromamba shell hook -s posix)" + + # install the develop environment + micromamba env create --yes --file environment.yml + micromamba activate $(project_name)-develop export PYTHONPATH=$(System.DefaultWorkingDirectory)/src/ export RUN_PACKAGE_VERSION_TEST=$(project_name) @@ -409,8 +417,8 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' - displayName: 'use Python 3.8' + versionSpec: '3.9' + displayName: 'use Python 3.9' - checkout: pytools - checkout: self @@ -474,8 +482,8 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' - displayName: 'use Python 3.8' + versionSpec: '3.9' + displayName: 'use Python 3.9' - checkout: pytools - checkout: self @@ -601,8 +609,8 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' - displayName: 'use Python 3.8' + versionSpec: '3.9' + displayName: 'use Python 3.9' - task: InstallSSHKey@0 inputs: @@ -611,7 +619,6 @@ stages: sshKeySecureFile: 'deploy_docs_$(project_name)' displayName: 'Install the deploy SSH key' - - checkout: pytools - checkout: self - script: dir $(Build.SourcesDirectory) @@ -622,7 +629,7 @@ stages: script: | set -eux - cd $(System.DefaultWorkingDirectory)/$(project_root) + cd $(System.DefaultWorkingDirectory) echo "Checking out github-pages" git checkout --track origin/github-pages @@ -642,16 +649,20 @@ stages: targetType: 'inline' script: | set -eux - eval "$(conda shell.bash hook)" - cd $(System.DefaultWorkingDirectory)/$(project_root) + cd $(System.DefaultWorkingDirectory) echo "Checking out $(branchName)" git checkout $(branchName) - conda env create - conda activate $(project_name)-develop + # install micromamba + curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba + export MAMBA_ROOT_PREFIX=~/micromamba + eval "$(./bin/micromamba shell hook -s posix)" + # install the develop environment + micromamba env create --yes --file environment.yml + micromamba activate $(project_name)-develop - export PYTHONPATH=$(System.DefaultWorkingDirectory)/$(project_root)/src/ + export PYTHONPATH=$(System.DefaultWorkingDirectory)/src/ python sphinx/make.py html @@ -662,21 +673,22 @@ stages: targetType: 'inline' script: | set -eux - eval "$(conda shell.bash hook)" + export MAMBA_ROOT_PREFIX=~/micromamba + eval "$(./bin/micromamba shell hook -s posix)" # install the tree utility sudo apt-get install tree - cd $(System.DefaultWorkingDirectory)/$(project_root) + cd $(System.DefaultWorkingDirectory) echo "Restoring previous documentation to the docs directory" mkdir -p docs mv $(Build.ArtifactStagingDirectory)/docs-version.bak docs/docs-version ls docs/docs-version - mkdir -p $(System.DefaultWorkingDirectory)/$(project_root)/sphinx/build/ + mkdir -p $(System.DefaultWorkingDirectory)/sphinx/build/ - conda activate $(project_name)-develop + micromamba activate $(project_name)-develop python sphinx/make.py prepare_docs_deployment echo "Current docs contents:" @@ -708,7 +720,7 @@ stages: targetType: 'inline' script: | set -eux - cd $(System.DefaultWorkingDirectory)/$(project_root) + cd $(System.DefaultWorkingDirectory) echo "Adjusting git credentials" git config --global user.name "Azure Pipelines" diff --git a/environment.yml b/environment.yml index 6c7a65053..4946782c2 100644 --- a/environment.yml +++ b/environment.yml @@ -4,36 +4,36 @@ channels: - bcg_gamma dependencies: # run - - joblib ~= 1.1 - - matplotlib ~= 3.5 - - numpy ~= 1.22 - - pandas ~= 1.4 + - joblib ~= 1.2 + - matplotlib ~= 3.6 + - numpy ~= 1.23 + - pandas ~= 2.0 - python ~= 3.9 - - scipy ~= 1.8 + - scipy ~= 1.10 - typing_extensions ~= 4.3 - typing_inspect ~= 0.7 # build/test - - conda-build ~= 3.21 - - conda-verify ~= 3.1 - - docutils ~= 0.17 - - flit ~= 3.0 - - jinja2 ~= 2.11 + - conda-build ~= 3.23.3 + - conda-verify ~= 3.1.1 + - docutils ~= 0.17.1 + - flit ~= 3.8.0 + - jinja2 ~= 2.11.3 - markupsafe ~= 2.0.1 # markupsafe 2.1 breaks support for jinja2 - - m2r ~= 0.2 - - pluggy ~= 0.13 - - pre-commit ~= 2.20 - - pytest ~= 7.1 - - pytest-cov ~= 2.12 - - pyyaml ~= 5.4 - - toml ~= 0.10 - - tox ~= 3.24 - - yaml ~= 0.2 + - m2r ~= 0.3.1 + - pluggy ~= 0.13.1 + - pre-commit ~= 2.21.0 + - pytest ~= 7.2.1 + - pytest-cov ~= 2.12.1 + - pyyaml ~= 5.4.1 + - toml ~= 0.10.2 + - tox ~= 3.27.1 + - yaml ~= 0.2.5 # sphinx - - sphinx ~= 4.5 - - sphinx-autodoc-typehints ~= 1.19 - nbsphinx ~= 0.8.9 + - sphinx ~= 4.5.0 + - sphinx-autodoc-typehints ~= 1.19.2 - pydata-sphinx-theme ~= 0.8.1 # notebooks - - jupyterlab ~= 3.2 - - openpyxl ~= 3.0 - - seaborn ~= 0.11 + - jupyterlab ~= 3.5.2 + - openpyxl ~= 3.0.10 + - seaborn ~= 0.12.2 diff --git a/pyproject.toml b/pyproject.toml index 11f8bef34..219cacb3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,8 +16,8 @@ license = "Apache Software License v2.0" requires = [ "joblib >=0.14,<2a", "matplotlib ~=3.0", - "numpy >=1.21,<2a", - "pandas >=0.24,<2a", + "numpy >=1.21,<2a", # cannot use ~= due to conda bug + "pandas >=1", "scipy ~=1.2", "typing_inspect ~=0.4", "typing_extensions ~=4.0", @@ -44,10 +44,7 @@ classifiers = [ [tool.flit.metadata.requires-extra] testing = [ "pytest ~= 7.1", - "pytest-cov ~= 2.8", - "flake8 ~= 3.8", - "flake8-comprehensions ~= 3.2", - "isort ~= 5.5", + "pytest-cov ~= 2.12", ] docs = [ "sphinx ~= 4.5", @@ -74,8 +71,8 @@ no-binary.min = ["matplotlib"] joblib = "~=0.14.0" matplotlib = "~=3.0.3" numpy = "==1.21.6" # cannot use ~= due to conda bug -pandas = "~=0.24.2" -python = "==3.7.12" # cannot use ~= due to conda bug +pandas = "~=1.0.5" +python = ">=3.7.12,<3.8a" # cannot use ~= due to conda bug scipy = "~=1.2.1" typing_inspect = "~=0.4.0" typing_extensions = "~=4.0.0" @@ -84,8 +81,8 @@ typing_extensions = "~=4.0.0" # maximum requirements of gamma-pytools joblib = "~=1.1" matplotlib = "~=3.5" -numpy = ">=1.23,<2a" # cannot use ~= due to conda bug -pandas = "~=1.4" +numpy = ">=1.24,<2a" # cannot use ~= due to conda bug +pandas = "~=2.0" python = ">=3.9,<4a" # cannot use ~= due to conda bug scipy = "~=1.8" typing_inspect = "~=0.7" diff --git a/src/pytools/__init__.py b/src/pytools/__init__.py index 110fd0678..58de84861 100644 --- a/src/pytools/__init__.py +++ b/src/pytools/__init__.py @@ -1,4 +1,4 @@ """ A collection of Python extensions and tools used in BCG GAMMA's open-source libraries. """ -__version__ = "2.1rc1" +__version__ = "2.1rc2" diff --git a/src/pytools/data/_matrix.py b/src/pytools/data/_matrix.py index 0be2386a2..f549adc00 100644 --- a/src/pytools/data/_matrix.py +++ b/src/pytools/data/_matrix.py @@ -128,7 +128,7 @@ def _arg_to_array( if axis_arg is None: return None else: - arr = np.array(axis_arg) + arr = np.asarray(axis_arg) if arr.ndim != 1: raise ValueError( f"arg {arg_name_}[{axis}] must be a 1d array, but has " diff --git a/src/pytools/expression/_expression.py b/src/pytools/expression/_expression.py index 25d76997a..cd433910b 100644 --- a/src/pytools/expression/_expression.py +++ b/src/pytools/expression/_expression.py @@ -5,7 +5,7 @@ import logging from abc import ABCMeta, abstractmethod -from typing import Any, Optional, Tuple, TypeVar +from typing import Any, Dict, Optional, Tuple, TypeVar import numpy as np import numpy.typing as npt @@ -107,6 +107,19 @@ def __str__(self) -> str: # to convert the expression to a formatted string return str(self.to_expression()) + def _repr_html_(self) -> str: + # get the expression representing this object, and use the default formatter + # to convert the expression to HTML for Jupyter notebooks + return f"
{self}\n
\n" + + def _repr_mimebundle_(self, **kwargs: Any) -> Dict[str, Any]: + # get plain text and HTML representations of this object + # for use in Jupyter notebooks + return { + "text/plain": str(self), + "text/html": self._repr_html_(), + } + @inheritdoc(match="[see superclass]") class Expression(HasExpressionRepr, metaclass=ABCMeta): diff --git a/src/pytools/sphinx/util/_util.py b/src/pytools/sphinx/util/_util.py index bf4b86028..c94dca749 100644 --- a/src/pytools/sphinx/util/_util.py +++ b/src/pytools/sphinx/util/_util.py @@ -71,7 +71,7 @@ class _Text(_Element): rawsource: str # noinspection SpellCheckingInspection - def astext(self) -> str: + def astext(self) -> str: # type: ignore ... Sphinx = type("Sphinx", (object,), {}) @@ -107,11 +107,11 @@ def astext(self) -> str: ] # -# Type aliases +# Type variables # -method_descriptor = type(str.__dict__["startswith"]) -wrapper_descriptor = type(str.__dict__["__add__"]) +method_descriptor: Type[Any] = type(str.startswith) +wrapper_descriptor: Type[Any] = type(str.__add__) # diff --git a/src/pytools/viz/color/_rgb.py b/src/pytools/viz/color/_rgb.py index d36f2257d..aa9409d49 100644 --- a/src/pytools/viz/color/_rgb.py +++ b/src/pytools/viz/color/_rgb.py @@ -32,7 +32,7 @@ # -# Type Aliases +# Type aliases # TupleRgb: TypeAlias = Tuple[float, float, float] diff --git a/test/test/pytools/test_expression.py b/test/test/pytools/test_expression.py index b04c12c82..62b17af2c 100644 --- a/test/test/pytools/test_expression.py +++ b/test/test/pytools/test_expression.py @@ -94,6 +94,34 @@ def test_expression_formatting() -> None: assert repr(expr_6) == "(lambda x, y: f((1 | 2) >> 'x' % x, abc=-5))(5, 6)" +def test_expression_repr_html() -> None: + + # create an expression + e = Call(Id.f, (1 | Lit(2)) >> Lit("x") % Id.x, abc=-Lit(5)) + expr = e * (e + e + e - e * e) + + # create the expected representations + expected_formatted_expression = """( + f((1 | 2) >> 'x' % x, abc=-5) + * ( + f((1 | 2) >> 'x' % x, abc=-5) + + f((1 | 2) >> 'x' % x, abc=-5) + + f((1 | 2) >> 'x' % x, abc=-5) + - f((1 | 2) >> 'x' % x, abc=-5) * f((1 | 2) >> 'x' % x, abc=-5) + ) +)""" + expected_html_expression = f"
{expected_formatted_expression}\n
\n" + + # test if the html representation is generated as expected + assert expr._repr_html_() == expected_html_expression + + # test if the mimebundle representation is generated as expected + assert expr._repr_mimebundle_() == { + "text/html": expected_html_expression, + "text/plain": expected_formatted_expression, + } + + def test_expression() -> None: lit_5 = Lit(5) lit_abc = Lit("abc")