Skip to content

Commit 9835dd8

Browse files
committed
Merge
2 parents b0754e6 + 770a1ee commit 9835dd8

38 files changed

+1034
-902
lines changed

.github/workflows/testCode.yaml

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,38 @@ on:
88

99
jobs:
1010
testCode:
11-
1211
runs-on: windows-latest
1312
strategy:
1413
matrix:
1514
python-version: [3.13]
1615
steps:
16+
- name: Install the latest version of uv
17+
uses: astral-sh/setup-uv@v6
1718
- name: Checkout code
18-
uses: actions/checkout@v3
19+
uses: actions/checkout@v5
1920
- name: Set up Python ${{ matrix.python-version }}
20-
uses: actions/setup-python@v3
21+
uses: actions/setup-python@v5
2122
with:
2223
python-version: ${{ matrix.python-version }}
23-
architecture: x86
24-
- name: Install dependencies
25-
run: |
26-
python -m pip install --upgrade pip
27-
python -m pip install tox
28-
- name: Test with tox
24+
architecture: x64
25+
- name: Run unit tests
26+
shell: cmd
2927
# Run automated/unit tests
30-
run: tox
31-
- name: Lint with flake8
28+
run: .\rununittests.bat
29+
- name: Lint
30+
shell: cmd
3231
# Check code with the linter
33-
run: .\runlint.ps1
32+
run: .\runlint.bat
3433
- name: Validate metadata
34+
shell: cmd
3535
# E2E: test to check the script can be run, no need to actually test the file.
3636
# The internal checks are covered with unit tests.
37-
run: .\runvalidate.ps1 --dry-run _test/testData/addons/fake/13.0.json _tests\testData\nvdaAPIVersions.json
37+
run: .\runvalidate.bat --dry-run _test/testData/addons/fake/13.0.json tests\testData\nvdaAPIVersions.json
3838
- name: Get sha256
39+
shell: cmd
3940
# E2E: test to check the script can be run
40-
run: .\runsha.ps1 _tests\testData\fake.nvda-addon
41+
run: .\runsha.bat tests\testData\fake.nvda-addon
4142
- name: Generate json file
43+
shell: cmd
4244
# E2E: test to check the script can be run
43-
run: .\runcreatejson.ps1 -f _tests\testData\fake.nvda-addon --dir _tests\testOutput\test_runcreatejson --channel=stable --publisher=fakepublisher --sourceUrl=https://github.com/fake/ --url=https://github.com/fake.nvda-addon --licName="GPL v2" --licUrl="https://www.gnu.org/licenses/gpl-2.0.html"
45+
run: .\runcreatejson.bat -f tests\testData\fake.nvda-addon --dir tests\testOutput\test_runcreatejson --channel=stable --publisher=fakepublisher --sourceUrl=https://github.com/fake/ --url=https://github.com/fake.nvda-addon --licName="GPL v2" --licUrl="https://www.gnu.org/licenses/gpl-2.0.html"

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.tox
21
.venv
32
__pycache__
4-
_tests/testOutput
3+
testOutput
4+
*.egg-info

.pre-commit-config.yaml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# https://pre-commit.ci/
2+
# Configuration for Continuous Integration service
3+
ci:
4+
# Can't run Windows scons scripts on Linux.
5+
# Pyright does not seem to work in pre-commit CI
6+
skip: [unitTest, pyright]
7+
autoupdate_schedule: monthly
8+
autoupdate_commit_msg: "Pre-commit auto-update"
9+
autofix_commit_msg: "Pre-commit auto-fix"
10+
submodules: true
11+
12+
default_language_version:
13+
python: python3.13
14+
15+
repos:
16+
- repo: https://github.com/pre-commit-ci/pre-commit-ci-config
17+
rev: v1.6.1
18+
hooks:
19+
- id: check-pre-commit-ci-config
20+
21+
- repo: meta
22+
hooks:
23+
# ensures that exclude directives apply to any file in the repository.
24+
- id: check-useless-excludes
25+
# ensures that the configured hooks apply to at least one file in the repository.
26+
- id: check-hooks-apply
27+
28+
- repo: https://github.com/pre-commit/pre-commit-hooks
29+
rev: v6.0.0
30+
hooks:
31+
# Prevents commits to certain branches
32+
- id: no-commit-to-branch
33+
args: ["--branch", "main"]
34+
# Checks that large files have not been added. Default cut-off for "large" files is 500kb.
35+
- id: check-added-large-files
36+
# Checks python syntax
37+
- id: check-ast
38+
# Checks for filenames that will conflict on case insensitive filesystems (the majority of Windows filesystems, most of the time)
39+
- id: check-case-conflict
40+
# Checks for artifacts from resolving merge conflicts.
41+
- id: check-merge-conflict
42+
# Checks Python files for debug statements, such as python's breakpoint function, or those inserted by some IDEs.
43+
- id: debug-statements
44+
# Removes trailing whitespace.
45+
- id: trailing-whitespace
46+
types_or: [python, batch, markdown, toml, yaml, powershell]
47+
# Ensures all files end in 1 (and only 1) newline.
48+
- id: end-of-file-fixer
49+
types_or: [python, batch, markdown, toml, yaml, powershell]
50+
# Removes the UTF-8 BOM from files that have it.
51+
# See https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/codingStandards.md#encoding
52+
- id: fix-byte-order-marker
53+
types_or: [python, batch, markdown, toml, yaml, powershell]
54+
# Validates TOML files.
55+
- id: check-toml
56+
# Validates YAML files.
57+
- id: check-yaml
58+
# Ensures that links to lines in files under version control point to a particular commit.
59+
- id: check-vcs-permalinks
60+
# Avoids using reserved Windows filenames.
61+
- id: check-illegal-windows-names
62+
# Checks that tests are named test_*.py.
63+
- id: name-tests-test
64+
args: ["--unittest"]
65+
66+
- repo: https://github.com/asottile/add-trailing-comma
67+
rev: v3.2.0
68+
hooks:
69+
# Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables,
70+
# if a trailing comma is added.
71+
# This adds a trailing comma to args/iterable items in case it was missed.
72+
- id: add-trailing-comma
73+
74+
- repo: https://github.com/astral-sh/ruff-pre-commit
75+
# Matches Ruff version in pyproject.
76+
rev: v0.13.0
77+
hooks:
78+
- id: ruff
79+
name: lint with ruff
80+
args: [ --fix ]
81+
- id: ruff-format
82+
name: format with ruff
83+
84+
- repo: https://github.com/RobertCraigie/pyright-python
85+
rev: v1.1.405
86+
hooks:
87+
- id: pyright
88+
name: Check types with pyright
89+
90+
- repo: https://github.com/astral-sh/uv-pre-commit
91+
rev: 0.8.17
92+
hooks:
93+
- id: uv-lock
94+
name: Verify uv lock file
95+
# Override python interpreter from .python-versions as that is too strict for pre-commit.ci
96+
args: ["-p3.13"]
97+
98+
- repo: local
99+
hooks:
100+
- id: unitTest
101+
name: unit tests
102+
entry: ./rununittests.bat
103+
language: script
104+
pass_filenames: false
105+
types_or: [python, batch]

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cpython-3.13-windows-x86_64-none

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The Action aims to validate the metadata of add-ons submitted to
1313
* The `*.nvda-addon` file can be downloaded
1414
* The Sha256 of the downloaded `*.nvda-addon` file matches.
1515
* Check data matches the addon's manifest file.
16-
* The manifest exists in the downloaded `*.nvda-addon` file and can be loaded by the `AddonManifest` class.
16+
* The manifest exists in the downloaded `*.nvda-addon` file and can be loaded by the `AddonManifest` class.
1717
* The submission addonName matches the manifest summary field
1818
* The submission description matches the manifest description field
1919
* The homepage URL matches the manifest URL field
@@ -40,8 +40,7 @@ From cmd.exe:
4040

4141
To test the scripts used in this action, you can run the unit tests.
4242

43-
1. Install [tox](https://pypi.org/project/tox): `pip install tox`
44-
1. `tox`
43+
1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/)
4544

4645
## Python linting
4746

_validate/addonManifest.py

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
1-
#!/usr/bin/env python
2-
3-
# Copyright (C) 2022-2023 NV Access Limited
1+
# Copyright (C) 2022-2025 NV Access Limited
42
# This file may be used under the terms of the GNU General Public License, version 2 or later.
53
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html
64

7-
import os
8-
import sys
9-
from typing import (
10-
Optional,
11-
TextIO,
12-
Tuple,
13-
)
14-
from io import StringIO
5+
from io import StringIO, TextIOBase
6+
from typing import Any, cast
157

168
from configobj import ConfigObj
179
from configobj.validate import Validator, ValidateError
1810

19-
sys.path.append(os.path.dirname(__file__))
20-
# E402 module level import not at top of file
21-
from majorMinorPatch import MajorMinorPatch # noqa:E402
22-
del sys.path[-1]
11+
from .majorMinorPatch import MajorMinorPatch
12+
13+
ApiVersionT = tuple[int, int, int] # major, minor, patch
2314

2415

2516
class AddonManifest(ConfigObj):
2617
"""From the NVDA addonHandler module. Should be kept in sync.
27-
Add-on manifest file. It contains metadata about an NVDA add-on package. """
28-
configspec = ConfigObj(StringIO(
29-
"""
18+
Add-on manifest file. It contains metadata about an NVDA add-on package."""
19+
20+
configspec = ConfigObj(
21+
StringIO(
22+
"""
3023
# NVDA Add-on Manifest configuration specification
3124
# Add-on unique name
3225
# Suggested convention is lowerCamelCase.
@@ -69,29 +62,32 @@ class AddonManifest(ConfigObj):
6962
# "0.0.0" is also valid.
7063
# The final integer can be left out, and in that case will default to 0. E.g. 2019.1
7164
65+
""",
66+
),
67+
)
68+
69+
def __init__(self, input: str | TextIOBase, translatedInput: str | None = None):
7270
"""
73-
))
71+
Constructs an :class:`AddonManifest` instance from manifest string data.
7472
75-
def __init__(self, input: TextIO, translatedInput: Optional[TextIO] = None):
76-
""" Constructs an L{AddonManifest} instance from manifest string data
77-
@param input: data to read the manifest information
78-
@param translatedInput: translated manifest input
73+
:param input: data to read the manifest information. Can be a filename or a file-like object.
74+
:param translatedInput: translated manifest input
7975
"""
80-
super().__init__(
76+
super().__init__( # type: ignore[reportUnknownMemberType]
8177
input,
8278
configspec=self.configspec,
83-
encoding='utf-8',
84-
default_encoding='utf-8',
79+
encoding="utf-8",
80+
default_encoding="utf-8",
8581
)
86-
self._errors: Optional[str] = None
87-
val = Validator({"apiVersion": validate_apiVersionString})
88-
result = self.validate(val, copy=True, preserve_errors=True)
82+
self._errors: str | None = None
83+
validator = Validator({"apiVersion": validate_apiVersionString})
84+
result = self.validate(validator, copy=True, preserve_errors=True) # type: ignore[reportUnknownMemberType]
8985
if result is not True:
9086
self._errors = result
9187
elif self._validateApiVersionRange() is not True:
9288
self._errors = "Constraint not met: minimumNVDAVersion ({}) <= lastTestedNVDAVersion ({})".format(
93-
self.get("minimumNVDAVersion"),
94-
self.get("lastTestedNVDAVersion")
89+
cast(ApiVersionT, self.get("minimumNVDAVersion")), # type: ignore[reportUnknownMemberType]
90+
cast(ApiVersionT, self.get("lastTestedNVDAVersion")), # type: ignore[reportUnknownMemberType]
9591
)
9692
self._translatedConfig = None
9793
if translatedInput is not None:
@@ -102,23 +98,23 @@ def __init__(self, input: TextIO, translatedInput: Optional[TextIO] = None):
10298
self[key] = val
10399

104100
@property
105-
def errors(self) -> str:
101+
def errors(self) -> str | None:
106102
return self._errors
107103

108104
def _validateApiVersionRange(self) -> bool:
109-
lastTested = self.get("lastTestedNVDAVersion")
110-
minRequiredVersion = self.get("minimumNVDAVersion")
105+
lastTested = cast(ApiVersionT, self.get("lastTestedNVDAVersion")) # type: ignore[reportUnknownMemberType]
106+
minRequiredVersion = cast(ApiVersionT, self.get("minimumNVDAVersion")) # type: ignore[reportUnknownMemberType]
111107
return minRequiredVersion <= lastTested
112108

113109

114-
def validate_apiVersionString(value: str) -> Tuple[int, int, int]:
110+
def validate_apiVersionString(value: str | Any) -> ApiVersionT:
115111
"""From the NVDA addonHandler module. Should be kept in sync."""
116112
if not value or value == "None":
117113
return (0, 0, 0)
118114
if not isinstance(value, str):
119115
raise ValidateError(
120116
"Expected an apiVersion in the form of a string. "
121-
f"e.g. '2019.1.0' instead of {value} (type {type(value)})"
117+
f"e.g. '2019.1.0' instead of {value} (type {type(value)})",
122118
)
123119
try:
124120
versionParsed = MajorMinorPatch.getFromStr(value)

0 commit comments

Comments
 (0)