From 7714ad37bf3156f323b9b6ec440bb9be1a89e5b6 Mon Sep 17 00:00:00 2001 From: phil65 Date: Tue, 15 Oct 2024 22:35:30 +0200 Subject: [PATCH] build: poetry -> uv --- .github/workflows/build.yaml | 33 ++-- .pre-commit-config.yaml | 2 +- duties.py | 28 +-- pyproject.toml | 343 ++++++++--------------------------- 4 files changed, 108 insertions(+), 298 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2b9418c16..c5996605e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -31,25 +31,23 @@ jobs: uses: yezz123/setup-uv@v4 - name: Install dependencies run: | - pip install --upgrade pip - pip install poetry - poetry install + uv sync - name: Test with pytest (PyQt6) # combine test coverage with --cov-append? uses: coactions/setup-xvfb@v1 timeout-minutes: 5 with: run: | - poetry install -E pyqt6 -E addons - poetry run pytest + uv sync --extra pyqt6 --extra addons + uv run pytest - name: Test with pytest (PySide6) uses: coactions/setup-xvfb@v1 # if: runner.os == 'Linux' timeout-minutes: 5 with: run: | - poetry install -E pyside6 -E addons - poetry run pytest --doctest-modules --junitxml=junit/test-results.xml --cov=prettyqt --cov-report=xml --cov-report=html + uv sync --extra pyside6 --extra addons + uv run pytest --doctest-modules --junitxml=junit/test-results.xml --cov=prettyqt --cov-report=xml --cov-report=html - name: Upload coverage to Codecov if: runner.os == 'Windows' && matrix.python-version == '3.11' @@ -63,8 +61,8 @@ jobs: timeout-minutes: 10 with: run: | - poetry install -E pyside6 -E addons - poetry run mkdocs build + uv sync --extra pyside6 --extra addons + uv run mkdocs build # - name: Build and publish # if: startsWith(github.ref, 'refs/tags/v') && matrix.python-version == '3.11' && runner.os == 'Linux' # run: | @@ -91,22 +89,23 @@ jobs: python-version: "3.11" - name: Install packages - # if: runner.os == 'Linux' run: | - pip install --upgrade pip - pip install poetry - poetry install -E pyside6 -E addons + uv sync --extra pyside6 --extra addons - name: Build documentation uses: coactions/setup-xvfb@v1 timeout-minutes: 10 with: run: | - poetry run mkdocs build - - name: Build and publish + uv run mkdocs build + - name: Build + run: | + uv build + - name: Publish on PyPI + env: + UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} run: | - poetry config pypi-token.pypi ${{ secrets.PYPI_SECRET }} - poetry publish --build + uv publish # - name: Deploy docs # # if: github.event_name == 'push' # uses: mhausenblas/mkdocs-deploy-gh-pages@master diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6dfeb6d88..5f6584b0c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: hooks: - id: pytest-check name: pytest-check - entry: poetry run pytest + entry: uv run pytest language: system stages: [pre-push] types: [python] diff --git a/duties.py b/duties.py index 09791478c..30eb45c64 100644 --- a/duties.py +++ b/duties.py @@ -5,21 +5,18 @@ from duty import duty -ENV_PREFIX = "poetry run " - - @duty(capture=False) def build(ctx, *args: str): """Build a MkNodes page.""" args_str = " " + " ".join(args) if args else "" - ctx.run(f"{ENV_PREFIX}mknodes build{args_str}") + ctx.run(f"uv run mknodes build{args_str}") @duty(capture=False) def serve(ctx, *args: str): """Serve a MkNodes page.""" args_str = " " + " ".join(args) if args else "" - ctx.run(f"{ENV_PREFIX}mknodes serve{args_str}") + ctx.run(f"uv run mknodes serve{args_str}") @duty(capture=False) @@ -35,7 +32,7 @@ def test_pyside6(ctx, *args: str): os.environ["QT_API"] = "pyside6" args = ("--cov-report=xml", *args) args_str = " " + " ".join(args) - ctx.run(f"{ENV_PREFIX}pytest{args_str}") + ctx.run(f"uv run pytest{args_str}") @duty(capture=False) @@ -44,7 +41,7 @@ def test_pyqt6(ctx, *args: str): os.environ["QT_API"] = "pyqt6" args = ("--cov-report=xml", *args) args_str = " " + " ".join(args) - ctx.run(f"{ENV_PREFIX}pytest{args_str}") + ctx.run(f"uv run pytest{args_str}") @duty(capture=False) @@ -56,27 +53,30 @@ def clean(ctx): @duty(capture=False) def update(ctx, *args: str): """Update all environment packages using pip directly.""" - ctx.run("poetry update") - ctx.run("poetry install --all-extras") + ctx.run("uv sync --all-extras") @duty(capture=False) def lint(ctx): - """Update all environment packages using pip directly.""" - ctx.run(f"{ENV_PREFIX}lint") + """Lint and fix the code.""" + ctx.run("uv run ruff check --fix .") + ctx.run("uv run ruff format .") + ctx.run("uv run mypy prettyqt/") @duty(capture=False) def lint_check(ctx): - """Update all environment packages using pip directly.""" - ctx.run(f"{ENV_PREFIX}lint-check") + """Lint the code.""" + ctx.run("uv run ruff check .") + ctx.run("uv run ruff format --check .") + ctx.run("uv run mypy prettyqt/") @duty(capture=False) def profile(ctx, *args: str): """Run generating the docs using pyinstrument.""" args_str = " " + " ".join(args) if args else "" - ctx.run(f"{ENV_PREFIX}pyinstrument mknodes/manual/root.py{args_str}") + ctx.run(f"uv run pyinstrument mknodes/manual/root.py{args_str}") @duty(capture=False) diff --git a/pyproject.toml b/pyproject.toml index ebfaea98d..767e65b1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ readme = 'README.md' requires-python = ">=3.11" license = "MIT" authors = [ - { name = "Philipp Temminghoff", email = "philipptemminghoff@gmail.com" }, + { name = "Philipp Temminghoff", email = "philipptemminghoff@gmail.com" }, ] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -21,15 +21,7 @@ classifiers = [ "Topic :: Software Development :: User Interfaces", "Topic :: Software Development :: Widget Sets", ] -keywords = [ - "qt", - "pyqt", - "pyside", - "widgets", - "components", - "framework", - "gui", -] +keywords = ["qt", "pyqt", "pyside", "widgets", "components", "framework", "gui"] dependencies = [ "bidict", "pywin32;platform_system == 'Windows'", @@ -46,29 +38,18 @@ Source = "https://github.com/phil65/prettyqt" Tracker = "https://github.com/phil65/prettyqt/issues" Changelog = "https://github.com/phil65/prettyqt/blob/main/CHANGELOG.md" -[project.optional-dependencies] -dev = [ - "pip", - "pyreadline3", - "devtools", -] - -typing = [ +[tool.uv] +dev-dependencies = [ "mypy", "types-Deprecated", "types-orjson", "types-python-dateutil", "types-requests", -] - -test = [ "pytest", "pytest-cov", "pytest-qt", "pytest-xvfb;platform_system == 'Linux'", "coverage", -] -docs = [ "mkdocs", "mkdocs-material", "mkdocstrings[python]", @@ -79,25 +60,14 @@ docs = [ "mkdocs-section-index", "mkdocs-gen-files", "markdown-exec", + "pyreadline3", + "devtools", ] -addons = [ - "orjson", - "fsspec", - "pillow", - "numpy", - "qtconsole", - "ipython", - "lxml", -] - -pyqt6 = [ - "PyQt6", - "PyQt6-Charts", - "PyQt6-WebEngine", - "PyQt6-QScintilla", -] +[project.optional-dependencies] +addons = ["orjson", "fsspec", "pillow", "numpy", "qtconsole", "ipython", "lxml"] +pyqt6 = ["PyQt6", "PyQt6-Charts", "PyQt6-WebEngine", "PyQt6-QScintilla"] pyside6 = ["pyside6"] [project.gui-scripts] @@ -107,175 +77,13 @@ iconbrowser = "prettyqt.custom_widgets.iconbrowser:run" [project.entry-points.pyinstaller40] hook-dirs = "prettyqt.__pyinstaller:get_hook_dirs" -[[envs.default.matrix]] -python = ["3.11", "3.11"] - -[tool.hatch.build.targets.wheel.hooks.mypyc] -dependencies = ["hatch-mypyc"] -# [build.targets.wheel.hooks.mypyc] -# include = ["/src/pkg/server"] -# mypy-args = [ -# "--disallow-untyped-defs", -# ] -# [tool.hatch.version] -# path = "src/foo/__about__.py" -[tool.hatch.envs.docs] -dependencies = [ - "mkdocs", - "mkdocs-material", - "mkdocstrings[python]", - "mkdocs-literate-nav", - "pymdown-extensions", - "mkdocs-glightbox", - # "mkdocs-autorefs", - "markdown-exec", - "mkdocs-section-index", - "mkdocs-gen-files", -] - -[envs.default.scripts] -test = 'pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=pkg --cov=tests' - - -[tool.hatch.envs.default.scripts] -test = 'pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=pkg --cov=tests' - - [tool.hatch.envs.docs.scripts] build = "mkdocs build --clean" serve = "mkdocs serve --dirtyreload" - -[tool.hatch.build] -sources = ["prettyqt"] -include = [ - "/prettyqt", - "/tests", -] - -[tool.hatch.envs.default] -features = [ - "addons", - "typing", - "docs", - "test", - "pyside6", - "pyqt6", - "dev", -] - -[tool.hatch.envs.test] -dependencies = [ - "coverage[toml]", - "pytest", - "pytest-cov", - "pytest-qt", -] - - -[tool.poetry] -name = "PrettyQt" -version = "1.57.4" -description = "A pythonic layer on top of PyQt6 / PySide6" -readme = 'README.md' -license = "MIT" -documentation = "https://phil65.github.io/PrettyQt/index.html" -keywords = [ - "qt", - "pyqt", - "pyside", - "widgets", - "components", - "framework", - "gui", -] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: X11 Applications :: Qt", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", - # "Programming Language :: Python :: 3.12", - "Topic :: Desktop Environment", - "Topic :: Software Development :: User Interfaces", - "Topic :: Software Development :: Widget Sets", -] -repository = "https://github.com/phil65/prettyqt" -homepage = "https://github.com/phil65/prettyqt" -authors = ["phil65 "] -packages = [ - { include = "prettyqt" }, -] - -[tool.poetry.scripts] -regexeditor = "prettyqt.custom_widgets.regexeditor.__main__:run" -iconbrowser = "prettyqt.custom_widgets.iconbrowser:run" - -[tool.poetry.plugins."pyinstaller40"] -hook-dirs = "prettyqt.__pyinstaller:get_hook_dirs" - -[tool.poetry.dependencies] -python = "^3.11" -bidict = "^0" -pygments = "^2.6" -qstylizer = "^0.2" -typing-extensions = "^4.5.0" - -orjson = {version = "^3", python = "<3.12", optional=true} -fsspec = {version = "^2023.1.0", optional=true} -pillow = {version = "^10", optional=true} -numpy = {version = "^1", optional=true} -qtconsole = {version = "^5", optional=true} -ipython = {version = "^8", optional=true} -attrs = {version = "*", optional = true} -lxml = {version = "*", optional = true} - -pywin32 = {version = "*", markers = "sys_platform == 'win32'"} -comtypes = {version = "^1.1", markers = "sys_platform == 'win32'"} - -PyQt6 = {version = "^6.5", optional=true} -PyQt6-Charts = {version = "^6.5", optional=true} -PyQt6-WebEngine = {version = "^6.5", optional=true} -PyQt6-QScintilla = {version = "^2.0", optional=true} - -pyside6 = {version = "^6.5", python = "<3.12", optional=true} - - -[tool.poetry.extras] -pyside6 = ["pyside6", "PySide6-QtAds"] -pyqt6 = ["pyqt6", "PyQt6-Charts", "PyQt6-WebEngine", "PyQt6-QScintilla", "PyQtAds"] -addons = ["orjson", "fsspec", "pillow", "numpy", "qtconsole", "ipython", "attrs", "lxml"] - -[tool.poetry.group.dev.dependencies] -pytest = "^7" -pip = "^23" -pyreadline3 = "^3" -pytest-cov = "^4" -pytest-qt = "^4.1" -mypy = "^1" -types-Deprecated = "^1.2" -types-orjson = "^3.6" -types-python-dateutil = "^2" -types-requests = "^2.31.0.1" -pytest-xvfb = {version = "^2", markers = "sys_platform == 'linux'"} -mkdocs_mknodes = "*" -mkdocs-material = {version = "^9", allow-prereleases=true} -mkdocstrings = {version = "^0", extras = ["python"]} -mkdocs-glightbox = "*" -markdown-exec = "*" -pymdown-extensions = "^10" - -devtools = "^0" - - [tool.pytest.ini_options] minversion = "7.0" -testpaths = [ - "tests", -] +testpaths = ["tests"] junit_family = "xunit2" qt_qapp_name = "prettyqt-test" # qt_api = "pyside6" @@ -290,113 +98,118 @@ exclude_lines = [ "if TYPE_CHECKING:", "@overload", "except ImportError", - 'if __name__ == "__main__":' + 'if __name__ == "__main__":', ] [tool.mypy] python_version = "3.11" -disable_error_code = ["assignment", "misc", "attr-defined", "import", "misc", "attr-defined"] +disable_error_code = [ + "assignment", + "misc", + "attr-defined", + "import", + "misc", + "attr-defined", +] [tool.ruff] line-length = 90 +extend-exclude = ['docs', '__init__.py', "prettyqt/qt/"] +target-version = "py311" + +[tool.ruff.lint] select = [ # "A", # Flake8-builtins # "ANN", # Flake8-Annotations # "ASYNC", # Flake8-Async # "ARG", # # Flake8-Unused-Arguments - "B", # flake8-bugbear + "B", # flake8-bugbear # "BLE", # Flake8-blind-except "C", - "C4", # flake8-comprehensions + "C4", # flake8-comprehensions # "C90", # MCCabe # "COM", # Flake8-commas - "CPY", # Copyright-related rules - "D", # PyDocStyle + "CPY", # Copyright-related rules + "D", # PyDocStyle # "DTZ", # Flake8- Datetimez - "E", # PyCodeStyle Error + "E", # PyCodeStyle Error # "EM", # flake8-errmsg # "ERA", # Eradicate - "EXE", # flake8-executable - "F", # PyFlakes + "EXE", # flake8-executable + "F", # PyFlakes "FA", # flake8-future-annotations # "FBT", # flake8-boolean-trap # "FIX", # flake8-fixme - "FLY", # flynt + "FLY", # flynt # "G", # flake8-logging-format - "I", # ISort - "ICN", # Flake8-import-conventions - "INP", # flake8-no-pep420 - "INT", # flake8-gettext - "ISC", # flake8-implicit-str-concat + "I", # ISort + "ICN", # Flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat # "N", # pep8-naming # "NPY", # numpy-specific rules # "PD", # pandas-vet "PERF", # perflint # "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PLE", # PyLint Error - "PLC", # PyLint convention + "PIE", # flake8-pie + "PLE", # PyLint Error + "PLC", # PyLint convention # "PLW", # PyLint Warning # "PLR", # PyLint refactor # "PT", # flake8-pytest-style - "PTH", # flake8-use-pathlib - "PYI", # flake8-pyi + "PTH", # flake8-use-pathlib + "PYI", # flake8-pyi # "Q", # flake8-quotes # "RET", # flake8-return - "RSE", # flake8-raise - "RUF", # ruff-specific rules + "RSE", # flake8-raise + "RUF", # ruff-specific rules # "S", # flake8-bandit # "SIM", # flake8-simplify "SLF", # flake8-self - "SLOT", # flake8-slots + "SLOT", # flake8-slots # "T", # "TD", # flake8-todos - "T10", # flake8-debugger + "T10", # flake8-debugger # "T20", # flake8-print # "TCH", # flake8-type-checking - "TID", # flake8-tidy-imports + "TID", # flake8-tidy-imports # "TRY", # tryceratops - "UP", # PyUpgrade - "W", # PyCodeStyle warning - "YTT", # flake8-2020 + "UP", # PyUpgrade + "W", # PyCodeStyle warning + "YTT", # flake8-2020 ] ignore = [ - "C408", # Unnecessary {obj_type} call (rewrite as a literal) - "B905", # zip() without an explicit strict= parameter - "C901", # {name} is too complex ({complexity} > {max_complexity}) - "D100", # Missing docstring in public module - "D101", # Missing docstring in public class - "D102", # Missing docstring in public method - "D103", # Missing docstring in public function - "D104", # Missing docstring in public package - "D105", # Missing docstring in magic method - "D106", # Missing docstring in public nested class - "D107", # Missing docstring in __init__ - "D203", # 1 blank line required before class docstring - "D204", # 1 blank line required after class docstring - "D213", # Multi-line docstring summary should start at the second line - "D215", # Section underline is over-indented ("{name}") - "D400", # First line should end with a period - "D401", # First line of docstring should be in imperative mood: "{first_line}" - "D404", # First word of the docstring should not be "This" - "D406", # Section name should end with a newline ("{name}") - "D407", # Missing dashed underline after section ("{name}") - "D408", # Section underline should be in the line following the section's name ("{name}") - "D409", # Section underline should match the length of its name ("{name}") - "D413", # Missing blank line after last section ("{name}") + "C408", # Unnecessary {obj_type} call (rewrite as a literal) + "B905", # zip() without an explicit strict= parameter + "C901", # {name} is too complex ({complexity} > {max_complexity}) + "D100", # Missing docstring in public module + "D101", # Missing docstring in public class + "D102", # Missing docstring in public method + "D103", # Missing docstring in public function + "D104", # Missing docstring in public package + "D105", # Missing docstring in magic method + "D106", # Missing docstring in public nested class + "D107", # Missing docstring in __init__ + "D203", # 1 blank line required before class docstring + "D204", # 1 blank line required after class docstring + "D213", # Multi-line docstring summary should start at the second line + "D215", # Section underline is over-indented ("{name}") + "D400", # First line should end with a period + "D401", # First line of docstring should be in imperative mood: "{first_line}" + "D404", # First word of the docstring should not be "This" + "D406", # Section name should end with a newline ("{name}") + "D407", # Missing dashed underline after section ("{name}") + "D408", # Section underline should be in the line following the section's name ("{name}") + "D409", # Section underline should match the length of its name ("{name}") + "D413", # Missing blank line after last section ("{name}") "INP001", # File is part of an implicit namespace package. Add an `__init__.py`. - "SLF001",# Private member accessed + "SLF001", # Private member accessed "RUF012", # Mutable class attributes should be annotated "RUF013", # PEP 484 prohibits implicit `Optional` ] -extend-exclude = [ - 'docs', - '__init__.py', - "prettyqt/qt/", -] -target-version = "py311" -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] docstring-quotes = "double" @@ -406,7 +219,7 @@ preview = true docstring-code-format = true -[tool.ruff.isort] +[tool.ruff.lint.isort] lines-after-imports = 2 lines-between-types = 1 # atomic = true @@ -415,7 +228,5 @@ combine-as-imports = true [build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" -# requires = ["hatchling"] -# build-backend = "hatchling.build" +requires = ["hatchling"] +build-backend = "hatchling.build"