Remove dependencies on requirements files and generate when needed

This commit is contained in:
Veronica Berglyd Olsen
2025-10-22 21:11:20 +02:00
parent 0b79bcd4b4
commit 06cb128876
7 changed files with 93 additions and 50 deletions
+24 -5
View File
@@ -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.
+20 -15
View File
@@ -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
================
+30 -5
View File
@@ -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)
+2 -1
View File
@@ -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
+1 -21
View File
@@ -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 "================================================================================"
+2 -3
View File
@@ -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("")
+14
View File
@@ -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.