From c1968ba4e34596e4b01b688ec51f6916dfe05032 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:41:25 +0200 Subject: [PATCH 01/23] Add dependency groups to pyproject and drop pytest-cov dependency --- pyproject.toml | 78 +++++++++++++++++++++++++++++--------------------- run_tests.py | 29 ++++++++++++------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f10c3529..1299f261 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,9 @@ build-backend = "setuptools.build_meta" [project] name = "novelWriter" -authors = [ - {name = "Veronica Berglyd Olsen", email = "code@vkbo.net"}, -] +authors = [{ name = "Veronica Berglyd Olsen", email = "code@vkbo.net" }] description = "A plain text editor for planning and writing novels" -readme = {file = "setup/description_pypi.md", content-type = "text/markdown"} +readme = { file = "setup/description_pypi.md", content-type = "text/markdown" } license = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0" license-files = ["LICENSE.md", "setup/LICENSE-Apache-2.0.txt"] classifiers = [ @@ -26,12 +24,28 @@ classifiers = [ "Topic :: Text Editors", ] requires-python = ">=3.10" -dependencies = [ - "pyqt6>=6.4", - "pyenchant>=3.0.0", -] +dependencies = ["pyqt6>=6.4", "pyenchant>=3.0.0"] dynamic = ["version"] +[dependency-groups] +dev = [ + { include-group = "docs" }, + { include-group = "test" }, + { include-group = "lint" }, +] +docs = [ + "docutils>=0.17.1", + "pygments>=2.7", + "sphinx-book-theme", + "sphinx-copybutton", + "sphinx-design", + "sphinx-favicon", + "sphinx-intl", + "sphinx>=5.0", +] +test = ["coverage>=7.2.0", "pytest-qt", "pytest-timeout", "pytest>=6.0.0"] +lint = ["isort", "pyright", "ruff"] + [project.urls] Homepage = "https://novelwriter.io" Documentation = "https://docs.novelwriter.io" @@ -42,13 +56,13 @@ Issues = "https://github.com/vkbo/novelWriter/issues" novelwriter = "novelwriter:main" [tool.setuptools.dynamic] -version = {attr = "novelwriter.__version__"} +version = { attr = "novelwriter.__version__" } [tool.setuptools.packages.find] include = ["novelwriter*"] [tool.isort] -py_version="310" +py_version = "310" line_length = 99 wrap_length = 79 multi_line_output = 5 @@ -64,26 +78,26 @@ preview = true # Rules: https://docs.astral.sh/ruff/rules select = [ - "A", # flake8-builtins (A) - "ANN", # flake8-annotations (ANN) - "B", # flake8-bugbear (B) - "D", # pydocstyle (D) - "E", # pycodestyle (E) - "F", # Pyflakes (F) - "FA", # flake8-future-annotations (FA) - "PERF", # Perflint (PERF) - "PLC", # Pylint Convention (PLC) - "PLE", # Pylint Error (PLE) - "PLR17", # Refactor (PLR) - Only PLR17xx - "PLW", # Pylint Warning (PLW) - "Q", # flake8-quotes (Q) - "RET", # flake8-return (RET) - "RUF", # Ruff-specific rules (RUF) - "SLF", # flake8-self (SLF) - "SLOT", # flake8-slots (SLOT) - "TC", # flake8-type-checking (TC) - "UP", # pyupgrade (UP) - "W", # pycodestyle (W) + "A", # flake8-builtins (A) + "ANN", # flake8-annotations (ANN) + "B", # flake8-bugbear (B) + "D", # pydocstyle (D) + "E", # pycodestyle (E) + "F", # Pyflakes (F) + "FA", # flake8-future-annotations (FA) + "PERF", # Perflint (PERF) + "PLC", # Pylint Convention (PLC) + "PLE", # Pylint Error (PLE) + "PLR17", # Refactor (PLR) - Only PLR17xx + "PLW", # Pylint Warning (PLW) + "Q", # flake8-quotes (Q) + "RET", # flake8-return (RET) + "RUF", # Ruff-specific rules (RUF) + "SLF", # flake8-self (SLF) + "SLOT", # flake8-slots (SLOT) + "TC", # flake8-type-checking (TC) + "UP", # pyupgrade (UP) + "W", # pycodestyle (W) ] ignore = [ "ANN401", # any-type @@ -149,6 +163,4 @@ branch = false [tool.coverage.report] precision = 2 -exclude_also = [ - "if TYPE_CHECKING:" -] +exclude_also = ["if TYPE_CHECKING:"] diff --git a/run_tests.py b/run_tests.py index b9687aa9..f2e91ec7 100755 --- a/run_tests.py +++ b/run_tests.py @@ -3,6 +3,7 @@ import argparse import os +import shlex import subprocess import sys @@ -12,27 +13,35 @@ if __name__ == "__main__": parser.add_argument("-o", action="store_true", help="Run off screen") parser.add_argument("-r", action="store_true", help="Generate reports") parser.add_argument("-t", action="store_true", help="Generate terminal report") - parser.add_argument("-m", help="Test modules") - parser.add_argument("-k", help="Test filters") + parser.add_argument("-u", action="store_true", help="Generate uncovered terminal report") + parser.add_argument("-m", help="Test modules", metavar="MARKEXPR") + parser.add_argument("-k", help="Test filters", metavar="EXPRESSION") args = parser.parse_args() env = os.environ.copy() env["QT_SCALE_FACTOR"] = "1.0" - cmd = [sys.executable, "-m", "pytest", "-vv"] + if args.r or args.t or args.u: + cmd = [sys.executable, "-m"] + else: + cmd = ["coverage", "-m"] + + cmd += ["pytest", "-vv"] if args.o: env["QT_QPA_PLATFORM"] = "offscreen" - if args.r or args.t: - cmd += ["--cov=novelwriter"] - if args.r: - cmd += ["--cov-report=xml", "--cov-report=html"] - if args.t: - cmd += ["--cov-report=term"] if args.m: cmd += ["-m", args.m] if args.k: cmd += ["-k", args.k] - print("Calling:", " ".join(cmd)) + print("Calling:", shlex.join(cmd)) subprocess.call(cmd, env=env) + + if args.r: + subprocess.call(["coverage", "xml"]) + subprocess.call(["coverage", "html"]) + if args.t and not args.u: + subprocess.call(["coverage", "report"]) + if args.u: + subprocess.call(["coverage", "report", "--skip-covered "]) From 10193520a4b83f18e72654fe34987640e121a365 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:46:47 +0200 Subject: [PATCH 02/23] Update Linux test workflows --- .github/workflows/test_linux.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 46f9b437..ec4ec56c 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -23,23 +23,28 @@ jobs: with: python-version: ${{ matrix.python-version }} architecture: x64 + - name: Install Packages (apt) run: | sudo apt update sudo apt install libenchant-2-dev qttools5-dev-tools + - name: Checkout Source uses: actions/checkout@v5 - - name: Install Dependencies (pip) - run: | - pip install -U -r requirements.txt -r tests/requirements.txt + + - name: Install UV + uses: astral-sh/setup-uv@v6 + - name: Run Build Commands run: | - python pkgutils.py qtlrelease - python pkgutils.py sample + uv run --group test pkgutils.py qtlrelease + uv run --group test pkgutils.py sample + - name: Run Tests run: | export QT_QPA_PLATFORM=offscreen - python -m pytest -v --cov=novelwriter --timeout=60 + uv run --group test coverage -m pytest -v --timeout=60 + - name: Upload to Codecov uses: codecov/codecov-action@v5 env: From 08d37dd9e8534fb94fe5df639ce629c02daf69d3 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:53:01 +0200 Subject: [PATCH 03/23] Fix bug in Linux test workflow --- .github/workflows/test_linux.yml | 6 +++--- run_tests.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index ec4ec56c..68b44078 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -37,13 +37,13 @@ jobs: - name: Run Build Commands run: | - uv run --group test pkgutils.py qtlrelease - uv run --group test pkgutils.py sample + uv run --no-dev pkgutils.py qtlrelease + uv run --no-dev pkgutils.py sample - name: Run Tests run: | export QT_QPA_PLATFORM=offscreen - uv run --group test coverage -m pytest -v --timeout=60 + uv run --no-dev --group test coverage run -m pytest -v --timeout=60 - name: Upload to Codecov uses: codecov/codecov-action@v5 diff --git a/run_tests.py b/run_tests.py index f2e91ec7..a2b8e937 100755 --- a/run_tests.py +++ b/run_tests.py @@ -23,9 +23,9 @@ if __name__ == "__main__": env["QT_SCALE_FACTOR"] = "1.0" if args.r or args.t or args.u: - cmd = [sys.executable, "-m"] + cmd = ["coverage", "run", "-m"] else: - cmd = ["coverage", "-m"] + cmd = [sys.executable, "-m"] cmd += ["pytest", "-vv"] if args.o: From b4c9d7c76cb132d0d2f873e19c4d8c990f25f4c9 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:58:59 +0200 Subject: [PATCH 04/23] Fix coverage reporting --- .github/workflows/test_linux.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 68b44078..0a4c7963 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -44,8 +44,10 @@ jobs: run: | export QT_QPA_PLATFORM=offscreen uv run --no-dev --group test coverage run -m pytest -v --timeout=60 + uv run --no-dev --group test coverage xml - name: Upload to Codecov uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true From 4aef01ef323527bc74e910c9965f698b059de588 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:08:40 +0200 Subject: [PATCH 05/23] Make sure only novelwriter source files are included in coverage --- .github/workflows/test_linux.yml | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 0a4c7963..0b6f9d28 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -27,7 +27,7 @@ jobs: - name: Install Packages (apt) run: | sudo apt update - sudo apt install libenchant-2-dev qttools5-dev-tools + sudo apt install qttools5-dev-tools - name: Checkout Source uses: actions/checkout@v5 diff --git a/pyproject.toml b/pyproject.toml index 1299f261..d0e66ec4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -160,6 +160,7 @@ markers = [ [tool.coverage.run] branch = false +source = ["novelwriter"] [tool.coverage.report] precision = 2 From 7a9464c592be3320d4b54406ddbf4b5d73c56e8c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:20:13 +0200 Subject: [PATCH 06/23] Update MacOS and Linux workflows --- .github/workflows/test_linux.yml | 33 ++++++++++++++++++------------- .github/workflows/test_mac.yml | 34 +++++++++++++++++++++----------- pyproject.toml | 1 + 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 0b6f9d28..efc5bce8 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -14,37 +14,42 @@ jobs: testLinux: strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" fail-fast: false runs-on: ubuntu-latest steps: - - name: Python Setup - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - architecture: x64 - - - name: Install Packages (apt) + - name: Install System Packages run: | sudo apt update sudo apt install qttools5-dev-tools + - name: Install UV and Python + uses: astral-sh/setup-uv@v6 + with: + python-version: ${{ matrix.python-version }} + - name: Checkout Source uses: actions/checkout@v5 - - name: Install UV - uses: astral-sh/setup-uv@v6 + - name: Sync UV + run: | + uv sync --no-dev --group test - name: Run Build Commands run: | - uv run --no-dev pkgutils.py qtlrelease - uv run --no-dev pkgutils.py sample + uv run --no-sync pkgutils.py qtlrelease + uv run --no-sync pkgutils.py sample - name: Run Tests run: | export QT_QPA_PLATFORM=offscreen - uv run --no-dev --group test coverage run -m pytest -v --timeout=60 - uv run --no-dev --group test coverage xml + uv run --no-sync coverage run -m pytest -v --timeout=60 + uv run --no-sync coverage xml - name: Upload to Codecov uses: codecov/codecov-action@v5 diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index 7ecb5116..21a5e6e6 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -14,24 +14,36 @@ jobs: testMac: runs-on: macos-latest steps: - - name: Python Setup - uses: actions/setup-python@v6 + # - name: Python Setup + # uses: actions/setup-python@v6 + # with: + # python-version: "3.13" + # architecture: x64 + + # - name: Install Packages (brew) + # run: | + # brew install enchant + + - name: Install UV and Python + uses: astral-sh/setup-uv@v6 with: python-version: "3.13" - architecture: x64 - - name: Install Packages (brew) - run: | - brew install enchant + - name: Checkout Source uses: actions/checkout@v5 - - name: Install Dependencies (pip) + + - name: Sync UV run: | - pip install -U pyobjc -r requirements.txt -r tests/requirements.txt + uv sync --no-dev --group test --group macos + - name: Run Tests run: | export QT_QPA_PLATFORM=offscreen - python -m pytest -v --cov=novelwriter --timeout=60 + uv run --no-sync coverage run -m pytest -v --timeout=60 + uv run --no-sync coverage xml + - name: Upload to Codecov uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true diff --git a/pyproject.toml b/pyproject.toml index d0e66ec4..945e8028 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ docs = [ ] test = ["coverage>=7.2.0", "pytest-qt", "pytest-timeout", "pytest>=6.0.0"] lint = ["isort", "pyright", "ruff"] +macos = ["pyobjc"] [project.urls] Homepage = "https://novelwriter.io" From 177d39c2a3113be0009d2e33de33c437f4702815 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:25:01 +0200 Subject: [PATCH 07/23] Move checkout source further up --- .github/workflows/test_linux.yml | 6 +++--- .github/workflows/test_mac.yml | 14 ++------------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index efc5bce8..6a0d18d0 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -23,6 +23,9 @@ jobs: fail-fast: false runs-on: ubuntu-latest steps: + - name: Checkout Source + uses: actions/checkout@v5 + - name: Install System Packages run: | sudo apt update @@ -33,9 +36,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Checkout Source - uses: actions/checkout@v5 - - name: Sync UV run: | uv sync --no-dev --group test diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index 21a5e6e6..5d24b3c9 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -14,24 +14,14 @@ jobs: testMac: runs-on: macos-latest steps: - # - name: Python Setup - # uses: actions/setup-python@v6 - # with: - # python-version: "3.13" - # architecture: x64 - - # - name: Install Packages (brew) - # run: | - # brew install enchant + - name: Checkout Source + uses: actions/checkout@v5 - name: Install UV and Python uses: astral-sh/setup-uv@v6 with: python-version: "3.13" - - name: Checkout Source - uses: actions/checkout@v5 - - name: Sync UV run: | uv sync --no-dev --group test --group macos From af8644838560a0d7fad42d6136be07c6f1aaf1b9 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:30:58 +0200 Subject: [PATCH 08/23] Add setuptools to MacOS workflow --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 945e8028..2160eefe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,12 @@ dynamic = ["version"] [dependency-groups] dev = [ + { include-group = "build" }, { include-group = "docs" }, { include-group = "test" }, { include-group = "lint" }, ] +build = ["setuptools>=77.0.3"] docs = [ "docutils>=0.17.1", "pygments>=2.7", @@ -45,7 +47,7 @@ docs = [ ] test = ["coverage>=7.2.0", "pytest-qt", "pytest-timeout", "pytest>=6.0.0"] lint = ["isort", "pyright", "ruff"] -macos = ["pyobjc"] +macos = ["pyobjc", { include-group = "build" }] [project.urls] Homepage = "https://novelwriter.io" From 40363068e378759d1b00c5df54ec541c31daa80c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:51:55 +0200 Subject: [PATCH 09/23] Update Windows workflow and try to fix MacOS --- .github/workflows/test_mac.yml | 2 +- .github/workflows/test_win.yml | 26 ++++++++++++++++---------- pyproject.toml | 4 +--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index 5d24b3c9..c88348db 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -24,7 +24,7 @@ jobs: - name: Sync UV run: | - uv sync --no-dev --group test --group macos + uv sync --no-dev --group test --group macos --python-platform x86_64-apple-darwin - name: Run Tests run: | diff --git a/.github/workflows/test_win.yml b/.github/workflows/test_win.yml index 1cdca205..4f6ad739 100644 --- a/.github/workflows/test_win.yml +++ b/.github/workflows/test_win.yml @@ -14,20 +14,26 @@ jobs: testWin: runs-on: windows-latest steps: - - name: Python Setup - uses: actions/setup-python@v6 - with: - python-version: "3.13" - architecture: x64 - name: Checkout Source uses: actions/checkout@v5 - - name: Install Dependencies (pip) + + - name: Install UV and Python + uses: astral-sh/setup-uv@v6 + with: + python-version: "3.13" + + - name: Sync UV run: | - pip install -U -r requirements.txt -r tests/requirements.txt + uv sync --no-dev --group test + - name: Run Tests run: | - python -m pytest -v --cov=novelwriter --timeout=60 + $env:QT_QPA_PLATFORM=offscreen + uv run --no-sync coverage run -m pytest -v --timeout=60 + uv run --no-sync coverage xml + - name: Upload to Codecov uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true diff --git a/pyproject.toml b/pyproject.toml index 2160eefe..945e8028 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,12 +29,10 @@ dynamic = ["version"] [dependency-groups] dev = [ - { include-group = "build" }, { include-group = "docs" }, { include-group = "test" }, { include-group = "lint" }, ] -build = ["setuptools>=77.0.3"] docs = [ "docutils>=0.17.1", "pygments>=2.7", @@ -47,7 +45,7 @@ docs = [ ] test = ["coverage>=7.2.0", "pytest-qt", "pytest-timeout", "pytest>=6.0.0"] lint = ["isort", "pyright", "ruff"] -macos = ["pyobjc", { include-group = "build" }] +macos = ["pyobjc"] [project.urls] Homepage = "https://novelwriter.io" From a6e29f0ded37e4527cf7062e19d6c2749c2c1b00 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:11:01 +0200 Subject: [PATCH 10/23] Add more debug output --- .github/workflows/test_mac.yml | 2 +- .github/workflows/test_win.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index c88348db..e6c99383 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -24,7 +24,7 @@ jobs: - name: Sync UV run: | - uv sync --no-dev --group test --group macos --python-platform x86_64-apple-darwin + uv sync --verbose --no-dev --group test --group macos --python-platform x86_64-apple-darwin - name: Run Tests run: | diff --git a/.github/workflows/test_win.yml b/.github/workflows/test_win.yml index 4f6ad739..e64bef4f 100644 --- a/.github/workflows/test_win.yml +++ b/.github/workflows/test_win.yml @@ -24,7 +24,7 @@ jobs: - name: Sync UV run: | - uv sync --no-dev --group test + uv sync --verbose --no-dev --group test - name: Run Tests run: | From e66f19914ea6d8d82e79389f8f09276408df4de0 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:14:33 +0200 Subject: [PATCH 11/23] Fix case conflict on MacOS and Windows --- novelWriter.py | 8 -------- pyproject.toml | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/novelWriter.py b/novelWriter.py index 78c8db27..9758edee 100755 --- a/novelWriter.py +++ b/novelWriter.py @@ -6,14 +6,6 @@ novelWriter – Start Script import os import sys -try: - import PyQt6.QtCore # noqa: F401 - import PyQt6.QtGui # noqa: F401 - import PyQt6.QtWidgets # noqa: F401 -except Exception: - print("ERROR: Failed to load dependency PyQt6") - sys.exit(1) - os.curdir = os.path.abspath(os.path.dirname(__file__)) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 945e8028..d5a0b673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,10 +57,10 @@ Issues = "https://github.com/vkbo/novelWriter/issues" novelwriter = "novelwriter:main" [tool.setuptools.dynamic] -version = { attr = "novelwriter.__version__" } +version = { attr = "novelwriter.__init__.__version__" } -[tool.setuptools.packages.find] -include = ["novelwriter*"] +[tool.setuptools] +packages = ["novelwriter"] [tool.isort] py_version = "310" From 359d5f36b5b986a9157e4021f74fb161145b477a Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:17:53 +0200 Subject: [PATCH 12/23] Remove debug settings from workflows --- .github/workflows/test_mac.yml | 2 +- .github/workflows/test_win.yml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index e6c99383..5d24b3c9 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -24,7 +24,7 @@ jobs: - name: Sync UV run: | - uv sync --verbose --no-dev --group test --group macos --python-platform x86_64-apple-darwin + uv sync --no-dev --group test --group macos - name: Run Tests run: | diff --git a/.github/workflows/test_win.yml b/.github/workflows/test_win.yml index e64bef4f..1cb2dbdd 100644 --- a/.github/workflows/test_win.yml +++ b/.github/workflows/test_win.yml @@ -24,11 +24,10 @@ jobs: - name: Sync UV run: | - uv sync --verbose --no-dev --group test + uv sync --no-dev --group test - name: Run Tests run: | - $env:QT_QPA_PLATFORM=offscreen uv run --no-sync coverage run -m pytest -v --timeout=60 uv run --no-sync coverage xml From e21d94446111d4f946483d8c815d750349e39d00 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:23:46 +0200 Subject: [PATCH 13/23] Fix mac workflow and update linting job --- .github/workflows/syntax.yml | 31 ++++++++++++++++++------------- .github/workflows/test_mac.yml | 4 ++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index d91c58fd..f3d723f9 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -14,24 +14,29 @@ jobs: checkSyntax: runs-on: ubuntu-latest steps: - - name: Python Setup - uses: actions/setup-python@v6 - with: - python-version: 3 - architecture: x64 - name: Checkout Source uses: actions/checkout@v5 - - name: Install Dependencies - run: pip install -r requirements.txt -r requirements-dev.txt + + - name: Install UV and Python + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Sync UV + run: | + uv sync --no-dev --group lint + - name: Ruff Check run: | - ruff --version - ruff check + uv run --no-sync ruff --version + uv run --no-sync ruff check + - name: Pyright Check run: | - pyright --version - pyright + uv run --no-sync pyright --version + uv run --no-sync pyright + - name: Isort Check run: | - isort --version - isort --check . + uv run --no-sync isort --version + uv run --no-sync isort --check . diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml index 5d24b3c9..d191ba4b 100644 --- a/.github/workflows/test_mac.yml +++ b/.github/workflows/test_mac.yml @@ -17,6 +17,10 @@ jobs: - name: Checkout Source uses: actions/checkout@v5 + - name: Install System Packages + run: | + brew install enchant + - name: Install UV and Python uses: astral-sh/setup-uv@v6 with: From 41c58a52e3faf0f9437fdf9cfc522b6261d51017 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 19 Oct 2025 00:19:47 +0200 Subject: [PATCH 14/23] Update Debian build --- novelwriter/__init__.py | 6 +++--- pyproject.toml | 6 ++++-- setup/debian/control | 2 ++ setup/make_release.sh | 16 ++++------------ utils/common.py | 11 ++++++++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index fa3dd30e..82200f17 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -49,9 +49,9 @@ __license__ = "GPLv3" __author__ = "Veronica Berglyd Olsen" __maintainer__ = "Veronica Berglyd Olsen" __email__ = "code@vkbo.net" -__version__ = "2.8a2" -__hexversion__ = "0x020800a2" -__date__ = "2025-07-16" +__version__ = "2.8a3" +__hexversion__ = "0x020800a3" +__date__ = "2025-10-18" __status__ = "Stable" __domain__ = "novelwriter.io" diff --git a/pyproject.toml b/pyproject.toml index d5a0b673..5b5c3a9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,12 @@ dynamic = ["version"] [dependency-groups] dev = [ + { include-group = "build" }, { include-group = "docs" }, { include-group = "test" }, { include-group = "lint" }, ] +build = ["build", "setuptools>=77.0.3"] docs = [ "docutils>=0.17.1", "pygments>=2.7", @@ -59,8 +61,8 @@ novelwriter = "novelwriter:main" [tool.setuptools.dynamic] version = { attr = "novelwriter.__init__.__version__" } -[tool.setuptools] -packages = ["novelwriter"] +[tool.setuptools.packages.find] +include = ["novelwriter*"] [tool.isort] py_version = "310" diff --git a/setup/debian/control b/setup/debian/control index 24f0cd25..30c7921c 100644 --- a/setup/debian/control +++ b/setup/debian/control @@ -4,6 +4,8 @@ Section: text Priority: optional Build-Depends: dh-python, + pybuild-plugin-pyproject, + python3-build, python3-setuptools, python3-all, debhelper (>= 9), diff --git a/setup/make_release.sh b/setup/make_release.sh index 97438bd9..8c59a933 100755 --- a/setup/make_release.sh +++ b/setup/make_release.sh @@ -1,8 +1,6 @@ #!/bin/bash set -e -ENVPATH=/tmp/nwBuild - if [ ! -f pkgutils.py ]; then echo "Must be called from the root folder of the source" exit 1 @@ -12,18 +10,12 @@ echo "" echo " Building Dependencies" echo "================================================================================" echo "" -if [ ! -d $ENVPATH ]; then - python3 -m venv $ENVPATH -fi -source $ENVPATH/bin/activate -pip3 install -r requirements.txt -r docs/requirements.txt -python3 pkgutils.py build-assets -python3 pkgutils.py icons optional -deactivate +uv run pkgutils.py build-assets +uv run pkgutils.py icons optional echo "" echo " Building Linux Packages" echo "================================================================================" echo "" -python3 pkgutils.py build-deb --sign -python3 pkgutils.py build-ubuntu --sign +uv run pkgutils.py build-deb --sign +uv run pkgutils.py build-ubuntu --sign diff --git a/utils/common.py b/utils/common.py index 54528ba2..723da500 100644 --- a/utils/common.py +++ b/utils/common.py @@ -92,14 +92,19 @@ def copySourceCode(dst: Path) -> None: def copyPackageFiles(dst: Path, oldLicense: bool = False) -> None: """Copy files needed for packaging.""" - copyFiles = ["LICENSE.md", "setup/LICENSE-Apache-2.0.txt", "CREDITS.md", "pyproject.toml"] + copyFiles = [ + ROOT_DIR / "LICENSE.md", + SETUP_DIR / "LICENSE-Apache-2.0.txt", + ROOT_DIR / "CREDITS.md", + ROOT_DIR / "pyproject.toml", + ] for copyFile in copyFiles: - shutil.copyfile(copyFile, dst / copyFile) + shutil.copyfile(copyFile, dst / copyFile.name) print("Copied:", copyFile, flush=True) writeFile(dst / "MANIFEST.in", ( "include LICENSE.md\n" - "include setup/LICENSE-Apache-2.0.txt\n" + "include LICENSE-Apache-2.0.txt\n" "include CREDITS.md\n" "recursive-include novelwriter/assets *\n" )) From 687bcc2779ef9623719d6b5070305da2850b601f Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 19:30:36 +0200 Subject: [PATCH 15/23] Update MacOS build --- .github/workflows/build_assets.yml | 24 ++++++++++++------------ .github/workflows/i18n.yml | 10 +++++++--- .github/workflows/test_linux.yml | 2 +- pyproject.toml | 9 ++++++--- setup/macos/build.sh | 1 - 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build_assets.yml b/.github/workflows/build_assets.yml index 291da081..bb90b5b5 100644 --- a/.github/workflows/build_assets.yml +++ b/.github/workflows/build_assets.yml @@ -6,27 +6,27 @@ jobs: buildAssets: runs-on: ubuntu-latest steps: - - name: Python Setup - uses: actions/setup-python@v6 - with: - python-version: "3.13" - architecture: x64 + - name: Checkout Source + uses: actions/checkout@v5 - - name: Install Packages (apt) + - name: Install System Packages run: | sudo apt update sudo apt install qttools5-dev-tools latexmk texlive texlive-latex-extra - - name: Checkout Source - uses: actions/checkout@v5 + - name: Install UV and Python + uses: astral-sh/setup-uv@v6 + with: + python-version: "3.13" - - name: Install Packages (pip) - run: pip install -U -r requirements.txt -r docs/requirements.txt + - name: Sync UV + run: | + uv sync --no-dev --group docs - name: Build Assets run: | - python pkgutils.py build-assets - python pkgutils.py icons optional + uv run --no-sync pkgutils.py qtlrelease + uv run --no-sync pkgutils.py sample - name: Upload Artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index 82a22f10..238ae27a 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -14,14 +14,18 @@ jobs: with: python-version: "3.13" architecture: x64 - - name: Install Packages (apt) + + - name: Checkout Source + uses: actions/checkout@v5 + + - name: Install System Packages run: | sudo apt update sudo apt install qttools5-dev-tools - - name: Checkout Source - uses: actions/checkout@v5 + - name: Build Assets run: python pkgutils.py qtlrelease + - name: Upload Artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 6a0d18d0..b900128f 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -40,7 +40,7 @@ jobs: run: | uv sync --no-dev --group test - - name: Run Build Commands + - name: Build Assets run: | uv run --no-sync pkgutils.py qtlrelease uv run --no-sync pkgutils.py sample diff --git a/pyproject.toml b/pyproject.toml index 5b5c3a9e..d8ebd34f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,8 @@ description = "A plain text editor for planning and writing novels" readme = { file = "setup/description_pypi.md", content-type = "text/markdown" } license = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0" license-files = ["LICENSE.md", "setup/LICENSE-Apache-2.0.txt"] +dynamic = ["version"] +requires-python = ">=3.10" classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", @@ -23,9 +25,10 @@ classifiers = [ "Natural Language :: English", "Topic :: Text Editors", ] -requires-python = ">=3.10" -dependencies = ["pyqt6>=6.4", "pyenchant>=3.0.0"] -dynamic = ["version"] +dependencies = [ + "pyqt6>=6.4", + "pyenchant>=3.3.0", # 3.3 is needed for MacOS AARCH64 builds +] [dependency-groups] dev = [ diff --git a/setup/macos/build.sh b/setup/macos/build.sh index ca475fa1..d9e4f601 100755 --- a/setup/macos/build.sh +++ b/setup/macos/build.sh @@ -109,7 +109,6 @@ conda install -c conda-forge enchant hunspell-en --yes # Install dependencies echo "Installing Python dependencies ..." pip install -r "$SRC_DIR/requirements.txt" -pip install pyenchant==3.3.0rc1 # Leave conda env conda deactivate From 0b79bcd4b4f772f3bc5e2e3c6014003beaa87da5 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 19:35:35 +0200 Subject: [PATCH 16/23] Fix assets build job --- .github/workflows/build_assets.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_assets.yml b/.github/workflows/build_assets.yml index bb90b5b5..945b0634 100644 --- a/.github/workflows/build_assets.yml +++ b/.github/workflows/build_assets.yml @@ -18,6 +18,7 @@ jobs: uses: astral-sh/setup-uv@v6 with: python-version: "3.13" + enable-cache: true - name: Sync UV run: | @@ -25,8 +26,8 @@ jobs: - name: Build Assets run: | - uv run --no-sync pkgutils.py qtlrelease - uv run --no-sync pkgutils.py sample + uv run --no-sync pkgutils.py build-assets + uv run --no-sync pkgutils.py icons optional - name: Upload Artifacts uses: actions/upload-artifact@v4 From 06cb12887692cf3b2b5f1cad5a48a4e67534418e Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:11:20 +0200 Subject: [PATCH 17/23] Remove dependencies on requirements files and generate when needed --- docs/source/technical/source.rst | 29 +++++++++++++++++++++----- docs/source/technical/tests.rst | 35 ++++++++++++++++++-------------- pkgutils.py | 35 +++++++++++++++++++++++++++----- setup/macos/build.sh | 3 ++- setup/make_pip.sh | 22 +------------------- utils/build_windows.py | 5 ++--- utils/common.py | 14 +++++++++++++ 7 files changed, 93 insertions(+), 50 deletions(-) diff --git a/docs/source/technical/source.rst b/docs/source/technical/source.rst index 6b69b491..44750bf2 100644 --- a/docs/source/technical/source.rst +++ b/docs/source/technical/source.rst @@ -7,6 +7,7 @@ Running from Source .. _GitHub: https://github.com/vkbo/novelWriter/releases .. _PyPi: https://pypi.org/project/novelWriter/ .. _Sphinx Docs: https://www.sphinx-doc.org/ +.. _uv: https://docs.astral.sh/uv/ This chapter describes various ways of running novelWriter directly from the source code, and how to build the various components like the translation files and documentation. @@ -28,6 +29,7 @@ by running: .. _docs_technical_source_depend: + Dependencies ============ @@ -43,13 +45,22 @@ The following Python packages are needed to run all features of novelWriter: If you want spell checking, you must install the ``PyEnchant`` package. The spell check library must be at least 3.0 to work with Windows. On Linux, 2.0 also works fine. -If you install from PyPi, these dependencies should be installed automatically. If you install from -source, dependencies can still be installed from PyPi with: +If you install novelWriter from PyPi, these dependencies should be installed automatically. + +If you run it from source, and want to install dependencies using ``pip``, you must first generate +the ``requirements.txt`` file: .. code-block:: bash + python pkgutils.py gen-req pip install -r requirements.txt +Otherwise you can run novelWriter with uv_: + +.. code-block:: bash + + uv run novelwriter + .. note:: On Linux distros, the Qt library is usually split up into multiple packages. In some cases, @@ -136,12 +147,14 @@ running: Building the Documentation ========================== -A local copy of this documentation can be generated as HTML. This requires installing some Python -packages from PyPi: +A local copy of this documentation can be generated as HTML. + +If you're using ``pip``, you must first generate the ``requirements.txt`` file: .. code-block:: bash - pip install -r docs/requirements.txt + python pkgutils.py gen-req docs + pip install -r requirements.txt The documentation can then be built from the root folder in the source code by running: @@ -149,6 +162,12 @@ The documentation can then be built from the root folder in the source code by r make -C docs html +Or you can run directly with uv_: + +.. code-block:: bash + + uv run make -C docs html + If successful, the documentation should be available in the ``docs/build/html`` folder and you can open the ``index.html`` file in your browser. diff --git a/docs/source/technical/tests.rst b/docs/source/technical/tests.rst index 56623ff4..4eb8c244 100644 --- a/docs/source/technical/tests.rst +++ b/docs/source/technical/tests.rst @@ -4,23 +4,12 @@ Running Tests ************* +.. _uv: https://docs.astral.sh/uv/ + The novelWriter source code is well covered by tests. The test framework used for the development is ``pytest`` with the use of an extension for Qt. -Dependencies -============ - -The dependencies for running the tests can be installed with: - -.. code-block:: bash - - pip install -r tests/requirements.txt - -This will install a couple of extra packages for coverage and test management. The minimum -requirement is ``pytest`` and ``pytest-qt``. - - Simple Test Run =============== @@ -28,19 +17,35 @@ To run the tests, you simply need to execute the following from the root of the .. code-block:: bash - pytest + uv run pytest + +This uses uv_. See below for manually installing dependencies using ``pip``. Since several of the tests involve opening up the novelWriter GUI, you may want to disable the GUI for the duration of the test run. Moving your mouse while the tests are running may otherwise interfere with the execution of some tests. -You can disable the renderring of the GUI by setting the flag ``QT_QPA_PLATFORM=offscreen``: +You can disable the rendering of the GUI by setting the flag ``QT_QPA_PLATFORM=offscreen``: .. code-block:: bash export QT_QPA_PLATFORM=offscreen pytest +Dependencies +------------ + +To run generate the requirements file and install using ``pip``, run: + +.. code-block:: bash + + python pkgutils.py gen-req app test + pip install -r tests/requirements.txt + +This will install a couple of extra packages for coverage and test management. The minimum +requirement is ``pytest`` and ``pytest-qt``. + + Advanced Options ================ diff --git a/pkgutils.py b/pkgutils.py index 29d6983f..fc49f94d 100755 --- a/pkgutils.py +++ b/pkgutils.py @@ -40,7 +40,10 @@ import utils.build_windows import utils.docs import utils.icon_themes -from utils.common import ROOT_DIR, SETUP_DIR, extractVersion, readFile, stripVersion, writeFile +from utils.common import ( + ROOT_DIR, SETUP_DIR, extractReqs, extractVersion, readFile, stripVersion, + writeFile +) OS_LINUX = sys.platform.startswith("linux") OS_DARWIN = sys.platform.startswith("darwin") @@ -61,7 +64,7 @@ def installPackages(args: argparse.Namespace) -> None: print("=======================") print("") - installQueue = ["pip", "-r requirements.txt"] + installQueue = ["pip", *extractReqs(["app"])] if args.mac: installQueue.append("pyobjc") elif args.win: @@ -130,6 +133,15 @@ def genMacOSPlist(args: argparse.Namespace) -> None: writeFile(outDir / "Info.plist", plistXML) +def genReqFiles(args: argparse.Namespace) -> None: + """Generate requirements.txt file from pyproject.toml.""" + select = [s.strip().lower() for s in args.groups] if args.groups else ["app"] + (ROOT_DIR / "requirements.txt").write_text( + "\n".join(extractReqs(select)), + encoding="utf-8" + ) + + if __name__ == "__main__": """Parse command line options and run the commands.""" parser = argparse.ArgumentParser( @@ -222,7 +234,7 @@ if __name__ == "__main__": cmdBuildHtmlDocs = parsers.add_parser( "docs-html", help="Build the HTML docs." ) - cmdBuildHtmlDocs.add_argument("lang", nargs="+") + cmdBuildHtmlDocs.add_argument("lang", nargs="+", help="Language codes to generate docs for.") cmdBuildHtmlDocs.set_defaults(func=utils.docs.buildHtmlDocs) # Build Sample @@ -295,10 +307,23 @@ if __name__ == "__main__": cmdBuildClean.set_defaults(func=cleanBuildDirs) # Generate MacOS PList File - cmdBuildMacOSPlist = parsers.add_parser( + cmdGenMacOSPlist = parsers.add_parser( "gen-plist", help="Generate an Info.plist for use in a MacOS Bundle." ) - cmdBuildMacOSPlist.set_defaults(func=genMacOSPlist) + cmdGenMacOSPlist.set_defaults(func=genMacOSPlist) + + # Generate Requirement File + cmdGenReq = parsers.add_parser( + "gen-req", help="Generate a requirements.txt file for pip." + ) + cmdGenReq.add_argument( + "groups", nargs="*", help=( + "Groups to generate for, or 'all' to generate for all groups. " + "Use 'app' to generate for just the core application. " + "Defaults to app dependencies." + ) + ) + cmdGenReq.set_defaults(func=genReqFiles) args = parser.parse_args() args.func(args) diff --git a/setup/macos/build.sh b/setup/macos/build.sh index d9e4f601..cafa196a 100755 --- a/setup/macos/build.sh +++ b/setup/macos/build.sh @@ -1,7 +1,7 @@ #! /bin/bash if [[ -z "$1" || -z "$2" || -z "$3" ]]; then - echo "Not enouch input arguments" + echo "Not enough input arguments" exit 1 fi @@ -108,6 +108,7 @@ conda install -c conda-forge enchant hunspell-en --yes # Install dependencies echo "Installing Python dependencies ..." +python3 pkgutils.py gen-req pip install -r "$SRC_DIR/requirements.txt" # Leave conda env diff --git a/setup/make_pip.sh b/setup/make_pip.sh index 681a3cb5..f7e82c65 100755 --- a/setup/make_pip.sh +++ b/setup/make_pip.sh @@ -1,24 +1,11 @@ #!/bin/bash set -e -ENVPATH=/tmp/nwBuild - if [ ! -f pkgutils.py ]; then echo "Must be called from the root folder of the source" exit 1 fi -echo "" -echo " Create Python Env" -echo "================================================================================" -echo "" - -if [ ! -d $ENVPATH ]; then - python3 -m venv $ENVPATH -fi -source $ENVPATH/bin/activate -pip3 install -U build twine -r requirements.txt -r docs/requirements.txt - echo "" echo " Building Dependencies" echo "================================================================================" @@ -30,7 +17,7 @@ echo "" echo " Building Packages" echo "================================================================================" echo "" -python3 -m build +uv build mkdir -pv dist_upload cp -v dist/novelwriter-*.whl dist_upload/ cd dist_upload @@ -38,13 +25,6 @@ FILE=$(ls -t | head -1) shasum -a 256 $FILE | tee $FILE.sha256 cd .. -echo "" -echo " Checking Packages" -echo "================================================================================" -echo "" -twine check dist/* -deactivate - echo "" echo " Done!" echo "================================================================================" diff --git a/utils/build_windows.py b/utils/build_windows.py index 523797da..debae0ef 100644 --- a/utils/build_windows.py +++ b/utils/build_windows.py @@ -30,7 +30,7 @@ import zipfile from pathlib import Path from utils.common import ( - ROOT_DIR, SETUP_DIR, copySourceCode, extractVersion, readFile, + ROOT_DIR, SETUP_DIR, copySourceCode, extractReqs, extractVersion, readFile, removeRedundantQt, systemCall, writeFile ) @@ -45,7 +45,6 @@ def prepareCode(outDir: Path) -> None: files = [ ROOT_DIR / "CREDITS.md", ROOT_DIR / "LICENSE.md", - ROOT_DIR / "requirements.txt", SETUP_DIR / "iss_license.txt", SETUP_DIR / "windows" / "novelWriter.ico", SETUP_DIR / "windows" / "novelWriter.exe", @@ -85,7 +84,7 @@ def installRequirements(libDir: Path) -> None: """Install dependencies.""" print("Install dependencies ...") systemCall([ - sys.executable, "-m", "pip", "install", "-r", "requirements.txt", "--target", libDir + sys.executable, "-m", "pip", "install", *extractReqs(["app"]), "--target", libDir ]) print("Done") print("") diff --git a/utils/common.py b/utils/common.py index 723da500..0136cf47 100644 --- a/utils/common.py +++ b/utils/common.py @@ -26,10 +26,24 @@ import sys from pathlib import Path +import tomllib + ROOT_DIR = Path(__file__).parent.parent SETUP_DIR = ROOT_DIR / "setup" +def extractReqs(groups: list[str]) -> list[str]: + """Generate requirements.txt file from pyproject.toml.""" + data = tomllib.loads((ROOT_DIR / "pyproject.toml").read_text(encoding="utf-8")) + reqs = [] + if "app" in groups or "all" in groups: + reqs += data["project"]["dependencies"] + for group in data["dependency-groups"]: + if group in groups or "all" in groups: + reqs += [d for d in data["dependency-groups"][group] if isinstance(d, str)] + return reqs + + def extractVersion(beQuiet: bool = False) -> tuple[str, str, str]: """Extract the novelWriter version number without having to import anything else from the main package. From 2d7a42d918a9792f562b4294e7598c1dce4b98d1 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:17:18 +0200 Subject: [PATCH 18/23] Remove requirements files --- .gitignore | 1 + docs/requirements.txt | 8 -------- requirements-all.txt | 5 ----- requirements-dev.txt | 4 ---- requirements.txt | 2 -- setup/make_pip.sh | 4 ++-- setup/requirements.txt | 2 -- tests/requirements.txt | 5 ----- 8 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 docs/requirements.txt delete mode 100644 requirements-all.txt delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt delete mode 100644 setup/requirements.txt delete mode 100644 tests/requirements.txt diff --git a/.gitignore b/.gitignore index 833d4182..5b91bed7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ setup.iss /setup/macos/Info.plist /setup/windows/build .venv +/uv.lock # Translations /novelwriter/assets/i18n/*.qm diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 767647e7..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils>=0.17.1 -pygments>=2.7 -sphinx-book-theme -sphinx-copybutton -sphinx-design -sphinx-favicon -sphinx-intl -sphinx>=5.0 diff --git a/requirements-all.txt b/requirements-all.txt deleted file mode 100644 index d140ead6..00000000 --- a/requirements-all.txt +++ /dev/null @@ -1,5 +0,0 @@ --r requirements.txt --r requirements-dev.txt --r docs/requirements.txt --r setup/requirements.txt --r tests/requirements.txt diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 794644e0..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -build -isort -pyright -ruff diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index fa1cf957..00000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pyqt6>=6.4 -pyenchant>=3.0.0 diff --git a/setup/make_pip.sh b/setup/make_pip.sh index f7e82c65..9d3c61b2 100755 --- a/setup/make_pip.sh +++ b/setup/make_pip.sh @@ -10,8 +10,8 @@ echo "" echo " Building Dependencies" echo "================================================================================" echo "" -python3 pkgutils.py build-assets -python3 pkgutils.py icons optional +uv run pkgutils.py build-assets +uv run pkgutils.py icons optional echo "" echo " Building Packages" diff --git a/setup/requirements.txt b/setup/requirements.txt deleted file mode 100644 index 120a3f4f..00000000 --- a/setup/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -setuptools>=77.0.3 -twine diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index 39f96e3c..00000000 --- a/tests/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -coverage>=7.2.0 -pytest-cov -pytest-qt -pytest-timeout -pytest>=6.0.0 From e676bdc3222045a47e3e06ed702dd7f1c7ba15a8 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:23:57 +0200 Subject: [PATCH 19/23] Drop support for Python 3.10 --- .github/workflows/test_linux.yml | 1 - novelwriter/__init__.py | 4 ++-- pyproject.toml | 5 ++--- setup/debian/control | 6 +++--- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index b900128f..1f84fa95 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -15,7 +15,6 @@ jobs: strategy: matrix: python-version: - - "3.10" - "3.11" - "3.12" - "3.13" diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index 82200f17..17c0298e 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -206,9 +206,9 @@ def main(sysArgs: list | None = None) -> GuiMain | None: # Check Packages and Versions errorData = [] errorCode = 0 - if sys.hexversion < 0x030a00f0: + if sys.hexversion < 0x030b00f0: errorData.append( - f"At least Python 3.10 is required, found {CONFIG.verPyString}" + f"At least Python 3.11 is required, found {CONFIG.verPyString}" ) errorCode |= 0x04 if CONFIG.verQtValue < 0x060400: diff --git a/pyproject.toml b/pyproject.toml index d8ebd34f..fe42db85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,9 @@ readme = { file = "setup/description_pypi.md", content-type = "text/markdown" } license = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0" license-files = ["LICENSE.md", "setup/LICENSE-Apache-2.0.txt"] dynamic = ["version"] -requires-python = ">=3.10" +requires-python = ">=3.11" classifiers = [ "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -153,7 +152,7 @@ include = ["novelwriter"] exclude = ["**/__pycache__"] reportIncompatibleMethodOverride = false -pythonVersion = "3.10" +pythonVersion = "3.11" [tool.pytest.ini_options] log_level = "DEBUG" diff --git a/setup/debian/control b/setup/debian/control index 30c7921c..06d63fa8 100644 --- a/setup/debian/control +++ b/setup/debian/control @@ -9,21 +9,21 @@ Build-Depends: python3-setuptools, python3-all, debhelper (>= 9), - python3 (>=3.10), + python3 (>=3.11), python3-pyqt6 (>= 6.4), python3-pyqt6.qtsvg (>= 6.4), python3-enchant (>= 2.0), qt6-image-formats-plugins (>= 6.4) Standards-Version: 4.5.1 Homepage: https://novelwriter.io -X-Python3-Version: >= 3.10 +X-Python3-Version: >= 3.11 Package: novelwriter Architecture: all Depends: ${misc:Depends}, ${python3:Depends}, - python3 (>=3.10), + python3 (>=3.11), python3-pyqt6 (>= 6.4), python3-pyqt6.qtsvg (>= 6.4), python3-enchant (>= 2.0), From df2766d80e65e04695dac33367dd5a80e9388902 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:26:38 +0200 Subject: [PATCH 20/23] Update linting --- novelwriter/gui/theme.py | 2 +- pyproject.toml | 2 +- utils/common.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index b83de9b7..f3a026f1 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -615,7 +615,7 @@ class GuiTheme: lookup = f"{prefix}{name} {key}" keys.append(lookup) data[lookup] = (file.stem, name, mode == "dark", file) - except Exception: # noqa: PERF203 + except Exception: logger.error("Could not read file: %s", file) logException() diff --git a/pyproject.toml b/pyproject.toml index fe42db85..17599847 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ version = { attr = "novelwriter.__init__.__version__" } include = ["novelwriter*"] [tool.isort] -py_version = "310" +py_version = "311" line_length = 99 wrap_length = 79 multi_line_output = 5 diff --git a/utils/common.py b/utils/common.py index 0136cf47..a718971e 100644 --- a/utils/common.py +++ b/utils/common.py @@ -23,11 +23,10 @@ from __future__ import annotations import shutil import subprocess import sys +import tomllib from pathlib import Path -import tomllib - ROOT_DIR = Path(__file__).parent.parent SETUP_DIR = ROOT_DIR / "setup" From b3c18001e29f313c56719a6fa313d439108c5a24 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:57:20 +0200 Subject: [PATCH 21/23] Add build for Ubuntu 26.04 --- utils/build_debian.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build_debian.py b/utils/build_debian.py index 9ac0bd2e..892ee718 100644 --- a/utils/build_debian.py +++ b/utils/build_debian.py @@ -183,6 +183,7 @@ def launchpad(args: argparse.Namespace) -> None: ("24.04", "noble", True), ("25.04", "plucky", True), ("25.10", "questing", False), + ("26.04", "resolute", False), ] print("Building Ubuntu packages for:") From 9640d95cef4725bf9e3f3d40e7bee2815e9a76d7 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:02:40 +0200 Subject: [PATCH 22/23] Fix docs a little --- docs/source/technical/source.rst | 16 ++++++++-------- docs/source/technical/tests.rst | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/technical/source.rst b/docs/source/technical/source.rst index 44750bf2..c5bce486 100644 --- a/docs/source/technical/source.rst +++ b/docs/source/technical/source.rst @@ -47,20 +47,20 @@ must be at least 3.0 to work with Windows. On Linux, 2.0 also works fine. If you install novelWriter from PyPi, these dependencies should be installed automatically. -If you run it from source, and want to install dependencies using ``pip``, you must first generate -the ``requirements.txt`` file: +You can run novelWriter directly from source with uv_: + +.. code-block:: bash + + uv run novelwriter + +If you prefer to install dependencies using ``pip``, you must first generate the +``requirements.txt`` file: .. code-block:: bash python pkgutils.py gen-req pip install -r requirements.txt -Otherwise you can run novelWriter with uv_: - -.. code-block:: bash - - uv run novelwriter - .. note:: On Linux distros, the Qt library is usually split up into multiple packages. In some cases, diff --git a/docs/source/technical/tests.rst b/docs/source/technical/tests.rst index 4eb8c244..532bd750 100644 --- a/docs/source/technical/tests.rst +++ b/docs/source/technical/tests.rst @@ -35,7 +35,7 @@ You can disable the rendering of the GUI by setting the flag ``QT_QPA_PLATFORM=o Dependencies ------------ -To run generate the requirements file and install using ``pip``, run: +To generate the requirements file and install dependencies using ``pip``, run: .. code-block:: bash From 3ceaadd0c014b4c1c1dbbd5c807bf4b7e1404de0 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:07:50 +0200 Subject: [PATCH 23/23] Fix outdated docstring --- utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/common.py b/utils/common.py index a718971e..f5637285 100644 --- a/utils/common.py +++ b/utils/common.py @@ -32,7 +32,7 @@ SETUP_DIR = ROOT_DIR / "setup" def extractReqs(groups: list[str]) -> list[str]: - """Generate requirements.txt file from pyproject.toml.""" + """Extract dependency groups from pyproject.toml.""" data = tomllib.loads((ROOT_DIR / "pyproject.toml").read_text(encoding="utf-8")) reqs = [] if "app" in groups or "all" in groups: