Switch to UV tooling (#2540)
This commit is contained in:
@@ -6,27 +6,28 @@ 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"
|
||||
enable-cache: true
|
||||
|
||||
- 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 build-assets
|
||||
uv run --no-sync pkgutils.py icons optional
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 .
|
||||
|
||||
@@ -14,33 +14,44 @@ jobs:
|
||||
testLinux:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
python-version:
|
||||
- "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)
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libenchant-2-dev qttools5-dev-tools
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v5
|
||||
- name: Install Dependencies (pip)
|
||||
|
||||
- name: Install System Packages
|
||||
run: |
|
||||
pip install -U -r requirements.txt -r tests/requirements.txt
|
||||
- name: Run Build Commands
|
||||
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: Sync UV
|
||||
run: |
|
||||
python pkgutils.py qtlrelease
|
||||
python pkgutils.py sample
|
||||
uv sync --no-dev --group test
|
||||
|
||||
- name: Build Assets
|
||||
run: |
|
||||
uv run --no-sync pkgutils.py qtlrelease
|
||||
uv run --no-sync pkgutils.py sample
|
||||
|
||||
- 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
|
||||
|
||||
@@ -14,24 +14,30 @@ 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 Dependencies (pip)
|
||||
|
||||
- name: Install System Packages
|
||||
run: |
|
||||
pip install -U pyobjc -r requirements.txt -r tests/requirements.txt
|
||||
brew install enchant
|
||||
|
||||
- name: Install UV and Python
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Sync UV
|
||||
run: |
|
||||
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
|
||||
|
||||
@@ -14,20 +14,25 @@ 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
|
||||
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
|
||||
|
||||
@@ -13,6 +13,7 @@ setup.iss
|
||||
/setup/macos/Info.plist
|
||||
/setup/windows/build
|
||||
.venv
|
||||
/uv.lock
|
||||
|
||||
# Translations
|
||||
/novelwriter/assets/i18n/*.qm
|
||||
|
||||
@@ -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
|
||||
@@ -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,11 +45,20 @@ 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.
|
||||
|
||||
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
|
||||
|
||||
.. note::
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 generate the requirements file and install dependencies 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
|
||||
================
|
||||
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
+30
-5
@@ -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)
|
||||
|
||||
+52
-34
@@ -4,16 +4,15 @@ 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"]
|
||||
dynamic = ["version"]
|
||||
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",
|
||||
@@ -25,12 +24,32 @@ classifiers = [
|
||||
"Natural Language :: English",
|
||||
"Topic :: Text Editors",
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pyqt6>=6.4",
|
||||
"pyenchant>=3.0.0",
|
||||
"pyenchant>=3.3.0", # 3.3 is needed for MacOS AARCH64 builds
|
||||
]
|
||||
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",
|
||||
"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"]
|
||||
macos = ["pyobjc"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://novelwriter.io"
|
||||
@@ -42,13 +61,13 @@ 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.isort]
|
||||
py_version="310"
|
||||
py_version = "311"
|
||||
line_length = 99
|
||||
wrap_length = 79
|
||||
multi_line_output = 5
|
||||
@@ -64,26 +83,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
|
||||
@@ -133,7 +152,7 @@ include = ["novelwriter"]
|
||||
exclude = ["**/__pycache__"]
|
||||
|
||||
reportIncompatibleMethodOverride = false
|
||||
pythonVersion = "3.10"
|
||||
pythonVersion = "3.11"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
log_level = "DEBUG"
|
||||
@@ -146,9 +165,8 @@ markers = [
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = false
|
||||
source = ["novelwriter"]
|
||||
|
||||
[tool.coverage.report]
|
||||
precision = 2
|
||||
exclude_also = [
|
||||
"if TYPE_CHECKING:"
|
||||
]
|
||||
exclude_also = ["if TYPE_CHECKING:"]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
-r requirements.txt
|
||||
-r requirements-dev.txt
|
||||
-r docs/requirements.txt
|
||||
-r setup/requirements.txt
|
||||
-r tests/requirements.txt
|
||||
@@ -1,4 +0,0 @@
|
||||
build
|
||||
isort
|
||||
pyright
|
||||
ruff
|
||||
@@ -1,2 +0,0 @@
|
||||
pyqt6>=6.4
|
||||
pyenchant>=3.0.0
|
||||
+19
-10
@@ -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 = ["coverage", "run", "-m"]
|
||||
else:
|
||||
cmd = [sys.executable, "-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 "])
|
||||
|
||||
@@ -4,24 +4,26 @@ Section: text
|
||||
Priority: optional
|
||||
Build-Depends:
|
||||
dh-python,
|
||||
pybuild-plugin-pyproject,
|
||||
python3-build,
|
||||
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),
|
||||
|
||||
@@ -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,8 +108,8 @@ 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"
|
||||
pip install pyenchant==3.3.0rc1
|
||||
|
||||
# Leave conda env
|
||||
conda deactivate
|
||||
|
||||
+3
-23
@@ -1,36 +1,23 @@
|
||||
#!/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 "================================================================================"
|
||||
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"
|
||||
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 "================================================================================"
|
||||
|
||||
+4
-12
@@ -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
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
setuptools>=77.0.3
|
||||
twine
|
||||
@@ -1,5 +0,0 @@
|
||||
coverage>=7.2.0
|
||||
pytest-cov
|
||||
pytest-qt
|
||||
pytest-timeout
|
||||
pytest>=6.0.0
|
||||
@@ -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:")
|
||||
|
||||
@@ -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("")
|
||||
|
||||
+21
-3
@@ -23,6 +23,7 @@ from __future__ import annotations
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tomllib
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
@@ -30,6 +31,18 @@ ROOT_DIR = Path(__file__).parent.parent
|
||||
SETUP_DIR = ROOT_DIR / "setup"
|
||||
|
||||
|
||||
def extractReqs(groups: list[str]) -> list[str]:
|
||||
"""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:
|
||||
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.
|
||||
@@ -92,14 +105,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"
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user