From 4f39341c13bf820bc7b599033d2cb85745448316 Mon Sep 17 00:00:00 2001 From: wpk Date: Wed, 1 Nov 2023 15:35:34 -0400 Subject: [PATCH 01/19] chore: update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 69bbfb1..9f452d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -288,7 +288,7 @@ ignore = [ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [tool.ruff.per-file-ignores] -"src/pyproject2conda.py" = ["UP006", "UP007"] +"src/pyproject2conda/cli.py" = ["UP006", "UP007"] [tool.ruff.isort] known-first-party = ["pyproject2conda"] From 958e8cb676ef3c24ffb9ebbb562dca128cf821cd Mon Sep 17 00:00:00 2001 From: wpk Date: Wed, 1 Nov 2023 16:05:07 -0400 Subject: [PATCH 02/19] chore: update cruft --- .cruft.json | 2 +- .pre-commit-config.yaml | 20 +++---- CHANGELOG.md | 7 ++- CONTRIBUTING.md | 13 +++-- Makefile | 10 +++- docs/conf.py | 8 ++- noxfile.py | 105 +++++++++++++++++++++++++++------- pyproject.toml | 49 ++++------------ src/pyproject2conda/parser.py | 4 +- tests/test_parser.py | 79 +++++++++++++++++++++++++ tools/create_pythons.py | 3 + 11 files changed, 214 insertions(+), 86 deletions(-) diff --git a/.cruft.json b/.cruft.json index 4c1d0fc..4a3e774 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "7ba78569cba4c2b39c2706d1f95b74c919b310cf", + "commit": "41b5d67426f708650af617193e42e081fe965649", "checkout": "develop", "context": { "cookiecutter": { diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b38489..a16a15c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ default_install_hook_types: repos: # * Top level - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -39,7 +39,7 @@ repos: # * Markdown - repo: https://github.com/DavidAnson/markdownlint-cli2 - rev: v0.9.2 + rev: v0.10.0 hooks: - id: markdownlint-cli2 args: ["--style prettier"] @@ -47,11 +47,11 @@ repos: # * Linting - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: "v0.0.289" + rev: "v0.1.3" hooks: - id: ruff - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.1 hooks: # NOTE: nbQA for notebook formatting - id: black @@ -60,19 +60,19 @@ repos: hooks: - id: blacken-docs additional_dependencies: - - black==23.9.1 + - black==23.10.1 # exclude: ^README.md - repo: https://github.com/nbQA-dev/nbQA rev: 1.7.0 hooks: - id: nbqa-ruff - additional_dependencies: [ruff==0.0.289] + additional_dependencies: [ruff==0.1.3] - id: nbqa-black - additional_dependencies: [black==23.9.1] + additional_dependencies: [black==23.10.1] # * Commit message - repo: https://github.com/commitizen-tools/commitizen - rev: 3.9.0 + rev: 3.12.0 hooks: - id: commitizen stages: [commit-msg] @@ -85,7 +85,7 @@ repos: - id: isort stages: [manual] - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.15.0 hooks: - id: pyupgrade stages: [manual] @@ -113,7 +113,7 @@ repos: stages: [manual] # ** spelling - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell types_or: [python, rst, markdown, cython, c] diff --git a/CHANGELOG.md b/CHANGELOG.md index 844cdea..bfcef24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ + + # Changelog @@ -6,8 +8,9 @@ Changelog for `pyproject2conda` ## Unreleased -See the fragment files in -[changelog.d](https://github.com/usnistgov/pyproject2conda) +[changelog.d]: https://github.com/usnistgov/pyproject2conda + +See the fragment files in [changelog.d] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e1a4b7f..da0f48f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,9 @@ You can contribute in many ways: ## Types of Contributions + [issues]: https://github.com/usnistgov/pyproject2conda/issues + ### Report Bugs @@ -31,9 +33,9 @@ and "help wanted" is open to whoever wants to implement it. ### Write Documentation -`pyproject2conda` could always use more documentation, whether as part of the -official `pyproject2conda` docs, in docstrings, or even on the web in blog -posts, articles, and such. +This project could always use more documentation, whether as part of the +official docs, in docstrings, or even on the web in blog posts, articles, and +such. ### Submit Feedback @@ -48,10 +50,9 @@ If you are proposing a feature: ## Making a contribution -Ready to contribute? Here's how to set up `pyproject2conda` for local -development. +Ready to contribute? Here's how to make a contribution. -- Fork the `pyproject2conda` repo on GitHub. +- Fork the repo on GitHub. - Clone your fork locally: diff --git a/Makefile b/Makefile index 4ff8a3e..d05ad2a 100644 --- a/Makefile +++ b/Makefile @@ -141,6 +141,9 @@ version-scm: ## check/update version of package with setuptools-scm version-import: ## check version from python import -python -c 'import pyproject2conda; print(pyproject2conda.__version__)' +version-update: ## update version using nox + nox -s update-version-scm + version: version-scm version-import ################################################################################ @@ -149,9 +152,10 @@ version: version-scm version-import .PHONY: requirements requirements: ## rebuild all requirements/environment files nox -s requirements - -requirements/%.yaml: pyproject.toml requirements -requirements/%.txt: pyproject.toml requirements +requirements/%.yaml: pyproject.toml + nox -s requirements +requirements/%.txt: pyproject.toml + nox -s requirements ################################################################################ # * NOX diff --git a/docs/conf.py b/docs/conf.py index 7d6c757..2f5b230 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,7 +58,6 @@ # "sphinx_design" ## myst stuff "myst_nb", - # project specific "sphinx_click", ] @@ -114,7 +113,10 @@ # nb_execution_mode = "auto" # set the kernel name -nb_kernel_rgx_aliases = {"pyproject2conda.*": "python3", "conda.*": "python3"} +nb_kernel_rgx_aliases = { + "pyproject2conda.*": "python3", + "conda.*": "python3", +} nb_execution_allow_errors = True @@ -478,7 +480,9 @@ def linkcode_resolve(domain, info): else: linespec = "" + # fmt: off fn = os.path.relpath(fn, start=os.path.dirname(pyproject2conda.__file__)) + # fmt: on return f"https://github.com/{github_username}/pyproject2conda/blob/{html_context['github_version']}/src/pyproject2conda/{fn}{linespec}" diff --git a/noxfile.py b/noxfile.py index 1cbd226..5a487f9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,21 +4,34 @@ import shutil import sys + +# Should only use on python version > 3.10 +assert sys.version_info >= (3, 10) + from dataclasses import replace # noqa from pathlib import Path from typing import ( - Annotated, Any, Callable, Iterator, Sequence, - TypeAlias, TypeVar, cast, ) -import nox -from noxopt import NoxOpt, Option, Session +if sys.version_info > (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +if sys.version_info > (3, 9): + from typing import Annotated +else: + from typing_extensions import Annotated + + +import nox # type: ignore[unused-ignore,import] +from noxopt import NoxOpt, Option, Session # type: ignore[unused-ignore,import] # fmt: off sys.path.insert(0, ".") @@ -88,6 +101,9 @@ DEFAULT_SESSION_VENV = cast(C[F], group.session(python=PYTHON_DEFAULT_VERSION)) # type: ignore ALL_SESSION_VENV = cast(C[F], group.session(python=PYTHON_ALL_VERSIONS)) # type: ignore +NOPYTHON_SESSION = cast(C[F], group.session(python=False)) # type: ignore +INHERITED_SESSION_VENV = cast(C[F], group.session) # type: ignore + OPTS_OPT = Option(nargs="*", type=str) # SET_KERNEL_OPT = Option(type=bool, help="If True, try to set the kernel name") RUN_OPT = Option( @@ -101,20 +117,20 @@ LOCK_OPT = Option(type=bool, help="If True, use conda-lock") -def opts_annotated(**kwargs: Any): # type: ignore - return Annotated[list[str], replace(OPTS_OPT, **kwargs)] +def opts_annotated(**kwargs: Any): # type: ignore[unused-ignore,no-untyped-def] + return Annotated["list[str]", replace(OPTS_OPT, **kwargs)] -def cmd_annotated(**kwargs: Any): # type: ignore - return Annotated[list[str], replace(CMD_OPT, **kwargs)] +def cmd_annotated(**kwargs: Any): # type: ignore[unused-ignore,no-untyped-def] + return Annotated["list[str]", replace(CMD_OPT, **kwargs)] -def run_annotated(**kwargs: Any): # type: ignore - return Annotated[list[list[str]], replace(RUN_OPT, **kwargs)] +def run_annotated(**kwargs: Any): # type: ignore[unused-ignore,no-untyped-def] + return Annotated["list[list[str]]", replace(RUN_OPT, **kwargs)] LOCK_CLI = Annotated[bool, LOCK_OPT] -RUN_CLI = Annotated[list[list[str]], RUN_OPT] +RUN_CLI = Annotated["list[list[str]]", RUN_OPT] TEST_OPTS_CLI = opts_annotated(help="extra arguments/flags to pytest") DEV_EXTRAS_CLI = cmd_annotated(help="extras included in user dev environment") PYTHON_PATHS_CLI = cmd_annotated(help="python paths to append to PATHS") @@ -192,8 +208,8 @@ def dev_venv( # ** bootstrap -@group.session(python=False) # type: ignore -def bootstrap(session: Session): +@NOPYTHON_SESSION +def bootstrap(session: Session) -> None: """Run config, reqs, and dev""" session.notify("config") @@ -202,7 +218,7 @@ def bootstrap(session: Session): # ** config -@group.session(python=False) # type: ignore +@NOPYTHON_SESSION def config( session: Session, dev_extras: DEV_EXTRAS_CLI = [], # type: ignore # noqa @@ -220,7 +236,7 @@ def config( # ** requirements -@group.session(python=False) # type: ignore +@NOPYTHON_SESSION def pyproject2conda( session: Session, update: UPDATE_CLI = False, @@ -229,7 +245,7 @@ def pyproject2conda( session.notify("requirements") -@group.session +@INHERITED_SESSION_VENV def requirements( session: Session, update: UPDATE_CLI = False, @@ -243,7 +259,7 @@ def requirements( """ pkg_install_venv( session=session, - reqs=["."], + reqs=["pyproject2conda>=0.8.0"], name="reqs", update=update, log_session=log_session, @@ -423,7 +439,7 @@ def _coverage( session_run_commands(session, run) if not cmd and not run and not run_internal: - cmd = ["combine", "report"] + cmd = ["combine", "html"] session.log(f"{cmd}") @@ -755,13 +771,13 @@ def dist_conda( elif command == "recipe-cat-full": import tempfile - with tempfile.TemporaryDirectory() as d: + with tempfile.TemporaryDirectory() as d: # type: ignore[assignment,unused-ignore] session.run( "grayskull", "pypi", sdist_path, "-o", - d, + str(d), ) session.run( "cat", str(Path(d) / PACKAGE_NAME / "meta.yaml"), external=True @@ -778,7 +794,7 @@ def dist_conda( # ** lint -@group.session +@INHERITED_SESSION_VENV def lint( session: nox.Session, lint_run: RUN_CLI = [], # noqa @@ -828,6 +844,15 @@ def _run_info(cmd: str) -> None: session.run("which", cmd, external=True) session.run(cmd, "--version", external=True) + if "clean" in cmd: + cmd = [x for x in cmd if x != "clean"] + + for name in [".mypy_cache", ".pytype"]: + p = Path(session.create_tmp()) / name + if p.exists(): + session.log(f"removing cache {p}") + shutil.rmtree(str(p)) + for c in cmd: if not c.startswith("nbqa"): _run_info(c) @@ -849,6 +874,7 @@ def typing( session: nox.Session, typing_cmd: cmd_annotated( # type: ignore choices=[ + "clean", "mypy", "pyright", "pytype", @@ -891,6 +917,7 @@ def typing_venv( session: nox.Session, typing_cmd: cmd_annotated( # type: ignore choices=[ + "clean", "mypy", "pyright", "pytype", @@ -1046,6 +1073,42 @@ def testdist_pypi_condaenv( ) +@DEFAULT_SESSION_VENV +def update_version_scm( + session: Session, + version: VERSION_CLI = "", + update: UPDATE_CLI = False, +) -> None: + """ + Get current version from setuptools-scm + + Note that the version of editable installs can get stale. + This will show the actual current version. + Avoids need to include setuptools-scm in develop/docs/etc. + """ + + if version: + session.env["SETUPTOOLS_SCM_PRETEND_VERSION"] = version + + pkg_install_venv( + session=session, + name="update-version-scm", + install_package=True, + # reqs=["setuptools_scm"], + update=True, + no_deps=True, + ) + + session.run( + "python", + "-c", + "import sys;" + "sys.path.insert(0, 'src');" + "from pyproject2conda._version import __version__;" + "print(__version__);", + ) + + # * Utilities -------------------------------------------------------------------------- def _create_doc_examples_symlinks(session: nox.Session, clean: bool = True) -> None: """Create symlinks from docs/examples/*.md files to /examples/usage/...""" diff --git a/pyproject.toml b/pyproject.toml index 9f452d1..75efcd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61.2", "setuptools_scm[toml]>=7.0"] +requires = ["setuptools>=61.2", "setuptools_scm[toml]>=8.0"] build-backend = "setuptools.build_meta" [project] @@ -20,29 +20,18 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Scientific/Engineering", ] dynamic = ["readme", "version"] requires-python = ">=3.8" -dependencies = [ - "tomli", - "ruamel.yaml", - "tomlkit", - "typer", - # "rich-click", - "packaging", - "typing-extensions; python_version<'3.9'", -] # additional packages +dependencies = ["typer"] [project.urls] homepage = "https://github.com/usnistgov/pyproject2conda" documentation = "https://pages.nist.gov/pyproject2conda/" [project.optional-dependencies] -all = [ - "rich", # - "shellingham", -] test = [ "pytest", # "pytest-xdist", @@ -50,17 +39,15 @@ test = [ "pytest-sugar", ] dev-extras = [ - "setuptools-scm", # + "setuptools-scm>=8.0", # "pytest-accept", # p2c: -p + # "nbval", "ipython", "ipykernel", - # "nbval", - ] typing-extras = [ "pytype; python_version < '3.11'", # "mypy >= 1.4.1", - "types-click", ] typing = [ "pyproject2conda[typing-extras]", # @@ -99,7 +86,6 @@ docs = [ "myst-nb", "sphinx-book-theme", "autodocsumm", - # project specific "sphinx-click", ] # to be parsed with pyproject2conda with --no-base option @@ -149,8 +135,7 @@ envs = ["dist-conda"] style = ["yaml"] [project.scripts] -pyproject2conda = "pyproject2conda.cli:app" -p2c = "pyproject2conda.cli:app" +pyproject2conda = "pyproject2conda.cli:main" ## grayskull still messes some things up, but use scripts/recipe-append.sh for this [tool.setuptools] @@ -173,7 +158,7 @@ readme = { file = [ [tool.setuptools_scm] fallback_version = "999" -write_to = "src/pyproject2conda/_version.py" +version_file = "src/pyproject2conda/_version.py" [tool.aliases] test = "pytest" @@ -284,12 +269,10 @@ ignore = [ # Allow relative imports "TID252", ] +per-file-ignores = { } # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -[tool.ruff.per-file-ignores] -"src/pyproject2conda/cli.py" = ["UP006", "UP007"] - [tool.ruff.isort] known-first-party = ["pyproject2conda"] @@ -307,7 +290,7 @@ ignore = [ "E203", # module level import not at top of file "E402", - # line too long - let black worry about that + # line t oo long - let black worry about that "E501", # do not assign a lambda expression, use a def "E731", @@ -343,14 +326,13 @@ use_shortcuts = true [tool.cruft] [tool.mypy] -files = ["src/pyproject2conda", "tests"] +files = ["src", "tests", "noxfile.py", "tools"] show_error_codes = true warn_unused_ignores = true warn_return_any = true warn_unused_configs = true exclude = [".eggs", ".tox", "doc", "docs", ".nox"] check_untyped_defs = true -strict = true [[tool.mypy.overrides]] ignore_missing_imports = true @@ -362,16 +344,7 @@ module = [] [tool.pyright] include = ["src", "tests"] -exclude = [ - "**/__pycache__", - ".tox/**", - ".nox/**", - "**/.mypy_cache", - "**/_version.py" -] -# extraPaths = ["."] -# TODO: add strict to pyright -# strict = ["src"] +exclude = ["**/__pycache__", ".tox/**", ".nox/**", "**/.mypy_cache"] pythonVersion = "3.10" typeCheckingMode = "basic" # enable subset of "strict" diff --git a/src/pyproject2conda/parser.py b/src/pyproject2conda/parser.py index 50b035d..4f58018 100644 --- a/src/pyproject2conda/parser.py +++ b/src/pyproject2conda/parser.py @@ -203,9 +203,7 @@ def _pyproject_to_value_comment_pairs( unique: bool = True, include_base_dependencies: bool = True, ) -> list[tuple[Tstr_opt, Tstr_opt]]: - package_name = cast( - str, get_in(["project", "name"], data, factory=_factory_empty_tomlkit_Array) - ) + package_name = cast(str, get_in(["project", "name"], data, default=None)) if package_name is None: raise ValueError("Must specify `project.package_name` in pyproject.toml") diff --git a/tests/test_parser.py b/tests/test_parser.py index 05e4cf3..26a3bf0 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -189,6 +189,85 @@ def test_output_to_yaml(): assert s == dedent(expected) +def test_infer(): + toml = dedent( + """\ + [project] + name = "hello" + dependencies = [ + "athing", # p2c: -p # a comment + "bthing", # p2c: -s bthing-conda + "cthing; python_version<'3.10'", # p2c: -c conda-forge + ] + + [project.optional-dependencies] + test = [ + "pandas", + "pytest", # p2c: -c conda-forge + ] + dev-extras = [ + # p2c: -s additional-thing # this is an additional conda package + "matplotlib", # p2c: -s conda-matplotlib + ] + dev = [ + "hello[test]", + "hello[dev-extras]", + ] + dist-pypi = [ + "setuptools", + "build", # p2c: -p + ] + + + [tool.pyproject2conda] + channels = ['conda-forge'] + """ + ) + + d = parser.PyProject2Conda.from_string(toml) + with pytest.raises(ValueError): + d.to_conda_yaml(python_include="infer") + + +def test_package_name(): + toml = dedent( + """\ + [project] + requires-python = ">=3.8,<3.11" + dependencies = [ + "athing", # p2c: -p # a comment + "bthing", # p2c: -s bthing-conda + "cthing; python_version<'3.10'", # p2c: -c conda-forge + ] + + [project.optional-dependencies] + test = [ + "pandas", + "pytest", # p2c: -c conda-forge + ] + dev-extras = [ + # p2c: -s additional-thing # this is an additional conda package + "matplotlib", # p2c: -s conda-matplotlib + ] + dev = [ + "hello[test]", + "hello[dev-extras]", + ] + dist-pypi = [ + "setuptools", + "build", # p2c: -p + ] + + + [tool.pyproject2conda] + channels = ['conda-forge'] + """ + ) + d = parser.PyProject2Conda.from_string(toml) + with pytest.raises(ValueError): + d.to_conda_lists() + + def test_complete(): toml = dedent( """\ diff --git a/tools/create_pythons.py b/tools/create_pythons.py index 1be3ec5..c075ea0 100644 --- a/tools/create_pythons.py +++ b/tools/create_pythons.py @@ -1,8 +1,11 @@ """Script to create pythons for use with virtualenvs""" from __future__ import annotations +import sys from functools import lru_cache +assert sys.version_info >= (3, 9) + @lru_cache def conda_cmd() -> str: From a06696b6f7256803819c5d1879bd03b1df393e34 Mon Sep 17 00:00:00 2001 From: wpk Date: Thu, 2 Nov 2023 10:17:57 -0400 Subject: [PATCH 03/19] chore: fixup --- noxfile.py | 18 ++++++++++++++++++ pyproject.toml | 21 ++++++++++++++++++--- requirements/dev-base.txt | 2 +- requirements/dev-complete.txt | 2 +- requirements/dev.txt | 2 +- requirements/py310-dev-base.yaml | 2 +- requirements/py310-dev-complete.yaml | 2 +- 7 files changed, 41 insertions(+), 8 deletions(-) diff --git a/noxfile.py b/noxfile.py index 4d71796..c10b598 100644 --- a/noxfile.py +++ b/noxfile.py @@ -504,6 +504,10 @@ def _docs( if open_page := "open" in cmd: cmd.remove("open") + if serve := "serve" in cmd: + open_webpage(url="http://localhost:8000") + cmd.remove("serve") + if cmd: args = ["make", "-C", "docs"] + combine_list_str(cmd) session.run(*args, external=True) @@ -511,6 +515,18 @@ def _docs( if open_page: open_webpage(path="./docs/_build/html/index.html") + if serve and "livehtml" not in cmd: + session.run( + "python", + "-m", + "http.server", + "-d", + "docs/_build/html", + "-b", + "127.0.0.1", + "8000", + ) + @DEFAULT_SESSION def docs( @@ -527,6 +543,7 @@ def docs( "showlinks", "release", "open", + "serve", ], flags=("--docs-cmd", "-d"), ) = (), @@ -567,6 +584,7 @@ def docs_venv( "showlinks", "release", "open", + "serve", ], flags=("--docs-cmd", "-d"), ) = (), diff --git a/pyproject.toml b/pyproject.toml index 75efcd2..3981fc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,13 +25,24 @@ classifiers = [ ] dynamic = ["readme", "version"] requires-python = ">=3.8" -dependencies = ["typer"] +dependencies = [ + "tomli", + "ruamel.yaml", + "tomlkit", + "typer", + "packaging", + "typing-extensions; python_version<'3.9'", +] [project.urls] homepage = "https://github.com/usnistgov/pyproject2conda" documentation = "https://pages.nist.gov/pyproject2conda/" [project.optional-dependencies] +all = [ + "rich", # + "shellingham", +] test = [ "pytest", # "pytest-xdist", @@ -48,6 +59,7 @@ dev-extras = [ typing-extras = [ "pytype; python_version < '3.11'", # "mypy >= 1.4.1", + "types-click", ] typing = [ "pyproject2conda[typing-extras]", # @@ -135,7 +147,7 @@ envs = ["dist-conda"] style = ["yaml"] [project.scripts] -pyproject2conda = "pyproject2conda.cli:main" +pyproject2conda = "pyproject2conda.cli:app" ## grayskull still messes some things up, but use scripts/recipe-append.sh for this [tool.setuptools] @@ -269,10 +281,12 @@ ignore = [ # Allow relative imports "TID252", ] -per-file-ignores = { } # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +[tool.ruff.per-file-ignores] +"src/pyproject2conda/cli.py" = ["UP006", "UP007"] + [tool.ruff.isort] known-first-party = ["pyproject2conda"] @@ -333,6 +347,7 @@ warn_return_any = true warn_unused_configs = true exclude = [".eggs", ".tox", "doc", "docs", ".nox"] check_untyped_defs = true +strict = true [[tool.mypy.overrides]] ignore_missing_imports = true diff --git a/requirements/dev-base.txt b/requirements/dev-base.txt index 2f29a5f..bee2e1e 100644 --- a/requirements/dev-base.txt +++ b/requirements/dev-base.txt @@ -18,7 +18,7 @@ pytest-sugar pytest-xdist pytype; python_version < '3.11' ruamel.yaml -setuptools-scm +setuptools-scm>=8.0 tomli tomlkit typer diff --git a/requirements/dev-complete.txt b/requirements/dev-complete.txt index ad0d5f7..75e7985 100644 --- a/requirements/dev-complete.txt +++ b/requirements/dev-complete.txt @@ -24,7 +24,7 @@ pytest-xdist pytype; python_version < '3.11' ruamel.yaml scriv -setuptools-scm +setuptools-scm>=8.0 tomli tomlkit typer diff --git a/requirements/dev.txt b/requirements/dev.txt index 03276c3..8a3ffd5 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -20,7 +20,7 @@ pytest-sugar pytest-xdist pytype; python_version < '3.11' ruamel.yaml -setuptools-scm +setuptools-scm>=8.0 tomli tomlkit typer diff --git a/requirements/py310-dev-base.yaml b/requirements/py310-dev-base.yaml index 54f9d7d..c007719 100644 --- a/requirements/py310-dev-base.yaml +++ b/requirements/py310-dev-base.yaml @@ -21,7 +21,7 @@ dependencies: - pytest-xdist - pytype - ruamel.yaml - - setuptools-scm + - setuptools-scm>=8.0 - tomli - tomlkit - typer diff --git a/requirements/py310-dev-complete.yaml b/requirements/py310-dev-complete.yaml index fc3f256..4e55e51 100644 --- a/requirements/py310-dev-complete.yaml +++ b/requirements/py310-dev-complete.yaml @@ -25,7 +25,7 @@ dependencies: - pytest-xdist - pytype - ruamel.yaml - - setuptools-scm + - setuptools-scm>=8.0 - tomli - tomlkit - typer From b0f3336052408ad2209fccd69969d2f17042bbc9 Mon Sep 17 00:00:00 2001 From: wpk Date: Thu, 2 Nov 2023 12:43:11 -0400 Subject: [PATCH 04/19] chore: udpate cruft --- .cruft.json | 2 +- CHANGELOG.md | 6 +++++- pyproject.toml | 2 +- tools/noxtools.py | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.cruft.json b/.cruft.json index 4a3e774..2869f94 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "41b5d67426f708650af617193e42e081fe965649", + "commit": "ab2f71c9e76533931d52cd32c7091fce972f7626", "checkout": "develop", "context": { "cookiecutter": { diff --git a/CHANGELOG.md b/CHANGELOG.md index bfcef24..c226e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,14 @@ Changelog for `pyproject2conda` ## Unreleased -[changelog.d]: https://github.com/usnistgov/pyproject2conda +[changelog.d]: https://github.com/usnistgov/pyproject2conda/tree/main/changelog.d See the fragment files in [changelog.d] + + + + ## v0.8.0 — 2023-10-02 diff --git a/pyproject.toml b/pyproject.toml index 3981fc0..769adc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -304,7 +304,7 @@ ignore = [ "E203", # module level import not at top of file "E402", - # line t oo long - let black worry about that + # line too long - let black worry about that "E501", # do not assign a lambda expression, use a def "E731", diff --git a/tools/noxtools.py b/tools/noxtools.py index 1ad3d8a..cd2426d 100644 --- a/tools/noxtools.py +++ b/tools/noxtools.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Iterable, Literal, Sequence, TextIO, cast -from ruamel.yaml import safe_load +from ruamel.yaml import YAML if TYPE_CHECKING: from collections.abc import Collection @@ -394,7 +394,7 @@ def _get_context(path: str | Path | TextIO) -> TextIO | Path: for path in paths: with _get_context(path) as f: - data = safe_load(f) + data = YAML(typ="safe", pure=True).load(f) channels.update(data.get("channels", [])) name = data.get("name", name) From e94d2c3a34f3d4d9d05e8df80803b341aa066bae Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 6 Nov 2023 15:33:32 -0500 Subject: [PATCH 05/19] chore: remove _copy_without_render from .cruft.json --- .cruft.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.cruft.json b/.cruft.json index 2869f94..316b6bc 100644 --- a/.cruft.json +++ b/.cruft.json @@ -11,16 +11,7 @@ "conda_channel": "wpk-nist", "project_name": "pyproject2conda", "project_slug": "pyproject2conda", - "_copy_without_render": [ - "*.html", - "docs/_templates/*.rst", - "docs/_templates/autosummary/*.rst", - "docs/_templates/autodocsumm/*.rst", - "docs/_static/css/*", - "docs/_static/js/*", - "changelog.d/templates/*.j2", - "changelog.d/templates/auto-changelog/*.jinja2" - ], + "_copy_without_render": [], "project_short_description": "A script to convert a Python project declared on a pyproject.toml to a conda environment.", "command_line_interface": "Typer", "sphinx_use_autodocsumm": "y", From 2ff50b1b90d635d95d38223a574daa6276764def Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 6 Nov 2023 15:34:24 -0500 Subject: [PATCH 06/19] chore: udpate cruft --- .cruft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cruft.json b/.cruft.json index 316b6bc..74b26cf 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "ab2f71c9e76533931d52cd32c7091fce972f7626", + "commit": "e216a57fbff8458b829095114546bb454c2f51db", "checkout": "develop", "context": { "cookiecutter": { From 604a93b62bc7775a331b7bb14e302cb724c43ed9 Mon Sep 17 00:00:00 2001 From: wpk Date: Thu, 9 Nov 2023 16:27:19 -0500 Subject: [PATCH 07/19] chore: update .cruft before update --- .cruft.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.cruft.json b/.cruft.json index 74b26cf..b1aaa09 100644 --- a/.cruft.json +++ b/.cruft.json @@ -16,6 +16,8 @@ "command_line_interface": "Typer", "sphinx_use_autodocsumm": "y", "sphinx_theme": "sphinx_book_theme", + "__year": "2023", + "__answers": "", "_template": "https://github.com/usnistgov/cookiecutter-nist-python.git" } }, From 58be7078b9d1f2f327993431330611e067c93743 Mon Sep 17 00:00:00 2001 From: wpk Date: Thu, 9 Nov 2023 16:32:05 -0500 Subject: [PATCH 08/19] chore: update cruft to new template --- .cruft.json | 2 +- .pre-commit-config.yaml | 2 +- CONTRIBUTING.md | 28 ++++--- noxfile.py | 60 ++++++--------- pyproject.toml | 1 - src/pyproject2conda/__init__.py | 10 +-- tools/noxtools.py | 128 ++++++++++++++++++-------------- 7 files changed, 119 insertions(+), 112 deletions(-) diff --git a/.cruft.json b/.cruft.json index b1aaa09..9e5c409 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "e216a57fbff8458b829095114546bb454c2f51db", + "commit": "bb73e10f6947a5ca80b8d1c4ac83875b965dab6a", "checkout": "develop", "context": { "cookiecutter": { diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a16a15c..cfcd77b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: stages: [commit] additional_dependencies: - prettier-plugin-toml - exclude: ^requirements/lock/.*[.]yml + exclude: ^requirements/lock/.*[.]yml|^.copier-answers.y?ml # * Markdown - repo: https://github.com/DavidAnson/markdownlint-cli2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da0f48f..cc317bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -279,6 +279,8 @@ where commands can be one of: [ghp-import](https://github.com/c-w/ghp-import)) - livehtml : Live documentation updates - open : open the documentation in a web browser +- serve : Serve the created documentation webpage (Need this to view javescript + in created pages). ## Testing with nox @@ -518,22 +520,23 @@ Before you submit a pull request, check that it meets these guidelines: [setuptools_scm]: https://github.com/pypa/setuptools_scm -Versioning is handled with [setuptools_scm].The package version is set by the +Versioning is handled with [setuptools_scm]. The package version is set by the git tag. For convenience, you can override the version with nox setting `--version ...`. This is useful for updating the docs, etc. -We use the `write_to` option to [setuptools_scm]. This stores the current -version in `_version.py`. Note that if you build the package (or, build docs -with the `--version` flag), this will overwrite information in `_version.py` in -the `src` directory. To refresh the version, run: +Note that the version in a given environment/session can become stale. The +easiest way to update the installed package version version is to reinstall the +package. This can be done using the following: ```bash -make version-scm +pip install -e . --no-deps ``` -Note also that the file `_version.py` SHOULD NOT be tracked by git. It will be -autogenerated when building the package. This scheme avoids having to install -`setuptools-scm` (and `setuptools`) in each environment. +To do this in a given session, use: + +```bash +nox -s {session} -- -P/--update-package +``` ## Serving the documentation @@ -543,4 +546,9 @@ To view to documentation with js headers/footers, you'll need to serve them: python -m http.server -d docs/_build/html ``` -Then open the address `localhost:8000` in a webbrowser. +Then open the address `localhost:8000` in a webbrowser. Alternatively, you can +run: + +```bash +nox -s docs -- -d serve +``` diff --git a/noxfile.py b/noxfile.py index c10b598..229bc96 100644 --- a/noxfile.py +++ b/noxfile.py @@ -144,6 +144,16 @@ def run_annotated(**kwargs: Any): # type: ignore[unused-ignore,no-untyped-def] ), ] + +UPDATE_PACKAGE_CLI = Annotated[ + bool, + Option( + type=bool, + help="If True, and session uses package, reinstall package", + flags=("--update-package", "-P"), + ), +] + VERSION_CLI = Annotated[ str, Option(type=str, help="Version to substitute or check against") ] @@ -165,6 +175,7 @@ def dev( dev_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, log_session: bool = False, ) -> None: """Create dev env using conda.""" @@ -177,6 +188,7 @@ def dev( display_name=f"{PACKAGE_NAME}-dev", install_package=True, update=update, + update_package=update_package, log_session=log_session, ) session_run_commands(session, dev_run) @@ -189,6 +201,7 @@ def dev_venv( dev_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, log_session: bool = False, ) -> None: """Create dev env using virtualenv.""" @@ -202,6 +215,7 @@ def dev_venv( display_name=f"{PACKAGE_NAME}-dev-venv", install_package=True, update=update, + update_package=update_package, log_session=log_session, ) session_run_commands(session, dev_run) @@ -375,6 +389,7 @@ def test( test_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, log_session: bool = False, no_cov: bool = False, ) -> None: @@ -386,6 +401,7 @@ def test( lock=lock, install_package=True, update=update, + update_package=update_package, log_session=log_session, ) @@ -406,6 +422,7 @@ def test_venv( test_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, # pyright: ignore update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, log_session: bool = False, no_cov: bool = False, ) -> None: @@ -417,6 +434,7 @@ def test_venv( install_package=True, requirement_paths="test.txt", update=update, + update_package=update_package, log_session=log_session, ) @@ -458,7 +476,7 @@ def _coverage( session_run_commands(session, run_internal, external=False) -@DEFAULT_SESSION_VENV +@INHERITED_SESSION_VENV def coverage( session: Session, coverage_cmd: cmd_annotated( # type: ignore @@ -550,6 +568,7 @@ def docs( docs_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, version: VERSION_CLI = "", log_session: bool = False, ) -> None: @@ -561,6 +580,7 @@ def docs( display_name=f"{PACKAGE_NAME}-docs", install_package=True, update=update, + update_package=update_package, log_session=log_session, ) @@ -591,6 +611,7 @@ def docs_venv( docs_run: RUN_CLI = [], # noqa lock: LOCK_CLI = False, update: UPDATE_CLI = False, + update_package: UPDATE_PACKAGE_CLI = False, version: VERSION_CLI = "", log_session: bool = False, ) -> None: @@ -602,6 +623,7 @@ def docs_venv( display_name=f"{PACKAGE_NAME}-docs-venv", install_package=True, update=update, + update_package=update_package, log_session=log_session, requirement_paths="docs.txt", ) @@ -1091,42 +1113,6 @@ def testdist_pypi_condaenv( ) -@DEFAULT_SESSION_VENV -def update_version_scm( - session: Session, - version: VERSION_CLI = "", - update: UPDATE_CLI = False, -) -> None: - """ - Get current version from setuptools-scm - - Note that the version of editable installs can get stale. - This will show the actual current version. - Avoids need to include setuptools-scm in develop/docs/etc. - """ - - if version: - session.env["SETUPTOOLS_SCM_PRETEND_VERSION"] = version - - pkg_install_venv( - session=session, - name="update-version-scm", - install_package=True, - # reqs=["setuptools_scm"], - update=True, - no_deps=True, - ) - - session.run( - "python", - "-c", - "import sys;" - "sys.path.insert(0, 'src');" - "from pyproject2conda._version import __version__;" - "print(__version__);", - ) - - # * Utilities -------------------------------------------------------------------------- def _create_doc_examples_symlinks(session: nox.Session, clean: bool = True) -> None: """Create symlinks from docs/examples/*.md files to /examples/usage/...""" diff --git a/pyproject.toml b/pyproject.toml index 769adc8..6fac6ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,6 @@ readme = { file = [ [tool.setuptools_scm] fallback_version = "999" -version_file = "src/pyproject2conda/_version.py" [tool.aliases] test = "pytest" diff --git a/src/pyproject2conda/__init__.py b/src/pyproject2conda/__init__.py index 3f67f00..2f12e24 100644 --- a/src/pyproject2conda/__init__.py +++ b/src/pyproject2conda/__init__.py @@ -2,15 +2,15 @@ Top level API (:mod:`pyproject2conda`) ====================================== """ +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version as _version -from .parser import PyProject2Conda - -# updated versioning scheme try: - from ._version import __version__ -except Exception: # pragma: no cover + __version__ = _version("pyproject2conda") +except PackageNotFoundError: # pragma: no cover __version__ = "999" +from .parser import PyProject2Conda __author__ = """William P. Krekelberg""" __email__ = "wpk@nist.gov" diff --git a/tools/noxtools.py b/tools/noxtools.py index cd2426d..afe84e7 100644 --- a/tools/noxtools.py +++ b/tools/noxtools.py @@ -60,6 +60,7 @@ def pkg_install_condaenv( display_name: str | None = None, install_package: bool = True, update: bool = False, + update_package: bool = False, log_session: bool = False, deps: Collection[str] | None = None, reqs: Collection[str] | None = None, @@ -86,6 +87,7 @@ def check_filename(filename: str | Path) -> str: lockfile=check_filename(filename), display_name=display_name, update=update, + update_package=update_package, install_package=install_package, **kwargs, ) @@ -96,6 +98,7 @@ def check_filename(filename: str | Path) -> str: check_filename(filename), display_name=display_name, update=update, + update_package=update_package, deps=deps, reqs=reqs, channels=channels, @@ -117,6 +120,7 @@ def pkg_install_venv( reqs: Collection[str] | None = None, display_name: str | None = None, update: bool = False, + update_package: bool = False, install_package: bool = False, no_deps: bool = True, log_session: bool = False, @@ -132,6 +136,7 @@ def pkg_install_venv( reqs=reqs, display_name=display_name, update=update, + update_package=update_package, install_package=install_package, no_deps=no_deps, lock=lock, @@ -317,6 +322,7 @@ def session_install_envs_lock( extras: str | list[str] | None = None, display_name: str | None = None, update: bool = False, + update_package: bool = False, install_package: bool = False, ) -> bool: """Install dependencies using conda-lock.""" @@ -327,34 +333,36 @@ def session_install_envs_lock( unchanged, hashes = env_unchanged( session, lockfile, prefix="lock", other=dict(install_package=install_package) ) - if unchanged and not update: - return unchanged - if extras: - if isinstance(extras, str): - extras = extras.split(",") - extras = cast(list[str], sum([["--extras", _] for _ in extras], [])) - else: - extras = [] - - session.run( - "conda-lock", - "install", - "--mamba", - *extras, - "-p", - str(session.virtualenv.location), - str(lockfile), - silent=True, - external=True, - ) + do_dep = update or (not unchanged) + do_pkg = install_package and (do_dep or update_package) - if install_package: - session_install_package(session) + if do_dep: + if extras: + if isinstance(extras, str): + extras = extras.split(",") + extras = cast(list[str], sum([["--extras", _] for _ in extras], [])) + else: + extras = [] + + session.run( + "conda-lock", + "install", + "--mamba", + *extras, + "-p", + str(session.virtualenv.location), + str(lockfile), + silent=True, + external=True, + ) - session_set_ipykernel_display_name(session, display_name) + if do_pkg: + session_install_package(session) - write_hashfile(hashes, session=session, prefix="lock") + if do_dep or do_pkg: + session_set_ipykernel_display_name(session, display_name) + write_hashfile(hashes, session=session, prefix="lock") return unchanged @@ -421,6 +429,7 @@ def session_install_envs( install_kws: dict[str, Any] | None = None, display_name: str | None = None, update: bool = False, + update_package: bool = False, install_package: bool = False, ) -> bool: """Parse and install everything. Pass an already merged yaml file.""" @@ -446,30 +455,32 @@ def session_install_envs( install_package=install_package, ), ) - if unchanged and not update: - return unchanged - if not channels: - channels = "" - if deps: - conda_install_kws = conda_install_kws or {} - conda_install_kws.update(channel=channels) - if update: - deps = ["--update-all"] + list(deps) + do_dep = update or (not unchanged) + do_pkg = install_package and (do_dep or update_package) - session.conda_install(*deps, **(conda_install_kws or {})) + if do_dep: + if not channels: + channels = "" + if deps: + conda_install_kws = conda_install_kws or {} + conda_install_kws.update(channel=channels) + if update: + deps = ["--update-all"] + list(deps) - if reqs: - if update: - reqs = ["--upgrade"] + list(reqs) - session.install(*reqs, **(install_kws or {})) + session.conda_install(*deps, **(conda_install_kws or {})) - if install_package: - session_install_package(session) + if reqs: + if update: + reqs = ["--upgrade"] + list(reqs) + session.install(*reqs, **(install_kws or {})) - session_set_ipykernel_display_name(session, display_name) + if do_pkg: + session_install_package(session) - write_hashfile(hashes, session=session, prefix="env") + if do_dep or do_pkg: + session_set_ipykernel_display_name(session, display_name) + write_hashfile(hashes, session=session, prefix="env") return unchanged @@ -483,6 +494,7 @@ def session_install_pip( reqs: str | Collection[str] | None = None, display_name: str | None = None, update: bool = False, + update_package: bool = False, install_package: bool = False, no_deps: bool = True, lock: bool = False, @@ -545,26 +557,28 @@ def _verify_paths(paths: str | list[str]) -> list[str]: ), ) - if unchanged and not update: - return unchanged + do_dep = update or (not unchanged) + do_pkg = install_package and (do_dep or update_package) - # do install - install_args = ( - prepend_flag("-r", *requirement_paths) - + prepend_flag("-c", *constraint_paths) - + list(reqs) - ) + if do_dep: + # do install + install_args = ( + prepend_flag("-r", *requirement_paths) + + prepend_flag("-c", *constraint_paths) + + list(reqs) + ) - if install_args: - if update: - install_args = ["--upgrade"] + list(install_args) - session.install(*install_args) + if install_args: + if update: + install_args = ["--upgrade"] + list(install_args) + session.install(*install_args) - if install_package: + if do_pkg: session.install(*install_package_args) - session_set_ipykernel_display_name(session, display_name) - write_hashfile(hashes, session=session, prefix="pip") + if do_dep or do_pkg: + session_set_ipykernel_display_name(session, display_name) + write_hashfile(hashes, session=session, prefix="pip") return unchanged From ce17f72d6a1e9102afeddbc784393ee02f1b3bc7 Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 11:00:04 -0500 Subject: [PATCH 09/19] chore: update .cruft before update --- .cruft.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.cruft.json b/.cruft.json index 9e5c409..404f994 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "bb73e10f6947a5ca80b8d1c4ac83875b965dab6a", + "commit": "e84f5884bfaa90e9397f41549b621ecacf747a3f", "checkout": "develop", "context": { "cookiecutter": { @@ -13,10 +13,10 @@ "project_slug": "pyproject2conda", "_copy_without_render": [], "project_short_description": "A script to convert a Python project declared on a pyproject.toml to a conda environment.", - "command_line_interface": "Typer", - "sphinx_use_autodocsumm": "y", + "command_line_interface": "typer", + "sphinx_use_autodocsumm": true, "sphinx_theme": "sphinx_book_theme", - "__year": "2023", + "year": "2023", "__answers": "", "_template": "https://github.com/usnistgov/cookiecutter-nist-python.git" } From 3bbc295043a54b15d7e98e91904c0e8b4e527acf Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 11:03:34 -0500 Subject: [PATCH 10/19] chore: update cruft --- .cruft.json | 10 +++++----- .pre-commit-config.yaml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.cruft.json b/.cruft.json index 404f994..e1ce2ba 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,23 +1,23 @@ { "template": "https://github.com/usnistgov/cookiecutter-nist-python.git", - "commit": "e84f5884bfaa90e9397f41549b621ecacf747a3f", + "commit": "8fe5103d25de7e7d13d400f599c6653866a69afc", "checkout": "develop", "context": { "cookiecutter": { + "project_name": "pyproject2conda", + "project_slug": "pyproject2conda", + "project_short_description": "A script to convert a Python project declared on a pyproject.toml to a conda environment.", "full_name": "William P. Krekelberg", "email": "wpk@nist.gov", "github_username": "usnistgov", "pypi_username": "conda-forge", "conda_channel": "wpk-nist", - "project_name": "pyproject2conda", - "project_slug": "pyproject2conda", - "_copy_without_render": [], - "project_short_description": "A script to convert a Python project declared on a pyproject.toml to a conda environment.", "command_line_interface": "typer", "sphinx_use_autodocsumm": true, "sphinx_theme": "sphinx_book_theme", "year": "2023", "__answers": "", + "_copy_without_render": [], "_template": "https://github.com/usnistgov/cookiecutter-nist-python.git" } }, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cfcd77b..f7037ad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: stages: [commit] additional_dependencies: - prettier-plugin-toml - exclude: ^requirements/lock/.*[.]yml|^.copier-answers.y?ml + exclude: ^requirements/lock/.*[.]yml|^.copier-answers.ya?ml # * Markdown - repo: https://github.com/DavidAnson/markdownlint-cli2 From 82ec20119c635877d1b587e613dcdac12c6c627e Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 15:55:40 -0500 Subject: [PATCH 11/19] feat: Remove whitespace now default for dependencies --- ...11_william.krekelberg_ignore_whitespace.md | 44 ++++++++++ pyproject.toml | 1 + requirements/dev-base.txt | 6 +- requirements/dev-complete.txt | 6 +- requirements/dev.txt | 6 +- requirements/docs.txt | 4 +- requirements/py310-dev-base.yaml | 2 +- requirements/py310-dev-complete.yaml | 2 +- requirements/py310-docs.yaml | 2 +- requirements/py310-typing.yaml | 2 +- requirements/py311-typing.yaml | 2 +- requirements/py38-typing.yaml | 2 +- requirements/py39-typing.yaml | 2 +- requirements/test.txt | 2 +- requirements/typing.txt | 6 +- src/pyproject2conda/cli.py | 17 ++++ src/pyproject2conda/config.py | 11 +++ src/pyproject2conda/parser.py | 25 ++++++ tests/test_cli.py | 36 ++++++-- tests/test_config.py | 37 ++++++++ tests/test_parser.py | 85 ++++++++++++++++++- 21 files changed, 272 insertions(+), 28 deletions(-) create mode 100644 changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md diff --git a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md new file mode 100644 index 0000000..1aab19c --- /dev/null +++ b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md @@ -0,0 +1,44 @@ + + + + + +### Added + +- Default is now to remove whitespace from dependencies. For example, the + dependency `module > 0.1` will become `module>0.1`. To override this + behaviour, pass the option `--no-remove-whitespace`. + + + + + diff --git a/pyproject.toml b/pyproject.toml index 6fac6ec..968aa4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -148,6 +148,7 @@ style = ["yaml"] [project.scripts] pyproject2conda = "pyproject2conda.cli:app" +p2c = "pyproject2conda.cli:app" ## grayskull still messes some things up, but use scripts/recipe-append.sh for this [tool.setuptools] diff --git a/requirements/dev-base.txt b/requirements/dev-base.txt index bee2e1e..c76fb3b 100644 --- a/requirements/dev-base.txt +++ b/requirements/dev-base.txt @@ -9,18 +9,18 @@ # ipykernel ipython -mypy >= 1.4.1 +mypy>=1.4.1 packaging pytest pytest-accept pytest-cov pytest-sugar pytest-xdist -pytype; python_version < '3.11' +pytype;python_version<'3.11' ruamel.yaml setuptools-scm>=8.0 tomli tomlkit typer types-click -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/requirements/dev-complete.txt b/requirements/dev-complete.txt index 75e7985..99322cf 100644 --- a/requirements/dev-complete.txt +++ b/requirements/dev-complete.txt @@ -9,7 +9,7 @@ # ipykernel ipython -mypy >= 1.4.1 +mypy>=1.4.1 nbqa nox noxopt @@ -21,7 +21,7 @@ pytest-accept pytest-cov pytest-sugar pytest-xdist -pytype; python_version < '3.11' +pytype;python_version<'3.11' ruamel.yaml scriv setuptools-scm>=8.0 @@ -29,4 +29,4 @@ tomli tomlkit typer types-click -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/requirements/dev.txt b/requirements/dev.txt index 8a3ffd5..98070d8 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -9,7 +9,7 @@ # ipykernel ipython -mypy >= 1.4.1 +mypy>=1.4.1 nox noxopt packaging @@ -18,11 +18,11 @@ pytest-accept pytest-cov pytest-sugar pytest-xdist -pytype; python_version < '3.11' +pytype;python_version<'3.11' ruamel.yaml setuptools-scm>=8.0 tomli tomlkit typer types-click -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/requirements/docs.txt b/requirements/docs.txt index 2eca0b3..e99473a 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -14,13 +14,13 @@ myst-nb packaging pyenchant ruamel.yaml -sphinx >= 5.3.0 sphinx-autobuild sphinx-book-theme sphinx-click sphinx-copybutton +sphinx>=5.3.0 sphinxcontrib-spelling tomli tomlkit typer -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/requirements/py310-dev-base.yaml b/requirements/py310-dev-base.yaml index c007719..fd92c2a 100644 --- a/requirements/py310-dev-base.yaml +++ b/requirements/py310-dev-base.yaml @@ -13,7 +13,7 @@ dependencies: - python=3.10 - ipykernel - ipython - - mypy >= 1.4.1 + - mypy>=1.4.1 - packaging - pytest - pytest-cov diff --git a/requirements/py310-dev-complete.yaml b/requirements/py310-dev-complete.yaml index 4e55e51..a46a26d 100644 --- a/requirements/py310-dev-complete.yaml +++ b/requirements/py310-dev-complete.yaml @@ -13,7 +13,7 @@ dependencies: - python=3.10 - ipykernel - ipython - - mypy >= 1.4.1 + - mypy>=1.4.1 - nbqa - nox - packaging diff --git a/requirements/py310-docs.yaml b/requirements/py310-docs.yaml index 40980dc..bda7cfb 100644 --- a/requirements/py310-docs.yaml +++ b/requirements/py310-docs.yaml @@ -18,7 +18,7 @@ dependencies: - packaging - pyenchant - ruamel.yaml - - sphinx >= 5.3.0 + - sphinx>=5.3.0 - sphinx-autobuild - sphinx-book-theme - sphinx-click diff --git a/requirements/py310-typing.yaml b/requirements/py310-typing.yaml index 32f99fb..b286b19 100644 --- a/requirements/py310-typing.yaml +++ b/requirements/py310-typing.yaml @@ -11,7 +11,7 @@ channels: - conda-forge dependencies: - python=3.10 - - mypy >= 1.4.1 + - mypy>=1.4.1 - packaging - pytest - pytest-cov diff --git a/requirements/py311-typing.yaml b/requirements/py311-typing.yaml index 51308cc..6d6d0c1 100644 --- a/requirements/py311-typing.yaml +++ b/requirements/py311-typing.yaml @@ -11,7 +11,7 @@ channels: - conda-forge dependencies: - python=3.11 - - mypy >= 1.4.1 + - mypy>=1.4.1 - packaging - pytest - pytest-cov diff --git a/requirements/py38-typing.yaml b/requirements/py38-typing.yaml index ccc2ec7..21b7c3c 100644 --- a/requirements/py38-typing.yaml +++ b/requirements/py38-typing.yaml @@ -11,7 +11,7 @@ channels: - conda-forge dependencies: - python=3.8 - - mypy >= 1.4.1 + - mypy>=1.4.1 - packaging - pytest - pytest-cov diff --git a/requirements/py39-typing.yaml b/requirements/py39-typing.yaml index 6a5938b..03d436c 100644 --- a/requirements/py39-typing.yaml +++ b/requirements/py39-typing.yaml @@ -11,7 +11,7 @@ channels: - conda-forge dependencies: - python=3.9 - - mypy >= 1.4.1 + - mypy>=1.4.1 - packaging - pytest - pytest-cov diff --git a/requirements/test.txt b/requirements/test.txt index 543fe03..306399d 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -16,4 +16,4 @@ ruamel.yaml tomli tomlkit typer -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/requirements/typing.txt b/requirements/typing.txt index a6d0985..47af58d 100644 --- a/requirements/typing.txt +++ b/requirements/typing.txt @@ -7,16 +7,16 @@ # You should not manually edit this file. # Instead edit the corresponding pyproject.toml file. # -mypy >= 1.4.1 +mypy>=1.4.1 packaging pytest pytest-cov pytest-sugar pytest-xdist -pytype; python_version < '3.11' +pytype;python_version<'3.11' ruamel.yaml tomli tomlkit typer types-click -typing-extensions; python_version<'3.9' +typing-extensions;python_version<'3.9' diff --git a/src/pyproject2conda/cli.py b/src/pyproject2conda/cli.py index 1dd5d1e..f178056 100644 --- a/src/pyproject2conda/cli.py +++ b/src/pyproject2conda/cli.py @@ -334,6 +334,17 @@ class Overwrite(str, Enum): ) +REMOVE_WHITESPACE_OPTION = typer.Option( + "--remove-whitespace/--no-remove-whitespace", + help=""" + What to do with whitespace in a dependency. The default (`--remove-whitespace`) is + to remove whitespace in a given dependency. For example, the dependency + `package >= 1.0` will be converted to `package>=1.0`. Pass `--no-remove-whitespace` + to keep the the whitespace in the output. + """, +) + + # * Utils ------------------------------------------------------------------------------ def _get_header_cmd( header: Optional[bool], output: Union[str, Path, None] @@ -467,6 +478,7 @@ def yaml( deps: DEPS_CLI = None, reqs: REQS_CLI = None, allow_empty: Annotated[bool, ALLOW_EMPTY_OPTION] = False, + remove_whitespace: Annotated[bool, REMOVE_WHITESPACE_OPTION] = True, ) -> None: """Create yaml file from dependencies and optional-dependencies.""" @@ -500,6 +512,7 @@ def yaml( deps=deps, reqs=reqs, allow_empty=allow_empty, + remove_whitespace=remove_whitespace, ) if not output: print(s, end="") @@ -520,6 +533,7 @@ def requirements( verbose: VERBOSE_CLI = None, # pyright: ignore reqs: REQS_CLI = None, allow_empty: Annotated[bool, ALLOW_EMPTY_OPTION] = False, + remove_whitespace: Annotated[bool, REMOVE_WHITESPACE_OPTION] = True, ) -> None: """Create requirements.txt for pip dependencies.""" @@ -539,6 +553,7 @@ def requirements( sort=sort, reqs=reqs, allow_empty=allow_empty, + remove_whitespace=remove_whitespace, ) if not output: print(s, end="") @@ -562,6 +577,7 @@ def project( dry: DRY_CLI = False, user_config: USER_CONFIG_CLI = "infer", allow_empty: Annotated[Optional[bool], ALLOW_EMPTY_OPTION] = None, + remove_whitespace: Annotated[Optional[bool], REMOVE_WHITESPACE_OPTION] = None, ) -> None: """Create multiple environment files from `pyproject.toml` specification.""" from pyproject2conda.config import Config @@ -580,6 +596,7 @@ def project( overwrite=overwrite.value, verbose=verbose, allow_empty=allow_empty, + remove_whitespace=remove_whitespace, ): if dry: # small header diff --git a/src/pyproject2conda/config.py b/src/pyproject2conda/config.py index 71af17a..b257be1 100644 --- a/src/pyproject2conda/config.py +++ b/src/pyproject2conda/config.py @@ -222,6 +222,15 @@ def allow_empty(self, env_name: str | None = None, default: bool = False) -> boo key="allow_empty", env_name=env_name, default=default ) + def remove_whitespace( + self, env_name: str | None = None, default: bool = True + ) -> bool: + return self._get_value( # type: ignore + key="remove_whitespace", + env_name=env_name, + default=default, + ) + def assign_user_config(self, user: Self) -> Self: """Assign user_config to self.""" from copy import deepcopy @@ -275,6 +284,7 @@ def _iter_yaml( "name", "channels", "allow_empty", + "remove_whitespace", ] data = {k: defaults.get(k, getattr(self, k)(env_name)) for k in keys} @@ -317,6 +327,7 @@ def _iter_reqs( "verbose", "reqs", "allow_empty", + "remove_whitespace", ] output, template, _ = self._get_output_and_templates(env_name, **defaults) diff --git a/src/pyproject2conda/parser.py b/src/pyproject2conda/parser.py index 4f58018..f807a14 100644 --- a/src/pyproject2conda/parser.py +++ b/src/pyproject2conda/parser.py @@ -53,6 +53,13 @@ def _check_allow_empty(allow_empty: bool) -> str: raise ValueError(msg) +_WHITE_SPACE_REGEX = re.compile(r"\s+") + + +def _remove_whitespace(s: str) -> str: + return re.sub(_WHITE_SPACE_REGEX, "", s) + + # --- Default parser ------------------------------------------------------------------- @@ -336,6 +343,7 @@ def pyproject_to_conda_lists( sort: bool = True, deps: Sequence[str] | None = None, reqs: Sequence[str] | None = None, + remove_whitespace: bool = True, ) -> dict[str, Any]: if python_include == "infer": # safer get @@ -374,6 +382,9 @@ def pyproject_to_conda_lists( output["dependencies"], python_version ) + if remove_whitespace: + output = {k: [_remove_whitespace(x) for x in v] for k, v in output.items()} + return output @@ -391,6 +402,7 @@ def pyproject_to_conda( deps: Sequence[str] | None = None, reqs: Sequence[str] | None = None, allow_empty: bool = False, + remove_whitespace: bool = True, ) -> str: output = pyproject_to_conda_lists( data=data, @@ -402,6 +414,7 @@ def pyproject_to_conda( sort=sort, deps=deps, reqs=reqs, + remove_whitespace=remove_whitespace, ) return _output_to_yaml( **output, @@ -550,6 +563,7 @@ def to_conda_yaml( deps: Sequence[str] | None = None, reqs: Sequence[str] | None = None, allow_empty: bool = False, + remove_whitespace: bool = True, ) -> str: self._check_extras(extras) @@ -567,6 +581,7 @@ def to_conda_yaml( deps=deps, reqs=reqs, allow_empty=allow_empty, + remove_whitespace=remove_whitespace, ) def to_conda_lists( @@ -579,6 +594,7 @@ def to_conda_lists( sort: bool = True, deps: Sequence[str] | None = None, reqs: Sequence[str] | None = None, + remove_whitespace: bool = True, ) -> dict[str, Any]: self._check_extras(extras) @@ -592,6 +608,7 @@ def to_conda_lists( sort=sort, deps=deps, reqs=reqs, + remove_whitespace=remove_whitespace, ) def to_requirement_list( @@ -600,6 +617,7 @@ def to_requirement_list( include_base_dependencies: bool = True, sort: bool = True, reqs: Sequence[str] | None = None, + remove_whitespace: bool = True, ) -> list[str]: self._check_extras(extras) @@ -613,6 +631,9 @@ def to_requirement_list( if reqs: out.extend(list(reqs)) + if remove_whitespace: + out = [_remove_whitespace(x) for x in out] + if sort: return sorted(out) else: @@ -627,6 +648,7 @@ def to_requirements( sort: bool = True, reqs: Sequence[str] | None = None, allow_empty: bool = False, + remove_whitespace: bool = True, ) -> str: """Create requirements.txt like file with pip dependencies.""" @@ -637,6 +659,7 @@ def to_requirements( include_base_dependencies=include_base_dependencies, sort=sort, reqs=reqs, + remove_whitespace=remove_whitespace, ) if not reqs: @@ -660,6 +683,7 @@ def to_conda_requirements( sort: bool = True, deps: Sequence[str] | None = None, reqs: Sequence[str] | None = None, + remove_whitespace: bool = True, ) -> tuple[str, str]: output = self.to_conda_lists( extras=extras, @@ -670,6 +694,7 @@ def to_conda_requirements( sort=sort, deps=deps, reqs=reqs, + remove_whitespace=remove_whitespace, ) deps = output.get("dependencies", None) diff --git a/tests/test_cli.py b/tests/test_cli.py index 426d8b4..c92166e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -353,7 +353,7 @@ def test_requirements(): expected = """\ athing bthing -cthing; python_version < '3.10' +cthing;python_version<'3.10' """ for cmd in ["r", "requirements"]: @@ -363,7 +363,7 @@ def test_requirements(): expected = """\ athing bthing -cthing; python_version < '3.10' +cthing;python_version<'3.10' pandas pytest matplotlib @@ -378,7 +378,7 @@ def test_requirements(): expected = """\ athing bthing -cthing; python_version < '3.10' +cthing;python_version<'3.10' pandas pytest matplotlib @@ -403,7 +403,7 @@ def test_requirements(): expected = """\ athing bthing -cthing; python_version < '3.10' +cthing;python_version<'3.10' matplotlib pandas pytest @@ -418,7 +418,7 @@ def test_requirements(): expected = """\ athing bthing -cthing; python_version < '3.10' +cthing;python_version<'3.10' matplotlib other pandas @@ -439,6 +439,32 @@ def test_requirements(): check_result(result, expected) + # allow whitespace: + expected = """\ +athing +bthing +cthing; python_version < '3.10' +matplotlib +other +pandas +pytest +thing; python_version < '3.10' + """ + + result = do_run( + runner, + "requirements", + "-e", + "dev", + "-r", + "thing; python_version < '3.10'", + "-r", + "other", + "--no-remove-whitespace", + ) + + check_result(result, expected) + expected = """\ setuptools build diff --git a/tests/test_config.py b/tests/test_config.py index b26d5d9..858bd44 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -98,6 +98,7 @@ def test_option_override(): "name": None, "channels": ["conda-forge"], "allow_empty": False, + "remove_whitespace": True, "output": "hello-base.yaml", }, ) @@ -118,6 +119,7 @@ def test_option_override(): "name": None, "channels": ["conda-forge"], "allow_empty": False, + "remove_whitespace": True, "output": "there-base.yaml", }, ) @@ -138,6 +140,35 @@ def test_option_override(): "name": None, "channels": ["conda-forge"], "allow_empty": True, + "remove_whitespace": True, + "output": "there-base.yaml", + }, + ) + + output = list( + d.iter( + envs=["base"], + allow_empty=True, + remove_whitespace=False, + template="there-{env}", + ) + ) + + assert output[0] == ( + "yaml", + { + "extras": [], + "sort": True, + "base": True, + "header": None, + "overwrite": "check", + "verbose": None, + "reqs": None, + "deps": None, + "name": None, + "channels": ["conda-forge"], + "allow_empty": True, + "remove_whitespace": False, "output": "there-base.yaml", }, ) @@ -192,6 +223,7 @@ def test_config_only_default(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ) ] @@ -251,6 +283,7 @@ def test_config_overrides(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ) @@ -308,6 +341,7 @@ def test_config_python_include_version(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ), ( @@ -327,6 +361,7 @@ def test_config_python_include_version(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ), ] @@ -374,6 +409,7 @@ def test_config_user_config(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ), ( @@ -392,6 +428,7 @@ def test_config_user_config(): "deps": None, "reqs": None, "allow_empty": False, + "remove_whitespace": True, }, ), ] diff --git a/tests/test_parser.py b/tests/test_parser.py index 26a3bf0..399c549 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -273,7 +273,7 @@ def test_complete(): """\ [project] name = "hello" - requires-python = ">=3.8,<3.11" + requires-python = ">=3.8, <3.11" dependencies = [ "athing", # p2c: -p # a comment "bthing", # p2c: -s bthing-conda @@ -342,6 +342,22 @@ def test_complete(): """ assert dedent(expected) == d.to_conda_yaml(python_include="infer") + # with remove spaces: + expected = """\ +channels: + - conda-forge +dependencies: + - python>=3.8, <3.11 + - bthing-conda + - conda-forge::cthing + - pip + - pip: + - athing + """ + assert dedent(expected) == d.to_conda_yaml( + python_include="infer", remove_whitespace=False + ) + expected = """\ channels: - conda-forge @@ -595,3 +611,70 @@ def test_missing_dependencies(): f() assert f(allow_empty=True) == "No dependencies for this environment\n" + + +def test_spaces(): + toml = dedent( + """\ + [project] + name = "hello" + requires-python = ">=3.8, <3.11" + dependencies = [ + "athing >0.5", # p2c: -p # a comment + "bthing > 1.0", # p2c: -s 'bthing-conda > 2.0' + "cthing; python_version < '3.10'", # p2c: -c conda-forge + ] + + + [tool.pyproject2conda] + channels = ['conda-forge'] + """ + ) + + d = parser.PyProject2Conda.from_string(toml) + + expected = """\ +channels: + - conda-forge +dependencies: + - python>=3.8, <3.11 + - bthing-conda > 2.0 + - conda-forge::cthing + - pip + - pip: + - athing >0.5 + """ + assert dedent(expected) == d.to_conda_yaml( + python_include="infer", remove_whitespace=False + ) + + expected = """\ +channels: + - conda-forge +dependencies: + - python>=3.8,<3.11 + - bthing-conda>2.0 + - conda-forge::cthing + - pip + - pip: + - athing>0.5 + """ + assert dedent(expected) == d.to_conda_yaml( + python_include="infer", remove_whitespace=True + ) + + expected = """\ + athing >0.5 + bthing > 1.0 + cthing; python_version < '3.10' + """ + + assert dedent(expected) == d.to_requirements(remove_whitespace=False) + + expected = """\ + athing>0.5 + bthing>1.0 + cthing;python_version<'3.10' + """ + + assert dedent(expected) == d.to_requirements(remove_whitespace=True) From 9ed14ce19d79821d5df68f261bacfaf3a30d27ca Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 15:59:47 -0500 Subject: [PATCH 12/19] feat: bump maximum tested version to 3.12 --- noxfile.py | 2 +- pyproject.toml | 3 ++- requirements/py312-test-extras.yaml | 17 +++++++++++++++++ requirements/py312-test.yaml | 22 ++++++++++++++++++++++ requirements/py312-typing.yaml | 24 ++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 requirements/py312-test-extras.yaml create mode 100644 requirements/py312-test.yaml create mode 100644 requirements/py312-typing.yaml diff --git a/noxfile.py b/noxfile.py index 229bc96..1c86d11 100644 --- a/noxfile.py +++ b/noxfile.py @@ -74,7 +74,7 @@ # * Options ---------------------------------------------------------------------------- -PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11"] +PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12"] PYTHON_DEFAULT_VERSION = "3.10" # conda/mamba diff --git a/pyproject.toml b/pyproject.toml index 968aa4f..eea517d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering", ] dynamic = ["readme", "version"] @@ -140,7 +141,7 @@ base = false [[tool.pyproject2conda.overrides]] envs = ["test", "typing", "test-extras"] -python = ["3.8", "3.9", "3.10", "3.11"] +python = ["3.8", "3.9", "3.10", "3.11", "3.12"] [[tool.pyproject2conda.overrides]] envs = ["dist-conda"] diff --git a/requirements/py312-test-extras.yaml b/requirements/py312-test-extras.yaml new file mode 100644 index 0000000..8b891d3 --- /dev/null +++ b/requirements/py312-test-extras.yaml @@ -0,0 +1,17 @@ +# +# This file is autogenerated by pyproject2conda +# with the following command: +# +# $ pyproject2conda project --verbose +# +# You should not manually edit this file. +# Instead edit the corresponding pyproject.toml file. +# +channels: + - conda-forge +dependencies: + - python=3.12 + - pytest + - pytest-cov + - pytest-sugar + - pytest-xdist diff --git a/requirements/py312-test.yaml b/requirements/py312-test.yaml new file mode 100644 index 0000000..ace8f0c --- /dev/null +++ b/requirements/py312-test.yaml @@ -0,0 +1,22 @@ +# +# This file is autogenerated by pyproject2conda +# with the following command: +# +# $ pyproject2conda project --verbose +# +# You should not manually edit this file. +# Instead edit the corresponding pyproject.toml file. +# +channels: + - conda-forge +dependencies: + - python=3.12 + - packaging + - pytest + - pytest-cov + - pytest-sugar + - pytest-xdist + - ruamel.yaml + - tomli + - tomlkit + - typer diff --git a/requirements/py312-typing.yaml b/requirements/py312-typing.yaml new file mode 100644 index 0000000..c6e3382 --- /dev/null +++ b/requirements/py312-typing.yaml @@ -0,0 +1,24 @@ +# +# This file is autogenerated by pyproject2conda +# with the following command: +# +# $ pyproject2conda project --verbose +# +# You should not manually edit this file. +# Instead edit the corresponding pyproject.toml file. +# +channels: + - conda-forge +dependencies: + - python=3.12 + - mypy>=1.4.1 + - packaging + - pytest + - pytest-cov + - pytest-sugar + - pytest-xdist + - ruamel.yaml + - tomli + - tomlkit + - typer + - types-click From f384970faffe6bcdc5ce2f0c49b17b28dbf5c586 Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 16:37:38 -0500 Subject: [PATCH 13/19] chore: add strict pyright for the code --- pyproject.toml | 1 + src/pyproject2conda/cli.py | 13 +++++++------ src/pyproject2conda/config.py | 8 +++++--- src/pyproject2conda/parser.py | 9 ++++++--- src/pyproject2conda/utils.py | 6 ++++-- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eea517d..9a7eba3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -361,6 +361,7 @@ module = [] [tool.pyright] include = ["src", "tests"] exclude = ["**/__pycache__", ".tox/**", ".nox/**", "**/.mypy_cache"] +strict = ["src/**/*.py"] pythonVersion = "3.10" typeCheckingMode = "basic" # enable subset of "strict" diff --git a/src/pyproject2conda/cli.py b/src/pyproject2conda/cli.py index f178056..7ecc682 100644 --- a/src/pyproject2conda/cli.py +++ b/src/pyproject2conda/cli.py @@ -1,3 +1,4 @@ +# pyright: reportUnknownMemberType=false """ Console script for pyproject2conda (:mod:`~pyproject2conda.cli`) ================================================================ @@ -108,7 +109,7 @@ def main( PYPROJECT_CLI = Annotated[ Path, - typer.Option( + typer.Option( # pyright: ignore[reportUnknownMemberType] "--file", "-f", help="input pyproject.toml file", @@ -116,7 +117,7 @@ def main( ] EXTRAS_CLI = Annotated[ Optional[List[str]], - typer.Option( + typer.Option( # pyright: ignore[reportUnknownMemberType] "--extra", "-e", help="Extra dependencies. Can specify multiple times for multiple extras.", @@ -124,7 +125,7 @@ def main( ] CHANNEL_CLI = Annotated[ Optional[List[str]], - typer.Option( + typer.Option( # pyright: ignore[reportUnknownMemberType] "--channel", "-c", help="conda channel. Can specify. Overrides [tool.pyproject2conda.channels]", @@ -132,7 +133,7 @@ def main( ] NAME_CLI = Annotated[ Optional[str], - typer.Option( + typer.Option( # pyright: ignore[reportUnknownMemberType] "--name", "-n", help="Name of conda env", @@ -140,7 +141,7 @@ def main( ] OUTPUT_CLI = Annotated[ Optional[Path], - typer.Option( + typer.Option( # pyright: ignore[reportUnknownMemberType] "--output", "-o", help="File to output results", @@ -417,7 +418,7 @@ def wrapped(*args: Any, **kwargs: Any) -> R: level = logging.WARN elif verbosity == 1: level = logging.INFO - elif verbosity >= 2: # pragma: no cover + else: # pragma: no cover level = logging.DEBUG logger.setLevel(level) diff --git a/src/pyproject2conda/config.py b/src/pyproject2conda/config.py index b257be1..547dc5a 100644 --- a/src/pyproject2conda/config.py +++ b/src/pyproject2conda/config.py @@ -98,7 +98,7 @@ def _get_value( if not isinstance(value, list): value = [value] - return value + return value # pyright: ignore def channels( self, env_name: str | None = None, inherit: bool = True @@ -250,9 +250,11 @@ def assign_user_config(self, user: Self) -> Self: if u is not None: d = data[key] if isinstance(d, list): - d.extend(u) + assert isinstance(u, list) + d.extend(u) # pyright: ignore elif isinstance(d, dict): - d.update(**u) + assert isinstance(u, dict) + d.update(**u) # pyright: ignore return type(self)(data) diff --git a/src/pyproject2conda/parser.py b/src/pyproject2conda/parser.py index f807a14..3751714 100644 --- a/src/pyproject2conda/parser.py +++ b/src/pyproject2conda/parser.py @@ -1,3 +1,4 @@ +# pyright: reportUnknownMemberType=false, reportGeneralTypeIssues=false """ Parse `pyproject.toml` (:mod:`~pyproject2conda.parser`) ======================================================= @@ -25,6 +26,8 @@ ) if TYPE_CHECKING: + import tomlkit.items + import tomlkit.toml_document from typing_extensions import Self import tomlkit @@ -210,7 +213,7 @@ def _pyproject_to_value_comment_pairs( unique: bool = True, include_base_dependencies: bool = True, ) -> list[tuple[Tstr_opt, Tstr_opt]]: - package_name = cast(str, get_in(["project", "name"], data, default=None)) + package_name = cast("str | None", get_in(["project", "name"], data, default=None)) if package_name is None: raise ValueError("Must specify `project.package_name` in pyproject.toml") @@ -276,7 +279,7 @@ def _limit_deps_by_python_version( r"(?P.*?);\s*python_version\s*(?P[<=>~]*)\s*[\'|\"](?P.*?)[\'|\"]" ) - output = [] + output: list[str] = [] for dep in deps: if match := matcher.match(dep): if not version or version in SpecifierSet( @@ -626,7 +629,7 @@ def to_requirement_list( extras=extras, include_base_dependencies=include_base_dependencies, ) - out = [x for x, y in values if x is not None] + out = [x for x, _ in values if x is not None] if reqs: out.extend(list(reqs)) diff --git a/src/pyproject2conda/utils.py b/src/pyproject2conda/utils.py index 78290b0..8561ebe 100644 --- a/src/pyproject2conda/utils.py +++ b/src/pyproject2conda/utils.py @@ -88,7 +88,7 @@ def update_target( update = True else: # check times - deps_filtered = [] + deps_filtered: list[Path] = [] for d in map(Path, deps): if d.exists(): deps_filtered.append(d) @@ -96,6 +96,8 @@ def update_target( target_time = target.stat().st_mtime update = any(target_time < dep.stat().st_mtime for dep in deps_filtered) + else: + raise ValueError(f"unknown option overwrite={overwrite}") return update @@ -121,7 +123,7 @@ def filename_from_template( if template is None: return None - kws = {} + kws: dict[str, str] = {} if python: py_version = python elif python_version: From b41fa08d8ad6b46a50cbda6e64fb4aca5e2e1f4f Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 18:57:31 -0500 Subject: [PATCH 14/19] chore: moved python version specific imports for typing to _typing_compat.py --- src/pyproject2conda/_typing.py | 2 +- src/pyproject2conda/_typing_compat.py | 25 +++++++++++++++++++++++++ src/pyproject2conda/cli.py | 7 +------ src/pyproject2conda/config.py | 2 +- src/pyproject2conda/parser.py | 3 ++- 5 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/pyproject2conda/_typing_compat.py diff --git a/src/pyproject2conda/_typing.py b/src/pyproject2conda/_typing.py index a096bc5..8fce015 100644 --- a/src/pyproject2conda/_typing.py +++ b/src/pyproject2conda/_typing.py @@ -1,6 +1,6 @@ from typing import Any, Callable, TypeVar -from typing_extensions import TypeAlias +from ._typing_compat import TypeAlias FuncType: TypeAlias = Callable[..., Any] diff --git a/src/pyproject2conda/_typing_compat.py b/src/pyproject2conda/_typing_compat.py new file mode 100644 index 0000000..7425efb --- /dev/null +++ b/src/pyproject2conda/_typing_compat.py @@ -0,0 +1,25 @@ +import sys + +if sys.version_info < (3, 10): + from typing_extensions import TypeAlias +else: + from typing import TypeAlias + + +if sys.version_info < (3, 9): + from typing_extensions import Annotated +else: + from typing import Annotated + + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + + +__all__ = [ + "TypeAlias", + "Annotated", + "Self", +] diff --git a/src/pyproject2conda/cli.py b/src/pyproject2conda/cli.py index 7ecc682..f564e68 100644 --- a/src/pyproject2conda/cli.py +++ b/src/pyproject2conda/cli.py @@ -7,18 +7,12 @@ import logging import os -import sys from enum import Enum from functools import lru_cache, wraps from inspect import signature from pathlib import Path from typing import Any, Callable, Iterable, List, Optional, Union, cast -if sys.version_info[:2] < (3, 9): - from typing_extensions import Annotated -else: - from typing import Annotated - # from click import click.Context import click import typer @@ -32,6 +26,7 @@ ) from ._typing import R +from ._typing_compat import Annotated # * Logger ----------------------------------------------------------------------------- diff --git a/src/pyproject2conda/config.py b/src/pyproject2conda/config.py index 547dc5a..dbcf312 100644 --- a/src/pyproject2conda/config.py +++ b/src/pyproject2conda/config.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: from typing import Any, Iterator, Sequence - from typing_extensions import Self + from ._typing_compat import Self # * Utilities diff --git a/src/pyproject2conda/parser.py b/src/pyproject2conda/parser.py index 3751714..beba1e1 100644 --- a/src/pyproject2conda/parser.py +++ b/src/pyproject2conda/parser.py @@ -28,7 +28,8 @@ if TYPE_CHECKING: import tomlkit.items import tomlkit.toml_document - from typing_extensions import Self + + from ._typing_compat import Self import tomlkit from packaging.specifiers import SpecifierSet From 1ce3969d93065f2afe3c9df9168dbb2f89e94e23 Mon Sep 17 00:00:00 2001 From: wpk Date: Mon, 13 Nov 2023 19:00:45 -0500 Subject: [PATCH 15/19] chore: update changelog snippet --- .../20231113_155311_william.krekelberg_ignore_whitespace.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md index 1aab19c..f5055ed 100644 --- a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md +++ b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md @@ -17,6 +17,7 @@ Uncomment the section that is right (remove the HTML comment wrapper). - Default is now to remove whitespace from dependencies. For example, the dependency `module > 0.1` will become `module>0.1`. To override this behaviour, pass the option `--no-remove-whitespace`. +- Now supports python version `>3.8,<=3.12` - + ```bash $ p2c project -f tests/data/test-pyproject.toml --dry diff --git a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md index f5055ed..a47ae32 100644 --- a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md +++ b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md @@ -18,6 +18,10 @@ Uncomment the section that is right (remove the HTML comment wrapper). dependency `module > 0.1` will become `module>0.1`. To override this behaviour, pass the option `--no-remove-whitespace`. - Now supports python version `>3.8,<=3.12` +- Can now specify `extras = false` in pyprojec.toml to skip any extras. The + default (`extras = true`) is the same as `extras = [env_name]` where + `env_name` is the name of the environment (e.g., + `tool.pyproject2conda.envs.env_name`). +## v0.9.0 — 2023-11-14 + +### Added + +- Default is now to remove whitespace from dependencies. For example, the + dependency `module > 0.1` will become `module>0.1`. To override this + behaviour, pass the option `--no-remove-whitespace`. +- Now supports python version `>3.8,<=3.12` +- Can now specify `extras = false` in pyprojec.toml to skip any extras. The + default (`extras = true`) is the same as `extras = [env_name]` where + `env_name` is the name of the environment (e.g., + `tool.pyproject2conda.envs.env_name`). + ## v0.8.0 — 2023-10-02 ### Added diff --git a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md b/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md deleted file mode 100644 index a47ae32..0000000 --- a/changelog.d/20231113_155311_william.krekelberg_ignore_whitespace.md +++ /dev/null @@ -1,49 +0,0 @@ - - - - - -### Added - -- Default is now to remove whitespace from dependencies. For example, the - dependency `module > 0.1` will become `module>0.1`. To override this - behaviour, pass the option `--no-remove-whitespace`. -- Now supports python version `>3.8,<=3.12` -- Can now specify `extras = false` in pyprojec.toml to skip any extras. The - default (`extras = true`) is the same as `extras = [env_name]` where - `env_name` is the name of the environment (e.g., - `tool.pyproject2conda.envs.env_name`). - - - - - From 2cc720b27e78950a586c73dc8b6ed610e7fdb04f Mon Sep 17 00:00:00 2001 From: wpk Date: Tue, 14 Nov 2023 12:05:02 -0500 Subject: [PATCH 19/19] chore: rerun cog --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54d18e4..d512ecb 100644 --- a/README.md +++ b/README.md @@ -515,10 +515,10 @@ style = ["requirements"] # or # extras = false + # # A value of `extras = true` also implies using the environment name # as the extras. - [tool.pyproject2conda.envs."test-extras"] extras = ["test"] style = ["yaml", "requirements"] @@ -630,10 +630,10 @@ style = ["requirements"] # or # extras = false + # # A value of `extras = true` also implies using the environment name # as the extras. - [tool.pyproject2conda.envs."test-extras"] extras = ["test"] style = ["yaml", "requirements"]