diff --git a/.github/workflows/edgetest.yml b/.github/workflows/edgetest.yml index e4f3289..ecd5cb2 100644 --- a/.github/workflows/edgetest.yml +++ b/.github/workflows/edgetest.yml @@ -19,4 +19,4 @@ jobs: edgetest-flags: '-c setup.cfg --export' base-branch: 'dev' skip-pr: 'false' - python-version: 3.10 + python-version: '3.10' diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 30e0406..34b0cb8 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -28,11 +28,11 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: python -m pip install .[dev] - - name: Check docstrings - run: python -m pydocstyle edgetest_pip_tools --convention=numpy + - name: Run ruff QA checks + run: python -m ruff check . + - name: Check formatting + run: python -m ruff format . --check - name: Check static typing run: python -m mypy edgetest_pip_tools - - name: Run flake8 - run: python -m flake8 edgetest_pip_tools - name: Run unit testing run: python -m pytest tests --cov=edgetest_pip_tools --cov-fail-under=80 diff --git a/README.md b/README.md index 8e7d5ce..d18951d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # pip-tools integration for edgetest ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/edgetest-pip-tools) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![PyPI version](https://badge.fury.io/py/edgetest-pip-tools.svg)](https://badge.fury.io/py/edgetest-pip-tools) [![Anaconda-Server Badge](https://anaconda.org/conda-forge/edgetest-pip-tools/badges/version.svg)](https://anaconda.org/conda-forge/edgetest-pip-tools) @@ -37,7 +37,7 @@ Getting Started --------------- This `edgetest` plugin runs after the test execution. If the last environment successfully -passes, this plugin will refresh `requirements.txt` using `pip-tools`. To use this plugin, +passes, this plugin will refresh `requirements.txt` using `uv pip compile`. To use this plugin, you must use the ``--export`` flag in your CLI call: ```console diff --git a/docs/source/conf.py b/docs/source/conf.py index 90b5658..1d87a9a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # @@ -25,7 +24,7 @@ author = "Akshay Gupta" # The short X.Y version -version = "2023.8.0" +version = "2024.8.0" # The full version, including alpha/beta/rc tags release = "" diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 9bc729f..12d09a1 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -22,7 +22,7 @@ Getting Started --------------- This `edgetest` plugin runs after the test execution. If the last environment successfully -passes, this plugin will refresh `requirements.txt` using `pip-tools`. To use this plugin, +passes, this plugin will refresh `requirements.txt` using `uv pip compile`. To use this plugin, supply ``--export`` to your CLI call: @@ -59,7 +59,7 @@ If you want to specify a PyPI index, supply `index_url` in your configuration: index_url = "https://myindex.com" -If you want to include extra installations in your `pip-tools` call add a newline-separated list of +If you want to include extra installations in your `uv pip compile` call add a newline-separated list of extras: diff --git a/edgetest_pip_tools/__init__.py b/edgetest_pip_tools/__init__.py index 478dd2d..592b20c 100644 --- a/edgetest_pip_tools/__init__.py +++ b/edgetest_pip_tools/__init__.py @@ -1,6 +1,6 @@ """Package initialization.""" -__version__ = "2023.8.0" +__version__ = "2024.8.0" __title__ = "edgetest-pip-tools" __description__ = "pip-tools integration for edgetest" diff --git a/edgetest_pip_tools/plugin.py b/edgetest_pip_tools/plugin.py index bca83e1..c223666 100644 --- a/edgetest_pip_tools/plugin.py +++ b/edgetest_pip_tools/plugin.py @@ -2,7 +2,7 @@ from configparser import ConfigParser from pathlib import Path -from typing import Dict, List, Any +from typing import Any, Dict, List import click import pluggy @@ -42,7 +42,7 @@ def addoption(schema: Schema): def get_reqfile(ctx: click.Context) -> Path: - """Get the requirements file to supply to ``pip-tools``. + """Get the requirements file to supply to ``uv``. Parameters ---------- @@ -64,7 +64,8 @@ def get_reqfile(ctx: click.Context) -> Path: else: reqfile = Path(ctx.params["requirements"]) elif Path(ctx.params["config"]).name == "pyproject.toml": - parser = load(open(Path(ctx.params["config"]))) + with open(Path(ctx.params["config"])) as buf: + parser = load(buf) if "dependencies" in parser["project"]: reqfile = Path(ctx.params["config"]) else: @@ -80,7 +81,9 @@ def post_run_hook(testers: List, conf: Dict): """Refresh a locked requirements file based on the test output.""" ctx = click.get_current_context() if not ctx.params["export"]: - LOG.info("Skipping ``pip-tools`` as the requirements have not been updated.") + LOG.info( + "Skipping ``uv pip compile`` as the requirements have not been updated." + ) elif testers[-1].status: reqfile = get_reqfile(ctx=ctx) try: @@ -91,9 +94,8 @@ def post_run_hook(testers: List, conf: Dict): options.append(f"--index-url={conf['pip_tools']['index_url']}") _run_command( - testers[-1].python_path, - "-m", - "piptools", + "uv", + "pip", "compile", "-U", *options, @@ -103,4 +105,4 @@ def post_run_hook(testers: List, conf: Dict): except RuntimeError: LOG.info("Unable to update the locked requirements file") else: - LOG.info("Skipping ``pip-tools`` refresh as the tests didn't pass.") + LOG.info("Skipping ``uv`` refresh as the tests didn't pass.") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5a3986c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,169 @@ +[project] +name = "edgetest-pip-tools" +requires-python = ">=3.8.0" +description = "pip-tools integration for edgetest" +authors = [ + { name = "Akshay Gupta", email = "akshay.gupta2@capitalone.com" }, + { name = "Faisal Dosani", email = "faisal.dosani@capitalone.com" }, + { name = "Jacob Dawang", email = "jacob.dawang@capitalone.com" } +] +license = { text = "Apache Software License" } +classifiers = [ + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = ["edgetest>2024.4.0"] +dynamic = ["readme", "version"] + +[project.optional-dependencies] +docs = [ + "furo", + "sphinx", + "sphinx-copybutton", + "sphinx-tabs", +] +tests = [ + "coverage", + "pytest", + "pytest-cov", +] +qa = [ + "mypy", + "pip-tools", + "pre-commit", + "ruff~=0.5", + "types-click", +] +build = [ + "build", + "twine", + "wheel", + "bumpver", +] +dev = [ + "edgetest-pip-tools[docs]", + "edgetest-pip-tools[tests]", + "edgetest-pip-tools[qa]", + "edgetest-pip-tools[build]", +] + +[project.urls] +"Documentation" = "https://capitalone.github.io/edgetest-pip-tools/" +"Bug Tracker" = "https://github.com/capitalone/edgetest-pip-tools/issues" +"Source Code" = "https://github.com/capitalone/edgetest-pip-tools" + +[project.entry-points."edgetest"] +piptools = "edgetest_pip_tools.plugin" + +# Build system +[build-system] +requires = ["setuptools>=64.0.0"] +build-backend = "setuptools.build_meta" + +############################################################################## +# Setuptools configuration +############################################################################## + +[tool.setuptools] +include-package-data = true +zip-safe = false + +[tool.setuptools.dynamic] +version = { attr = "edgetest_pip_tools.__version__" } +readme = { file = ["README.md"], content-type = "text/markdown" } + +############################################################################## +# Tooling +############################################################################## + +# BUMPVER -------------------------------------------------------------------- + +[bumpver] +current_version = "2024.8.0" +version_pattern = "YYYY.MM.INC0" + +[bumpver.file_patterns] +"docs/source/conf.py" = [ + 'version = "{version}"', +] +"pyproject.toml" = [ + 'current_version = "{version}"', +] +"edgetest_pip_tools/__init__.py" = [ + '__version__ = "{version}"', +] + +# MYPY ----------------------------------------------------------------------- + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +ignore_missing_imports = true +allow_redefinition = true +disable_error_code = "empty-body" + +# PYTEST --------------------------------------------------------------------- + +[tool.pytest.ini_options] +markers = [ + "unit: mark unit tests that do not require external interfaces and use mocking", + "integration: mark test that interact with an external system", +] +addopts = "--verbose" + +# RUFF ----------------------------------------------------------------------- + +[tool.ruff] +extend-include = ["*.ipynb"] +target-version = "py39" + +[tool.ruff.lint] +preview = true +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "D", # pydocstyle + "I", # isort + "UP", # pyupgrade + "B", # flake8-bugbear + "C", # flake8-comprehensions + "T20", # flake8-print + "TID252", # flake8-tidy-imports ban relative imports + "SIM", # flake8-simplify + "LOG", # flake8-logging + "RUF", # Ruff errors +] +ignore = [ + "C901", # Add back in later + "E111", # Check indentation level. Using formatter instead. + "E114", # Check indentation level. Using formatter instead. + "E117", # Check indentation level. Using formatter instead. + "E203", # Check whitespace. Using formatter instead. + "E501", # Line too long. Using formatter instead. + "D206", # Docstring indentation. Using formatter instead. + "D300", # Use triple single quotes. Using formatter instead. + "SIM108", # Use ternary operator instead of if-else blocks. + "SIM105", # Use ``contextlib.suppress(FileNotFoundError)`` insetad of try - execpt - pass. + "UP035", # ``typing.x`` is deprecated, use ``x`` instead + "UP006", # ``typing.x`` is deprecated, use ``x`` instead +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["E402"] +"**/{tests,docs}/*" = ["E402", "D", "F841", "ARG"] + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[tool.ruff.lint.pydocstyle] +convention = "numpy" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ce58331 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,41 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --output-file=requirements.txt setup.cfg +# +build==1.0.3 + # via pip-tools +cerberus==1.3.4 + # via edgetest +click==8.1.3 + # via + # edgetest + # pip-tools +edgetest==2023.6.1 + # via edgetest-pip-tools (setup.cfg) +packaging==23.0 + # via + # build + # edgetest +pip-tools==7.3.0 + # via edgetest-pip-tools (setup.cfg) +pluggy==1.0.0 + # via edgetest +pyproject-hooks==1.0.0 + # via build +tabulate==0.9.0 + # via edgetest +tomli==2.0.1 + # via + # build + # pip-tools + # pyproject-hooks +tomlkit==0.11.4 + # via edgetest +wheel==0.42.0 + # via pip-tools + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 0f89a3e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,147 +0,0 @@ -[metadata] -name = edgetest-pip-tools -version = attr: edgetest_pip_tools.__version__ -description = pip-tools integration for edgetest -long_description = file: README.md, MAINTAINERS -long_description_content_type = text/markdown -author = Akshay Gupta -author_email = akshay.gupta2@capitalone.com -maintainer = Akshay Gupta -maintainer_email = akshay.gupta2@capitalone.com -url = https://github.com/capitalone/edgetest-pip-tools -python_requires = - >=3.8.0 -project_urls = - Documentation = https://capitalone.github.io/edgetest-pip-tools/ - Bug Tracker = https://github.com/capitalone/edgetest-pip-tools/issues - Source Code = https://github.com/capitalone/edgetest-pip-tools -classifiers = - Intended Audience :: Developers - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - -[options] -zip_safe = False -include_package_data = True -packages = find: -install_requires = - edgetest - pip-tools<=7.3.0,>=6.0.0 - -[options.extras_require] -docs = - furo - sphinx - sphinx-copybutton - sphinx-tabs -tests = - coverage - flake8 - mypy - pydocstyle - pytest - pytest-cov -qa = - black - isort - pip-tools - pre-commit - pylint - types-click -build = - build - twine - wheel - bumpver -dev = - coverage - flake8 - mypy - pydocstyle - pytest - pytest-cov - furo - sphinx - sphinx-copybutton - sphinx-tabs - black - isort - pip-tools - pre-commit - pylint - types-click - build - twine - wheel - bumpver - -[options.entry_points] -edgetest = - piptools = edgetest_pip_tools.plugin - -[bumpver] -current_version = "2023.8.0" -version_pattern = "YYYY.MM.INC0" -commit_message = "Bump {old_version} to {new_version}" -commit = True - -[bumpver:file_patterns] -docs/source/conf.py = - version = "{version}" -setup.cfg = - current_version = "{version}" -edgetest_pip_tools/__init__.py = - __version__ = "{version}" - -[aliases] -lint = pylint - -[bdist_wheel] -python-tag = py3 - -[flake8] -max-line-length = 100 -exclude = tests/* -max-complexity = 17 -ignore = E203, W503 - -[isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 - -[mypy] -python_version = 3.10 -warn_return_any = True -warn_unused_configs = True -ignore_missing_imports = True -allow_redefinition = True - -[pylint] -pylint_minimum_score = 9.5 - -[tool:pytest] -markers = - unit: mark unit tests that do not require external interfaces and use mocking - integration: mark test that interact with an external system -addopts = --verbose - -[edgetest.envs.core] -python_version = 3.10 -upgrade = - edgetest - pip-tools -extras = - tests -deps = - pip-tools - diff --git a/setup.py b/setup.py deleted file mode 100644 index 9d2bf76..0000000 --- a/setup.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Setup file for the package. For configuration information, see the ``setup.cfg``.""" - -from setuptools import setup - -setup() \ No newline at end of file diff --git a/tests/test_hook.py b/tests/test_hook.py index 04dcdac..f7a7ef9 100644 --- a/tests/test_hook.py +++ b/tests/test_hook.py @@ -155,44 +155,47 @@ def test_update_reqs_cfg(mock_popen, mock_cpopen, mock_builder): env_loc = Path(loc) / ".edgetest" / "myenv" if platform.system() == "Windows": - py_loc = str(Path(env_loc) / "Scripts" / "python") + py_loc = str(Path(env_loc) / "Scripts" / "python.exe") else: py_loc = str(Path(env_loc) / "bin" / "python") assert result.exit_code == 0 assert mock_popen.call_args_list == [ call( - (f"{str(py_loc)}", "-m", "pip", "install", "."), + ("uv", "pip", "install", f"--python={py_loc!s}", "."), stdout=-1, + stderr=-1, universal_newlines=True, ), call( ( - f"{str(py_loc)}", - "-m", + "uv", "pip", "install", + f"--python={py_loc!s}", "myupgrade", "--upgrade", ), stdout=-1, + stderr=-1, universal_newlines=True, ), call( - (f"{str(py_loc)}", "-m", "pip", "list", "--format", "json"), + ("uv", "pip", "list", f"--python={py_loc!s}", "--format", "json"), stdout=-1, + stderr=-1, universal_newlines=True, ), call( - (f"{str(py_loc)}", "-m", "pip", "list", "--format", "json"), + ("uv", "pip", "list", f"--python={py_loc!s}", "--format", "json"), stdout=-1, + stderr=-1, universal_newlines=True, ), call( ( - f"{str(py_loc)}", - "-m", - "piptools", + "uv", + "pip", "compile", "-U", "--index-url=myindexurl", @@ -200,6 +203,7 @@ def test_update_reqs_cfg(mock_popen, mock_cpopen, mock_builder): "setup.cfg", ), stdout=-1, + stderr=-1, universal_newlines=True, ), ] @@ -220,48 +224,51 @@ def test_update_reqs_toml(mock_popen, mock_cpopen, mock_builder): with runner.isolated_filesystem() as loc: with open("pyproject.toml", "w") as outfile: outfile.write(TOML_ART) - result = runner.invoke(cli, [f"--config=pyproject.toml", "--export"]) + result = runner.invoke(cli, ["--config=pyproject.toml", "--export"]) env_loc = Path(loc) / ".edgetest" / "myenv" if platform.system() == "Windows": - py_loc = str(Path(env_loc) / "Scripts" / "python") + py_loc = str(Path(env_loc) / "Scripts" / "python.exe") else: py_loc = str(Path(env_loc) / "bin" / "python") assert result.exit_code == 0 assert mock_popen.call_args_list == [ call( - (f"{str(py_loc)}", "-m", "pip", "install", "."), + ("uv", "pip", "install", f"--python={py_loc!s}", "."), stdout=-1, + stderr=-1, universal_newlines=True, ), call( ( - f"{str(py_loc)}", - "-m", + "uv", "pip", "install", + f"--python={py_loc!s}", "myupgrade", "--upgrade", ), stdout=-1, + stderr=-1, universal_newlines=True, ), call( - (f"{str(py_loc)}", "-m", "pip", "list", "--format", "json"), + ("uv", "pip", "list", f"--python={py_loc!s}", "--format", "json"), stdout=-1, + stderr=-1, universal_newlines=True, ), call( - (f"{str(py_loc)}", "-m", "pip", "list", "--format", "json"), + ("uv", "pip", "list", f"--python={py_loc!s}", "--format", "json"), stdout=-1, + stderr=-1, universal_newlines=True, ), call( ( - f"{str(py_loc)}", - "-m", - "piptools", + "uv", + "pip", "compile", "-U", "--extra=complete,another", @@ -270,6 +277,7 @@ def test_update_reqs_toml(mock_popen, mock_cpopen, mock_builder): "pyproject.toml", ), stdout=-1, + stderr=-1, universal_newlines=True, ), ]