diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f011d60..5007481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,9 @@ jobs: run: ci/install-tools.sh --tests - name: Type check - run: uv check --quiet --preview-features=check-command --no-sync + uses: j178/prek-action@e98a699c41eb69ab013a45817a0406469a748f8d # v2.0.5 + with: + extra-args: "--all-files ty" build_wheel: name: Build wheels diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f2c2028..b8b285d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,10 +23,10 @@ repos: hooks: - id: validate-pyproject additional_dependencies: - - validate-pyproject-schema-store[all]==2026.07.17 + - validate-pyproject-schema-store[all]==2026.07.28 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 2700fd5671c633760d912769c041bfcde2b9a01b # frozen: v0.15.22 + rev: cb8c523fd4835aba42af70f4cad5568db4df0b6c # frozen: v0.16.0 hooks: - id: ruff-check args: ["--fix", "--show-fixes", "--unsafe-fixes"] @@ -59,7 +59,7 @@ repos: - id: shellcheck - repo: https://github.com/astral-sh/ty-pre-commit - rev: 73d9ef74d17f01127e8a495a7e77ce9941aae703 # frozen: v0.0.63 + rev: 3940f891290063e0fde330228f5bff73e18ad2f2 # frozen: v0.0.64 hooks: - id: ty args: [--no-sync] diff --git a/ci/build-wheel.sh b/ci/build-wheel.sh index 2c081a7..76d6667 100755 --- a/ci/build-wheel.sh +++ b/ci/build-wheel.sh @@ -35,9 +35,6 @@ else if [[ $PY_PLATFORM == linux* ]]; then PLATFORM_TAG=many${PY_PLATFORM/-/_} PLATFORM_TAG_MASK=${PLATFORM_TAG/_/*_} - if ! [ "$CI" == "true" ] && which podman &>/dev/null; then - export CIBW_CONTAINER_ENGINE=podman - fi elif [[ $PY_PLATFORM == macosx* ]]; then PLATFORM_TAG=macosx_universal2 PLATFORM_TAG_MASK="macosx_*" @@ -111,20 +108,30 @@ _build_sdist () { } _build_wheel () { - local args=$* + local args + read -ra args <<<"$*" if [ "$IS_CONDA" == "1" ] || [ "$IS_MINGW" == "1" ]; then $PYTHON -m build -n -x --wheel -o wheelhouse - elif [ "$ZIP_SAFE" == "true" ]; then - uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse - elif [[ $PY_PLATFORM == win* ]] && [[ $args == *--only* ]]; then - uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse - elif [[ $PY_PLATFORM == macos* ]] && [[ $args == *--only* ]]; then - uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse else if [ "$CI" == "true" ] && [[ $PY_PLATFORM == win* ]]; then export UV_LINK_MODE=copy fi - "$INSTALL_DIR/cibuildwheel" "$args" + if [ "$ZIP_SAFE" == "true" ]; then + uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse + elif [[ $PY_PLATFORM == win* ]] && [[ ${args[0]} == *--only* ]]; then + uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse + elif [[ $PY_PLATFORM == macos* ]] && [[ ${args[0]} == *--only* ]]; then + uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse + else + if ! [ "$CI" == "true" ] && which podman &>/dev/null; then + export CIBW_CONTAINER_ENGINE=podman + fi + if [ -f "$INSTALL_DIR/cibuildwheel" ]; then + "$INSTALL_DIR/cibuildwheel" "${args[@]}" + else + uv tool run cibuildwheel "${args[@]}" + fi + fi fi } diff --git a/ci/install-tools.sh b/ci/install-tools.sh index be615fd..698e7c7 100755 --- a/ci/install-tools.sh +++ b/ci/install-tools.sh @@ -3,13 +3,18 @@ # Usage if [ -n "$1" ] && [ "$1" == "--help" ]; then echo "Usage:" - echo "$0 [--tests]" + echo "$0 [--dev][--doc][--tests]" echo "Where:" echo " --dev Install additional packages for development." + echo " --doc Install additional packages for documentation." echo " --tests Install additional packages to run 'pytest'." exit 1 fi +# Get script directory (without using /usr/bin/realpath) +THIS_DIR=$(dirname "${BASH_SOURCE[0]}") +CI_DIR=$(cd "$THIS_DIR" && pwd) + # Detect environment. For mingw and conda environments, python is not required # to be installed, but will be installed by this script. IS_CONDA="0" @@ -25,7 +30,7 @@ elif which python &>/dev/null; then if ! [ "$IS_MINGW" == "1" ]; then IS_UV="1" fi - python ci/requirements.py + python "$CI_DIR/requirements.py" else if [ -n "$MINGW_PACKAGE_PREFIX" ]; then IS_MINGW="1" @@ -39,11 +44,17 @@ fi INSTALL_DIR="$HOME/bin" mkdir -p "$INSTALL_DIR" +TOP_DIR=$(dirname "$CI_DIR") +pushd "$TOP_DIR" >/dev/null || exit + INSTALL_DEV="" +INSTALL_DOC="" INSTALL_TESTS="" while [ -n "$1" ]; do if [ "$1" == "--dev" ]; then INSTALL_DEV="1" + elif [ "$1" == "--doc" ]; then + INSTALL_DOC="1" elif [ "$1" == "--tests" ]; then INSTALL_TESTS="1" else @@ -147,11 +158,13 @@ else fi fi +# Get Python version +PY_VERSION=$(python -c "import sysconfig; print(sysconfig.get_python_version(), end='')") +PY_ABI_THREAD=$(python -c "import sysconfig; print(sysconfig.get_config_var('abi_thread') or '', end='')") + # Install dev tools if [ "$INSTALL_DEV" == "1" ]; then if [ -f requirements-dev.txt ]; then - PY_VERSION=$(python -c "import sysconfig; print(sysconfig.get_python_version(), end='')") - PY_ABI_THREAD=$(python -c "import sysconfig; print(sysconfig.get_config_var('abi_thread') or '', end='')") PY_VER_ABI="$PY_VERSION$PY_ABI_THREAD" if [ "$PY_VERSION" == "3.10" ] || [ "$PY_VERSION" == "3.11" ]; then PY_VER_ABI="3.12" @@ -163,6 +176,8 @@ if [ "$INSTALL_DEV" == "1" ]; then $CONDA_EXE install -c conda-forge "$name" -S -q -y elif [ "$name" != "cibuildwheel" ] && [ "$IS_UV" == "1" ]; then uv pip install --upgrade "$name" + elif [ "$IS_UV" == "1" ]; then + uv tool install -p "$PY_VER_ABI" "$line" else filename=$INSTALL_DIR/$name echo "Create $filename" @@ -173,4 +188,17 @@ if [ "$INSTALL_DEV" == "1" ]; then done < requirements-dev.txt fi fi + +# Install doc tools (sphinx and extensions) +if [ "$INSTALL_DOC" == "1" ]; then + if [ -f doc/requirements.txt ]; then + PY_VER_ABI="$PY_VERSION$PY_ABI_THREAD" + if [ "$PY_VERSION" == "3.10" ] || [ "$PY_VERSION" == "3.11" ]; then + PY_VER_ABI="3.12" + fi + uv tool install -p "$PY_VER_ABI" \ + --with-requirements=doc/requirements.txt sphinx + fi +fi +popd >/dev/null || true echo "::endgroup::" diff --git a/ci/requirements.py b/ci/requirements.py index c51015e..6397e5c 100644 --- a/ci/requirements.py +++ b/ci/requirements.py @@ -10,16 +10,15 @@ def main() -> None: """Entry point.""" - pyproject_toml = Path("pyproject.toml") + root_dir = Path(__file__).resolve().parent.parent + pyproject_toml = root_dir / "pyproject.toml" if not pyproject_toml.exists(): print("pyproject.toml not found", file=sys.stderr) sys.exit(1) with pyproject_toml.open("rb") as file: config = tomllib.load(file) - root_dir = pyproject_toml.parent requirements = root_dir / "requirements.txt" - try: dependencies = config["project"]["dependencies"] contents = list(dependencies) diff --git a/pyproject.toml b/pyproject.toml index d34b94f..08feafd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ Documentation = "https://docs.python.org/3.12/library/msilib.html" dev = [ "cibuildwheel>=4.1.0", "prek>=0.4.10,<0.5.0", - "ty>=0.0.63", ] tests = [ "coverage>=7.13.0", @@ -173,17 +172,13 @@ ignore = [ "D107", # Missing docstring in `__init__` "D203", # conflict with D211 "D213", # conflict with D212 - # should be revised in future - last revised using ruff 0.14.2 + "PLR0913", # too-many-arguments + "PLR0917", # too-many-positional-arguments + # should be revised in future - last revised using ruff 0.16.0 "D101", # Missing docstring in public class "D102", # Missing docstring in public method "D103", # Missing docstring in public function - "D205", # 1 blank line required between summary line and description "PLR2004", # Magic value used in comparison, consider replacing - "PLR0912", # too-many-branches - "PLR0913", # too-many-arguments - "PLR0915", # too-many-statements - # prone to false positives (https://github.com/astral-sh/ruff/issues/4045) - "S603", ] [tool.ruff.lint.per-file-ignores] diff --git a/requirements-dev.txt b/requirements-dev.txt index 7a86739..67f8d04 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,2 @@ cibuildwheel>=4.1.0 prek>=0.4.10,<0.5.0 -ty>=0.0.63 diff --git a/src/msilib/__init__.py b/src/msilib/__init__.py index 5e09ec5..239ea03 100644 --- a/src/msilib/__init__.py +++ b/src/msilib/__init__.py @@ -433,15 +433,16 @@ def __init__( default: str, componentflags: int | None = None, ) -> None: - """Create a new directory in the Directory table. There is a current - component at each point in time for the directory, which is either - explicitly created through start_component, or implicitly when files - are added for the first time. Files are added into the current - component, and into the cab file. To create a directory, a base - directory object needs to be specified (can be None), the path to the - physical directory, and a logical directory name. Default specifies the - DefaultDir slot in the directory table. componentflags specifies the - default flags that new components get. + """Create a new directory in the Directory table. + + There is a current component at each point in time for the directory, + which is either explicitly created through start_component, or + implicitly when files are added for the first time. Files are added + into the current component, and into the cab file. To create a + directory, a base directory object needs to be specified (can be None), + the path to the physical directory, and a logical directory name. + Default specifies the DefaultDir slot in the directory table. + componentflags specifies the default flags that new components get. """ index = 1 _logical = make_id(_logical) @@ -476,11 +477,13 @@ def start_component( keyfile: str | None = None, uuid: str | None = None, ) -> None: - """Add an entry to the Component table, and make this component the - current for this directory. If no component name is given, the - directory name is used. If no feature is given, the current feature is - used. If no flags are given, the directory's default flags are used. If - no keyfile is given, the KeyPath is left null in the Component table. + """Add an entry to the Component table. + + And make this component the current for this directory. If no component + name is given, the directory name is used. If no feature is given, the + current feature is used. If no flags are given, the directory's default + flags are used. If no keyfile is given, the KeyPath is left null in the + Component table. """ if flags is None: flags = self.componentflags or 0 @@ -556,12 +559,13 @@ def add_file( version: str | None = None, language: str | None = None, ) -> str | None: - """Add a file to the current component of the directory, starting a new - one if there is no current component. By default, the file name in the - source and the file table will be identical. If the src file is - specified, it is interpreted relative to the current directory. - Optionally, a version and a language can be specified for the entry in - the File table. + """Add a file to the current component of the directory. + + Starting a new one if there is no current component. By default, the + file name in the source and the file table will be identical. If the + src file is specified, it is interpreted relative to the current + directory. Optionally, a version and a language can be specified for + the entry in the File table. """ if not self.component: self.start_component(self.logical, current_feature, 0) @@ -638,7 +642,9 @@ def add_file( def glob( self, pattern: str, exclude: Container[str] | None = None ) -> list[str]: - """Add a list of files to the current component as specified in the + """Add a list of files to the current component. + + Add a list of files to the current component as specified in the glob pattern. Individual files can be excluded in the exclude list. """ try: diff --git a/src/msilib/schema.py b/src/msilib/schema.py index 3289fde..cfdc663 100644 --- a/src/msilib/schema.py +++ b/src/msilib/schema.py @@ -1,6 +1,7 @@ -"""The standard MSI schema for MSI 2.0, with the tables variable providing a -list of table definitions, and _Validation_records providing the data for MSI -validation. +"""The standard MSI schema for MSI 2.0. + +With the tables variable providing a list of table definitions, and +_Validation_records providing the data for MSI validation. """ from __future__ import annotations diff --git a/src/msilib/sequence.py b/src/msilib/sequence.py index d55b7c7..eb12aac 100644 --- a/src/msilib/sequence.py +++ b/src/msilib/sequence.py @@ -1,4 +1,6 @@ -"""Table contents for the standard sequence tables: +"""Table contents for the standard sequence tables. + +The standard sequence tables are: AdminExecuteSequence, AdminUISequence, AdvtExecuteSequence, InstallExecuteSequence, and InstallUISequence. """ diff --git a/src/msilib/text.py b/src/msilib/text.py index c155b73..422b4c4 100644 --- a/src/msilib/text.py +++ b/src/msilib/text.py @@ -1,5 +1,6 @@ -"""Definitions for the UIText and ActionText tables, for the standard installer -actions. +"""Definitions for the UIText and ActionText tables. + +For the standard installer actions. """ from __future__ import annotations