From b6faf9144777aeeff6171e24dd243152700bc1a4 Mon Sep 17 00:00:00 2001 From: derek riemer Date: Sat, 17 Jun 2023 15:15:30 -0600 Subject: [PATCH 001/119] Depend on the correct moFile for a given md file. We need the correct title for a given languages translated documentation, which comes from the moFile. However, the previous version of the build script accidentally depended on a variable assigned inside the for loop which was used to assign the languages actual moFile. This loop variable is assigned inside the loop, and not outside it, is overridden on each iteration, and is not persisted anywhere so we cann reference the correct moFile when we later generate the html. This pr fixes that. in this pr, I create a moByLang dictionary, and store the moFile as a value. The key for this moFile is the language code. That way, the html for language KR depends on the moFile for kr, and not the moFile for whatever language was last assigned in the for loop. That way, if the ES languagges po file updates, the dependency system will correctly attribute that Korean is the language we need to regenenrate HTML for. This also undoes the try/except logic that incorrectly hid the real issue, which was the inappropriate refrencing of a for loop variable that may have never been created. --- sconstruct | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sconstruct b/sconstruct index f1bad99..9596a33 100644 --- a/sconstruct +++ b/sconstruct @@ -279,9 +279,11 @@ addon = env.NVDAAddon(addonFile, env.Dir('addon')) langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated +moByLang = {} for dir in langDirs: poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) moFile = env.gettextMoFile(poFile) + moByLang[dir] = moFile env.Depends(moFile, poFile) translatedManifest = env.NVDATranslatedManifest( dir.File("manifest.ini"), @@ -298,13 +300,14 @@ for file in pythonFiles: # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager createAddonHelp("addon") for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): + # the title of the html file is translated based on the contents of something in the moFile for a language. + # Thus, we find the moFile for this language and depend on it if it exists. + lang = os.path.basename(os.path.dirname(mdFile.get_abspath())) + moFile = moByLang.get(lang) htmlFile = env.markdown(mdFile) - try: # It is possible that no moFile was set, because an add-on has no translations. - moFile - except NameError: # Runs if there is no moFile - env.Depends(htmlFile, mdFile) - else: # Runs if there is a moFile - env.Depends(htmlFile, [mdFile, moFile]) + env.Depends(htmlFile, mdFile) + if moFile: + env.Depends(htmlFile, moFile) env.Depends(addon, htmlFile) # Pot target From 3b22bfa813dc77c5cd2518e6ebe39f55493da62e Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Wed, 20 Nov 2024 05:10:15 -0700 Subject: [PATCH 002/119] SConstruct: reformat via Ruff --- sconstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index e7ca0b5..9b576af 100644 --- a/sconstruct +++ b/sconstruct @@ -266,7 +266,7 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager createAddonHelp("addon") -for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): +for mdFile in env.Glob(os.path.join("addon", "doc", "*", "*.md")): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. lang = os.path.basename(os.path.dirname(mdFile.get_abspath())) From 4e80d7b822185fbd9acc863f4a811082ec397fed Mon Sep 17 00:00:00 2001 From: hwf1324 <1398969445@qq.com> Date: Thu, 2 Jan 2025 21:52:06 +0800 Subject: [PATCH 003/119] The meta elements in the generated HTML are on separate lines. --- sconstruct | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index 9b576af..38d2483 100644 --- a/sconstruct +++ b/sconstruct @@ -51,7 +51,8 @@ def md2html(source, dest): "", f'', "", - '' '', + '', + '', '', f"{title}", "\n", From 5584c62b45e1a89f9e6f245b1a069769942f68e2 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 2 Jan 2025 08:23:21 -0700 Subject: [PATCH 004/119] Readme and SConstruct: update copyright years, update dependency versions --- readme.md | 14 ++++++++------ sconstruct | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index e6e43f0..b8613f9 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ For details about NVDA add-on development, please see the [NVDA Add-on Developme The NVDA add-on development/discussion list [is here](https://nvda-addons.groups.io/g/nvda-addons) Information specific to NV Access add-on store [can be found here](https://github.com/nvaccess/addon-datastore). -Copyright (C) 2012-2024 NVDA Add-on team contributors. +Copyright (C) 2012-2025 NVDA Add-on team contributors. This package is distributed under the terms of the GNU General Public License, version 2 or later. Please see the file COPYING.txt for further details. @@ -35,8 +35,8 @@ In addition, this template includes configuration files for the following tools * Ruff (pyproject.toml/tool.ruff sections): a Python linter written in Rust. Sections starting with tool.ruff house configuration options for Ruff. * Configuration for VS Code. It requires NVDA's repo at the same level as the add-on folder containing your actual source files, with prepared source code (`scons source`). preparing the source code is a step in the instructions for building NVDA itself, see [The NVDA Repository](https://github.com/nvaccess/nvda) for details. - * Place the .vscode in this repo within the addon folder, where your add-on source files (will) reside. The settings file within this folder assumes the NVDA repository is within the parent folder of this folder. If your addon folder is within the addonTemplate folder, then your NVDA repository folder needs to also be within the addonTemplate folder, or the source will not be found. - * Open the addon folder in VS Code. This should initialize VS Code with the correct settings and provide you with code completion and other VS Code features. + * Place the .vscode in this repo within the addon folder, where your add-on source files (will) reside. The settings file within this folder assumes the NVDA repository is within the parent folder of this folder. If your addon folder is within the addonTemplate folder, then your NVDA repository folder needs to also be within the addonTemplate folder, or the source will not be found. + * Open the addon folder in VS Code. This should initialize VS Code with the correct settings and provide you with code completion and other VS Code features. * Press `control+shift+m` after saving a file to search for problems. * Use arrow and tab keys for the autocompletion feature. * Press `control+shift+p` to open the commands palette and search for recommended extensions to install or check if they are installed. @@ -46,9 +46,9 @@ In addition, this template includes configuration files for the following tools You need the following software to use this code for your NVDA add-on development and packaging: * a Python distribution (3.11 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.11. -* Scons - [Website](https://www.scons.org/) - version 4.5.2 or later. You can install it via PIP. +* Scons - [Website](https://www.scons.org/) - version 4.8.1 or later. You can install it via PIP. * GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php). -* Markdown 3.3.0 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. +* Markdown 3.7 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. Note, that you may not need these tools in a local build environment, if you are using [Appveyor](https://appveyor.com/) or [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions), to build and package your add-ons. @@ -86,7 +86,7 @@ and file: An add-on manifest generated manually or via `buildVars.py` must include the following information: -* Name (string): a unique identifier for the add-on. It must use camel case (e.g. someModule). This is also used as part of add-on store to identify the add-on uniquely. +* Name (string): a unique internal identifier for the add-on. It must use camel case (e.g. someModule). This is also used as part of add-on store to identify the add-on uniquely. * Summary (string): name as shown on NVDA's Add-on store. * Description (string): a short detailed description about the add-on. * Version (string), ideally number.number with an optional third number, denoting major.minor.patch. @@ -108,6 +108,7 @@ In addition, the following information must be filled out (not used in the manif In addition to the core manifest data, custom add-on information can be specified. ###### Braille translation tables + Information on custom braille tables must be specified in buildVars under `brailleTables` dictionary as follows: * Table name (string key for a nested dictionary): each `brailleTables` entry is a filename for the included custom braille table placed in `brailleTables` folder inside `addon` folder. This nested dictionary should specify: @@ -119,6 +120,7 @@ Information on custom braille tables must be specified in buildVars under `brail Note: you must fill out this dictionary if at least one custom braille table is included in the add-on. If not, leave the dictionary empty. ###### Speech symbol dictionaries + Information on custom symbol dictionaries must be specified in buildVars under `symbolDictionaries` dictionary as follows: * Dictionary name (string key for a nested dictionary): each `symbolDictionaries` entry is a name for the included custom symbol dictionary placed in `locale\` folder inside `addon` folder. The file is named `symbols-.dic`. This nested dictionary should specify: diff --git a/sconstruct b/sconstruct index 38d2483..369b1f7 100644 --- a/sconstruct +++ b/sconstruct @@ -1,5 +1,5 @@ # NVDA add-on template SCONSTRUCT file -# Copyright (C) 2012-2024 Rui Batista, Noelia Martinez, Joseph Lee +# Copyright (C) 2012-2025 Rui Batista, Noelia Martinez, Joseph Lee # This file is covered by the GNU General Public License. # See the file COPYING.txt for more details. From ac411c428e1343f8366811b0785c5a1bb5a9aa1c Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 2 Jan 2025 08:27:49 -0700 Subject: [PATCH 005/119] Add-on template: remove template-based NV Access add-on store json file as it is generated when submitting the add-on to the store --- _template_addon_release.json | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 _template_addon_release.json diff --git a/_template_addon_release.json b/_template_addon_release.json deleted file mode 100644 index c6d3a5f..0000000 --- a/_template_addon_release.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "addonId": "easyAddonTech.XYZ", - "addonVersionNumber": { - "major": 21, - "minor": 6, - "patch": 0 - }, - "addonVersionName": "21.06", - "displayName": "My addon", - "publisher": "easyAddonTech", - "description": "Makes doing XYZ easier", - "homepage": "https://github.com/nvaccess/addon-datastore", - "minNVDAVersion": { - "major": 2019, - "minor": 3, - "patch": 0 - }, - "lastTestedVersion": { - "major": 2020, - "minor": 4, - "patch": 0 - }, - "channel": "beta", - "URL": "https://github.com/nvaccess/addon-datastore/releases/download/v0.1.0/myAddon.nvda-addon", - "sha256": "69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82", - "sourceURL": "https://github.com/nvaccess/addon-datastore/", - "license": "GPL v2", - "licenseURL": "https://github.com/nvaccess/addon-datastore/license.MD" -} From 3e124a499e118293913161578aa69f9d6bb29919 Mon Sep 17 00:00:00 2001 From: Tyler Spivey Date: Mon, 3 Feb 2025 08:05:11 -0800 Subject: [PATCH 006/119] Enable write permission when creating releases According to the [action-gh-release documentation](https://github.com/softprops/action-gh-release#permissions), write permission is required for contents. --- .github/workflows/build_addon.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 1e56dcc..1cf0f09 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -51,6 +51,8 @@ jobs: runs-on: ubuntu-latest if: ${{ startsWith(github.ref, 'refs/tags/') }} needs: ["build"] + permissions: + contents: write steps: - uses: actions/checkout@v4 - name: download releases files From c0aa5a6d66d562d5c43763fa7d84b1da2a7412d4 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Wed, 26 Feb 2025 06:28:20 -0700 Subject: [PATCH 007/119] BuildVars: add-ons manager -> add-on store, clarify that summary can be add-on title --- buildVars.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildVars.py b/buildVars.py index c62935e..ddc31f2 100644 --- a/buildVars.py +++ b/buildVars.py @@ -17,12 +17,12 @@ def _(arg): addon_info = { # add-on Name/identifier, internal for NVDA "addon_name": "addonTemplate", - # Add-on summary, usually the user visible name of the addon. - # Translators: Summary for this add-on - # to be shown on installation and add-on information found in Add-ons Manager. + # Add-on summary/title, usually the user visible name of the add-on + # Translators: Summary/title for this add-on + # to be shown on installation and add-on information found in add-on store "addon_summary": _("Add-on user visible name"), # Add-on description - # Translators: Long description to be shown for this add-on on add-on information from add-ons manager + # Translators: Long description to be shown for this add-on on add-on information from add-on store "addon_description": _("""Description for the add-on. It can span multiple lines."""), # version From cb03ab8915d29fdf1b18f3a2320045485f6d4b5b Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Wed, 26 Feb 2025 06:28:59 -0700 Subject: [PATCH 008/119] BuildVars: update minimum and last tesed version examples based on add-on store metadata (store minimum version is 2019.1.0) --- buildVars.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildVars.py b/buildVars.py index ddc31f2..85f1e4a 100644 --- a/buildVars.py +++ b/buildVars.py @@ -35,9 +35,9 @@ def _(arg): "addon_sourceURL": None, # Documentation file name "addon_docFileName": "readme.html", - # Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional) + # Minimum NVDA version supported (e.g. "2019.3.0", minor version is optional) "addon_minimumNVDAVersion": None, - # Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version) + # Last NVDA version supported/tested (e.g. "2024.4.0", ideally more recent than minimum version) "addon_lastTestedNVDAVersion": None, # Add-on update channel (default is None, denoting stable releases, # and for development releases, use "dev".) From eb4f6430dd88d1c0cf99277c02ba41f946938bea Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Wed, 12 Mar 2025 21:53:37 -0600 Subject: [PATCH 009/119] BuildVars, readme, sconstruct: remove extraneous whitespace --- buildVars.py | 2 +- readme.md | 2 +- sconstruct | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildVars.py b/buildVars.py index 85f1e4a..785c17c 100644 --- a/buildVars.py +++ b/buildVars.py @@ -94,6 +94,6 @@ def _(arg): # If your add-on includes custom speech symbol dictionaries (most will not), fill out this dictionary. # Each key is the name of the dictionary, # with keys inside recording the following attributes: -# displayName (name of the speech dictionary shown to users and translatable), +# displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. symbolDictionaries = {} diff --git a/readme.md b/readme.md index b8613f9..9f72969 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ This package is distributed under the terms of the GNU General Public License, v [alekssamos](https://github.com/alekssamos/) added automatic package of add-ons through Github Actions. -For details about Github Actions please see the [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions). +For details about Github Actions, see the [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions). Copyright (C) 2022 alekssamos diff --git a/sconstruct b/sconstruct index 369b1f7..ac689d2 100644 --- a/sconstruct +++ b/sconstruct @@ -1,4 +1,4 @@ -# NVDA add-on template SCONSTRUCT file +# NVDA add-on template SCONSTRUCT file # Copyright (C) 2012-2025 Rui Batista, Noelia Martinez, Joseph Lee # This file is covered by the GNU General Public License. # See the file COPYING.txt for more details. From ba7c6574aec858f3345a05f5c09de7c41e719b5a Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sun, 22 Jun 2025 14:39:32 -0600 Subject: [PATCH 010/119] Pyproject: add Pyright support. By default, Pyright rules will be reported and add-on authors can add bad rules with explanations to bad rules list in the end. For examples, see Emoticons and StationPlaylist add-ons. --- pyproject.toml | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4d76bfe..97189ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,3 +38,124 @@ ignore = [ # sconstruct contains many inbuilt functions not recognised by the lint, # so ignore F821. "sconstruct" = ["F821"] + +[tool.pyright] +pythonPlatform = "Windows" +typeCheckingMode = "strict" + +include = [ + "**/*.py", +] + +exclude = [ + "sconstruct", + ".git", + "__pycache__", + # When excluding concrete paths relative to a directory, + # not matching multiple folders by name e.g. `__pycache__`, + # paths are relative to the configuration file. +] + +# Tell pyright where to load python code from +extraPaths = [ + "./addon", +] + +# General config +analyzeUnannotatedFunctions = true +deprecateTypingAliases = true + +# Stricter typing +strictParameterNoneValue = true +strictListInference = true +strictDictionaryInference = true +strictSetInference = true + +# Compliant rules +reportAbstractUsage = true +reportArgumentType = true +reportAssertAlwaysTrue = true +reportAssertTypeFailure = true +reportAssignmentType = true +reportAttributeAccessIssue = true +reportCallInDefaultInitializer = true +reportCallIssue = true +reportConstantRedefinition = true +reportDuplicateImport = true +reportFunctionMemberAccess = true +reportGeneralTypeIssues = true +reportImplicitOverride = true +reportImplicitStringConcatenation = true +reportImportCycles = true +reportIncompatibleMethodOverride = true +reportIncompatibleVariableOverride = true +reportIncompleteStub = true +reportInconsistentConstructor = true +reportInconsistentOverload = true +reportIndexIssue = true +reportInvalidStringEscapeSequence = true +reportInvalidStubStatement = true +reportInvalidTypeArguments = true +reportInvalidTypeForm = true +reportInvalidTypeVarUse = true +reportMatchNotExhaustive = true +reportMissingImports = true +reportMissingModuleSource = true +reportMissingParameterType = true +reportMissingSuperCall = true +reportMissingTypeArgument = true +reportNoOverloadImplementation = true +reportOperatorIssue = true +reportOptionalCall = true +reportOptionalContextManager = true +reportOptionalIterable = true +reportOptionalMemberAccess = true +reportOptionalOperand = true +reportOptionalSubscript = true +reportOverlappingOverload = true +reportPossiblyUnboundVariable = true +reportPrivateImportUsage = true +reportPrivateUsage = true +reportPropertyTypeMismatch = true +reportRedeclaration = true +reportReturnType = true +reportSelfClsParameterName = true +reportShadowedImports = true +reportTypeCommentUsage = true +reportTypedDictNotRequiredAccess = true +reportUnboundVariable = true +reportUndefinedVariable = true +reportUnhashable = true +reportUninitializedInstanceVariable = true +reportUnknownArgumentType = true +reportUnknownLambdaType = true +reportUnknownMemberType = true +reportUnknownParameterType = true +reportUnknownVariableType = true +reportUnnecessaryCast = true +reportUnnecessaryComparison = true +reportUnnecessaryContains = true +reportUnnecessaryIsInstance = true +reportUnnecessaryTypeIgnoreComment = true +reportUnsupportedDunderAll = true +reportUntypedBaseClass = true +reportUntypedClassDecorator = true +reportUntypedFunctionDecorator = true +reportUntypedNamedTuple = true +reportUnusedCallResult = true +reportUnusedClass = true +reportUnusedCoroutine = true +reportUnusedExcept = true +reportUnusedExpression = true +reportUnusedFunction = true +reportUnusedImport = true +reportUnusedVariable = true +reportWildcardImportFromLibrary = true + +reportDeprecated = true + +# Can be enabled by generating type stubs for modules via pyright CLI +reportMissingTypeStubs = false + +# Bad rules +# These are sorted alphabetically and should be enabled and moved to compliant rules section when resolved. From fd73176e53e5015889837c30e148ca0e33d947f2 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sun, 22 Jun 2025 14:47:12 -0600 Subject: [PATCH 011/119] Readme: mention Pyright --- readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 9f72969..0495029 100644 --- a/readme.md +++ b/readme.md @@ -40,15 +40,17 @@ In addition, this template includes configuration files for the following tools * Press `control+shift+m` after saving a file to search for problems. * Use arrow and tab keys for the autocompletion feature. * Press `control+shift+p` to open the commands palette and search for recommended extensions to install or check if they are installed. +* Pyright (pyproject.toml/tool.pyright sections): a Python static type checker. Sections starting with tool.pyright house configuration options for Pyright. ## Requirements You need the following software to use this code for your NVDA add-on development and packaging: -* a Python distribution (3.11 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.11. +* a Python distribution (3.11 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.11 with 64-bit version planned. * Scons - [Website](https://www.scons.org/) - version 4.8.1 or later. You can install it via PIP. * GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php). * Markdown 3.7 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. +* Optional: additional tools such as linters and type checkers defined in pyproject.toml file. Note, that you may not need these tools in a local build environment, if you are using [Appveyor](https://appveyor.com/) or [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions), to build and package your add-ons. @@ -148,7 +150,8 @@ Note: you must fill out this dictionary if at least one custom symbol dictionary The template includes configuration files for use with additional tools such as linters. These include: -* Ruff (pyproject.toml): a Python linter written in Rust (0.4.10 or later, can be installed with PIP). +* Ruff: a Python linter written in Rust (0.4.10 or later, can be installed with PIP). +* Pyright: a Python static type checker (1.1.402 or later, can be installed with PIP). Read the documentation for the tools you wish to use when building and developing add-ons. From 72a2c1408b23045e0bdc9634e89796c20bba6d45 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 17 Jul 2025 10:26:57 +0000 Subject: [PATCH 012/119] buildVars: move down dont_write_bytecode after comment --- sconstruct | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index ac689d2..257d1b4 100644 --- a/sconstruct +++ b/sconstruct @@ -13,9 +13,10 @@ import sys # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. EnsurePythonVersion(3, 10) -sys.dont_write_bytecode = True # Bytecode should not be written for build vars module to keep the repository root folder clean. +sys.dont_write_bytecode = True + import buildVars # NOQA: E402 From 21abd0e9ac0c41074526e9bd4c593ca8e57c0479 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 17 Jul 2025 10:53:02 +0000 Subject: [PATCH 013/119] SConstruct: add matchesNoPatterns function --- sconstruct | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sconstruct b/sconstruct index 257d1b4..4c1ee79 100644 --- a/sconstruct +++ b/sconstruct @@ -9,6 +9,7 @@ import os import os.path import zipfile import sys +from pathlib import Path # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -163,6 +164,10 @@ def createAddonHelp(dir): env.Depends(addon, readmeTarget) +def machesNoPatterns(path: Path, patterns: list[str]) -> bool: + return all([not path.match(pattern) for pattern in patterns]) + + def createAddonBundleFromPath(path, dest): """Creates a bundle from a directory that contains an addon manifest file.""" basedir = os.path.abspath(path) From a0244e8b6c67a556ce6da03515d8075b7af003bd Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 17 Jul 2025 11:05:15 +0000 Subject: [PATCH 014/119] SConstruct: The function X is rewritten Replaced os.path with pathlib.Path. buildVars.excludedFiles are now checked with Path.match, which means that patterns are supported, not just specific paths. The issue with the difference between \ and / is now fixed. --- sconstruct | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sconstruct b/sconstruct index 4c1ee79..4590298 100644 --- a/sconstruct +++ b/sconstruct @@ -168,18 +168,18 @@ def machesNoPatterns(path: Path, patterns: list[str]) -> bool: return all([not path.match(pattern) for pattern in patterns]) -def createAddonBundleFromPath(path, dest): +def createAddonBundleFromPath(path: str|Path, dest: str): """Creates a bundle from a directory that contains an addon manifest file.""" - basedir = os.path.abspath(path) + if isinstance(path, str): + path = Path(path) + basedir = path.absolute() with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: - # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. - for dir, dirnames, filenames in os.walk(basedir): - relativePath = os.path.relpath(dir, basedir) - for filename in filenames: - pathInBundle = os.path.join(relativePath, filename) - absPath = os.path.join(dir, filename) - if pathInBundle not in buildVars.excludedFiles: - z.write(absPath, pathInBundle) + for p in basedir.rglob("*"): + if p.is_dir(): + continue + pathInBundle = p.relative_to(basedir) + if machesNoPatterns(pathInBundle, buildVars.excludedFiles): + z.write(p, pathInBundle) return dest From 935ef8d1b1de4f68088450d9bae8b3095df7138c Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 17 Jul 2025 12:44:25 +0000 Subject: [PATCH 015/119] buildVars: Extend comment before excludedFiles --- buildVars.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildVars.py b/buildVars.py index 785c17c..58844ad 100644 --- a/buildVars.py +++ b/buildVars.py @@ -64,6 +64,8 @@ def _(arg): # Files that will be ignored when building the nvda-addon file # Paths are relative to the addon directory, not to the root directory of your addon sources. +# You can either list every file (using ""/") as a path separator, +# or use glob expressions. excludedFiles = [] # Base language for the NVDA add-on From 99722d694488ebc444e486f0dc34d44c2ac82155 Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 21 Jul 2025 17:22:54 +0000 Subject: [PATCH 016/119] SConstruct: Refactor md2html func Replaced os.path with pathlib.Path to simplify the code and make it easier to understand. The code has been slightly formatted for easier reading. --- sconstruct | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/sconstruct b/sconstruct index 4590298..449644a 100644 --- a/sconstruct +++ b/sconstruct @@ -21,16 +21,21 @@ sys.dont_write_bytecode = True import buildVars # NOQA: E402 -def md2html(source, dest): +def md2html(source: str|Path, dest: str): + if isinstance(source, str): + source = Path(source) + import markdown # Use extensions if defined. mdExtensions = buildVars.markdownExtensions - lang = os.path.basename(os.path.dirname(source)).replace("_", "-") - localeLang = os.path.basename(os.path.dirname(source)) + localeLang = source.parent.name + lang = localeLang.replace("_", "-") try: _ = gettext.translation( - "nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang] + "nvda", + localedir=os.path.join("addon", "locale"), + languages=[localeLang] ).gettext summary = _(buildVars.addon_info["addon_summary"]) except Exception: @@ -42,14 +47,14 @@ def md2html(source, dest): '[[!meta title="': "# ", '"]]': " #", } - with codecs.open(source, "r", "utf-8") as f: + with codecs.open(str(source), "r", "utf-8") as f: mdText = f.read() - for k, v in headerDic.items(): - mdText = mdText.replace(k, v, 1) - htmlText = markdown.markdown(mdText, extensions=mdExtensions) + for k, v in headerDic.items(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText, extensions=mdExtensions) # Optimization: build resulting HTML text in one go instead of writing parts separately. docText = "\n".join( - [ + ( "", f'', "", @@ -60,7 +65,7 @@ def md2html(source, dest): "\n", htmlText, "\n", - ] + ) ) with codecs.open(dest, "w", "utf-8") as f: f.write(docText) From b982d4dcf20ee16d684a17816b7392b8a80e3c0a Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 21 Jul 2025 18:34:26 +0000 Subject: [PATCH 017/119] SConstruct: Refactor createAddonHelp func - Add typehints - Replace os.path in pathlib.Path --- sconstruct | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sconstruct b/sconstruct index 449644a..686d726 100644 --- a/sconstruct +++ b/sconstruct @@ -157,15 +157,20 @@ env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) -def createAddonHelp(dir): - docsDir = os.path.join(dir, "doc") - if os.path.isfile("style.css"): - cssPath = os.path.join(docsDir, "style.css") - cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) +def createAddonHelp(dir: str|Path): + if isinstance(dir, str): + dir = Path(dir) + + docsDir = dir / "doc" + cssFile = Path("style.css") + if cssFile.is_file(): + cssPath = docsDir / cssFile + cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) env.Depends(addon, cssTarget) - if os.path.isfile("readme.md"): - readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") - readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) + readmeFile = Path("readme.md") + if readmeFile.is_file(): + readmePath = docsDir / buildVars.baseLanguage / readmeFile + readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) env.Depends(addon, readmeTarget) From e8e4a274c0420ce1a2b3814c6dc9612dccc716bb Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 21 Jul 2025 18:45:06 +0000 Subject: [PATCH 018/119] SConstruct: Fix typo in name machesNoPaterns Rename the function to matchesNoPatterns --- sconstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 686d726..6c427f2 100644 --- a/sconstruct +++ b/sconstruct @@ -174,7 +174,7 @@ def createAddonHelp(dir: str|Path): env.Depends(addon, readmeTarget) -def machesNoPatterns(path: Path, patterns: list[str]) -> bool: +def matchesNoPatterns(path: Path, patterns: list[str]) -> bool: return all([not path.match(pattern) for pattern in patterns]) @@ -188,7 +188,7 @@ def createAddonBundleFromPath(path: str|Path, dest: str): if p.is_dir(): continue pathInBundle = p.relative_to(basedir) - if machesNoPatterns(pathInBundle, buildVars.excludedFiles): + if matchesNoPatterns(pathInBundle, buildVars.excludedFiles): z.write(p, pathInBundle) return dest From 8ca614698f615d81958065e33fbc91b764043ff3 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 22 Jul 2025 09:52:55 +0000 Subject: [PATCH 019/119] SConstruct: Refactor validateVersionNumber Add typehints to input args. Use Key in raises messages --- sconstruct | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sconstruct b/sconstruct index 6c427f2..52523c5 100644 --- a/sconstruct +++ b/sconstruct @@ -84,16 +84,16 @@ def mdTool(env): env["BUILDERS"]["markdown"] = mdBuilder -def validateVersionNumber(key, val, env): +def validateVersionNumber(key: str, val: str, _): # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. # Ignore all this if version number is not specified. if val == "0.0.0": return versionNumber = val.split(".") if len(versionNumber) < 3: - raise ValueError("versionNumber must have three parts (major.minor.patch)") + raise ValueError(f"{key} must have three parts (major.minor.patch)") if not all([part.isnumeric() for part in versionNumber]): - raise ValueError("versionNumber (major.minor.patch) must be integers") + raise ValueError(f"{key} (major.minor.patch) must be integers") vars = Variables() From 31a57c3185f3f69c15730ac4f55e73dbcd60582b Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 22 Jul 2025 09:58:55 +0000 Subject: [PATCH 020/119] SConstruct: Refactor if env["dev"] section --- sconstruct | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sconstruct b/sconstruct index 52523c5..292a3e6 100644 --- a/sconstruct +++ b/sconstruct @@ -106,13 +106,12 @@ env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", mdTool]) env.Append(**buildVars.addon_info) if env["dev"]: - import datetime + from datetime import date - buildDate = datetime.datetime.now() - year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) - versionTimestamp = "".join([year, month.zfill(2), day.zfill(2)]) - env["addon_version"] = f"{versionTimestamp}.0.0" - env["versionNumber"] = f"{versionTimestamp}.0.0" + versionTimestamp = date.today().strftime('%Y%m%d') + version = f"{versionTimestamp}.0.0" + env["addon_version"] = version + env["versionNumber"] = version env["channel"] = "dev" elif env["version"] is not None: env["addon_version"] = env["version"] From 4b2d2acda1ee5044cb7ec33db9be350fa663d3b3 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 23 Jul 2025 12:14:57 +0000 Subject: [PATCH 021/119] SConstruct: Refactor matchesNoPatterns The argument `patterns` can be any iterable, not only `list` Use `generator expression` instead of `list comprehension` --- sconstruct | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 292a3e6..3c7b1cf 100644 --- a/sconstruct +++ b/sconstruct @@ -10,6 +10,7 @@ import os.path import zipfile import sys from pathlib import Path +from collections.abc import Iterable # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -173,8 +174,9 @@ def createAddonHelp(dir: str|Path): env.Depends(addon, readmeTarget) -def matchesNoPatterns(path: Path, patterns: list[str]) -> bool: - return all([not path.match(pattern) for pattern in patterns]) +def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: + """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" + return not any((path.match(pattern) for pattern in patterns)) def createAddonBundleFromPath(path: str|Path, dest: str): From 12cc3aff2b8ab39c79675634ea84caa4f8cf4403 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 24 Jul 2025 12:42:09 +0000 Subject: [PATCH 022/119] SConstruct: Update expandGlobs func - env.Glob has been replaced with Path.glob, as this allows patterns such as `dir/**/*.py` or `**.py` to be used, rather than just `dir/*/*/*.py`. - Also added type hints for arguments and return value. --- sconstruct | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 3c7b1cf..84a74bd 100644 --- a/sconstruct +++ b/sconstruct @@ -256,8 +256,9 @@ def generateTranslatedManifest(source, language, out): f.write(result) -def expandGlobs(files): - return [f for pattern in files for f in env.Glob(pattern)] +def expandGlobs(patterns: Iterable[str]) -> list: + base = Path(".") + return [env.File(f) for pattern in patterns for f in base.glob(pattern.lstrip('/'))] addon = env.NVDAAddon(addonFile, env.Dir("addon")) From 8b1b9541500674e4507c40c96e45c47a4dbb516f Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 08:33:14 +0000 Subject: [PATCH 023/119] SConstruct: Removed unnecessary os.path.join Which passed one argument. --- sconstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 84a74bd..174d64c 100644 --- a/sconstruct +++ b/sconstruct @@ -273,7 +273,7 @@ for dir in langDirs: moByLang[dir] = moFile env.Depends(moFile, poFile) translatedManifest = env.NVDATranslatedManifest( - dir.File("manifest.ini"), [moFile, os.path.join("manifest-translated.ini.tpl")] + dir.File("manifest.ini"), [moFile, "manifest-translated.ini.tpl"] ) env.Depends(translatedManifest, ["buildVars.py"]) env.Depends(addon, [translatedManifest, moFile]) @@ -312,7 +312,7 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl")) +manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), "manifest.ini.tpl") # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") From 5b1ba4b6c8c3e02123cfe36ec81159e063458ed0 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 08:54:38 +0000 Subject: [PATCH 024/119] SConstruct: Migrated to pathlib.Path from os.path - Add addonDir & localeDir constants - Most os.path has been replaced with pathlib where it seems appropriate --- sconstruct | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sconstruct b/sconstruct index 174d64c..c3ee599 100644 --- a/sconstruct +++ b/sconstruct @@ -11,6 +11,7 @@ import zipfile import sys from pathlib import Path from collections.abc import Iterable +from typing import Final # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -22,6 +23,10 @@ sys.dont_write_bytecode = True import buildVars # NOQA: E402 +addonDir: Final = Path("addon/") +localeDir: Final = addonDir / "locale" + + def md2html(source: str|Path, dest: str): if isinstance(source, str): source = Path(source) @@ -35,7 +40,7 @@ def md2html(source: str|Path, dest: str): try: _ = gettext.translation( "nvda", - localedir=os.path.join("addon", "locale"), + localedir=localeDir, languages=[localeLang] ).gettext summary = _(buildVars.addon_info["addon_summary"]) @@ -142,8 +147,8 @@ def manifestGenerator(target, source, env, for_signature): def translatedManifestGenerator(target, source, env, for_signature): - dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) - lang = os.path.basename(dir) + dir = Path(str(source[0])).absolute().parents[1] + lang = dir.name action = env.Action( lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) and None, @@ -224,7 +229,7 @@ def generateManifest(source, dest): def generateTranslatedManifest(source, language, out): - _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext + _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext vars = {} for var in ("addon_summary", "addon_description"): vars[var] = _(buildVars.addon_info[var]) @@ -263,7 +268,7 @@ def expandGlobs(patterns: Iterable[str]) -> list: addon = env.NVDAAddon(addonFile, env.Dir("addon")) -langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] +langDirs = [f for f in env.Glob(localeDir/"*")] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated moByLang = {} @@ -285,10 +290,10 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager createAddonHelp("addon") -for mdFile in env.Glob(os.path.join("addon", "doc", "*", "*.md")): +for mdFile in env.Glob(addonDir/"doc/*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. - lang = os.path.basename(os.path.dirname(mdFile.get_abspath())) + lang = Path(mdFile.get_abspath()).parent.name moFile = moByLang.get(lang) htmlFile = env.markdown(mdFile) env.Depends(htmlFile, mdFile) @@ -312,7 +317,7 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), "manifest.ini.tpl") +manifest = env.NVDAManifest(addonDir/"manifest.ini", "manifest.ini.tpl") # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") From df6abb9a6a18184a6055494521def73c257f0e19 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 09:15:45 +0000 Subject: [PATCH 025/119] SConstruct: Replaced literal "addon" with const addonDir --- sconstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index c3ee599..90274bf 100644 --- a/sconstruct +++ b/sconstruct @@ -266,7 +266,7 @@ def expandGlobs(patterns: Iterable[str]) -> list: return [env.File(f) for pattern in patterns for f in base.glob(pattern.lstrip('/'))] -addon = env.NVDAAddon(addonFile, env.Dir("addon")) +addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) langDirs = [f for f in env.Glob(localeDir/"*")] @@ -289,7 +289,7 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager -createAddonHelp("addon") +createAddonHelp(addonDir) for mdFile in env.Glob(addonDir/"doc/*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. From 9b52fc4dfe8fd45cf9036dfcc8fd4cbd84b92523 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 11:11:17 +0000 Subject: [PATCH 026/119] SConstruct: Moved down a definition of addonFile --- sconstruct | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 90274bf..483e4b6 100644 --- a/sconstruct +++ b/sconstruct @@ -127,8 +127,6 @@ if "channel" in env and env["channel"] is not None: buildVars.addon_info["addon_version"] = env["addon_version"] buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] -addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") - def addonGenerator(target, source, env, for_signature): action = env.Action( @@ -266,6 +264,7 @@ def expandGlobs(patterns: Iterable[str]) -> list: return [env.File(f) for pattern in patterns for f in base.glob(pattern.lstrip('/'))] +addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) langDirs = [f for f in env.Glob(localeDir/"*")] From 2c49e6c6458d97d252d7b8108ee93316caa7d4d6 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 11:24:13 +0000 Subject: [PATCH 027/119] SConstruct: Rewrote iteration over markdown doc files - Defined docsDir constant - Rewrote iteration over addon/doc/*/*.md files --- sconstruct | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 483e4b6..95955a5 100644 --- a/sconstruct +++ b/sconstruct @@ -25,6 +25,7 @@ import buildVars # NOQA: E402 addonDir: Final = Path("addon/") localeDir: Final = addonDir / "locale" +docsDir: Final = addonDir / "doc" def md2html(source: str|Path, dest: str): @@ -289,10 +290,11 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager createAddonHelp(addonDir) -for mdFile in env.Glob(addonDir/"doc/*/*.md"): +for mdPath in docsDir.glob("*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. - lang = Path(mdFile.get_abspath()).parent.name + lang = mdPath.absolute().parent.name + mdFile = env.File(mdPath) moFile = moByLang.get(lang) htmlFile = env.markdown(mdFile) env.Depends(htmlFile, mdFile) From dea8dbf3ce55099c0d31f0987683c6921c7bc85e Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 11:31:10 +0000 Subject: [PATCH 028/119] SConstruct: Updated createAddonHelp func Now the function accepts the path directly to the documentation directory. --- sconstruct | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sconstruct b/sconstruct index 95955a5..8673c9d 100644 --- a/sconstruct +++ b/sconstruct @@ -161,11 +161,10 @@ env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) -def createAddonHelp(dir: str|Path): - if isinstance(dir, str): - dir = Path(dir) +def createAddonHelp(docsDir: str|Path): + if isinstance(docsDir, str): + docsDir = Path(docsDir) - docsDir = dir / "doc" cssFile = Path("style.css") if cssFile.is_file(): cssPath = docsDir / cssFile @@ -289,7 +288,7 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager -createAddonHelp(addonDir) +createAddonHelp(docsDir) for mdPath in docsDir.glob("*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. From 1a259252710532062516865f43b528d4c5f1b81c Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 11:34:59 +0000 Subject: [PATCH 029/119] SConstruct: Rewrote definition of localeDirs list --- sconstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index 8673c9d..d0063bb 100644 --- a/sconstruct +++ b/sconstruct @@ -267,7 +267,7 @@ def expandGlobs(patterns: Iterable[str]) -> list: addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) -langDirs = [f for f in env.Glob(localeDir/"*")] +langDirs = [env.Dir(d) for d in localeDir.glob("*/")] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated moByLang = {} From bbe7b16c841aae317b31465329d9d7da92da00cd Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 14:17:38 +0000 Subject: [PATCH 030/119] SConstruct: Added explicit imports for linter compatibility --- sconstruct | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sconstruct b/sconstruct index d0063bb..9440172 100644 --- a/sconstruct +++ b/sconstruct @@ -13,6 +13,12 @@ from pathlib import Path from collections.abc import Iterable from typing import Final +# While names imported below are available by default in every SConscript +# Linters aren't aware about them. +# To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. +# When using other Scons functions please add them to the line below. +from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment + # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. EnsurePythonVersion(3, 10) From dddf7afdfc60591966365c5e9fe0fd89257c3b27 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 14:22:25 +0000 Subject: [PATCH 031/119] SConstruct: Added type hints from SCons.Node.FS - Added hints for global variables langDirs and moByLang. - The return value of the expandGlobs function is typed. --- sconstruct | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sconstruct b/sconstruct index 9440172..2d040eb 100644 --- a/sconstruct +++ b/sconstruct @@ -18,6 +18,7 @@ from typing import Final # To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. # When using other Scons functions please add them to the line below. from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment +from SCons.Node import FS # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -265,7 +266,7 @@ def generateTranslatedManifest(source, language, out): f.write(result) -def expandGlobs(patterns: Iterable[str]) -> list: +def expandGlobs(patterns: Iterable[str]) -> list[FS.File]: base = Path(".") return [env.File(f) for pattern in patterns for f in base.glob(pattern.lstrip('/'))] @@ -273,10 +274,10 @@ def expandGlobs(patterns: Iterable[str]) -> list: addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) -langDirs = [env.Dir(d) for d in localeDir.glob("*/")] +langDirs: list[FS.Dir] = [env.Dir(d) for d in localeDir.glob("*/")] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated -moByLang = {} +moByLang: dict[FS.Dir, FS.File] = {} for dir in langDirs: poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) moFile = env.gettextMoFile(poFile) From 0151f4f29340ceefbb02f208bba6c20641f99b1f Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 14:35:39 +0000 Subject: [PATCH 032/119] buildVars: Added type hints for _ function --- buildVars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildVars.py b/buildVars.py index 58844ad..294f1fc 100644 --- a/buildVars.py +++ b/buildVars.py @@ -9,7 +9,7 @@ # Gettext recognizes only strings given as parameters to the `_` function. # To avoid initializing translations in this module we simply roll our own "fake" `_` function # which returns whatever is given to it as an argument. -def _(arg): +def _(arg: str) -> str: return arg From 917d540fe8f09a4052fab5724361cb7cacbe7b75 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 14:55:07 +0000 Subject: [PATCH 033/119] buildVars: Added type hints to variables Vars: - pythonSources - i18nSources - excludedFiles - baseLanguage - markdownExtensions --- buildVars.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/buildVars.py b/buildVars.py index 294f1fc..2d90f5f 100644 --- a/buildVars.py +++ b/buildVars.py @@ -1,5 +1,3 @@ -# -*- coding: UTF-8 -*- - # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. @@ -57,29 +55,29 @@ def _(arg: str) -> str: # pythonSources = ["addon/globalPlugins/*.py"] # For more information on SCons Glob expressions please take a look at: # https://scons.org/doc/production/HTML/scons-user/apd.html -pythonSources = [] +pythonSources: list[str] = [] # Files that contain strings for translation. Usually your python sources -i18nSources = pythonSources + ["buildVars.py"] +i18nSources: list[str] = pythonSources + ["buildVars.py"] # Files that will be ignored when building the nvda-addon file # Paths are relative to the addon directory, not to the root directory of your addon sources. # You can either list every file (using ""/") as a path separator, # or use glob expressions. -excludedFiles = [] +excludedFiles: list[str] = [] # Base language for the NVDA add-on # If your add-on is written in a language other than english, modify this variable. # For example, set baseLanguage to "es" if your add-on is primarily written in spanish. # You must also edit .gitignore file to specify base language files to be ignored. -baseLanguage = "en" +baseLanguage: str = "en" # Markdown extensions for add-on documentation # Most add-ons do not require additional Markdown extensions. # If you need to add support for markup such as tables, fill out the below list. # Extensions string must be of the form "markdown.extensions.extensionName" # e.g. "markdown.extensions.tables" to add tables. -markdownExtensions = [] +markdownExtensions: list[str] = [] # Custom braille translation tables # If your add-on includes custom braille tables (most will not), fill out this dictionary. From 30ae096e6df51b997548f5ece8869f12900c69e4 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 15:09:09 +0000 Subject: [PATCH 034/119] buildVars: Added AddonInfo typed dict --- buildVars.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/buildVars.py b/buildVars.py index 2d90f5f..3db2cc2 100644 --- a/buildVars.py +++ b/buildVars.py @@ -1,6 +1,7 @@ # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. +from typing import TypedDict # Since some strings in `addon_info` are translatable, # we need to include them in the .po files. @@ -11,8 +12,24 @@ def _(arg: str) -> str: return arg +# A typed dictionary for storing meta information about add-on. +class AddonInfo(TypedDict): + addon_name: str + addon_summary: str + addon_description: str + addon_version: str + addon_author: str + addon_url: str|None + addon_sourceURL: str|None + addon_docFileName: str + addon_minimumNVDAVersion: str|None + addon_lastTestedNVDAVersion: str|None + addon_updateChannel: str|None + addon_license: str|None + addon_licenseURL: str|None + # Add-on information variables -addon_info = { +addon_info: AddonInfo = { # add-on Name/identifier, internal for NVDA "addon_name": "addonTemplate", # Add-on summary/title, usually the user visible name of the add-on From 2577e43f0cae39cfa0da3e174efdb92f2a4db12c Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 27 Jul 2025 16:04:38 +0000 Subject: [PATCH 035/119] SConstruct: Fixed build in Linux environment --- sconstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index 2d040eb..6bda520 100644 --- a/sconstruct +++ b/sconstruct @@ -324,7 +324,7 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(addonDir/"manifest.ini", "manifest.ini.tpl") +manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl") # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") From 0f434d43369d74f60496c77c7ad3e0057265e2cd Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 09:10:42 +0000 Subject: [PATCH 036/119] SConstruct: Extend commit bbe7b16 Added Builder & Copy to list of imports from SCons.Script --- sconstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index 6bda520..80be12a 100644 --- a/sconstruct +++ b/sconstruct @@ -17,7 +17,7 @@ from typing import Final # Linters aren't aware about them. # To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. # When using other Scons functions please add them to the line below. -from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment +from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment, Builder, Copy from SCons.Node import FS # Add-on localization exchange facility and the template requires Python 3.10. From 97ed6d16978d08a2857807de7a33decb2e0ce076 Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 10:17:53 +0000 Subject: [PATCH 037/119] SConstruct: Improved type hints Input arguments & return value type hints for functions: - addonGenerator - manifestGenerator - translatedManifestGenerator - generateManifest - generateTranslatedManifest --- sconstruct | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sconstruct b/sconstruct index 80be12a..e1de63d 100644 --- a/sconstruct +++ b/sconstruct @@ -18,7 +18,10 @@ from typing import Final # To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. # When using other Scons functions please add them to the line below. from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment, Builder, Copy + +# Imports for type hints from SCons.Node import FS +from SCons.Action import CommandAction # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -85,7 +88,7 @@ def md2html(source: str|Path, dest: str): f.write(docText) -def mdTool(env): +def mdTool(env: Environment): mdAction = env.Action( lambda target, source, env: md2html(source[0].path, target[0].path), lambda target, source, env: f"Generating {target[0]}", @@ -136,7 +139,7 @@ buildVars.addon_info["addon_version"] = env["addon_version"] buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] -def addonGenerator(target, source, env, for_signature): +def addonGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: action = env.Action( lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, lambda target, source, env: f"Generating Addon {target[0]}", @@ -144,7 +147,7 @@ def addonGenerator(target, source, env, for_signature): return action -def manifestGenerator(target, source, env, for_signature): +def manifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: action = env.Action( lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, lambda target, source, env: f"Generating manifest {target[0]}", @@ -152,7 +155,7 @@ def manifestGenerator(target, source, env, for_signature): return action -def translatedManifestGenerator(target, source, env, for_signature): +def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: dir = Path(str(source[0])).absolute().parents[1] lang = dir.name action = env.Action( @@ -204,7 +207,7 @@ def createAddonBundleFromPath(path: str|Path, dest: str): return dest -def generateManifest(source, dest): +def generateManifest(source: str, dest: str): # Prepare the root manifest section addon_info = buildVars.addon_info with codecs.open(source, "r", "utf-8") as f: @@ -233,7 +236,7 @@ def generateManifest(source, dest): f.write(manifest) -def generateTranslatedManifest(source, language, out): +def generateTranslatedManifest(source: str, language: str, out: str): _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext vars = {} for var in ("addon_summary", "addon_description"): From 28459b8792c702e14e6d87f586e1b1d973ace719 Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 10:33:25 +0000 Subject: [PATCH 038/119] SConstruct: Updated expandGlobs function Replaced FS.File to FS.Entry Added rootdir not required argument Renamded f to e --- sconstruct | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sconstruct b/sconstruct index e1de63d..ae77839 100644 --- a/sconstruct +++ b/sconstruct @@ -269,9 +269,8 @@ def generateTranslatedManifest(source: str, language: str, out: str): f.write(result) -def expandGlobs(patterns: Iterable[str]) -> list[FS.File]: - base = Path(".") - return [env.File(f) for pattern in patterns for f in base.glob(pattern.lstrip('/'))] +def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.Entry]: + return [env.Entry(e) for pattern in patterns for e in rootdir.glob(pattern.lstrip('/'))] addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") From e9efd87ed467a7676282e85713f74ec862b0540b Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 12:52:50 +0000 Subject: [PATCH 039/119] SConstruct: Updated mdTool All dependencies are now accepted as arguments, instead of global variables (localeDir) or modules (buildVars). --- sconstruct | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/sconstruct b/sconstruct index ae77839..0bdd047 100644 --- a/sconstruct +++ b/sconstruct @@ -38,28 +38,34 @@ localeDir: Final = addonDir / "locale" docsDir: Final = addonDir / "doc" -def md2html(source: str|Path, dest: str): +def md2html( + source: str|Path, + dest: str, + *, + localeDir: Path, + loacleFileName: str, + mdExtensions: list[str], + addonSummary: str, + addonVersion: str + ): if isinstance(source, str): source = Path(source) import markdown # Use extensions if defined. - mdExtensions = buildVars.markdownExtensions localeLang = source.parent.name lang = localeLang.replace("_", "-") try: _ = gettext.translation( - "nvda", + loacleFileName, localedir=localeDir, languages=[localeLang] ).gettext - summary = _(buildVars.addon_info["addon_summary"]) + summary = _(addonSummary) except Exception: - summary = buildVars.addon_info["addon_summary"] - title = "{addonSummary} {addonVersion}".format( - addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] - ) + summary = addonSummary + title = f"{summary} {addonVersion}" headerDic = { '[[!meta title="': "# ", '"]]': " #", @@ -89,8 +95,20 @@ def md2html(source: str|Path, dest: str): def mdTool(env: Environment): + env.SetDefault(localeDir = Path("locale/")) + env.SetDefault(localeFileName = "messages") + env.SetDefault(mdExtensions = {}) + mdAction = env.Action( - lambda target, source, env: md2html(source[0].path, target[0].path), + lambda target, source, env: md2html( + source[0].path, + target[0].path, + localeDir=env["localeDir"], + loacleFileName=env["localeFileName"], + mdExtensions=env["mdExtensions"], + addonSummary=env["addon_summary"], + addonVersion=env["addon_version"] + ), lambda target, source, env: f"Generating {target[0]}", ) mdBuilder = env.Builder( @@ -304,7 +322,7 @@ for mdPath in docsDir.glob("*/*.md"): lang = mdPath.absolute().parent.name mdFile = env.File(mdPath) moFile = moByLang.get(lang) - htmlFile = env.markdown(mdFile) + htmlFile = env.markdown(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) if moFile: env.Depends(htmlFile, moFile) From ec449448d94bbe3849fdf1258b1cc805e75c4b58 Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 14:35:51 +0000 Subject: [PATCH 040/119] SConstruct, mdTool: Moved mdTool & md2html to site_tools --- sconstruct | 83 +---------------- site_scons/site_tools/mdTool/__init__.py | 108 +++++++++++++++++++++++ 2 files changed, 109 insertions(+), 82 deletions(-) create mode 100644 site_scons/site_tools/mdTool/__init__.py diff --git a/sconstruct b/sconstruct index 0bdd047..735936e 100644 --- a/sconstruct +++ b/sconstruct @@ -38,87 +38,6 @@ localeDir: Final = addonDir / "locale" docsDir: Final = addonDir / "doc" -def md2html( - source: str|Path, - dest: str, - *, - localeDir: Path, - loacleFileName: str, - mdExtensions: list[str], - addonSummary: str, - addonVersion: str - ): - if isinstance(source, str): - source = Path(source) - - import markdown - - # Use extensions if defined. - localeLang = source.parent.name - lang = localeLang.replace("_", "-") - try: - _ = gettext.translation( - loacleFileName, - localedir=localeDir, - languages=[localeLang] - ).gettext - summary = _(addonSummary) - except Exception: - summary = addonSummary - title = f"{summary} {addonVersion}" - headerDic = { - '[[!meta title="': "# ", - '"]]': " #", - } - with codecs.open(str(source), "r", "utf-8") as f: - mdText = f.read() - for k, v in headerDic.items(): - mdText = mdText.replace(k, v, 1) - htmlText = markdown.markdown(mdText, extensions=mdExtensions) - # Optimization: build resulting HTML text in one go instead of writing parts separately. - docText = "\n".join( - ( - "", - f'', - "", - '', - '', - '', - f"{title}", - "\n", - htmlText, - "\n", - ) - ) - with codecs.open(dest, "w", "utf-8") as f: - f.write(docText) - - -def mdTool(env: Environment): - env.SetDefault(localeDir = Path("locale/")) - env.SetDefault(localeFileName = "messages") - env.SetDefault(mdExtensions = {}) - - mdAction = env.Action( - lambda target, source, env: md2html( - source[0].path, - target[0].path, - localeDir=env["localeDir"], - loacleFileName=env["localeFileName"], - mdExtensions=env["mdExtensions"], - addonSummary=env["addon_summary"], - addonVersion=env["addon_version"] - ), - lambda target, source, env: f"Generating {target[0]}", - ) - mdBuilder = env.Builder( - action=mdAction, - suffix=".html", - src_suffix=".md", - ) - env["BUILDERS"]["markdown"] = mdBuilder - - def validateVersionNumber(key: str, val: str, _): # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. # Ignore all this if version number is not specified. @@ -137,7 +56,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", mdTool]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool"]) env.Append(**buildVars.addon_info) if env["dev"]: diff --git a/site_scons/site_tools/mdTool/__init__.py b/site_scons/site_tools/mdTool/__init__.py new file mode 100644 index 0000000..8128189 --- /dev/null +++ b/site_scons/site_tools/mdTool/__init__.py @@ -0,0 +1,108 @@ +""" +This tool allows you to generate HTML files from Markdown. + +Adds the following Builder to the constructed environment: + +- markdown: Build HTML from Markdown + +The following variables are required in the environment: + +- localeDir +- localeFileName +- mdExtensions +- addon_summary +- addon_version + + +""" + +import gettext +import codecs +from pathlib import Path + +import markdown +from SCons.Script import Environment + + + +def md2html( + source: str|Path, + dest: str, + *, + localeDir: Path, + loacleFileName: str, + mdExtensions: list[str], + addonSummary: str, + addonVersion: str + ): + if isinstance(source, str): + source = Path(source) + + # Use extensions if defined. + localeLang = source.parent.name + lang = localeLang.replace("_", "-") + try: + _ = gettext.translation( + loacleFileName, + localedir=localeDir, + languages=[localeLang] + ).gettext + summary = _(addonSummary) + except Exception: + summary = addonSummary + title = f"{summary} {addonVersion}" + headerDic = { + '[[!meta title="': "# ", + '"]]': " #", + } + with codecs.open(str(source), "r", "utf-8") as f: + mdText = f.read() + for k, v in headerDic.items(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText, extensions=mdExtensions) + # Optimization: build resulting HTML text in one go instead of writing parts separately. + docText = "\n".join( + ( + "", + f'', + "", + '', + '', + '', + f"{title}", + "\n", + htmlText, + "\n", + ) + ) + with codecs.open(dest, "w", "utf-8") as f: + f.write(docText) + + +def generate(env: Environment): + env.SetDefault(localeDir = Path("locale/")) + env.SetDefault(localeFileName = "messages") + env.SetDefault(mdExtensions = {}) + + mdAction = env.Action( + lambda target, source, env: md2html( + source[0].path, + target[0].path, + localeDir=env["localeDir"], + loacleFileName=env["localeFileName"], + mdExtensions=env["mdExtensions"], + addonSummary=env["addon_summary"], + addonVersion=env["addon_version"] + ), + lambda target, source, env: f"Generating {target[0]}", + ) + mdBuilder = env.Builder( + action=mdAction, + suffix=".html", + src_suffix=".md", + ) + env["BUILDERS"]["markdown"] = mdBuilder + + +def exists(): + return True From 53363bf985efcabf4cec23c44a9484b18814371b Mon Sep 17 00:00:00 2001 From: balaraz Date: Mon, 28 Jul 2025 14:48:35 +0000 Subject: [PATCH 041/119] mdTool: Fixed mdAction lambda funcs Fixed the return value of the first lambda function so that it is negative and the second function is called, which returns the string that is output to the terminal. --- site_scons/site_tools/mdTool/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site_scons/site_tools/mdTool/__init__.py b/site_scons/site_tools/mdTool/__init__.py index 8128189..05fac06 100644 --- a/site_scons/site_tools/mdTool/__init__.py +++ b/site_scons/site_tools/mdTool/__init__.py @@ -93,7 +93,7 @@ def generate(env: Environment): mdExtensions=env["mdExtensions"], addonSummary=env["addon_summary"], addonVersion=env["addon_version"] - ), + ) and None, lambda target, source, env: f"Generating {target[0]}", ) mdBuilder = env.Builder( From a34ce1faad0f4510a3f78b83a4492ea176279498 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 06:29:19 +0000 Subject: [PATCH 042/119] SConstruct: Returned env.Glob instead of Path.glob When iterating over localization directories and documentation files, env.Glob is now used again to fix the generation of the base language HTML file --- sconstruct | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index 735936e..d7d9bff 100644 --- a/sconstruct +++ b/sconstruct @@ -213,7 +213,7 @@ def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.E addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) -langDirs: list[FS.Dir] = [env.Dir(d) for d in localeDir.glob("*/")] +langDirs: list[FS.Dir] = [d for d in env.Glob(localeDir/"*/")] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated moByLang: dict[FS.Dir, FS.File] = {} @@ -235,11 +235,10 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager createAddonHelp(docsDir) -for mdPath in docsDir.glob("*/*.md"): +for mdFile in env.Glob(docsDir/"*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. - lang = mdPath.absolute().parent.name - mdFile = env.File(mdPath) + lang = Path(str(mdFile)).absolute().parent.name moFile = moByLang.get(lang) htmlFile = env.markdown(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) From 20ff630cda350d2f86e814c74f98316a793c301b Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 09:45:11 +0000 Subject: [PATCH 043/119] buildVars, SConstruct: Added type hints to non typed vars buildVars: - brailleTables - symbolDictionaries SConstruct: - gettextvars - generateTranslatedManifest:vars diff --git a/buildVars.py b/buildVars.py index 3db2cc2..4eaaef1 100644 --- a/buildVars.py +++ b/buildVars.py @@ -104,7 +104,7 @@ markdownExtensions: list[str] = [] # contracted (contracted (True) or uncontracted (False) braille code), # output (shown in output table list), # input (shown in input table list). -brailleTables = {} +brailleTables: dict[str, dict[str, str]] = {} # Custom speech symbol dictionaries # Symbol dictionary files reside in the locale folder, e.g. `locale\en`, and are named `symbols-.dic`. @@ -113,4 +113,4 @@ brailleTables = {} # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. -symbolDictionaries = {} +symbolDictionaries: dict[str, dict[str, str]] = {} diff --git a/buildVars.py b/buildVars.py index 3db2cc2..4eaaef1 100644 --- a/buildVars.py +++ b/buildVars.py @@ -104,7 +104,7 @@ markdownExtensions: list[str] = [] # contracted (contracted (True) or uncontracted (False) braille code), # output (shown in output table list), # input (shown in input table list). -brailleTables = {} +brailleTables: dict[str, dict[str, str]] = {} # Custom speech symbol dictionaries # Symbol dictionary files reside in the locale folder, e.g. `locale\en`, and are named `symbols-.dic`. @@ -113,4 +113,4 @@ brailleTables = {} # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. -symbolDictionaries = {} +symbolDictionaries: dict[str, dict[str, str]] = {} --- buildVars.py | 4 ++-- sconstruct | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/buildVars.py b/buildVars.py index 3db2cc2..4eaaef1 100644 --- a/buildVars.py +++ b/buildVars.py @@ -104,7 +104,7 @@ class AddonInfo(TypedDict): # contracted (contracted (True) or uncontracted (False) braille code), # output (shown in output table list), # input (shown in input table list). -brailleTables = {} +brailleTables: dict[str, dict[str, str]] = {} # Custom speech symbol dictionaries # Symbol dictionary files reside in the locale folder, e.g. `locale\en`, and are named `symbols-.dic`. @@ -113,4 +113,4 @@ class AddonInfo(TypedDict): # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. -symbolDictionaries = {} +symbolDictionaries: dict[str, dict[str, str]] = {} diff --git a/sconstruct b/sconstruct index d7d9bff..bd4d961 100644 --- a/sconstruct +++ b/sconstruct @@ -175,7 +175,7 @@ def generateManifest(source: str, dest: str): def generateTranslatedManifest(source: str, language: str, out: str): _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext - vars = {} + vars: dict[str, str] = {} for var in ("addon_summary", "addon_description"): vars[var] = _(buildVars.addon_info[var]) with codecs.open(source, "r", "utf-8") as f: @@ -248,7 +248,7 @@ for mdFile in env.Glob(docsDir/"*/*.md"): # Pot target i18nFiles = expandGlobs(buildVars.i18nSources) -gettextvars = { +gettextvars: dict[str, str] = { "gettext_package_bugs_address": "nvda-translations@groups.io", "gettext_package_name": buildVars.addon_info["addon_name"], "gettext_package_version": buildVars.addon_info["addon_version"], From 0071a85ad54d2b2f97ce202735f1c8123bdc0b27 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 14:02:53 +0000 Subject: [PATCH 044/119] SConstruct:Refactored if getattr calls Replaced getattr to hasattr in: - generateManifest - generateTranslatedManifest --- sconstruct | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index bd4d961..051925f 100644 --- a/sconstruct +++ b/sconstruct @@ -152,7 +152,7 @@ def generateManifest(source: str, dest: str): manifest = manifest_template.format(**addon_info) # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if getattr(buildVars, "brailleTables", {}): + if hasattr(buildVars, "brailleTables"): manifest_brailleTables = ["\n[brailleTables]"] for table in buildVars.brailleTables.keys(): manifest_brailleTables.append(f"[[{table}]]") @@ -161,7 +161,7 @@ def generateManifest(source: str, dest: str): manifest += "\n".join(manifest_brailleTables) + "\n" # Custom speech symbol dictionaries - if getattr(buildVars, "symbolDictionaries", {}): + if hasattr(buildVars, "symbolDictionaries"): manifest_symbolDictionaries = ["\n[symbolDictionaries]"] for dictionary in buildVars.symbolDictionaries.keys(): manifest_symbolDictionaries.append(f"[[{dictionary}]]") @@ -183,7 +183,7 @@ def generateTranslatedManifest(source: str, language: str, out: str): result = manifest_template.format(**vars) # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if getattr(buildVars, "brailleTables", {}): + if hasattr(buildVars, "brailleTables"): result_brailleTables = ["\n[brailleTables]"] for table in buildVars.brailleTables.keys(): result_brailleTables.append(f"[[{table}]]") @@ -192,7 +192,7 @@ def generateTranslatedManifest(source: str, language: str, out: str): result += "\n".join(result_brailleTables) + "\n" # Custom speech symbol dictionaries - if getattr(buildVars, "symbolDictionaries", {}): + if hasattr(buildVars, "symbolDictionaries"): result_symbolDictionaries = ["\n[symbolDictionaries]"] for dictionary in buildVars.symbolDictionaries.keys(): result_symbolDictionaries.append(f"[[{dictionary}]]") From 76b7120bcc1df53462ddf9bcbf76a1264e0b1cc5 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 14:20:26 +0000 Subject: [PATCH 045/119] SConstruct: Added format_nested_section func --- sconstruct | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sconstruct b/sconstruct index 051925f..078d792 100644 --- a/sconstruct +++ b/sconstruct @@ -11,7 +11,7 @@ import zipfile import sys from pathlib import Path from collections.abc import Iterable -from typing import Final +from typing import Final, Mapping # While names imported below are available by default in every SConscript # Linters aren't aware about them. @@ -144,6 +144,18 @@ def createAddonBundleFromPath(path: str|Path, dest: str): return dest +def format_nested_section( + section_name: str, + data: Mapping[str, Mapping[str, str]] +) -> str: + lines = [f"\n[{section_name}]"] + for item_name, inner_dict in data.items(): + lines.append(f"[[{item_name}]]") + for key, val in inner_dict.items(): + lines.append(f"{key} = {val}") + return "\n".join(lines) + "\n" + + def generateManifest(source: str, dest: str): # Prepare the root manifest section addon_info = buildVars.addon_info From 7bbf1af7f14f112f6f72604e66db60503977808c Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 14:39:04 +0000 Subject: [PATCH 046/119] SConstruct: Update generateManifest Use format_nested_section in both `if hasattr` blocks --- sconstruct | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/sconstruct b/sconstruct index 078d792..6a366c5 100644 --- a/sconstruct +++ b/sconstruct @@ -165,21 +165,11 @@ def generateManifest(source: str, dest: str): # Add additional manifest sections such as custom braile tables # Custom braille translation tables if hasattr(buildVars, "brailleTables"): - manifest_brailleTables = ["\n[brailleTables]"] - for table in buildVars.brailleTables.keys(): - manifest_brailleTables.append(f"[[{table}]]") - for key, val in buildVars.brailleTables[table].items(): - manifest_brailleTables.append(f"{key} = {val}") - manifest += "\n".join(manifest_brailleTables) + "\n" + manifest += format_nested_section("brailleTables", buildVars.brailleTables) # Custom speech symbol dictionaries if hasattr(buildVars, "symbolDictionaries"): - manifest_symbolDictionaries = ["\n[symbolDictionaries]"] - for dictionary in buildVars.symbolDictionaries.keys(): - manifest_symbolDictionaries.append(f"[[{dictionary}]]") - for key, val in buildVars.symbolDictionaries[dictionary].items(): - manifest_symbolDictionaries.append(f"{key} = {val}") - manifest += "\n".join(manifest_symbolDictionaries) + "\n" + manifest += format_nested_section("symbolDictionaries", buildVars.symbolDictionaries) with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) From 21b273316b5e7bf77a47296926560eaaeedec5d4 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 17:13:38 +0000 Subject: [PATCH 047/119] SConstruct: Refactored generateTranslatedManifest - Renamed input argument `out` to `dest` - Renamed local var `result` to `manifest` - Created inner _format_section_only_with_displayName function - `if hasattr` blocks has been changed to use the above-mentioned function diff --git a/sconstruct b/sconstruct index 6a366c5..d57bd5b 100644 --- a/sconstruct +++ b/sconstruct @@ -175,37 +175,34 @@ def generateManifest(source: str, dest: str): f.write(manifest) -def generateTranslatedManifest(source: str, language: str, out: str): +def generateTranslatedManifest(source: str, language: str, dest: str): _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext vars: dict[str, str] = {} for var in ("addon_summary", "addon_description"): vars[var] = _(buildVars.addon_info[var]) with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() - result = manifest_template.format(**vars) + manifest = manifest_template.format(**vars) + + def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, str]]) -> str: + lines = [f"\n[{section_name}]"] + for item, inner_dict in data.items(): + lines.append(f"[[{item}]]") + # Fetch display name only. + lines.append(f"displayName = {_(inner_dict['displayName'])}") + return "\n".join(lines) + "\n" + # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if hasattr(buildVars, "brailleTables"): - result_brailleTables = ["\n[brailleTables]"] - for table in buildVars.brailleTables.keys(): - result_brailleTables.append(f"[[{table}]]") - # Fetch display name only. - result_brailleTables.append(f"displayName = {_(buildVars.brailleTables[table]['displayName'])}") - result += "\n".join(result_brailleTables) + "\n" + if tables := getattr(buildVars, "brailleTables", None): + manifest += _format_section_only_with_displayName("brailleTables", tables) # Custom speech symbol dictionaries - if hasattr(buildVars, "symbolDictionaries"): - result_symbolDictionaries = ["\n[symbolDictionaries]"] - for dictionary in buildVars.symbolDictionaries.keys(): - result_symbolDictionaries.append(f"[[{dictionary}]]") - # Fetch display name only. - result_symbolDictionaries.append( - f"displayName = {_(buildVars.symbolDictionaries[dictionary]['displayName'])}" - ) - result += "\n".join(result_symbolDictionaries) + "\n" + if dicts := getattr(buildVars, "symbolDictionaries", None): + manifest += _format_section_only_with_displayName("symbolDictionaries", dicts) - with codecs.open(out, "w", "utf-8") as f: - f.write(result) + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.Entry]: --- sconstruct | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/sconstruct b/sconstruct index 6a366c5..d57bd5b 100644 --- a/sconstruct +++ b/sconstruct @@ -175,37 +175,34 @@ def generateManifest(source: str, dest: str): f.write(manifest) -def generateTranslatedManifest(source: str, language: str, out: str): +def generateTranslatedManifest(source: str, language: str, dest: str): _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext vars: dict[str, str] = {} for var in ("addon_summary", "addon_description"): vars[var] = _(buildVars.addon_info[var]) with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() - result = manifest_template.format(**vars) + manifest = manifest_template.format(**vars) + + def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, str]]) -> str: + lines = [f"\n[{section_name}]"] + for item, inner_dict in data.items(): + lines.append(f"[[{item}]]") + # Fetch display name only. + lines.append(f"displayName = {_(inner_dict['displayName'])}") + return "\n".join(lines) + "\n" + # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if hasattr(buildVars, "brailleTables"): - result_brailleTables = ["\n[brailleTables]"] - for table in buildVars.brailleTables.keys(): - result_brailleTables.append(f"[[{table}]]") - # Fetch display name only. - result_brailleTables.append(f"displayName = {_(buildVars.brailleTables[table]['displayName'])}") - result += "\n".join(result_brailleTables) + "\n" + if tables := getattr(buildVars, "brailleTables", None): + manifest += _format_section_only_with_displayName("brailleTables", tables) # Custom speech symbol dictionaries - if hasattr(buildVars, "symbolDictionaries"): - result_symbolDictionaries = ["\n[symbolDictionaries]"] - for dictionary in buildVars.symbolDictionaries.keys(): - result_symbolDictionaries.append(f"[[{dictionary}]]") - # Fetch display name only. - result_symbolDictionaries.append( - f"displayName = {_(buildVars.symbolDictionaries[dictionary]['displayName'])}" - ) - result += "\n".join(result_symbolDictionaries) + "\n" + if dicts := getattr(buildVars, "symbolDictionaries", None): + manifest += _format_section_only_with_displayName("symbolDictionaries", dicts) - with codecs.open(out, "w", "utf-8") as f: - f.write(result) + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.Entry]: From a25f882440d3b9b2cf0887e54efddee9d6135a40 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 17:30:48 +0000 Subject: [PATCH 048/119] mdTool, SConstruct: Renamed markdown builder to md2html diff --git a/sconstruct b/sconstruct index d57bd5b..29e584c 100644 --- a/sconstruct +++ b/sconstruct @@ -239,7 +239,7 @@ for mdFile in env.Glob(docsDir/"*/*.md"): # Thus, we find the moFile for this language and depend on it if it exists. lang = Path(str(mdFile)).absolute().parent.name moFile = moByLang.get(lang) - htmlFile = env.markdown(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) + htmlFile = env.md2html(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) if moFile: env.Depends(htmlFile, moFile) diff --git a/site_scons/site_tools/mdTool/__init__.py b/site_scons/site_tools/mdTool/__init__.py index 05fac06..5547237 100644 --- a/site_scons/site_tools/mdTool/__init__.py +++ b/site_scons/site_tools/mdTool/__init__.py @@ -3,7 +3,7 @@ This tool allows you to generate HTML files from Markdown. Adds the following Builder to the constructed environment: -- markdown: Build HTML from Markdown +- md2html: Build HTML from Markdown The following variables are required in the environment: @@ -101,7 +101,7 @@ def generate(env: Environment): suffix=".html", src_suffix=".md", ) - env["BUILDERS"]["markdown"] = mdBuilder + env["BUILDERS"]["md2html"] = mdBuilder def exists(): --- sconstruct | 2 +- site_scons/site_tools/mdTool/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sconstruct b/sconstruct index d57bd5b..29e584c 100644 --- a/sconstruct +++ b/sconstruct @@ -239,7 +239,7 @@ for mdFile in env.Glob(docsDir/"*/*.md"): # Thus, we find the moFile for this language and depend on it if it exists. lang = Path(str(mdFile)).absolute().parent.name moFile = moByLang.get(lang) - htmlFile = env.markdown(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) + htmlFile = env.md2html(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) if moFile: env.Depends(htmlFile, moFile) diff --git a/site_scons/site_tools/mdTool/__init__.py b/site_scons/site_tools/mdTool/__init__.py index 05fac06..5547237 100644 --- a/site_scons/site_tools/mdTool/__init__.py +++ b/site_scons/site_tools/mdTool/__init__.py @@ -3,7 +3,7 @@ Adds the following Builder to the constructed environment: -- markdown: Build HTML from Markdown +- md2html: Build HTML from Markdown The following variables are required in the environment: @@ -101,7 +101,7 @@ def generate(env: Environment): suffix=".html", src_suffix=".md", ) - env["BUILDERS"]["markdown"] = mdBuilder + env["BUILDERS"]["md2html"] = mdBuilder def exists(): From 9526341f5fd10437feadc96f0cd8897724a2bf23 Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 29 Jul 2025 17:45:26 +0000 Subject: [PATCH 049/119] SConstruct: Removed createAddonHelp func The script has been simplified because the function used the global variable addon, which is declared after the function itself. This made it difficult to understand, and it was called only once. diff --git a/sconstruct b/sconstruct index 29e584c..bc6ff74 100644 --- a/sconstruct +++ b/sconstruct @@ -108,22 +108,6 @@ env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) -def createAddonHelp(docsDir: str|Path): - if isinstance(docsDir, str): - docsDir = Path(docsDir) - - cssFile = Path("style.css") - if cssFile.is_file(): - cssPath = docsDir / cssFile - cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) - env.Depends(addon, cssTarget) - readmeFile = Path("readme.md") - if readmeFile.is_file(): - readmePath = docsDir / buildVars.baseLanguage / readmeFile - readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) - env.Depends(addon, readmeTarget) - - def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" return not any((path.match(pattern) for pattern in patterns)) @@ -233,7 +217,16 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager -createAddonHelp(docsDir) +if (cssFile := Path("style.css")).is_file(): + cssPath = docsDir / cssFile + cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, cssTarget) + +if (readmeFile := Path("readme.md")).is_file(): + readmePath = docsDir / buildVars.baseLanguage / readmeFile + readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, readmeTarget) + for mdFile in env.Glob(docsDir/"*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. --- sconstruct | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/sconstruct b/sconstruct index 29e584c..bc6ff74 100644 --- a/sconstruct +++ b/sconstruct @@ -108,22 +108,6 @@ env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) -def createAddonHelp(docsDir: str|Path): - if isinstance(docsDir, str): - docsDir = Path(docsDir) - - cssFile = Path("style.css") - if cssFile.is_file(): - cssPath = docsDir / cssFile - cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) - env.Depends(addon, cssTarget) - readmeFile = Path("readme.md") - if readmeFile.is_file(): - readmePath = docsDir / buildVars.baseLanguage / readmeFile - readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) - env.Depends(addon, readmeTarget) - - def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" return not any((path.match(pattern) for pattern in patterns)) @@ -233,7 +217,16 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager -createAddonHelp(docsDir) +if (cssFile := Path("style.css")).is_file(): + cssPath = docsDir / cssFile + cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, cssTarget) + +if (readmeFile := Path("readme.md")).is_file(): + readmePath = docsDir / buildVars.baseLanguage / readmeFile + readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, readmeTarget) + for mdFile in env.Glob(docsDir/"*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. From a2068626cd15e2fc0cef526dfed51e4cf867e184 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 05:19:17 +0000 Subject: [PATCH 050/119] SConstruct: Refactored script Moved global variables after defining all functions. - Dirs (addonDir, localeDir, docsDir) - vars, env --- sconstruct | 72 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/sconstruct b/sconstruct index bc6ff74..cda3be5 100644 --- a/sconstruct +++ b/sconstruct @@ -33,11 +33,6 @@ sys.dont_write_bytecode = True import buildVars # NOQA: E402 -addonDir: Final = Path("addon/") -localeDir: Final = addonDir / "locale" -docsDir: Final = addonDir / "doc" - - def validateVersionNumber(key: str, val: str, _): # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. # Ignore all this if version number is not specified. @@ -50,32 +45,6 @@ def validateVersionNumber(key: str, val: str, _): raise ValueError(f"{key} (major.minor.patch) must be integers") -vars = Variables() -vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) -vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0", validateVersionNumber) -vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) -vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) - -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool"]) -env.Append(**buildVars.addon_info) - -if env["dev"]: - from datetime import date - - versionTimestamp = date.today().strftime('%Y%m%d') - version = f"{versionTimestamp}.0.0" - env["addon_version"] = version - env["versionNumber"] = version - env["channel"] = "dev" -elif env["version"] is not None: - env["addon_version"] = env["version"] -if "channel" in env and env["channel"] is not None: - env["addon_updateChannel"] = env["channel"] - -buildVars.addon_info["addon_version"] = env["addon_version"] -buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] - - def addonGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: action = env.Action( lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, @@ -103,11 +72,6 @@ def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], return action -env["BUILDERS"]["NVDAAddon"] = Builder(generator=addonGenerator) -env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) -env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) - - def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" return not any((path.match(pattern) for pattern in patterns)) @@ -193,6 +157,42 @@ def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.E return [env.Entry(e) for pattern in patterns for e in rootdir.glob(pattern.lstrip('/'))] +addonDir: Final = Path("addon/") +localeDir: Final = addonDir / "locale" +docsDir: Final = addonDir / "doc" + + +vars = Variables() +vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) +vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0", validateVersionNumber) +vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) +vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) + +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool"]) +env.Append(**buildVars.addon_info) + +if env["dev"]: + from datetime import date + + versionTimestamp = date.today().strftime('%Y%m%d') + version = f"{versionTimestamp}.0.0" + env["addon_version"] = version + env["versionNumber"] = version + env["channel"] = "dev" +elif env["version"] is not None: + env["addon_version"] = env["version"] +if "channel" in env and env["channel"] is not None: + env["addon_updateChannel"] = env["channel"] + +buildVars.addon_info["addon_version"] = env["addon_version"] +buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] + + +env["BUILDERS"]["NVDAAddon"] = Builder(generator=addonGenerator) +env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) +env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) + + addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) From 16b0d3b95a19dd8de85e8fe65f343d25199bccf1 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 05:23:46 +0000 Subject: [PATCH 051/119] SConstruct: Update addonGenerator Converted addonGenerator to addonTool, since generators accept additional arguments that are not needed in this case. --- sconstruct | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index cda3be5..e5e427b 100644 --- a/sconstruct +++ b/sconstruct @@ -45,12 +45,16 @@ def validateVersionNumber(key: str, val: str, _): raise ValueError(f"{key} (major.minor.patch) must be integers") -def addonGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: +def addonTool(env: Environment): action = env.Action( lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, lambda target, source, env: f"Generating Addon {target[0]}", ) - return action + env["BUILDERS"]["NVDAAddon"] = Builder( + action=action, + suffix=".nvda-addon", + src_suffix="/" + ) def manifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: @@ -168,7 +172,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool"]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", addonTool]) env.Append(**buildVars.addon_info) if env["dev"]: @@ -188,7 +192,6 @@ buildVars.addon_info["addon_version"] = env["addon_version"] buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] -env["BUILDERS"]["NVDAAddon"] = Builder(generator=addonGenerator) env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) From 857867beb4ce138fbe6f526ee59c3d0ea74ceff6 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 06:09:11 +0000 Subject: [PATCH 052/119] SConstruct: Update manifestGenerator Converted manifestGenerator to manifestTool, since generators accept additional arguments that are not needed in this case. --- sconstruct | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index e5e427b..43484b6 100644 --- a/sconstruct +++ b/sconstruct @@ -57,12 +57,16 @@ def addonTool(env: Environment): ) -def manifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: +def manifestTool(env: Environment): action = env.Action( lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, lambda target, source, env: f"Generating manifest {target[0]}", ) - return action + env["BUILDERS"]["NVDAManifest"] = Builder( + action=action, + suffix=".ini", + src_siffix=".ini.tpl" + ) def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: @@ -172,7 +176,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", addonTool]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", addonTool, manifestTool]) env.Append(**buildVars.addon_info) if env["dev"]: @@ -192,7 +196,6 @@ buildVars.addon_info["addon_version"] = env["addon_version"] buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] -env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) From 6d85c81a45f09f72c31cf2663596213ce11177d7 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 06:43:16 +0000 Subject: [PATCH 053/119] SConstruct: Update addonTool The path patterns that need to be excluded from the .nvda-addon file are passed as arguments to the createAddonBundleFromPath function. --- sconstruct | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index 43484b6..8c79bc6 100644 --- a/sconstruct +++ b/sconstruct @@ -46,8 +46,11 @@ def validateVersionNumber(key: str, val: str, _): def addonTool(env: Environment): + env.SetDefault(excludePatterns=tuple()) action = env.Action( - lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, + lambda target, source, env: createAddonBundleFromPath( + source[0].abspath, target[0].abspath, env["excludePatterns"] + ) and None, lambda target, source, env: f"Generating Addon {target[0]}", ) env["BUILDERS"]["NVDAAddon"] = Builder( @@ -85,7 +88,7 @@ def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: return not any((path.match(pattern) for pattern in patterns)) -def createAddonBundleFromPath(path: str|Path, dest: str): +def createAddonBundleFromPath(path: str|Path, dest: str, excludePatterns: Iterable[str]): """Creates a bundle from a directory that contains an addon manifest file.""" if isinstance(path, str): path = Path(path) @@ -95,7 +98,7 @@ def createAddonBundleFromPath(path: str|Path, dest: str): if p.is_dir(): continue pathInBundle = p.relative_to(basedir) - if matchesNoPatterns(pathInBundle, buildVars.excludedFiles): + if matchesNoPatterns(pathInBundle, excludePatterns): z.write(p, pathInBundle) return dest @@ -200,7 +203,7 @@ env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifest addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") -addon = env.NVDAAddon(addonFile, env.Dir(addonDir)) +addon = env.NVDAAddon(addonFile, env.Dir(addonDir), excludePatterns=buildVars.excludedFiles) langDirs: list[FS.Dir] = [d for d in env.Glob(localeDir/"*/")] From 7fbfbd7adc877a70c063c462b82211f2635526a0 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 08:01:21 +0000 Subject: [PATCH 054/119] SConstruct, NVDATool: Created NVDATool The NVDAAddon builder has been moved to NVDATool. --- sconstruct | 38 +--------------------- site_scons/site_tools/NVDATool/__init__.py | 32 ++++++++++++++++++ site_scons/site_tools/NVDATool/addon.py | 24 ++++++++++++++ 3 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 site_scons/site_tools/NVDATool/__init__.py create mode 100644 site_scons/site_tools/NVDATool/addon.py diff --git a/sconstruct b/sconstruct index 8c79bc6..be48f35 100644 --- a/sconstruct +++ b/sconstruct @@ -7,7 +7,6 @@ import codecs import gettext import os import os.path -import zipfile import sys from pathlib import Path from collections.abc import Iterable @@ -45,21 +44,6 @@ def validateVersionNumber(key: str, val: str, _): raise ValueError(f"{key} (major.minor.patch) must be integers") -def addonTool(env: Environment): - env.SetDefault(excludePatterns=tuple()) - action = env.Action( - lambda target, source, env: createAddonBundleFromPath( - source[0].abspath, target[0].abspath, env["excludePatterns"] - ) and None, - lambda target, source, env: f"Generating Addon {target[0]}", - ) - env["BUILDERS"]["NVDAAddon"] = Builder( - action=action, - suffix=".nvda-addon", - src_suffix="/" - ) - - def manifestTool(env: Environment): action = env.Action( lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, @@ -83,26 +67,6 @@ def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], return action -def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: - """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" - return not any((path.match(pattern) for pattern in patterns)) - - -def createAddonBundleFromPath(path: str|Path, dest: str, excludePatterns: Iterable[str]): - """Creates a bundle from a directory that contains an addon manifest file.""" - if isinstance(path, str): - path = Path(path) - basedir = path.absolute() - with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: - for p in basedir.rglob("*"): - if p.is_dir(): - continue - pathInBundle = p.relative_to(basedir) - if matchesNoPatterns(pathInBundle, excludePatterns): - z.write(p, pathInBundle) - return dest - - def format_nested_section( section_name: str, data: Mapping[str, Mapping[str, str]] @@ -179,7 +143,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", addonTool, manifestTool]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool", manifestTool]) env.Append(**buildVars.addon_info) if env["dev"]: diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py new file mode 100644 index 0000000..1bd2d39 --- /dev/null +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -0,0 +1,32 @@ +""" +This tool generates NVDA extensions. + +Builders: + +- NVDAAddon: Creates a .nvda-addon zip file. Requires the `excludePatterns` environment variable. + +""" + +from SCons.Script import Environment, Builder + +from .addon import createAddonBundleFromPath + + + +def generate(env: Environment): + env.SetDefault(excludePatterns=tuple()) + + addonAction = env.Action( + lambda target, source, env: createAddonBundleFromPath( + source[0].abspath, target[0].abspath, env["excludePatterns"] + ) and None, + lambda target, source, env: f"Generating Addon {target[0]}", + ) + env["BUILDERS"]["NVDAAddon"] = Builder( + action=addonAction, + suffix=".nvda-addon", + src_suffix="/" + ) + +def exists(): + return True diff --git a/site_scons/site_tools/NVDATool/addon.py b/site_scons/site_tools/NVDATool/addon.py new file mode 100644 index 0000000..adc1424 --- /dev/null +++ b/site_scons/site_tools/NVDATool/addon.py @@ -0,0 +1,24 @@ +import zipfile +from collections.abc import Iterable +from pathlib import Path + + + +def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: + """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" + return not any((path.match(pattern) for pattern in patterns)) + + +def createAddonBundleFromPath(path: str|Path, dest: str, excludePatterns: Iterable[str]): + """Creates a bundle from a directory that contains an addon manifest file.""" + if isinstance(path, str): + path = Path(path) + basedir = path.absolute() + with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: + for p in basedir.rglob("*"): + if p.is_dir(): + continue + pathInBundle = p.relative_to(basedir) + if matchesNoPatterns(pathInBundle, excludePatterns): + z.write(p, pathInBundle) + return dest From fced6949fecc8b9e3e2aa36edbc98da342e70dd2 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 09:56:22 +0000 Subject: [PATCH 055/119] buildVars, NVDATool: Moved AddonInfo class to NVDATool --- buildVars.py | 18 +----------------- site_scons/site_tools/NVDATool/typings.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 site_scons/site_tools/NVDATool/typings.py diff --git a/buildVars.py b/buildVars.py index 4eaaef1..b50c5e1 100644 --- a/buildVars.py +++ b/buildVars.py @@ -1,7 +1,7 @@ # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. -from typing import TypedDict +from site_scons.site_tools.NVDATool.typings import AddonInfo # Since some strings in `addon_info` are translatable, # we need to include them in the .po files. @@ -12,22 +12,6 @@ def _(arg: str) -> str: return arg -# A typed dictionary for storing meta information about add-on. -class AddonInfo(TypedDict): - addon_name: str - addon_summary: str - addon_description: str - addon_version: str - addon_author: str - addon_url: str|None - addon_sourceURL: str|None - addon_docFileName: str - addon_minimumNVDAVersion: str|None - addon_lastTestedNVDAVersion: str|None - addon_updateChannel: str|None - addon_license: str|None - addon_licenseURL: str|None - # Add-on information variables addon_info: AddonInfo = { # add-on Name/identifier, internal for NVDA diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py new file mode 100644 index 0000000..3309cc8 --- /dev/null +++ b/site_scons/site_tools/NVDATool/typings.py @@ -0,0 +1,18 @@ +from typing import TypedDict + + + +class AddonInfo(TypedDict): + addon_name: str + addon_summary: str + addon_description: str + addon_version: str + addon_author: str + addon_url: str|None + addon_sourceURL: str|None + addon_docFileName: str + addon_minimumNVDAVersion: str|None + addon_lastTestedNVDAVersion: str|None + addon_updateChannel: str|None + addon_license: str|None + addon_licenseURL: str|None From 958a157af48e92f81eb805cea3f2e038d6acfe42 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 10:08:41 +0000 Subject: [PATCH 056/119] buildVars, NVDATool: Update brl tabs & sym dicts type hints Added to NVDATool.typings: - BrailleTableAttributes (TypedDict) - BrailleTables (dict) - SymbolDictionaryAttributes (TypedDict) - SymbolDictionaries (dict) Used above dicts types in buildVars --- buildVars.py | 6 +++--- site_scons/site_tools/NVDATool/typings.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/buildVars.py b/buildVars.py index b50c5e1..33401e0 100644 --- a/buildVars.py +++ b/buildVars.py @@ -1,7 +1,7 @@ # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. -from site_scons.site_tools.NVDATool.typings import AddonInfo +from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries # Since some strings in `addon_info` are translatable, # we need to include them in the .po files. @@ -88,7 +88,7 @@ def _(arg: str) -> str: # contracted (contracted (True) or uncontracted (False) braille code), # output (shown in output table list), # input (shown in input table list). -brailleTables: dict[str, dict[str, str]] = {} +brailleTables: BrailleTables = {} # Custom speech symbol dictionaries # Symbol dictionary files reside in the locale folder, e.g. `locale\en`, and are named `symbols-.dic`. @@ -97,4 +97,4 @@ def _(arg: str) -> str: # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. -symbolDictionaries: dict[str, dict[str, str]] = {} +symbolDictionaries: SymbolDictionaries = {} diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index 3309cc8..a76aa9c 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -16,3 +16,19 @@ class AddonInfo(TypedDict): addon_updateChannel: str|None addon_license: str|None addon_licenseURL: str|None + + +class BrailleTableAttributes(TypedDict): + displayName: str + contracted: bool + output: bool + input: bool + + +class SymbolDictionaryAttributes(TypedDict): + displayName: str + mandatory: bool + + +BrailleTables = dict[str, BrailleTableAttributes] +SymbolDictionaries = dict[str, SymbolDictionaryAttributes] From c769cac0458d0f42ff39227fe1e974f13cdbee6a Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 10:26:33 +0000 Subject: [PATCH 057/119] SConstruct: Fixed import Mapping typing.Mapping is deprecated --- sconstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index be48f35..11334e3 100644 --- a/sconstruct +++ b/sconstruct @@ -9,8 +9,8 @@ import os import os.path import sys from pathlib import Path -from collections.abc import Iterable -from typing import Final, Mapping +from collections.abc import Iterable, Mapping +from typing import Final # While names imported below are available by default in every SConscript # Linters aren't aware about them. From b5ed0ed87f76fafd7d1c118f65bac58552fb9982 Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 10:49:31 +0000 Subject: [PATCH 058/119] NVDATool: Added Strable protocol --- site_scons/site_tools/NVDATool/typings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index a76aa9c..c4d74ea 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -1,4 +1,4 @@ -from typing import TypedDict +from typing import TypedDict, Protocol, override @@ -32,3 +32,8 @@ class SymbolDictionaryAttributes(TypedDict): BrailleTables = dict[str, BrailleTableAttributes] SymbolDictionaries = dict[str, SymbolDictionaryAttributes] + + +class Strable(Protocol): + @override + def __str__(self) -> str: ... From ea7d99d3dabbdddc2d63294bfe8800313deb85dd Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 10:57:14 +0000 Subject: [PATCH 059/119] SConstruct: Updated functions signatures, used Strable - format_nested_section - generateTranslatedManifest._format_section_only_with_displayName --- sconstruct | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sconstruct b/sconstruct index 11334e3..05e33ae 100644 --- a/sconstruct +++ b/sconstruct @@ -30,6 +30,7 @@ EnsurePythonVersion(3, 10) sys.dont_write_bytecode = True import buildVars # NOQA: E402 +from site_scons.site_tools.NVDATool.typings import Strable # NOQA: E402 def validateVersionNumber(key: str, val: str, _): @@ -69,7 +70,7 @@ def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], def format_nested_section( section_name: str, - data: Mapping[str, Mapping[str, str]] + data: Mapping[str, Mapping[str, Strable]] ) -> str: lines = [f"\n[{section_name}]"] for item_name, inner_dict in data.items(): @@ -107,7 +108,7 @@ def generateTranslatedManifest(source: str, language: str, dest: str): manifest_template = f.read() manifest = manifest_template.format(**vars) - def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, str]]) -> str: + def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, Strable]]) -> str: lines = [f"\n[{section_name}]"] for item, inner_dict in data.items(): lines.append(f"[[{item}]]") From 7ef5669c0cf45bed652f35c08af7af5e06ecd0fb Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 14:54:41 +0000 Subject: [PATCH 060/119] SConstruct: Updated generateManifest All parameters are passed to the function arguments. --- sconstruct | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/sconstruct b/sconstruct index 05e33ae..cb6e410 100644 --- a/sconstruct +++ b/sconstruct @@ -30,7 +30,7 @@ EnsurePythonVersion(3, 10) sys.dont_write_bytecode = True import buildVars # NOQA: E402 -from site_scons.site_tools.NVDATool.typings import Strable # NOQA: E402 +from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable # NOQA: E402 def validateVersionNumber(key: str, val: str, _): @@ -46,8 +46,17 @@ def validateVersionNumber(key: str, val: str, _): def manifestTool(env: Environment): + env.SetDefault(brailleTables={}) + env.SetDefault(symbolDictionaries={}) + action = env.Action( - lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, + lambda target, source, env: generateManifest( + source[0].abspath, + target[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) and None, lambda target, source, env: f"Generating manifest {target[0]}", ) env["BUILDERS"]["NVDAManifest"] = Builder( @@ -80,20 +89,25 @@ def format_nested_section( return "\n".join(lines) + "\n" -def generateManifest(source: str, dest: str): +def generateManifest( + source: str, + dest: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, + ): # Prepare the root manifest section - addon_info = buildVars.addon_info with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() manifest = manifest_template.format(**addon_info) # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if hasattr(buildVars, "brailleTables"): - manifest += format_nested_section("brailleTables", buildVars.brailleTables) + if brailleTables: + manifest += format_nested_section("brailleTables", brailleTables) # Custom speech symbol dictionaries - if hasattr(buildVars, "symbolDictionaries"): - manifest += format_nested_section("symbolDictionaries", buildVars.symbolDictionaries) + if symbolDictionaries: + manifest += format_nested_section("symbolDictionaries", symbolDictionaries) with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) @@ -228,7 +242,10 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl") +manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl", + addon_info=buildVars.addon_info, + brailleTables=buildVars.brailleTables, + symbolDictionaries=buildVars.symbolDictionaries) # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") From ae3af0d98f36360851ca8da980ef184a78b726db Mon Sep 17 00:00:00 2001 From: balaraz Date: Wed, 30 Jul 2025 15:00:39 +0000 Subject: [PATCH 061/119] SConstruct: Updated generateTranslatedManifest - All parameters are passed to the function arguments. - The parameters are set via env.Append. --- sconstruct | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/sconstruct b/sconstruct index cb6e410..14d4e7b 100644 --- a/sconstruct +++ b/sconstruct @@ -70,8 +70,14 @@ def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], dir = Path(str(source[0])).absolute().parents[1] lang = dir.name action = env.Action( - lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) - and None, + lambda target, source, env: generateTranslatedManifest( + source[1].abspath, + target[0].abspath, + lang, env["localedir"], + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) and None, lambda target, source, env: f"Generating translated manifest {target[0]}", ) return action @@ -113,11 +119,19 @@ def generateManifest( f.write(manifest) -def generateTranslatedManifest(source: str, language: str, dest: str): +def generateTranslatedManifest( + source: str, + dest: str, + language: str, + localeDir: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, + ): _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext vars: dict[str, str] = {} for var in ("addon_summary", "addon_description"): - vars[var] = _(buildVars.addon_info[var]) + vars[var] = _(addon_info[var]) with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() manifest = manifest_template.format(**vars) @@ -132,12 +146,12 @@ def generateTranslatedManifest(source: str, language: str, dest: str): # Add additional manifest sections such as custom braile tables # Custom braille translation tables - if tables := getattr(buildVars, "brailleTables", None): - manifest += _format_section_only_with_displayName("brailleTables", tables) + if brailleTables: + manifest += _format_section_only_with_displayName("brailleTables", brailleTables) # Custom speech symbol dictionaries - if dicts := getattr(buildVars, "symbolDictionaries", None): - manifest += _format_section_only_with_displayName("symbolDictionaries", dicts) + if symbolDictionaries: + manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) @@ -159,7 +173,13 @@ vars.Add(BoolVariable("dev", "Whether this is a daily development version", Fals vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool", manifestTool]) -env.Append(**buildVars.addon_info) +env.Append( + addon_info=buildVars.addon_info, + localedir=localeDir, + brailleTables=buildVars.brailleTables, + symbolDictionaries=buildVars.symbolDictionaries, + **buildVars.addon_info +) if env["dev"]: from datetime import date @@ -242,10 +262,7 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl", - addon_info=buildVars.addon_info, - brailleTables=buildVars.brailleTables, - symbolDictionaries=buildVars.symbolDictionaries) +manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl") # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") From 57e90816ed79e54871e1b2fb18dae842c5dd7321 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 31 Jul 2025 08:51:54 +0000 Subject: [PATCH 062/119] SConstruct, NVDATool: Moved manifest generators to NVDATool - manifestTool insert to NVDATool.generate - translatedManifestGenrator moved to NVDATool.__init__ - generateManifest & generateTranslatedManifest moved to NVDATool.manifests --- sconstruct | 123 +------------------- site_scons/site_tools/NVDATool/__init__.py | 54 +++++++++ site_scons/site_tools/NVDATool/manifests.py | 81 +++++++++++++ 3 files changed, 137 insertions(+), 121 deletions(-) create mode 100644 site_scons/site_tools/NVDATool/manifests.py diff --git a/sconstruct b/sconstruct index 14d4e7b..f4d4ac4 100644 --- a/sconstruct +++ b/sconstruct @@ -3,13 +3,11 @@ # This file is covered by the GNU General Public License. # See the file COPYING.txt for more details. -import codecs -import gettext import os import os.path import sys from pathlib import Path -from collections.abc import Iterable, Mapping +from collections.abc import Iterable from typing import Final # While names imported below are available by default in every SConscript @@ -20,7 +18,6 @@ from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environme # Imports for type hints from SCons.Node import FS -from SCons.Action import CommandAction # Add-on localization exchange facility and the template requires Python 3.10. # For best practice, use Python 3.11 or later to align with NVDA development. @@ -30,7 +27,6 @@ EnsurePythonVersion(3, 10) sys.dont_write_bytecode = True import buildVars # NOQA: E402 -from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable # NOQA: E402 def validateVersionNumber(key: str, val: str, _): @@ -45,118 +41,6 @@ def validateVersionNumber(key: str, val: str, _): raise ValueError(f"{key} (major.minor.patch) must be integers") -def manifestTool(env: Environment): - env.SetDefault(brailleTables={}) - env.SetDefault(symbolDictionaries={}) - - action = env.Action( - lambda target, source, env: generateManifest( - source[0].abspath, - target[0].abspath, - addon_info=env["addon_info"], - brailleTables=env["brailleTables"], - symbolDictionaries=env["symbolDictionaries"], - ) and None, - lambda target, source, env: f"Generating manifest {target[0]}", - ) - env["BUILDERS"]["NVDAManifest"] = Builder( - action=action, - suffix=".ini", - src_siffix=".ini.tpl" - ) - - -def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: - dir = Path(str(source[0])).absolute().parents[1] - lang = dir.name - action = env.Action( - lambda target, source, env: generateTranslatedManifest( - source[1].abspath, - target[0].abspath, - lang, env["localedir"], - addon_info=env["addon_info"], - brailleTables=env["brailleTables"], - symbolDictionaries=env["symbolDictionaries"], - ) and None, - lambda target, source, env: f"Generating translated manifest {target[0]}", - ) - return action - - -def format_nested_section( - section_name: str, - data: Mapping[str, Mapping[str, Strable]] -) -> str: - lines = [f"\n[{section_name}]"] - for item_name, inner_dict in data.items(): - lines.append(f"[[{item_name}]]") - for key, val in inner_dict.items(): - lines.append(f"{key} = {val}") - return "\n".join(lines) + "\n" - - -def generateManifest( - source: str, - dest: str, - addon_info: AddonInfo, - brailleTables: BrailleTables, - symbolDictionaries: SymbolDictionaries, - ): - # Prepare the root manifest section - with codecs.open(source, "r", "utf-8") as f: - manifest_template = f.read() - manifest = manifest_template.format(**addon_info) - # Add additional manifest sections such as custom braile tables - # Custom braille translation tables - if brailleTables: - manifest += format_nested_section("brailleTables", brailleTables) - - # Custom speech symbol dictionaries - if symbolDictionaries: - manifest += format_nested_section("symbolDictionaries", symbolDictionaries) - - with codecs.open(dest, "w", "utf-8") as f: - f.write(manifest) - - -def generateTranslatedManifest( - source: str, - dest: str, - language: str, - localeDir: str, - addon_info: AddonInfo, - brailleTables: BrailleTables, - symbolDictionaries: SymbolDictionaries, - ): - _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext - vars: dict[str, str] = {} - for var in ("addon_summary", "addon_description"): - vars[var] = _(addon_info[var]) - with codecs.open(source, "r", "utf-8") as f: - manifest_template = f.read() - manifest = manifest_template.format(**vars) - - def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, Strable]]) -> str: - lines = [f"\n[{section_name}]"] - for item, inner_dict in data.items(): - lines.append(f"[[{item}]]") - # Fetch display name only. - lines.append(f"displayName = {_(inner_dict['displayName'])}") - return "\n".join(lines) + "\n" - - # Add additional manifest sections such as custom braile tables - # Custom braille translation tables - if brailleTables: - manifest += _format_section_only_with_displayName("brailleTables", brailleTables) - - # Custom speech symbol dictionaries - if symbolDictionaries: - manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) - - with codecs.open(dest, "w", "utf-8") as f: - f.write(manifest) - - def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.Entry]: return [env.Entry(e) for pattern in patterns for e in rootdir.glob(pattern.lstrip('/'))] @@ -172,7 +56,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool", manifestTool]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool"]) env.Append( addon_info=buildVars.addon_info, localedir=localeDir, @@ -198,9 +82,6 @@ buildVars.addon_info["addon_version"] = env["addon_version"] buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] -env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) - - addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir), excludePatterns=buildVars.excludedFiles) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 1bd2d39..997f05a 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -4,12 +4,44 @@ Builders: - NVDAAddon: Creates a .nvda-addon zip file. Requires the `excludePatterns` environment variable. +- NVDAManifest: Creates the manifest.ini file. +- NVDATranslatedManifest: Creates the manifest.ini file with only translated information. + +The following environment variables are required to create the manifest: + +- addon_info: .typing.AddonInfo +- lcaledir: str | pathlib.Path +- brailleTables: .typings.BrailleTables +- symbolDictionaries: .typings.SymbolDictionaries """ +from pathlib import Path + from SCons.Script import Environment, Builder +from SCons.Node import FS +from SCons.Action import CommandAction from .addon import createAddonBundleFromPath +from .manifests import generateManifest, generateTranslatedManifest + + + +def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: + dir = Path(str(source[0])).absolute().parents[1] + lang = dir.name + action = env.Action( + lambda target, source, env: generateTranslatedManifest( + source[1].abspath, + target[0].abspath, + lang, env["localedir"], + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) and None, + lambda target, source, env: f"Generating translated manifest {target[0]}", + ) + return action @@ -28,5 +60,27 @@ def generate(env: Environment): src_suffix="/" ) + env.SetDefault(brailleTables={}) + env.SetDefault(symbolDictionaries={}) + + manifestAction = env.Action( + lambda target, source, env: generateManifest( + source[0].abspath, + target[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) and None, + lambda target, source, env: f"Generating manifest {target[0]}", + ) + env["BUILDERS"]["NVDAManifest"] = Builder( + action=manifestAction, + suffix=".ini", + src_siffix=".ini.tpl" + ) + + env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) + + def exists(): return True diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py new file mode 100644 index 0000000..b1b07bc --- /dev/null +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -0,0 +1,81 @@ + +import codecs +import gettext +from collections.abc import Mapping + +from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable # NOQA: E402 + + + +def format_nested_section( + section_name: str, + data: Mapping[str, Mapping[str, Strable]] +) -> str: + lines = [f"\n[{section_name}]"] + for item_name, inner_dict in data.items(): + lines.append(f"[[{item_name}]]") + for key, val in inner_dict.items(): + lines.append(f"{key} = {val}") + return "\n".join(lines) + "\n" + + +def generateManifest( + source: str, + dest: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, + ): + # Prepare the root manifest section + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**addon_info) + # Add additional manifest sections such as custom braile tables + # Custom braille translation tables + if brailleTables: + manifest += format_nested_section("brailleTables", brailleTables) + + # Custom speech symbol dictionaries + if symbolDictionaries: + manifest += format_nested_section("symbolDictionaries", symbolDictionaries) + + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) + + +def generateTranslatedManifest( + source: str, + dest: str, + language: str, + localeDir: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, + ): + _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext + vars: dict[str, str] = {} + for var in ("addon_summary", "addon_description"): + vars[var] = _(addon_info[var]) + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**vars) + + def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, Strable]]) -> str: + lines = [f"\n[{section_name}]"] + for item, inner_dict in data.items(): + lines.append(f"[[{item}]]") + # Fetch display name only. + lines.append(f"displayName = {_(str(inner_dict['displayName']))}") + return "\n".join(lines) + "\n" + + # Add additional manifest sections such as custom braile tables + # Custom braille translation tables + if brailleTables: + manifest += _format_section_only_with_displayName("brailleTables", brailleTables) + + # Custom speech symbol dictionaries + if symbolDictionaries: + manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) + + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) From d7545ba25fc59c8d542aa315551d8c09422f5dee Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 31 Jul 2025 15:10:53 +0000 Subject: [PATCH 063/119] SConstruct, NVDATool: Updated translatedManifestGenrator --- sconstruct | 1 - site_scons/site_tools/NVDATool/__init__.py | 5 +---- site_scons/site_tools/NVDATool/manifests.py | 7 ++++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/sconstruct b/sconstruct index f4d4ac4..785e4ca 100644 --- a/sconstruct +++ b/sconstruct @@ -59,7 +59,6 @@ vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool"]) env.Append( addon_info=buildVars.addon_info, - localedir=localeDir, brailleTables=buildVars.brailleTables, symbolDictionaries=buildVars.symbolDictionaries, **buildVars.addon_info diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 997f05a..9df217e 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -10,7 +10,6 @@ The following environment variables are required to create the manifest: - addon_info: .typing.AddonInfo -- lcaledir: str | pathlib.Path - brailleTables: .typings.BrailleTables - symbolDictionaries: .typings.SymbolDictionaries @@ -28,13 +27,11 @@ def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: - dir = Path(str(source[0])).absolute().parents[1] - lang = dir.name action = env.Action( lambda target, source, env: generateTranslatedManifest( source[1].abspath, target[0].abspath, - lang, env["localedir"], + mo=source[0].abspath, addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index b1b07bc..7df44b6 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -46,13 +46,14 @@ def generateManifest( def generateTranslatedManifest( source: str, dest: str, - language: str, - localeDir: str, + *, + mo: str, addon_info: AddonInfo, brailleTables: BrailleTables, symbolDictionaries: SymbolDictionaries, ): - _ = gettext.translation("nvda", localedir=localeDir, languages=[language]).gettext + with open(mo, "rb") as f: + _ = gettext.GNUTranslations(f).gettext vars: dict[str, str] = {} for var in ("addon_summary", "addon_description"): vars[var] = _(addon_info[var]) From d5e3462e1a75035163a027f9bcdd19382dfe5561 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 31 Jul 2025 15:19:35 +0000 Subject: [PATCH 064/119] NVDATOOl: Removed translatedManifestGenerator Moved action to NVDATool.generate function --- site_scons/site_tools/NVDATool/__init__.py | 36 ++++++++++------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 9df217e..dcfe8d2 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -18,30 +18,12 @@ from pathlib import Path from SCons.Script import Environment, Builder -from SCons.Node import FS -from SCons.Action import CommandAction from .addon import createAddonBundleFromPath from .manifests import generateManifest, generateTranslatedManifest -def translatedManifestGenerator(target: list[FS.Entry], source: list[FS.Entry], env: Environment, for_signature: bool) -> CommandAction: - action = env.Action( - lambda target, source, env: generateTranslatedManifest( - source[1].abspath, - target[0].abspath, - mo=source[0].abspath, - addon_info=env["addon_info"], - brailleTables=env["brailleTables"], - symbolDictionaries=env["symbolDictionaries"], - ) and None, - lambda target, source, env: f"Generating translated manifest {target[0]}", - ) - return action - - - def generate(env: Environment): env.SetDefault(excludePatterns=tuple()) @@ -76,7 +58,23 @@ def generate(env: Environment): src_siffix=".ini.tpl" ) - env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) + translatedManifestAction = env.Action( + lambda target, source, env: generateTranslatedManifest( + source[1].abspath, + target[0].abspath, + mo=source[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) and None, + lambda target, source, env: f"Generating translated manifest {target[0]}", + ) + + env["BUILDERS"]["NVDATranslatedManifest"] = Builder( + action=translatedManifestAction, + suffix=".ini", + src_siffix=".ini.tpl" + ) def exists(): From 0f4c508efac88d6b2db950f2fcde2ffa70ac0541 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 31 Jul 2025 15:56:47 +0000 Subject: [PATCH 065/119] NVDATool: Remove typing.override from typings.py --- site_scons/site_tools/NVDATool/typings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index c4d74ea..e7b1791 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -1,4 +1,4 @@ -from typing import TypedDict, Protocol, override +from typing import TypedDict, Protocol @@ -35,5 +35,4 @@ class SymbolDictionaryAttributes(TypedDict): class Strable(Protocol): - @override def __str__(self) -> str: ... From bc09b7167e46bb8e8247f4f5ea28231737997024 Mon Sep 17 00:00:00 2001 From: balaraz Date: Thu, 31 Jul 2025 18:28:17 +0000 Subject: [PATCH 066/119] SConstruct: Removed unneeded update buildVars.addon_info --- sconstruct | 3 --- 1 file changed, 3 deletions(-) diff --git a/sconstruct b/sconstruct index 785e4ca..021eaf0 100644 --- a/sconstruct +++ b/sconstruct @@ -77,9 +77,6 @@ elif env["version"] is not None: if "channel" in env and env["channel"] is not None: env["addon_updateChannel"] = env["channel"] -buildVars.addon_info["addon_version"] = env["addon_version"] -buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] - addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir), excludePatterns=buildVars.excludedFiles) From e7e900fac3e5f3cde9a72c427dffc0c69a3a1dc8 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 08:28:27 +0000 Subject: [PATCH 067/119] SConstruct, mdTool, NVDATool: Removed mdTool Moved md2html builder to NVDATool, as it is not a pure HTML to Markdown converter. Specific actions for NVDA add-ons are present. --- sconstruct | 2 +- site_scons/site_tools/NVDATool/__init__.py | 32 +++++++++++++ .../{mdTool/__init__.py => NVDATool/docs.py} | 47 ------------------- 3 files changed, 33 insertions(+), 48 deletions(-) rename site_scons/site_tools/{mdTool/__init__.py => NVDATool/docs.py} (58%) diff --git a/sconstruct b/sconstruct index 021eaf0..8c3e82d 100644 --- a/sconstruct +++ b/sconstruct @@ -56,7 +56,7 @@ vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0 vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "mdTool", "NVDATool"]) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "NVDATool"]) env.Append( addon_info=buildVars.addon_info, brailleTables=buildVars.brailleTables, diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index dcfe8d2..95677be 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -6,6 +6,7 @@ - NVDAAddon: Creates a .nvda-addon zip file. Requires the `excludePatterns` environment variable. - NVDAManifest: Creates the manifest.ini file. - NVDATranslatedManifest: Creates the manifest.ini file with only translated information. +- md2html: Build HTML from Markdown The following environment variables are required to create the manifest: @@ -13,6 +14,14 @@ - brailleTables: .typings.BrailleTables - symbolDictionaries: .typings.SymbolDictionaries +The following environment variables are required to build the HTML: + +- localeDir: str +- localeFileName: str +- mdExtensions: list[str] +- addon_summary: str +- addon_version: str + """ from pathlib import Path @@ -21,6 +30,7 @@ from .addon import createAddonBundleFromPath from .manifests import generateManifest, generateTranslatedManifest +from .docs import md2html @@ -76,6 +86,28 @@ def generate(env: Environment): src_siffix=".ini.tpl" ) + env.SetDefault(localeDir = Path("locale/")) + env.SetDefault(localeFileName = "messages") + env.SetDefault(mdExtensions = {}) + + mdAction = env.Action( + lambda target, source, env: md2html( + source[0].path, + target[0].path, + localeDir=env["localeDir"], + loacleFileName=env["localeFileName"], + mdExtensions=env["mdExtensions"], + addonSummary=env["addon_summary"], + addonVersion=env["addon_version"] + ) and None, + lambda target, source, env: f"Generating {target[0]}", + ) + env["BUILDERS"]["md2html"] = env.Builder( + action=mdAction, + suffix=".html", + src_suffix=".md", + ) + def exists(): return True diff --git a/site_scons/site_tools/mdTool/__init__.py b/site_scons/site_tools/NVDATool/docs.py similarity index 58% rename from site_scons/site_tools/mdTool/__init__.py rename to site_scons/site_tools/NVDATool/docs.py index 5547237..ade5dcb 100644 --- a/site_scons/site_tools/mdTool/__init__.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -1,27 +1,9 @@ -""" -This tool allows you to generate HTML files from Markdown. - -Adds the following Builder to the constructed environment: - -- md2html: Build HTML from Markdown - -The following variables are required in the environment: - -- localeDir -- localeFileName -- mdExtensions -- addon_summary -- addon_version - - -""" import gettext import codecs from pathlib import Path import markdown -from SCons.Script import Environment @@ -77,32 +59,3 @@ def md2html( ) with codecs.open(dest, "w", "utf-8") as f: f.write(docText) - - -def generate(env: Environment): - env.SetDefault(localeDir = Path("locale/")) - env.SetDefault(localeFileName = "messages") - env.SetDefault(mdExtensions = {}) - - mdAction = env.Action( - lambda target, source, env: md2html( - source[0].path, - target[0].path, - localeDir=env["localeDir"], - loacleFileName=env["localeFileName"], - mdExtensions=env["mdExtensions"], - addonSummary=env["addon_summary"], - addonVersion=env["addon_version"] - ) and None, - lambda target, source, env: f"Generating {target[0]}", - ) - mdBuilder = env.Builder( - action=mdAction, - suffix=".html", - src_suffix=".md", - ) - env["BUILDERS"]["md2html"] = mdBuilder - - -def exists(): - return True From 1a0968d5d8f537c0a0b66b386b8a61329fe0a05a Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 09:40:42 +0000 Subject: [PATCH 068/119] NVDATool: Updated md2html Replaced separate variables addon_summary and addon_version with one common addon_info --- site_scons/site_tools/NVDATool/__init__.py | 6 ++---- site_scons/site_tools/NVDATool/docs.py | 11 ++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 95677be..79d5b68 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -19,8 +19,7 @@ - localeDir: str - localeFileName: str - mdExtensions: list[str] -- addon_summary: str -- addon_version: str +- addon_info: .typings.AddonInfo """ @@ -97,8 +96,7 @@ def generate(env: Environment): localeDir=env["localeDir"], loacleFileName=env["localeFileName"], mdExtensions=env["mdExtensions"], - addonSummary=env["addon_summary"], - addonVersion=env["addon_version"] + addon_info=env["addon_info"], ) and None, lambda target, source, env: f"Generating {target[0]}", ) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index ade5dcb..6c4f607 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -5,6 +5,8 @@ import markdown +from .typings import AddonInfo + def md2html( @@ -14,8 +16,7 @@ def md2html( localeDir: Path, loacleFileName: str, mdExtensions: list[str], - addonSummary: str, - addonVersion: str + addon_info: AddonInfo ): if isinstance(source, str): source = Path(source) @@ -29,10 +30,10 @@ def md2html( localedir=localeDir, languages=[localeLang] ).gettext - summary = _(addonSummary) + summary = _(addon_info["addon_summary"]) except Exception: - summary = addonSummary - title = f"{summary} {addonVersion}" + summary = addon_info["addon_summary"] + title = f"{summary} {addon_info["addon_version"]}" headerDic = { '[[!meta title="': "# ", '"]]': " #", From 933418916a3c5a95d89d90f36872774510df5b6f Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 10:20:32 +0000 Subject: [PATCH 069/119] buildVars, NVDATool: Moved _ func to NVDATool.utils --- buildVars.py | 5 ++--- site_scons/site_tools/NVDATool/utils.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 site_scons/site_tools/NVDATool/utils.py diff --git a/buildVars.py b/buildVars.py index 33401e0..b57a438 100644 --- a/buildVars.py +++ b/buildVars.py @@ -6,10 +6,9 @@ # Since some strings in `addon_info` are translatable, # we need to include them in the .po files. # Gettext recognizes only strings given as parameters to the `_` function. -# To avoid initializing translations in this module we simply roll our own "fake" `_` function +# To avoid initializing translations in this module we simply import a "fake" `_` function # which returns whatever is given to it as an argument. -def _(arg: str) -> str: - return arg +from site_scons.site_tools.NVDATool.utils import _ # Add-on information variables diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py new file mode 100644 index 0000000..326d394 --- /dev/null +++ b/site_scons/site_tools/NVDATool/utils.py @@ -0,0 +1,7 @@ + +def _(arg: str) -> str: + """ + A function that passes the string to it without doing anything to it. + Needed for recognizing strings for translation by Gettext. + """ + return arg From f3347a08f46af205d55227ef4aeeb52fe3d78758 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 10:25:43 +0000 Subject: [PATCH 070/119] NVDATool: Refactored import typings in NVDATool.manifests --- site_scons/site_tools/NVDATool/manifests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index 7df44b6..9317419 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -3,8 +3,7 @@ import gettext from collections.abc import Mapping -from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable # NOQA: E402 - +from .typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable def format_nested_section( From 68e292ef0908d03f972b11bb25dfb6d69fb4a9a2 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 10:54:32 +0000 Subject: [PATCH 071/119] NVDATool: Moved format_nested_section func to utils.py --- site_scons/site_tools/NVDATool/manifests.py | 12 +----------- site_scons/site_tools/NVDATool/utils.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index 9317419..f75bca3 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -4,19 +4,9 @@ from collections.abc import Mapping from .typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable +from .utils import format_nested_section -def format_nested_section( - section_name: str, - data: Mapping[str, Mapping[str, Strable]] -) -> str: - lines = [f"\n[{section_name}]"] - for item_name, inner_dict in data.items(): - lines.append(f"[[{item_name}]]") - for key, val in inner_dict.items(): - lines.append(f"{key} = {val}") - return "\n".join(lines) + "\n" - def generateManifest( source: str, diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py index 326d394..c103770 100644 --- a/site_scons/site_tools/NVDATool/utils.py +++ b/site_scons/site_tools/NVDATool/utils.py @@ -1,3 +1,8 @@ +from collections.abc import Mapping + +from .typings import Strable + + def _(arg: str) -> str: """ @@ -5,3 +10,15 @@ def _(arg: str) -> str: Needed for recognizing strings for translation by Gettext. """ return arg + + +def format_nested_section( + section_name: str, + data: Mapping[str, Mapping[str, Strable]] +) -> str: + lines = [f"\n[{section_name}]"] + for item_name, inner_dict in data.items(): + lines.append(f"[[{item_name}]]") + for key, val in inner_dict.items(): + lines.append(f"{key} = {val}") + return "\n".join(lines) + "\n" From 3420405844a12b0ddcebbacabf9036c9b021a4d5 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 12:06:33 +0000 Subject: [PATCH 072/119] NVDATool: Update format_nested_section util Added an argument to limit the fields that will be added. Also, the value is now translated using the passed function. --- site_scons/site_tools/NVDATool/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py index c103770..36bb3b3 100644 --- a/site_scons/site_tools/NVDATool/utils.py +++ b/site_scons/site_tools/NVDATool/utils.py @@ -1,4 +1,4 @@ -from collections.abc import Mapping +from collections.abc import Callable, Container, Mapping from .typings import Strable @@ -14,11 +14,15 @@ def _(arg: str) -> str: def format_nested_section( section_name: str, - data: Mapping[str, Mapping[str, Strable]] + data: Mapping[str, Mapping[str, Strable]], + include_only_keys: Container[str]|None = None, + _: Callable[[str], str] = _, ) -> str: lines = [f"\n[{section_name}]"] for item_name, inner_dict in data.items(): lines.append(f"[[{item_name}]]") for key, val in inner_dict.items(): - lines.append(f"{key} = {val}") + if include_only_keys and key not in include_only_keys: + continue + lines.append(f"{key} = {_(str(val))}") return "\n".join(lines) + "\n" From b989853491aaf5e8abd6dd3e3e93f56363582b08 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 12:11:43 +0000 Subject: [PATCH 073/119] NVDATool: Refactored generateTranslatedManifest Removed _format_section_only_with_displayName function This function has now been replaced by format_nested_section with fixed arguments via functools.partial. --- site_scons/site_tools/NVDATool/manifests.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index f75bca3..53446ea 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -1,9 +1,9 @@ import codecs import gettext -from collections.abc import Mapping +from functools import partial -from .typings import AddonInfo, BrailleTables, SymbolDictionaries, Strable +from .typings import AddonInfo, BrailleTables, SymbolDictionaries from .utils import format_nested_section @@ -50,13 +50,11 @@ def generateTranslatedManifest( manifest_template = f.read() manifest = manifest_template.format(**vars) - def _format_section_only_with_displayName(section_name: str, data: Mapping[str, Mapping[str, Strable]]) -> str: - lines = [f"\n[{section_name}]"] - for item, inner_dict in data.items(): - lines.append(f"[[{item}]]") - # Fetch display name only. - lines.append(f"displayName = {_(str(inner_dict['displayName']))}") - return "\n".join(lines) + "\n" + _format_section_only_with_displayName = partial( + format_nested_section, + include_only_keys = ("displayName",), + _ = _, + ) # Add additional manifest sections such as custom braile tables # Custom braille translation tables From b6c38dacc730de30f8401f0163278d7bc6779028 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 12:49:22 +0000 Subject: [PATCH 074/119] NVDATool: Update md2html func Replaced codecs.open with pathlib.Path.open --- site_scons/site_tools/NVDATool/docs.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index 6c4f607..dc354bf 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -1,6 +1,5 @@ import gettext -import codecs from pathlib import Path import markdown @@ -11,7 +10,7 @@ def md2html( source: str|Path, - dest: str, + dest: str|Path, *, localeDir: Path, loacleFileName: str, @@ -20,6 +19,8 @@ def md2html( ): if isinstance(source, str): source = Path(source) + if isinstance(dest, str): + dest = Path(dest) # Use extensions if defined. localeLang = source.parent.name @@ -38,7 +39,7 @@ def md2html( '[[!meta title="': "# ", '"]]': " #", } - with codecs.open(str(source), "r", "utf-8") as f: + with source.open("r") as f: mdText = f.read() for k, v in headerDic.items(): mdText = mdText.replace(k, v, 1) @@ -58,5 +59,5 @@ def md2html( "\n", ) ) - with codecs.open(dest, "w", "utf-8") as f: - f.write(docText) + with dest.open("w") as f: + f.write(docText) # type: ignore From 307361c4116c826b9218d69d88d0cd56f8dc39bb Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 14:38:27 +0000 Subject: [PATCH 075/119] SConstruct: Fixed moFile binding to mdFile Due to the mismatch of key types when adding and retrieving moFile from moByLang, they were not generated in the correct sequence. --- sconstruct | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sconstruct b/sconstruct index 8c3e82d..bac5725 100644 --- a/sconstruct +++ b/sconstruct @@ -81,20 +81,21 @@ if "channel" in env and env["channel"] is not None: addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") addon = env.NVDAAddon(addonFile, env.Dir(addonDir), excludePatterns=buildVars.excludedFiles) -langDirs: list[FS.Dir] = [d for d in env.Glob(localeDir/"*/")] +langDirs: list[FS.Dir] = [env.Dir(d) for d in env.Glob(localeDir/"*/") if d.isdir()] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated -moByLang: dict[FS.Dir, FS.File] = {} +moByLang: dict[str, FS.File] = {} for dir in langDirs: poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) - moFile = env.gettextMoFile(poFile) - moByLang[dir] = moFile - env.Depends(moFile, poFile) + moTarget = env.gettextMoFile(poFile) + moFile = env.File(moTarget[0]) + moByLang[dir.name] = moFile + env.Depends(moTarget, poFile) translatedManifest = env.NVDATranslatedManifest( dir.File("manifest.ini"), [moFile, "manifest-translated.ini.tpl"] ) env.Depends(translatedManifest, ["buildVars.py"]) - env.Depends(addon, [translatedManifest, moFile]) + env.Depends(addon, [translatedManifest, moTarget]) pythonFiles = expandGlobs(buildVars.pythonSources) for file in pythonFiles: @@ -115,7 +116,7 @@ if (readmeFile := Path("readme.md")).is_file(): for mdFile in env.Glob(docsDir/"*/*.md"): # the title of the html file is translated based on the contents of something in the moFile for a language. # Thus, we find the moFile for this language and depend on it if it exists. - lang = Path(str(mdFile)).absolute().parent.name + lang = mdFile.dir.name moFile = moByLang.get(lang) htmlFile = env.md2html(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) From 604778e7119f445710dcfcf30cbb31866bc1cbb4 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sat, 2 Aug 2025 18:14:46 +0000 Subject: [PATCH 076/119] SConstruct, NVDATool: Updated md2html Now, instead of localeDir and localeFileName, and then specifying the language, you immediately get the .mo file. --- sconstruct | 2 +- site_scons/site_tools/NVDATool/__init__.py | 8 ++------ site_scons/site_tools/NVDATool/docs.py | 19 +++++++++++-------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/sconstruct b/sconstruct index bac5725..df8d74e 100644 --- a/sconstruct +++ b/sconstruct @@ -118,7 +118,7 @@ for mdFile in env.Glob(docsDir/"*/*.md"): # Thus, we find the moFile for this language and depend on it if it exists. lang = mdFile.dir.name moFile = moByLang.get(lang) - htmlFile = env.md2html(mdFile, localeDir=localeDir, localeFileName="nvda", mdExtensions=buildVars.markdownExtensions) + htmlFile = env.md2html(mdFile, moFile=moFile, mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) if moFile: env.Depends(htmlFile, moFile) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 79d5b68..9367d19 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -16,8 +16,7 @@ The following environment variables are required to build the HTML: -- localeDir: str -- localeFileName: str +- moFile: str|Path|None - mdExtensions: list[str] - addon_info: .typings.AddonInfo @@ -85,16 +84,13 @@ def generate(env: Environment): src_siffix=".ini.tpl" ) - env.SetDefault(localeDir = Path("locale/")) - env.SetDefault(localeFileName = "messages") env.SetDefault(mdExtensions = {}) mdAction = env.Action( lambda target, source, env: md2html( source[0].path, target[0].path, - localeDir=env["localeDir"], - loacleFileName=env["localeFileName"], + moFile=env["moFile"].path if env["moFile"] else None, mdExtensions=env["mdExtensions"], addon_info=env["addon_info"], ) and None, diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index dc354bf..fa22d06 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -4,6 +4,7 @@ import markdown +from . import utils from .typings import AddonInfo @@ -12,8 +13,7 @@ def md2html( source: str|Path, dest: str|Path, *, - localeDir: Path, - loacleFileName: str, + moFile: str|Path|None, mdExtensions: list[str], addon_info: AddonInfo ): @@ -21,19 +21,22 @@ def md2html( source = Path(source) if isinstance(dest, str): dest = Path(dest) + if isinstance(moFile, str): + moFile = Path(moFile) # Use extensions if defined. localeLang = source.parent.name lang = localeLang.replace("_", "-") try: - _ = gettext.translation( - loacleFileName, - localedir=localeDir, - languages=[localeLang] - ).gettext - summary = _(addon_info["addon_summary"]) + if moFile is not None: + with moFile.open("rb") as f: + _ = gettext.GNUTranslations(f).gettext + else: + _ = utils._ except Exception: summary = addon_info["addon_summary"] + else: + summary = _(addon_info["addon_summary"]) title = f"{summary} {addon_info["addon_version"]}" headerDic = { '[[!meta title="': "# ", From 57d14fcab931f9dfa5c64c965e053f203a3534e5 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 3 Aug 2025 06:41:10 +0000 Subject: [PATCH 077/119] buildVars: Refactored definition of addon_info Use function-like init style, with keywords args --- buildVars.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/buildVars.py b/buildVars.py index b57a438..9843f38 100644 --- a/buildVars.py +++ b/buildVars.py @@ -12,40 +12,40 @@ # Add-on information variables -addon_info: AddonInfo = { +addon_info = AddonInfo( # add-on Name/identifier, internal for NVDA - "addon_name": "addonTemplate", + addon_name="addonTemplate", # Add-on summary/title, usually the user visible name of the add-on # Translators: Summary/title for this add-on # to be shown on installation and add-on information found in add-on store - "addon_summary": _("Add-on user visible name"), + addon_summary=_("Add-on user visible name"), # Add-on description # Translators: Long description to be shown for this add-on on add-on information from add-on store - "addon_description": _("""Description for the add-on. + addon_description=_("""Description for the add-on. It can span multiple lines."""), # version - "addon_version": "x.y", + addon_version="x.y", # Author(s) - "addon_author": "name ", + addon_author="name ", # URL for the add-on documentation support - "addon_url": None, + addon_url=None, # URL for the add-on repository where the source code can be found - "addon_sourceURL": None, + addon_sourceURL=None, # Documentation file name - "addon_docFileName": "readme.html", + addon_docFileName="readme.html", # Minimum NVDA version supported (e.g. "2019.3.0", minor version is optional) - "addon_minimumNVDAVersion": None, + addon_minimumNVDAVersion=None, # Last NVDA version supported/tested (e.g. "2024.4.0", ideally more recent than minimum version) - "addon_lastTestedNVDAVersion": None, + addon_lastTestedNVDAVersion=None, # Add-on update channel (default is None, denoting stable releases, # and for development releases, use "dev".) # Do not change unless you know what you are doing! - "addon_updateChannel": None, + addon_updateChannel=None, # Add-on license such as GPL 2 - "addon_license": None, + addon_license=None, # URL for the license document the ad-on is licensed under - "addon_licenseURL": None, -} + addon_licenseURL=None, +) # Define the python files that are the sources of your add-on. # You can either list every file (using ""/") as a path separator, From 3ccc1ebcfc2ad6694248aca30759514af34c0661 Mon Sep 17 00:00:00 2001 From: balaraz Date: Sun, 3 Aug 2025 07:49:14 +0000 Subject: [PATCH 078/119] NVDATool.md2html: Fixed title definition In target Python 3.11 does not support extended f-string --- site_scons/site_tools/NVDATool/docs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index fa22d06..d846e92 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -37,7 +37,8 @@ def md2html( summary = addon_info["addon_summary"] else: summary = _(addon_info["addon_summary"]) - title = f"{summary} {addon_info["addon_version"]}" + version = addon_info["addon_version"] + title = f"{summary} {version}" headerDic = { '[[!meta title="': "# ", '"]]': " #", From 1eef48a9f6b81340b6938c9bc0cbb3de49a2f66e Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 5 Aug 2025 13:58:09 +0000 Subject: [PATCH 079/119] NVDATool: Refactored md2html func --- site_scons/site_tools/NVDATool/docs.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index d846e92..55a1417 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -4,7 +4,6 @@ import markdown -from . import utils from .typings import AddonInfo @@ -24,21 +23,16 @@ def md2html( if isinstance(moFile, str): moFile = Path(moFile) - # Use extensions if defined. - localeLang = source.parent.name - lang = localeLang.replace("_", "-") try: - if moFile is not None: - with moFile.open("rb") as f: - _ = gettext.GNUTranslations(f).gettext - else: - _ = utils._ + with moFile.open("rb") as f: + _ = gettext.GNUTranslations(f).gettext except Exception: summary = addon_info["addon_summary"] else: summary = _(addon_info["addon_summary"]) version = addon_info["addon_version"] title = f"{summary} {version}" + lang = source.parent.name.replace("_", "-") headerDic = { '[[!meta title="': "# ", '"]]': " #", From 6919905e981a3b1cf79f4b75dfe5c2b379b6a20d Mon Sep 17 00:00:00 2001 From: balaraz Date: Tue, 5 Aug 2025 13:59:15 +0000 Subject: [PATCH 080/119] SConstruct: Fixed applying addon_info --- sconstruct | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sconstruct b/sconstruct index df8d74e..282b019 100644 --- a/sconstruct +++ b/sconstruct @@ -61,7 +61,6 @@ env.Append( addon_info=buildVars.addon_info, brailleTables=buildVars.brailleTables, symbolDictionaries=buildVars.symbolDictionaries, - **buildVars.addon_info ) if env["dev"]: @@ -69,13 +68,16 @@ if env["dev"]: versionTimestamp = date.today().strftime('%Y%m%d') version = f"{versionTimestamp}.0.0" - env["addon_version"] = version + env["addon_info"]["addon_version"] = version env["versionNumber"] = version env["channel"] = "dev" elif env["version"] is not None: - env["addon_version"] = env["version"] + env["addon_info"]["addon_version"] = env["version"] if "channel" in env and env["channel"] is not None: - env["addon_updateChannel"] = env["channel"] + env["addon_info"]["addon_updateChannel"] = env["channel"] + +# This is necessary for further use in formatting file names. +env.Append(**env["addon_info"]) addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") From d069b54b6af01c513409aab4caa140c5e0041f5c Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 5 Aug 2025 08:28:34 -0600 Subject: [PATCH 081/119] SConstruct, NVDA tool: remove unused imports --- sconstruct | 2 +- site_scons/site_tools/NVDATool/__init__.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sconstruct b/sconstruct index 282b019..481a7ac 100644 --- a/sconstruct +++ b/sconstruct @@ -14,7 +14,7 @@ from typing import Final # Linters aren't aware about them. # To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. # When using other Scons functions please add them to the line below. -from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment, Builder, Copy +from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment, Copy # Imports for type hints from SCons.Node import FS diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 9367d19..5f40c2d 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -22,8 +22,6 @@ """ -from pathlib import Path - from SCons.Script import Environment, Builder from .addon import createAddonBundleFromPath From 7dba876e1f605cf03e5c4c7f411e5c5b509f5c78 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 5 Aug 2025 08:38:09 -0600 Subject: [PATCH 082/119] Site tools: spacing around 'or' operator, mention pathlib.Path --- site_scons/site_tools/NVDATool/__init__.py | 2 +- site_scons/site_tools/NVDATool/addon.py | 2 +- site_scons/site_tools/NVDATool/docs.py | 6 +++--- site_scons/site_tools/NVDATool/typings.py | 14 +++++++------- site_scons/site_tools/NVDATool/utils.py | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 5f40c2d..6b4a37c 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -16,7 +16,7 @@ The following environment variables are required to build the HTML: -- moFile: str|Path|None +- moFile: str | pathlib.Path | None - mdExtensions: list[str] - addon_info: .typings.AddonInfo diff --git a/site_scons/site_tools/NVDATool/addon.py b/site_scons/site_tools/NVDATool/addon.py index adc1424..42e8d0e 100644 --- a/site_scons/site_tools/NVDATool/addon.py +++ b/site_scons/site_tools/NVDATool/addon.py @@ -9,7 +9,7 @@ def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: return not any((path.match(pattern) for pattern in patterns)) -def createAddonBundleFromPath(path: str|Path, dest: str, excludePatterns: Iterable[str]): +def createAddonBundleFromPath(path: str | Path, dest: str, excludePatterns: Iterable[str]): """Creates a bundle from a directory that contains an addon manifest file.""" if isinstance(path, str): path = Path(path) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index 55a1417..09cc27b 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -9,10 +9,10 @@ def md2html( - source: str|Path, - dest: str|Path, + source: str | Path, + dest: str | Path, *, - moFile: str|Path|None, + moFile: str | Path|None, mdExtensions: list[str], addon_info: AddonInfo ): diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index e7b1791..927f97f 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -8,14 +8,14 @@ class AddonInfo(TypedDict): addon_description: str addon_version: str addon_author: str - addon_url: str|None - addon_sourceURL: str|None + addon_url: str | None + addon_sourceURL: str | None addon_docFileName: str - addon_minimumNVDAVersion: str|None - addon_lastTestedNVDAVersion: str|None - addon_updateChannel: str|None - addon_license: str|None - addon_licenseURL: str|None + addon_minimumNVDAVersion: str | None + addon_lastTestedNVDAVersion: str | None + addon_updateChannel: str | None + addon_license: str | None + addon_licenseURL: str | None class BrailleTableAttributes(TypedDict): diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py index 36bb3b3..0cc833c 100644 --- a/site_scons/site_tools/NVDATool/utils.py +++ b/site_scons/site_tools/NVDATool/utils.py @@ -15,7 +15,7 @@ def _(arg: str) -> str: def format_nested_section( section_name: str, data: Mapping[str, Mapping[str, Strable]], - include_only_keys: Container[str]|None = None, + include_only_keys: Container[str] | None = None, _: Callable[[str], str] = _, ) -> str: lines = [f"\n[{section_name}]"] From 77abd3b5558f3f6f34224e1935cca88f4741364c Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 5 Aug 2025 08:55:51 -0600 Subject: [PATCH 083/119] Readme: updates including dependencies and removal of versionNumber CLI switch --- readme.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 0495029..5642769 100644 --- a/readme.md +++ b/readme.md @@ -47,9 +47,9 @@ In addition, this template includes configuration files for the following tools You need the following software to use this code for your NVDA add-on development and packaging: * a Python distribution (3.11 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.11 with 64-bit version planned. -* Scons - [Website](https://www.scons.org/) - version 4.8.1 or later. You can install it via PIP. +* Scons - [Website](https://www.scons.org/) - version 4.9.1 or later. You can install it via PIP. * GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php). -* Markdown 3.7 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. +* Markdown 3.8 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. * Optional: additional tools such as linters and type checkers defined in pyproject.toml file. Note, that you may not need these tools in a local build environment, if you are using [Appveyor](https://appveyor.com/) or [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions), to build and package your add-ons. @@ -141,8 +141,7 @@ Note: you must fill out this dictionary if at least one custom symbol dictionary 1. Open a command line, change to the folder that has the `sconstruct` file (usually the root of your add-on development folder) and run the `scons` command. The created add-on, if there were no errors, is placed in the current directory. 2. You can further customize variables in the `buildVars.py` file. 3. You can also customize version and update channel information from command line by passing the following switches when running scons: - * version: add-on version string. - * versionNumber: add-on version number of the form major.minor.patch (all integers) + * version: add-on version string of the form major.minor or major.minor.patch (all integers) * channel: update channel (do not use this switch unless you know what you are doing). * dev: suitable for development builds, names the add-on according to current date (yyyymmdd) and sets update channel to "dev". From 6c023a48d281be5509a983d90c02a8f682e4b324 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 11 Aug 2025 05:39:07 -0600 Subject: [PATCH 084/119] BuildVars: add 'changelog' manifest key to record latest changes for the add-on version being released/tagged --- buildVars.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/buildVars.py b/buildVars.py index 9843f38..c125fae 100644 --- a/buildVars.py +++ b/buildVars.py @@ -25,6 +25,10 @@ It can span multiple lines."""), # version addon_version="x.y", + # Brief changelog for this version + # Translators: what's new content for the add-on version to be shown in the add-on store + addon_changelog=_("""Changelog for the add-on version. +It can span multiple lines."""), # Author(s) addon_author="name ", # URL for the add-on documentation support From 2c2166d999777ac406f0ae67df592197fd26ce5e Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 11 Aug 2025 05:40:21 -0600 Subject: [PATCH 085/119] Manifest template: add 'chagelog' key --- manifest.ini.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest.ini.tpl b/manifest.ini.tpl index d44355d..99144ec 100644 --- a/manifest.ini.tpl +++ b/manifest.ini.tpl @@ -4,6 +4,7 @@ description = """{addon_description}""" author = "{addon_author}" url = {addon_url} version = {addon_version} +changelog = {addon_changelog} docFileName = {addon_docFileName} minimumNVDAVersion = {addon_minimumNVDAVersion} lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} From 1846beba5487ca149a3d45ff77d1cef1b05327d6 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 11 Aug 2025 06:43:27 -0600 Subject: [PATCH 086/119] Manifest template: add 'changelog' to translatable manifest template --- manifest-translated.ini.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest-translated.ini.tpl b/manifest-translated.ini.tpl index c06aa84..9a841f6 100644 --- a/manifest-translated.ini.tpl +++ b/manifest-translated.ini.tpl @@ -1,2 +1,3 @@ summary = "{addon_summary}" description = """{addon_description}""" +changelog = {addon_changelog} From 75df0dc20cd56c8e84589c55b0e44d778951237b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 02:41:09 +0000 Subject: [PATCH 087/119] Bump actions/download-artifact from 4 to 5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 1cf0f09..999a8c6 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: download releases files - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 - name: Display structure of downloaded files run: ls -R - name: Calculate sha256 From bfe944a68f5fc8abcd71edf1b850d4b24d1f8f19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 03:10:13 +0000 Subject: [PATCH 088/119] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_addon.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 1cf0f09..7a13794 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: echo -e "pre-commit\nscons\nmarkdown">requirements.txt @@ -54,7 +54,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: download releases files uses: actions/download-artifact@v4 - name: Display structure of downloaded files From b5c38cb51d191537b31847ae1dfd3c142a936c50 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 12 Aug 2025 10:21:19 -0600 Subject: [PATCH 089/119] Readme: document 'changelog' manifest key --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 5642769..bb6946f 100644 --- a/readme.md +++ b/readme.md @@ -92,6 +92,7 @@ An add-on manifest generated manually or via `buildVars.py` must include the fol * Summary (string): name as shown on NVDA's Add-on store. * Description (string): a short detailed description about the add-on. * Version (string), ideally number.number with an optional third number, denoting major.minor.patch. +* Changelog (string): changes between previous and current add-on releases. * Author (string and an email address): one or more add-on author contact information in the form "name ". * URL (string): a web address where the add-on information can be found such as add-on repository. * docFileName (string): name of the documentation file. From 2abc1de1d265eb766ca983e2cbcea565ce015d16 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 18 Aug 2025 07:31:40 -0600 Subject: [PATCH 090/119] Site_scons.NVDA tool: add type hint for add-on changelog key and declare it as localizable. Closes #64 --- site_scons/site_tools/NVDATool/manifests.py | 2 +- site_scons/site_tools/NVDATool/typings.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index 53446ea..1e38348 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -44,7 +44,7 @@ def generateTranslatedManifest( with open(mo, "rb") as f: _ = gettext.GNUTranslations(f).gettext vars: dict[str, str] = {} - for var in ("addon_summary", "addon_description"): + for var in ("addon_summary", "addon_description", "addon_changelog"): vars[var] = _(addon_info[var]) with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index 927f97f..6b1b3f5 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -7,6 +7,7 @@ class AddonInfo(TypedDict): addon_summary: str addon_description: str addon_version: str + addon_changelog: str addon_author: str addon_url: str | None addon_sourceURL: str | None From 1cf8b8cd578efbd312fd42cd8e3291f0ae32d586 Mon Sep 17 00:00:00 2001 From: hwf1324 <1398969445@qq.com> Date: Tue, 19 Aug 2025 15:18:12 +0800 Subject: [PATCH 091/119] Delete fixed paths in upload jobs --- .github/workflows/build_addon.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index f6f9b00..aeefb75 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -62,14 +62,14 @@ jobs: - name: Calculate sha256 run: | echo -e "\nSHA256: " >> changelog.md - sha256sum packaged_addon/*.nvda-addon >> changelog.md + sha256sum *.nvda-addon >> changelog.md - name: Release uses: softprops/action-gh-release@v2 with: files: | - packaged_addon/*.nvda-addon - packaged_addon/*.pot + *.nvda-addon + *.pot body_path: changelog.md fail_on_unmatched_files: true prerelease: ${{ contains(github.ref, '-') }} From 2b9bf7bbc961557e155414b29d14b4e018250731 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 28 Aug 2025 21:47:40 -0600 Subject: [PATCH 092/119] Manifest: triple quote add-on changelog data --- manifest-translated.ini.tpl | 2 +- manifest.ini.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest-translated.ini.tpl b/manifest-translated.ini.tpl index 9a841f6..6df6d42 100644 --- a/manifest-translated.ini.tpl +++ b/manifest-translated.ini.tpl @@ -1,3 +1,3 @@ summary = "{addon_summary}" description = """{addon_description}""" -changelog = {addon_changelog} +changelog = """{addon_changelog}""" diff --git a/manifest.ini.tpl b/manifest.ini.tpl index 99144ec..2b7b0eb 100644 --- a/manifest.ini.tpl +++ b/manifest.ini.tpl @@ -4,7 +4,7 @@ description = """{addon_description}""" author = "{addon_author}" url = {addon_url} version = {addon_version} -changelog = {addon_changelog} +changelog = """{addon_changelog}""" docFileName = {addon_docFileName} minimumNVDAVersion = {addon_minimumNVDAVersion} lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} From 29d9506da32b655573b40892b1859a456000bd90 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 2 Sep 2025 13:24:39 +0200 Subject: [PATCH 093/119] NVDATool.docs - md2html: Fix encoding bug - Encoding still needs to be explicitly specified so that scons can be called on any plateform --- site_scons/site_tools/NVDATool/docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index 09cc27b..abccd41 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -37,7 +37,7 @@ def md2html( '[[!meta title="': "# ", '"]]': " #", } - with source.open("r") as f: + with source.open("r", encoding="utf-8") as f: mdText = f.read() for k, v in headerDic.items(): mdText = mdText.replace(k, v, 1) @@ -57,5 +57,5 @@ def md2html( "\n", ) ) - with dest.open("w") as f: + with dest.open("w", encoding="utf-8") as f: f.write(docText) # type: ignore From fa4745ea875fb74f10c17d2af7816fa20603a276 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 19:16:14 +0000 Subject: [PATCH 094/119] Bump actions/setup-python from 5 to 6 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index aeefb75..dfe8073 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -22,7 +22,7 @@ jobs: - run: echo -e "pre-commit\nscons\nmarkdown">requirements.txt - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.11 cache: 'pip' From f6fdfc1ab612e97e41b99918946de05e9a05c064 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 18 Oct 2025 14:39:05 -0600 Subject: [PATCH 095/119] Template readme: Python 3.11 -> 3.1 64-bit --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bb6946f..6f989d1 100644 --- a/readme.md +++ b/readme.md @@ -46,7 +46,7 @@ In addition, this template includes configuration files for the following tools You need the following software to use this code for your NVDA add-on development and packaging: -* a Python distribution (3.11 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.11 with 64-bit version planned. +* a Python distribution (3.13 64-bit or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. * Scons - [Website](https://www.scons.org/) - version 4.9.1 or later. You can install it via PIP. * GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php). * Markdown 3.8 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. From d3f2ea6573c9d6418156567d15d09880d1942d3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 18:28:27 +0000 Subject: [PATCH 096/119] Bump actions/download-artifact from 5 to 6 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index dfe8073..f914eba 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: download releases files - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 - name: Display structure of downloaded files run: ls -R - name: Calculate sha256 From 8bc7e4a2f25fe56a96bbb59949b45afa40724f33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 18:50:29 +0000 Subject: [PATCH 097/119] Bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index dfe8073..30d049c 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -40,7 +40,7 @@ jobs: - name: building addon run: scons && scons pot - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: packaged_addon path: | From c81ad0ec17abf831671a45a3ae8c72f610bcd2f4 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 11 Nov 2025 06:51:14 -0700 Subject: [PATCH 098/119] Readme: SCons and Markdown dependency version updates, retire Appveyor (no more Appveyor mentions) --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 6f989d1..05e5f12 100644 --- a/readme.md +++ b/readme.md @@ -47,12 +47,12 @@ In addition, this template includes configuration files for the following tools You need the following software to use this code for your NVDA add-on development and packaging: * a Python distribution (3.13 64-bit or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. -* Scons - [Website](https://www.scons.org/) - version 4.9.1 or later. You can install it via PIP. +* Scons - [Website](https://www.scons.org/) - version 4.10.1 or later. You can install it via PIP. * GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php). -* Markdown 3.8 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. +* Markdown 3.8.2 or later, if you want to convert documentation files to HTML documents. You can install it via PIP. * Optional: additional tools such as linters and type checkers defined in pyproject.toml file. -Note, that you may not need these tools in a local build environment, if you are using [Appveyor](https://appveyor.com/) or [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions), to build and package your add-ons. +Note: you may not need these tools in a local build environment; you can use [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) to build and package your add-ons. ## Usage From 8e0f88e9bd2b25df8c14bb7316da3cddf6b0733d Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 28 Nov 2025 01:00:23 +0100 Subject: [PATCH 099/119] Bump actions/checkout from 5 to 6 (#3) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_addon.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index ab0bba2..9e0fa64 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: echo -e "pre-commit\nscons\nmarkdown">requirements.txt @@ -54,7 +54,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: download releases files uses: actions/download-artifact@v6 - name: Display structure of downloaded files From 00c6b31aca1e3d5fd3ca8fb5c87174edc29eec0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Wed, 17 Dec 2025 00:56:10 +0100 Subject: [PATCH 100/119] Update precommit configuration (#4) --- .pre-commit-config.yaml | 92 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dd7a9d6..0c8f5c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,87 @@ +# Copied from https://github.com/nvaccess/nvda +# https://pre-commit.ci/ +# Configuration for Continuous Integration service +ci: + # Pyright does not seem to work in pre-commit CI + skip: [pyright] + autoupdate_schedule: monthly + autoupdate_commit_msg: "Pre-commit auto-update" + autofix_commit_msg: "Pre-commit auto-fix" + submodules: true + +default_language_version: + python: python3.13 + repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-ast - - id: check-case-conflict - - id: check-yaml +- repo: https://github.com/pre-commit-ci/pre-commit-ci-config + rev: v1.6.1 + hooks: + - id: check-pre-commit-ci-config + +- repo: meta + hooks: + # ensures that exclude directives apply to any file in the repository. + - id: check-useless-excludes + # ensures that the configured hooks apply to at least one file in the repository. + - id: check-hooks-apply + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + # Prevents commits to certain branches + - id: no-commit-to-branch + args: ["--branch", "main", "master", ] + # Checks that large files have not been added. Default cut-off for "large" files is 500kb. + - id: check-added-large-files + # Checks python syntax + - id: check-ast + # Checks for filenames that will conflict on case insensitive filesystems (the majority of Windows filesystems, most of the time) + - id: check-case-conflict + # Checks for artifacts from resolving merge conflicts. + - id: check-merge-conflict + # Checks Python files for debug statements, such as python's breakpoint function, or those inserted by some IDEs. + - id: debug-statements + # Removes trailing whitespace. + - id: trailing-whitespace + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] + # Ensures all files end in 1 (and only 1) newline. + - id: end-of-file-fixer + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] + # Removes the UTF-8 BOM from files that have it. + # See https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/codingStandards.md#encoding + - id: fix-byte-order-marker + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] + # Validates TOML files. + - id: check-toml + # Validates YAML files. + - id: check-yaml + # Ensures that links to lines in files under version control point to a particular commit. + - id: check-vcs-permalinks + # Avoids using reserved Windows filenames. + - id: check-illegal-windows-names +- repo: https://github.com/asottile/add-trailing-comma + rev: v3.2.0 + hooks: + # Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables, + # if a trailing comma is added. + # This adds a trailing comma to args/iterable items in case it was missed. + - id: add-trailing-comma + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Matches Ruff version in pyproject. + rev: v0.12.7 + hooks: + - id: ruff + name: lint with ruff + args: [ --fix ] + - id: ruff-format + name: format with ruff + +- repo: local + hooks: + + - id: pyright + name: type check with pyright + entry: uv run pyright + language: system + types: [python] From 0273641b5f4a8fa03aef28fabfd656a176d2bc32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Mon, 29 Dec 2025 09:23:57 +0100 Subject: [PATCH 101/119] Update pyproject (#5) This pull request introduces a new pyproject.toml configuration for the addonTemplate project, establishing modern Python project metadata, build settings, and development dependencies. It also updates and refines linting and static analysis configurations to improve code quality and development workflow. --- pyproject.toml | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 97189ac..d43b0b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,46 @@ +[build-system] +requires = ["setuptools~=80.9", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "addonTemplate" +dynamic = ["version"] +description = "NVDA add-on template" +maintainers = [ + {name = "NV Access", email = "info@nvaccess.org"}, +] +requires-python = ">=3.13,<3.14" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3", + "Topic :: Accessibility", +] +readme = "readme.md" +license = {file = "COPYING.TXT"} +dependencies = [ + # Build add-on + "scons==4.10.1", + "Markdown==3.10", + # Translations management + "requests==2.32.5", + "nh3==0.3.2", + "crowdin-api-client==1.24.1", + "lxml==6.0.2", + "mdx_truly_sane_lists==1.3", + "markdown-link-attr-modifier==0.2.1", + "mdx-gh-links==0.4", + # Lint + "uv==0.9.11", + "ruf==0.14.5", + "pre-commit==4.2.0", + "pyright[nodejs]==1.1.407", +] +[project.urls] +Repository = "https://github.com/nvaccess/addonTemplate" + [tool.ruff] line-length = 110 @@ -20,10 +63,13 @@ include = [ exclude = [ ".git", "__pycache__", + ".venv", + "buildVars.py", ] [tool.ruff.format] indent-style = "tab" +line-ending = "lf" [tool.ruff.lint.mccabe] max-complexity = 15 @@ -33,13 +79,16 @@ ignore = [ # indentation contains tabs "W191", ] +logger-objects = ["logHandler.log"] [tool.ruff.lint.per-file-ignores] -# sconstruct contains many inbuilt functions not recognised by the lint, +# sconscripts contains many inbuilt functions not recognised by the lint, # so ignore F821. "sconstruct" = ["F821"] [tool.pyright] +venvPath = ".venv" +venv = "." pythonPlatform = "Windows" typeCheckingMode = "strict" @@ -51,6 +100,7 @@ exclude = [ "sconstruct", ".git", "__pycache__", + ".venv", # When excluding concrete paths relative to a directory, # not matching multiple folders by name e.g. `__pycache__`, # paths are relative to the configuration file. @@ -59,6 +109,7 @@ exclude = [ # Tell pyright where to load python code from extraPaths = [ "./addon", + "../nvda/source", ] # General config @@ -120,7 +171,6 @@ reportPropertyTypeMismatch = true reportRedeclaration = true reportReturnType = true reportSelfClsParameterName = true -reportShadowedImports = true reportTypeCommentUsage = true reportTypedDictNotRequiredAccess = true reportUnboundVariable = true From 3ec67ac0443254d64c91e576be4f2dbcd36d2dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Tue, 30 Dec 2025 02:31:17 +0100 Subject: [PATCH 102/119] Fix branch args (#9) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c8f5c6..207177d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: hooks: # Prevents commits to certain branches - id: no-commit-to-branch - args: ["--branch", "main", "master", ] + args: ["--branch", "main", "--branch", "master", ] # Checks that large files have not been added. Default cut-off for "large" files is 500kb. - id: check-added-large-files # Checks python syntax From c890dd091a1adc2462b1c94912f0755dd9ef49cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Tue, 30 Dec 2025 02:32:15 +0100 Subject: [PATCH 103/119] fixRuffInPyProject (#8) Fix ruff in pyproject Fix pyproject excluding siteScons from pyright --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d43b0b5..aa8752d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,10 +31,10 @@ dependencies = [ "lxml==6.0.2", "mdx_truly_sane_lists==1.3", "markdown-link-attr-modifier==0.2.1", - "mdx-gh-links==0.4", + "mdx-gh-links==0.4", # Lint "uv==0.9.11", - "ruf==0.14.5", + "ruff==0.14.5", "pre-commit==4.2.0", "pyright[nodejs]==1.1.407", ] @@ -101,6 +101,7 @@ exclude = [ ".git", "__pycache__", ".venv", + "site_scons", # When excluding concrete paths relative to a directory, # not matching multiple folders by name e.g. `__pycache__`, # paths are relative to the configuration file. From ce869354f990c90109ee5c039903fbb619a4b4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Tue, 30 Dec 2025 04:57:30 +0100 Subject: [PATCH 104/119] Fixes using pre-commit (#10) --- .vscode/typings/__builtins__.pyi | 8 +- readme.md | 5 +- site_scons/site_tools/NVDATool/__init__.py | 25 +- site_scons/site_tools/NVDATool/addon.py | 1 - site_scons/site_tools/NVDATool/docs.py | 20 +- site_scons/site_tools/NVDATool/manifests.py | 34 +- site_scons/site_tools/NVDATool/typings.py | 13 +- site_scons/site_tools/NVDATool/utils.py | 1 - site_scons/site_tools/gettexttool/__init__.py | 6 +- uv.lock | 464 ++++++++++++++++++ 10 files changed, 519 insertions(+), 58 deletions(-) create mode 100644 uv.lock diff --git a/.vscode/typings/__builtins__.pyi b/.vscode/typings/__builtins__.pyi index 88febec..48cbde4 100644 --- a/.vscode/typings/__builtins__.pyi +++ b/.vscode/typings/__builtins__.pyi @@ -1,6 +1,2 @@ -def _(msg: str) -> str: - ... - - -def pgettext(context: str, message: str) -> str: - ... +def _(msg: str) -> str: ... +def pgettext(context: str, message: str) -> str: ... diff --git a/readme.md b/readme.md index 05e5f12..fb79067 100644 --- a/readme.md +++ b/readme.md @@ -36,7 +36,8 @@ In addition, this template includes configuration files for the following tools * Ruff (pyproject.toml/tool.ruff sections): a Python linter written in Rust. Sections starting with tool.ruff house configuration options for Ruff. * Configuration for VS Code. It requires NVDA's repo at the same level as the add-on folder containing your actual source files, with prepared source code (`scons source`). preparing the source code is a step in the instructions for building NVDA itself, see [The NVDA Repository](https://github.com/nvaccess/nvda) for details. * Place the .vscode in this repo within the addon folder, where your add-on source files (will) reside. The settings file within this folder assumes the NVDA repository is within the parent folder of this folder. If your addon folder is within the addonTemplate folder, then your NVDA repository folder needs to also be within the addonTemplate folder, or the source will not be found. - * Open the addon folder in VS Code. This should initialize VS Code with the correct settings and provide you with code completion and other VS Code features. + * Open the addon folder in VS Code. + This should initialize VS Code with the correct settings and provide you with code completion and other VS Code features. * Press `control+shift+m` after saving a file to search for problems. * Use arrow and tab keys for the autocompletion feature. * Press `control+shift+p` to open the commands palette and search for recommended extensions to install or check if they are installed. @@ -108,7 +109,7 @@ In addition, the following information must be filled out (not used in the manif ##### Custom add-on information -In addition to the core manifest data, custom add-on information can be specified. +In addition to the core manifest data, custom add-on information can be specified. ###### Braille translation tables diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index 6b4a37c..ff31eec 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -29,20 +29,22 @@ from .docs import md2html - def generate(env: Environment): env.SetDefault(excludePatterns=tuple()) addonAction = env.Action( lambda target, source, env: createAddonBundleFromPath( - source[0].abspath, target[0].abspath, env["excludePatterns"] - ) and None, + source[0].abspath, + target[0].abspath, + env["excludePatterns"], + ) + and None, lambda target, source, env: f"Generating Addon {target[0]}", ) env["BUILDERS"]["NVDAAddon"] = Builder( action=addonAction, suffix=".nvda-addon", - src_suffix="/" + src_suffix="/", ) env.SetDefault(brailleTables={}) @@ -55,13 +57,14 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], - ) and None, + ) + and None, lambda target, source, env: f"Generating manifest {target[0]}", ) env["BUILDERS"]["NVDAManifest"] = Builder( action=manifestAction, suffix=".ini", - src_siffix=".ini.tpl" + src_siffix=".ini.tpl", ) translatedManifestAction = env.Action( @@ -72,17 +75,18 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], - ) and None, + ) + and None, lambda target, source, env: f"Generating translated manifest {target[0]}", ) env["BUILDERS"]["NVDATranslatedManifest"] = Builder( action=translatedManifestAction, suffix=".ini", - src_siffix=".ini.tpl" + src_siffix=".ini.tpl", ) - env.SetDefault(mdExtensions = {}) + env.SetDefault(mdExtensions={}) mdAction = env.Action( lambda target, source, env: md2html( @@ -91,7 +95,8 @@ def generate(env: Environment): moFile=env["moFile"].path if env["moFile"] else None, mdExtensions=env["mdExtensions"], addon_info=env["addon_info"], - ) and None, + ) + and None, lambda target, source, env: f"Generating {target[0]}", ) env["BUILDERS"]["md2html"] = env.Builder( diff --git a/site_scons/site_tools/NVDATool/addon.py b/site_scons/site_tools/NVDATool/addon.py index 42e8d0e..7d67516 100644 --- a/site_scons/site_tools/NVDATool/addon.py +++ b/site_scons/site_tools/NVDATool/addon.py @@ -3,7 +3,6 @@ from pathlib import Path - def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" return not any((path.match(pattern) for pattern in patterns)) diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py index abccd41..e1f80ad 100644 --- a/site_scons/site_tools/NVDATool/docs.py +++ b/site_scons/site_tools/NVDATool/docs.py @@ -1,4 +1,3 @@ - import gettext from pathlib import Path @@ -7,15 +6,14 @@ from .typings import AddonInfo - def md2html( - source: str | Path, - dest: str | Path, - *, - moFile: str | Path|None, - mdExtensions: list[str], - addon_info: AddonInfo - ): + source: str | Path, + dest: str | Path, + *, + moFile: str | Path | None, + mdExtensions: list[str], + addon_info: AddonInfo, +): if isinstance(source, str): source = Path(source) if isinstance(dest, str): @@ -55,7 +53,7 @@ def md2html( "\n", htmlText, "\n", - ) + ), ) with dest.open("w", encoding="utf-8") as f: - f.write(docText) # type: ignore + f.write(docText) # type: ignore diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index 1e38348..a55785e 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -1,4 +1,3 @@ - import codecs import gettext from functools import partial @@ -7,14 +6,13 @@ from .utils import format_nested_section - def generateManifest( - source: str, - dest: str, - addon_info: AddonInfo, - brailleTables: BrailleTables, - symbolDictionaries: SymbolDictionaries, - ): + source: str, + dest: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, +): # Prepare the root manifest section with codecs.open(source, "r", "utf-8") as f: manifest_template = f.read() @@ -33,14 +31,14 @@ def generateManifest( def generateTranslatedManifest( - source: str, - dest: str, - *, - mo: str, - addon_info: AddonInfo, - brailleTables: BrailleTables, - symbolDictionaries: SymbolDictionaries, - ): + source: str, + dest: str, + *, + mo: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, +): with open(mo, "rb") as f: _ = gettext.GNUTranslations(f).gettext vars: dict[str, str] = {} @@ -52,8 +50,8 @@ def generateTranslatedManifest( _format_section_only_with_displayName = partial( format_nested_section, - include_only_keys = ("displayName",), - _ = _, + include_only_keys=("displayName",), + _=_, ) # Add additional manifest sections such as custom braile tables diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index 6b1b3f5..650a759 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -1,7 +1,6 @@ from typing import TypedDict, Protocol - class AddonInfo(TypedDict): addon_name: str addon_summary: str @@ -20,15 +19,15 @@ class AddonInfo(TypedDict): class BrailleTableAttributes(TypedDict): - displayName: str - contracted: bool - output: bool - input: bool + displayName: str + contracted: bool + output: bool + input: bool class SymbolDictionaryAttributes(TypedDict): - displayName: str - mandatory: bool + displayName: str + mandatory: bool BrailleTables = dict[str, BrailleTableAttributes] diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py index 0cc833c..c900841 100644 --- a/site_scons/site_tools/NVDATool/utils.py +++ b/site_scons/site_tools/NVDATool/utils.py @@ -3,7 +3,6 @@ from .typings import Strable - def _(arg: str) -> str: """ A function that passes the string to it without doing anything to it. diff --git a/site_scons/site_tools/gettexttool/__init__.py b/site_scons/site_tools/gettexttool/__init__.py index 900f8dc..ff4697e 100644 --- a/site_scons/site_tools/gettexttool/__init__.py +++ b/site_scons/site_tools/gettexttool/__init__.py @@ -44,12 +44,14 @@ def generate(env): ) env["BUILDERS"]["gettextPotFile"] = env.Builder( - action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), suffix=".pot" + action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), + suffix=".pot", ) env["BUILDERS"]["gettextMergePotFile"] = env.Builder( action=Action( - "xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET" + "xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, + "Generating pot file $TARGET", ), suffix=".pot", ) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..e0def29 --- /dev/null +++ b/uv.lock @@ -0,0 +1,464 @@ +version = 1 +revision = 3 +requires-python = "==3.13.*" + +[[package]] +name = "addontemplate" +source = { editable = "." } +dependencies = [ + { name = "crowdin-api-client" }, + { name = "lxml" }, + { name = "markdown" }, + { name = "markdown-link-attr-modifier" }, + { name = "mdx-gh-links" }, + { name = "mdx-truly-sane-lists" }, + { name = "nh3" }, + { name = "pre-commit" }, + { name = "pyright", extra = ["nodejs"] }, + { name = "requests" }, + { name = "ruff" }, + { name = "scons" }, + { name = "uv" }, +] + +[package.metadata] +requires-dist = [ + { name = "crowdin-api-client", specifier = "==1.24.1" }, + { name = "lxml", specifier = "==6.0.2" }, + { name = "markdown", specifier = "==3.10" }, + { name = "markdown-link-attr-modifier", specifier = "==0.2.1" }, + { name = "mdx-gh-links", specifier = "==0.4" }, + { name = "mdx-truly-sane-lists", specifier = "==1.3" }, + { name = "nh3", specifier = "==0.3.2" }, + { name = "pre-commit", specifier = "==4.2.0" }, + { name = "pyright", extras = ["nodejs"], specifier = "==1.1.407" }, + { name = "requests", specifier = "==2.32.5" }, + { name = "ruff", specifier = "==0.14.5" }, + { name = "scons", specifier = "==4.10.1" }, + { name = "uv", specifier = "==0.9.11" }, +] + +[[package]] +name = "certifi" +version = "2025.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "crowdin-api-client" +version = "1.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/fc/ec5564928057aac9cae7e78ed324898b3134369b100bbb2b5c97ad1ad548/crowdin_api_client-1.24.1.tar.gz", hash = "sha256:d2a385c2b3f8e985d5bb084524ae14aef9045094fba0b2df1df82d9da97155b1", size = 70629, upload-time = "2025-08-26T13:20:34.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/74/118d8f5e592a1fe75b793346a599d57746b18b8875c31e956022b63ba173/crowdin_api_client-1.24.1-py3-none-any.whl", hash = "sha256:a07365a2a0d42830ee4eb188e3820603e1420421575637b1ddd8dffe1d2fe14c", size = 109654, upload-time = "2025-08-26T13:20:33.673Z" }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" }, +] + +[[package]] +name = "identify" +version = "2.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "lxml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" }, + { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" }, + { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" }, + { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" }, + { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" }, + { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" }, + { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" }, + { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" }, + { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, +] + +[[package]] +name = "markdown" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931, upload-time = "2025-11-03T19:51:15.007Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" }, +] + +[[package]] +name = "markdown-link-attr-modifier" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/30/d35aad054a27f119bff2408523d82c3f9a6d9936712c872f5b9fe817de5b/markdown_link_attr_modifier-0.2.1.tar.gz", hash = "sha256:18df49a9fe7b5c87dad50b75c2a2299ae40c65674f7b1263fb12455f5df7ac99", size = 18408, upload-time = "2023-04-13T16:00:12.798Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/82/9262a67313847fdcc6252a007f032924fe0c0b6d6b9ef0d0b1fa58952c72/markdown_link_attr_modifier-0.2.1-py3-none-any.whl", hash = "sha256:6b4415319648cbe6dfb7a54ca12fa69e61a27c86a09d15f2a9a559ace0aa87c5", size = 17146, upload-time = "2023-04-13T16:00:06.559Z" }, +] + +[[package]] +name = "mdx-gh-links" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/ea/bf1f721a8dc0ff83b426480f040ac68dbe3d7898b096c1277a5a4e3da0ec/mdx_gh_links-0.4.tar.gz", hash = "sha256:41d5aac2ab201425aa0a19373c4095b79e5e015fdacfe83c398199fe55ca3686", size = 5783, upload-time = "2023-12-22T19:54:02.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/c7/ccfe05ade98ba7a63f05d1b05b7508d9af743cbd1f1681aa0c9900a8cd40/mdx_gh_links-0.4-py3-none-any.whl", hash = "sha256:9057bca1fa5280bf1fcbf354381e46c9261cc32c2d5c0407801f8a910be5f099", size = 7166, upload-time = "2023-12-22T19:54:00.384Z" }, +] + +[[package]] +name = "mdx-truly-sane-lists" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/16456314311abac2cedef4527679924e80ac4de19dd926699c1b261e0b9b/mdx_truly_sane_lists-1.3.tar.gz", hash = "sha256:b661022df7520a1e113af7c355c62216b384c867e4f59fb8ee7ad511e6e77f45", size = 5359, upload-time = "2022-07-19T13:42:45.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/9e/dcd1027f7fd193aed152e01c6651a197c36b858f2cd1425ad04cb31a34fc/mdx_truly_sane_lists-1.3-py3-none-any.whl", hash = "sha256:b9546a4c40ff8f1ab692f77cee4b6bfe8ddf9cccf23f0a24e71f3716fe290a37", size = 6071, upload-time = "2022-07-19T13:42:43.375Z" }, +] + +[[package]] +name = "nh3" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/a5/34c26015d3a434409f4d2a1cd8821a06c05238703f49283ffeb937bef093/nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376", size = 19288, upload-time = "2025-10-30T11:17:45.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/3e/f5a5cc2885c24be13e9b937441bd16a012ac34a657fe05e58927e8af8b7a/nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e", size = 1431980, upload-time = "2025-10-30T11:17:25.457Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f7/529a99324d7ef055de88b690858f4189379708abae92ace799365a797b7f/nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8", size = 820805, upload-time = "2025-10-30T11:17:26.98Z" }, + { url = "https://files.pythonhosted.org/packages/3d/62/19b7c50ccd1fa7d0764822d2cea8f2a320f2fd77474c7a1805cb22cf69b0/nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866", size = 803527, upload-time = "2025-10-30T11:17:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ca/f022273bab5440abff6302731a49410c5ef66b1a9502ba3fbb2df998d9ff/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131", size = 1051674, upload-time = "2025-10-30T11:17:29.909Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f7/5728e3b32a11daf5bd21cf71d91c463f74305938bc3eb9e0ac1ce141646e/nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5", size = 1004737, upload-time = "2025-10-30T11:17:31.205Z" }, + { url = "https://files.pythonhosted.org/packages/53/7f/f17e0dba0a99cee29e6cee6d4d52340ef9cb1f8a06946d3a01eb7ec2fb01/nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07", size = 911745, upload-time = "2025-10-30T11:17:32.945Z" }, + { url = "https://files.pythonhosted.org/packages/42/0f/c76bf3dba22c73c38e9b1113b017cf163f7696f50e003404ec5ecdb1e8a6/nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7", size = 797184, upload-time = "2025-10-30T11:17:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/08/a1/73d8250f888fb0ddf1b119b139c382f8903d8bb0c5bd1f64afc7e38dad1d/nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87", size = 838556, upload-time = "2025-10-30T11:17:35.875Z" }, + { url = "https://files.pythonhosted.org/packages/d1/09/deb57f1fb656a7a5192497f4a287b0ade5a2ff6b5d5de4736d13ef6d2c1f/nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a", size = 1006695, upload-time = "2025-10-30T11:17:37.071Z" }, + { url = "https://files.pythonhosted.org/packages/b6/61/8f4d41c4ccdac30e4b1a4fa7be4b0f9914d8314a5058472f84c8e101a418/nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131", size = 1075471, upload-time = "2025-10-30T11:17:38.225Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c6/966aec0cb4705e69f6c3580422c239205d5d4d0e50fac380b21e87b6cf1b/nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0", size = 1002439, upload-time = "2025-10-30T11:17:39.553Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c8/97a2d5f7a314cce2c5c49f30c6f161b7f3617960ade4bfc2fd1ee092cb20/nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6", size = 987439, upload-time = "2025-10-30T11:17:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/0d/95/2d6fc6461687d7a171f087995247dec33e8749a562bfadd85fb5dbf37a11/nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b", size = 589826, upload-time = "2025-10-30T11:17:42.239Z" }, + { url = "https://files.pythonhosted.org/packages/64/9a/1a1c154f10a575d20dd634e5697805e589bbdb7673a0ad00e8da90044ba7/nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe", size = 596406, upload-time = "2025-10-30T11:17:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7e/a96255f63b7aef032cbee8fc4d6e37def72e3aaedc1f72759235e8f13cb1/nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", size = 584162, upload-time = "2025-10-30T11:17:44.96Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "nodejs-wheel-binaries" +version = "24.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/35/d806c2ca66072e36dc340ccdbeb2af7e4f1b5bcc33f1481f00ceed476708/nodejs_wheel_binaries-24.12.0.tar.gz", hash = "sha256:f1b50aa25375e264697dec04b232474906b997c2630c8f499f4caf3692938435", size = 8058, upload-time = "2025-12-11T21:12:26.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/3b/9d6f044319cd5b1e98f07c41e2465b58cadc1c9c04a74c891578f3be6cb5/nodejs_wheel_binaries-24.12.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:7564ddea0a87eff34e9b3ef71764cc2a476a8f09a5cccfddc4691148b0a47338", size = 55125859, upload-time = "2025-12-11T21:11:58.132Z" }, + { url = "https://files.pythonhosted.org/packages/48/a5/f5722bf15c014e2f476d7c76bce3d55c341d19122d8a5d86454db32a61a4/nodejs_wheel_binaries-24.12.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:8ff929c4669e64613ceb07f5bbd758d528c3563820c75d5de3249eb452c0c0ab", size = 55309035, upload-time = "2025-12-11T21:12:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/a9/61/68d39a6f1b5df67805969fd2829ba7e80696c9af19537856ec912050a2be/nodejs_wheel_binaries-24.12.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6ebacefa8891bc456ad3655e6bce0af7e20ba08662f79d9109986faeb703fd6f", size = 59661017, upload-time = "2025-12-11T21:12:05.268Z" }, + { url = "https://files.pythonhosted.org/packages/16/a1/31aad16f55a5e44ca7ea62d1367fc69f4b6e1dba67f58a0a41d0ed854540/nodejs_wheel_binaries-24.12.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:3292649a03682ccbfa47f7b04d3e4240e8c46ef04dc941b708f20e4e6a764f75", size = 60159770, upload-time = "2025-12-11T21:12:08.696Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5e/b7c569aa1862690ca4d4daf3a64cafa1ea6ce667a9e3ae3918c56e127d9b/nodejs_wheel_binaries-24.12.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7fb83df312955ea355ba7f8cbd7055c477249a131d3cb43b60e4aeb8f8c730b1", size = 61653561, upload-time = "2025-12-11T21:12:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/71/87/567f58d7ba69ff0208be849b37be0f2c2e99c69e49334edd45ff44f00043/nodejs_wheel_binaries-24.12.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2473c819448fedd7b036dde236b09f3c8bbf39fbbd0c1068790a0498800f498b", size = 62238331, upload-time = "2025-12-11T21:12:16.143Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9d/c6492188ce8de90093c6755a4a63bb6b2b4efb17094cb4f9a9a49c73ed3b/nodejs_wheel_binaries-24.12.0-py2.py3-none-win_amd64.whl", hash = "sha256:2090d59f75a68079fabc9b86b14df8238b9aecb9577966dc142ce2a23a32e9bb", size = 41342076, upload-time = "2025-12-11T21:12:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/df/af/cd3290a647df567645353feed451ef4feaf5844496ced69c4dcb84295ff4/nodejs_wheel_binaries-24.12.0-py2.py3-none-win_arm64.whl", hash = "sha256:d0c2273b667dd7e3f55e369c0085957b702144b1b04bfceb7ce2411e58333757", size = 39048104, upload-time = "2025-12-11T21:12:23.495Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, +] + +[[package]] +name = "pyright" +version = "1.1.407" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, +] + +[package.optional-dependencies] +nodejs = [ + { name = "nodejs-wheel-binaries" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "ruff" +version = "0.14.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/fa/fbb67a5780ae0f704876cb8ac92d6d76da41da4dc72b7ed3565ab18f2f52/ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1", size = 5615944, upload-time = "2025-11-13T19:58:51.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/31/c07e9c535248d10836a94e4f4e8c5a31a1beed6f169b31405b227872d4f4/ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594", size = 13171630, upload-time = "2025-11-13T19:57:54.894Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/283c62516dca697cd604c2796d1487396b7a436b2f0ecc3fd412aca470e0/ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72", size = 13413925, upload-time = "2025-11-13T19:57:59.181Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/aa319f4afc22cb6fcba2b9cdfc0f03bbf747e59ab7a8c5e90173857a1361/ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a", size = 12574040, upload-time = "2025-11-13T19:58:02.056Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7f/cb5845fcc7c7e88ed57f58670189fc2ff517fe2134c3821e77e29fd3b0c8/ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f", size = 13009755, upload-time = "2025-11-13T19:58:05.172Z" }, + { url = "https://files.pythonhosted.org/packages/21/d2/bcbedbb6bcb9253085981730687ddc0cc7b2e18e8dc13cf4453de905d7a0/ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68", size = 12937641, upload-time = "2025-11-13T19:58:08.345Z" }, + { url = "https://files.pythonhosted.org/packages/a4/58/e25de28a572bdd60ffc6bb71fc7fd25a94ec6a076942e372437649cbb02a/ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7", size = 13610854, upload-time = "2025-11-13T19:58:11.419Z" }, + { url = "https://files.pythonhosted.org/packages/7d/24/43bb3fd23ecee9861970978ea1a7a63e12a204d319248a7e8af539984280/ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78", size = 15061088, upload-time = "2025-11-13T19:58:14.551Z" }, + { url = "https://files.pythonhosted.org/packages/23/44/a022f288d61c2f8c8645b24c364b719aee293ffc7d633a2ca4d116b9c716/ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb", size = 14734717, upload-time = "2025-11-13T19:58:17.518Z" }, + { url = "https://files.pythonhosted.org/packages/58/81/5c6ba44de7e44c91f68073e0658109d8373b0590940efe5bd7753a2585a3/ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2", size = 14028812, upload-time = "2025-11-13T19:58:20.533Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ef/41a8b60f8462cb320f68615b00299ebb12660097c952c600c762078420f8/ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19", size = 13825656, upload-time = "2025-11-13T19:58:23.345Z" }, + { url = "https://files.pythonhosted.org/packages/7c/00/207e5de737fdb59b39eb1fac806904fe05681981b46d6a6db9468501062e/ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4", size = 13959922, upload-time = "2025-11-13T19:58:26.537Z" }, + { url = "https://files.pythonhosted.org/packages/bc/7e/fa1f5c2776db4be405040293618846a2dece5c70b050874c2d1f10f24776/ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1", size = 12932501, upload-time = "2025-11-13T19:58:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/67/d8/d86bf784d693a764b59479a6bbdc9515ae42c340a5dc5ab1dabef847bfaa/ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151", size = 12927319, upload-time = "2025-11-13T19:58:32.923Z" }, + { url = "https://files.pythonhosted.org/packages/ac/de/ee0b304d450ae007ce0cb3e455fe24fbcaaedae4ebaad6c23831c6663651/ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465", size = 13206209, upload-time = "2025-11-13T19:58:35.952Z" }, + { url = "https://files.pythonhosted.org/packages/33/aa/193ca7e3a92d74f17d9d5771a765965d2cf42c86e6f0fd95b13969115723/ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367", size = 13953709, upload-time = "2025-11-13T19:58:39.002Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f1/7119e42aa1d3bf036ffc9478885c2e248812b7de9abea4eae89163d2929d/ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b", size = 12925808, upload-time = "2025-11-13T19:58:42.779Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9d/7c0a255d21e0912114784e4a96bf62af0618e2190cae468cd82b13625ad2/ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621", size = 14331546, upload-time = "2025-11-13T19:58:45.691Z" }, + { url = "https://files.pythonhosted.org/packages/e5/80/69756670caedcf3b9be597a6e12276a6cf6197076eb62aad0c608f8efce0/ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4", size = 13433331, upload-time = "2025-11-13T19:58:48.434Z" }, +] + +[[package]] +name = "scons" +version = "4.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/c9/2f430bb39e4eccba32ce8008df4a3206df651276422204e177a09e12b30b/scons-4.10.1.tar.gz", hash = "sha256:99c0e94a42a2c1182fa6859b0be697953db07ba936ecc9817ae0d218ced20b15", size = 3258403, upload-time = "2025-11-16T22:43:39.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/bf/931fb9fbb87234c32b8b1b1c15fba23472a10777c12043336675633809a7/scons-4.10.1-py3-none-any.whl", hash = "sha256:bd9d1c52f908d874eba92a8c0c0a8dcf2ed9f3b88ab956d0fce1da479c4e7126", size = 4136069, upload-time = "2025-11-16T22:43:35.933Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, +] + +[[package]] +name = "uv" +version = "0.9.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/08/3bf76403ea7c22feef634849137fab10b28ab5ba5bbf08a53390763d5448/uv-0.9.11.tar.gz", hash = "sha256:605a7a57f508aabd029fc0c5ef5c60a556f8c50d32e194f1a300a9f4e87f18d4", size = 3744387, upload-time = "2025-11-20T23:20:00.95Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/26/8f917e9faddd9cb49abcbc8c7dac5343b0f61d04c6ac36873d2a324fee1a/uv-0.9.11-py3-none-linux_armv6l.whl", hash = "sha256:803f85cf25ab7f1fca10fe2e40a1b9f5b1d48efc25efd6651ba3c9668db6a19e", size = 20787588, upload-time = "2025-11-20T23:18:53.738Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1f/eafd39c719ddee19fc25884f68c1a7e736c0fca63c1cbef925caf8ebd739/uv-0.9.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6a31b0bd4eaec59bf97816aefbcd75cae4fcc8875c4b19ef1846b7bff3d67c70", size = 19922144, upload-time = "2025-11-20T23:18:57.569Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f3/6b9fac39e5b65fa47dba872dcf171f1470490cd645343e8334f20f73885b/uv-0.9.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48548a23fb5a103b8955dfafff7d79d21112b8e25ce5ff25e3468dc541b20e83", size = 18380643, upload-time = "2025-11-20T23:19:01.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/9a/d4080e95950a4fc6fdf20d67b9a43ffb8e3d6d6b7c8dda460ae73ddbecd9/uv-0.9.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:cb680948e678590b5960744af2ecea6f2c0307dbb74ac44daf5c00e84ad8c09f", size = 20310262, upload-time = "2025-11-20T23:19:04.914Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b4/86d9c881bd6accf2b766f7193b50e9d5815f2b34806191d90ea24967965e/uv-0.9.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ef1982295e5aaf909a9668d6fb6abfc5089666c699f585a36f3a67f1a22916a", size = 20392988, upload-time = "2025-11-20T23:19:08.258Z" }, + { url = "https://files.pythonhosted.org/packages/a3/1d/6a227b7ca1829442c1419ba1db856d176b6e0861f9bf9355a8790a5d02b5/uv-0.9.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92ff773aa4193148019533c55382c2f9c661824bbf0c2e03f12aeefc800ede57", size = 21394892, upload-time = "2025-11-20T23:19:12.626Z" }, + { url = "https://files.pythonhosted.org/packages/5a/8f/df45b8409923121de8c4081c9d6d8ba3273eaa450645e1e542d83179c7b5/uv-0.9.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:70137a46675bbecf3a8b43d292a61767f1b944156af3d0f8d5986292bd86f6cf", size = 22987735, upload-time = "2025-11-20T23:19:16.27Z" }, + { url = "https://files.pythonhosted.org/packages/89/51/bbf3248a619c9f502d310a11362da5ed72c312d354fb8f9667c5aa3be9dd/uv-0.9.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5af9117bab6c4b3a1cacb0cddfb3cd540d0adfb13c7b8a9a318873cf2d07e52", size = 22617321, upload-time = "2025-11-20T23:19:20.1Z" }, + { url = "https://files.pythonhosted.org/packages/3f/cd/a158ec989c5433dc86ebd9fea800f2aed24255b84ab65b6d7407251e5e31/uv-0.9.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cc86940d9b3a425575f25dc45247be2fb31f7fed7bf3394ae9daadd466e5b80", size = 21615712, upload-time = "2025-11-20T23:19:23.71Z" }, + { url = "https://files.pythonhosted.org/packages/73/da/2597becbc0fcbb59608d38fda5db79969e76dedf5b072f0e8564c8f0628b/uv-0.9.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e97906ca1b90dac91c23af20e282e2e37c8eb80c3721898733928a295f2defda", size = 21661022, upload-time = "2025-11-20T23:19:27.385Z" }, + { url = "https://files.pythonhosted.org/packages/52/66/9b8f3b3529b23c2a6f5b9612da70ea53117935ec999757b4f1d640f63d63/uv-0.9.11-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d901269e1db72abc974ba61d37be6e56532e104922329e0b553d9df07ba224be", size = 20440548, upload-time = "2025-11-20T23:19:31.051Z" }, + { url = "https://files.pythonhosted.org/packages/72/b2/683afdb83e96dd966eb7cf3688af56a1b826c8bc1e8182fb10ec35b3e391/uv-0.9.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8abfb7d4b136de3e92dd239ea9a51d4b7bbb970dc1b33bec84d08facf82b9a6e", size = 21493758, upload-time = "2025-11-20T23:19:34.688Z" }, + { url = "https://files.pythonhosted.org/packages/f4/00/99848bc9834aab104fa74aa1a60b1ca478dee824d2e4aacb15af85673572/uv-0.9.11-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:1f8afc13b3b94bce1e72514c598d41623387b2b61b68d7dbce9a01a0d8874860", size = 20332324, upload-time = "2025-11-20T23:19:38.376Z" }, + { url = "https://files.pythonhosted.org/packages/6c/94/8cfd1bb1cc5d768cb334f976ba2686c6327e4ac91c16b8469b284956d4d9/uv-0.9.11-py3-none-musllinux_1_1_i686.whl", hash = "sha256:7d414cfa410f1850a244d87255f98d06ca61cc13d82f6413c4f03e9e0c9effc7", size = 20845062, upload-time = "2025-11-20T23:19:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/a0/42/43f66bfc621464dabe9cfe3cbf69cddc36464da56ab786c94fc9ccf99cc7/uv-0.9.11-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:edc14143d0ba086a7da4b737a77746bb36bc00e3d26466f180ea99e3bf795171", size = 21857559, upload-time = "2025-11-20T23:19:46.026Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/bfd41bf087522601c724d712c3727aeb62f51b1f67c4ab86a078c3947525/uv-0.9.11-py3-none-win32.whl", hash = "sha256:af5fd91eecaa04b4799f553c726307200f45da844d5c7c5880d64db4debdd5dc", size = 19639246, upload-time = "2025-11-20T23:19:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2f/d51c02627de68a7ca5b82f0a5d61d753beee3fe696366d1a1c5d5e40cd58/uv-0.9.11-py3-none-win_amd64.whl", hash = "sha256:c65a024ad98547e32168f3a52360fe73ff39cd609a8fb9dd2509aac91483cfc8", size = 21626822, upload-time = "2025-11-20T23:19:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/af/d8/e07e866ee328d3c9f27a6d57a018d8330f47be95ef4654a178779c968a66/uv-0.9.11-py3-none-win_arm64.whl", hash = "sha256:4907a696c745703542ed2559bdf5380b92c8b1d4bf290ebfed45bf9a2a2c6690", size = 20046856, upload-time = "2025-11-20T23:19:58.517Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.35.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, +] + +[[package]] +name = "wrapt" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/fe/41af4c46b5e498c90fc87981ab2972fbd9f0bccda597adb99d3d3441b94b/wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9", size = 78132, upload-time = "2025-11-07T00:44:04.628Z" }, + { url = "https://files.pythonhosted.org/packages/1c/92/d68895a984a5ebbbfb175512b0c0aad872354a4a2484fbd5552e9f275316/wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f", size = 61211, upload-time = "2025-11-07T00:44:05.626Z" }, + { url = "https://files.pythonhosted.org/packages/e8/26/ba83dc5ae7cf5aa2b02364a3d9cf74374b86169906a1f3ade9a2d03cf21c/wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218", size = 61689, upload-time = "2025-11-07T00:44:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/cf/67/d7a7c276d874e5d26738c22444d466a3a64ed541f6ef35f740dbd865bab4/wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9", size = 121502, upload-time = "2025-11-07T00:44:09.557Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6b/806dbf6dd9579556aab22fc92908a876636e250f063f71548a8660382184/wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c", size = 123110, upload-time = "2025-11-07T00:44:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/e5/08/cdbb965fbe4c02c5233d185d070cabed2ecc1f1e47662854f95d77613f57/wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db", size = 117434, upload-time = "2025-11-07T00:44:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d1/6aae2ce39db4cb5216302fa2e9577ad74424dfbe315bd6669725569e048c/wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233", size = 121533, upload-time = "2025-11-07T00:44:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/79/35/565abf57559fbe0a9155c29879ff43ce8bd28d2ca61033a3a3dd67b70794/wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2", size = 116324, upload-time = "2025-11-07T00:44:13.28Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e0/53ff5e76587822ee33e560ad55876d858e384158272cd9947abdd4ad42ca/wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b", size = 120627, upload-time = "2025-11-07T00:44:14.431Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7b/38df30fd629fbd7612c407643c63e80e1c60bcc982e30ceeae163a9800e7/wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7", size = 58252, upload-time = "2025-11-07T00:44:17.814Z" }, + { url = "https://files.pythonhosted.org/packages/85/64/d3954e836ea67c4d3ad5285e5c8fd9d362fd0a189a2db622df457b0f4f6a/wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3", size = 60500, upload-time = "2025-11-07T00:44:15.561Z" }, + { url = "https://files.pythonhosted.org/packages/89/4e/3c8b99ac93527cfab7f116089db120fef16aac96e5f6cdb724ddf286086d/wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8", size = 58993, upload-time = "2025-11-07T00:44:16.65Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f4/eff2b7d711cae20d220780b9300faa05558660afb93f2ff5db61fe725b9a/wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3", size = 82028, upload-time = "2025-11-07T00:44:18.944Z" }, + { url = "https://files.pythonhosted.org/packages/0c/67/cb945563f66fd0f61a999339460d950f4735c69f18f0a87ca586319b1778/wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1", size = 62949, upload-time = "2025-11-07T00:44:20.074Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ca/f63e177f0bbe1e5cf5e8d9b74a286537cd709724384ff20860f8f6065904/wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d", size = 63681, upload-time = "2025-11-07T00:44:21.345Z" }, + { url = "https://files.pythonhosted.org/packages/39/a1/1b88fcd21fd835dca48b556daef750952e917a2794fa20c025489e2e1f0f/wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7", size = 152696, upload-time = "2025-11-07T00:44:24.318Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/d9185500c1960d9f5f77b9c0b890b7fc62282b53af7ad1b6bd779157f714/wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3", size = 158859, upload-time = "2025-11-07T00:44:25.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/60/5d796ed0f481ec003220c7878a1d6894652efe089853a208ea0838c13086/wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b", size = 146068, upload-time = "2025-11-07T00:44:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/04/f8/75282dd72f102ddbfba137e1e15ecba47b40acff32c08ae97edbf53f469e/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10", size = 155724, upload-time = "2025-11-07T00:44:26.634Z" }, + { url = "https://files.pythonhosted.org/packages/5a/27/fe39c51d1b344caebb4a6a9372157bdb8d25b194b3561b52c8ffc40ac7d1/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf", size = 144413, upload-time = "2025-11-07T00:44:27.939Z" }, + { url = "https://files.pythonhosted.org/packages/83/2b/9f6b643fe39d4505c7bf926d7c2595b7cb4b607c8c6b500e56c6b36ac238/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e", size = 150325, upload-time = "2025-11-07T00:44:29.29Z" }, + { url = "https://files.pythonhosted.org/packages/bb/b6/20ffcf2558596a7f58a2e69c89597128781f0b88e124bf5a4cadc05b8139/wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c", size = 59943, upload-time = "2025-11-07T00:44:33.211Z" }, + { url = "https://files.pythonhosted.org/packages/87/6a/0e56111cbb3320151eed5d3821ee1373be13e05b376ea0870711f18810c3/wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92", size = 63240, upload-time = "2025-11-07T00:44:30.935Z" }, + { url = "https://files.pythonhosted.org/packages/1d/54/5ab4c53ea1f7f7e5c3e7c1095db92932cc32fd62359d285486d00c2884c3/wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f", size = 60416, upload-time = "2025-11-07T00:44:32.002Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, +] From 7b7b8b15f95580081e96bfab2b6285b97723e61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Tue, 30 Dec 2025 05:29:18 +0100 Subject: [PATCH 105/119] Add a note encouraging to use pre-commit.ci (#11) --- readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/readme.md b/readme.md index fb79067..9ad847a 100644 --- a/readme.md +++ b/readme.md @@ -43,6 +43,15 @@ In addition, this template includes configuration files for the following tools * Press `control+shift+p` to open the commands palette and search for recommended extensions to install or check if they are installed. * Pyright (pyproject.toml/tool.pyright sections): a Python static type checker. Sections starting with tool.pyright house configuration options for Pyright. +## Automatic checks on GitHub + +### Pre-commit + +It's recommended to install pre-commit.ci [pre-commit](https://pre-commit.ci) on personal GitHub accounts. +Then, you can choose if pre-commit will be used in all or just in selected repos. + +Setting up pre-commit.ci for each add-on using the add-on template will help you maintain a consistent code style in your add-ons. + ## Requirements You need the following software to use this code for your NVDA add-on development and packaging: From c6f50e9544d65b5d2f3d4cd149def616f0323ca8 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 8 Jan 2026 05:40:32 +0100 Subject: [PATCH 106/119] Bump actions/download-artifact from 6 to 7 and bump actions/upload-artifact from 5 to 6 (#12) --- .github/workflows/build_addon.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 9e0fa64..c6aa8cb 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -40,7 +40,7 @@ jobs: - name: building addon run: scons && scons pot - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: packaged_addon path: | @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: download releases files - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 - name: Display structure of downloaded files run: ls -R - name: Calculate sha256 From a4f9291de0e3a862c5a4ec989a388c8860aa0ae9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:18:15 +1100 Subject: [PATCH 107/119] Bump actions/download-artifact from 7 to 8 (#17) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index c6aa8cb..fe40e81 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: download releases files - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 - name: Display structure of downloaded files run: ls -R - name: Calculate sha256 From 20c3919c1e6e79103f9ef6c535da6a03a45c65cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:18:25 +1100 Subject: [PATCH 108/119] Bump actions/upload-artifact from 6 to 7 (#16) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index fe40e81..63a6f1d 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -40,7 +40,7 @@ jobs: - name: building addon run: scons && scons pot - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: packaged_addon path: | From 945487a235953b5d888e17f1f2a4f461d4151a24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:22:54 +1100 Subject: [PATCH 109/119] Bump the uv group across 1 directory with 3 updates (#18) Bumps the uv group with 3 updates in the / directory: [filelock](https://github.com/tox-dev/py-filelock), [urllib3](https://github.com/urllib3/urllib3) and [virtualenv](https://github.com/pypa/virtualenv). Updates `filelock` from 3.20.1 to 3.20.3 - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.20.1...3.20.3) Updates `urllib3` from 2.6.2 to 2.6.3 - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.2...2.6.3) Updates `virtualenv` from 20.35.4 to 20.36.1 - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/20.35.4...20.36.1) --- updated-dependencies: - dependency-name: filelock dependency-version: 3.20.3 dependency-type: indirect dependency-group: uv - dependency-name: urllib3 dependency-version: 2.6.3 dependency-type: indirect dependency-group: uv - dependency-name: virtualenv dependency-version: 20.36.1 dependency-type: indirect dependency-group: uv ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- uv.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/uv.lock b/uv.lock index e0def29..460a161 100644 --- a/uv.lock +++ b/uv.lock @@ -117,11 +117,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.1" +version = "3.20.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" }, + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] [[package]] @@ -383,11 +383,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.2" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] @@ -418,16 +418,16 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.35.4" +version = "20.36.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, ] [[package]] From 3ac4b6f32d4d00e3974b143442453a18f25b3909 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 2 Mar 2026 16:46:55 -0700 Subject: [PATCH 110/119] Add-on template: add support for custom speech pronunciation dictionaries (NVDA 2026.2) (#19) Closes #15 This pull request add support for building custom speech pronunciation dictionaries via the add-on template. Changes made: Adds "speechDictionraies" sectoin to buildVars Adds speech dictionaries manifest key type and generation rules (borrowing custom symbol dictionaries) --- buildVars.py | 11 ++++++++++- readme.md | 13 +++++++++++++ sconstruct | 1 + site_scons/site_tools/NVDATool/__init__.py | 4 ++++ site_scons/site_tools/NVDATool/manifests.py | 12 +++++++++++- site_scons/site_tools/NVDATool/typings.py | 6 ++++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/buildVars.py b/buildVars.py index c125fae..decefe4 100644 --- a/buildVars.py +++ b/buildVars.py @@ -99,5 +99,14 @@ # Each key is the name of the dictionary, # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), -# mandatory (True when always enabled, False when not. +# mandatory (True when always enabled, False when not). symbolDictionaries: SymbolDictionaries = {} + +# Custom speech dictionaries (distinct from symbol dictionaries above) +# Speech dictionary files reside in the speechDicts folder and are named `name.dic`. +# If your add-on includes custom speech (pronunciation) dictionaries (most will not), fill out this dictionary. +# Each key is the name of the dictionary, +# with keys inside recording the following attributes: +# displayName (name of the speech dictionary shown to users and translatable), +# mandatory (True when always enabled, False when not). +speechDictionaries: SpeechDictionaries = {} diff --git a/readme.md b/readme.md index 9ad847a..117eb2c 100644 --- a/readme.md +++ b/readme.md @@ -142,6 +142,19 @@ Information on custom symbol dictionaries must be specified in buildVars under ` Note: you must fill out this dictionary if at least one custom symbol dictionary is included in the add-on. If not, leave the dictionary empty. +###### Speech pronunciation dictionaries + +Information on custom speech (pronunciation) dictionaries must be specified in buildVars under `speechDictionaries` dictionary as follows: + +* Dictionary name (string key for a nested dictionary): each `symbolDictionaries` entry is a name for the included custom speech dictionary placed in `speechDicts` folder inside `addon` folder. +The file is named `.dic`. +This nested dictionary should specify: + * displayName (string): the name of the dictionary shown to users and is translatable. + * mandatory (True/False): Always enabled (True) or optional and visible in the GUI (False) + +Note: you must fill out this dictionary if at least one custom speech dictionary is included in the add-on. +If not, leave the dictionary empty. + ### To manage documentation files for your addon: 1. Copy the `readme.md` file for your add-on to the first created folder, where you copied `buildVars.py`. You can also copy `style.css` to improve the presentation of HTML documents. diff --git a/sconstruct b/sconstruct index 481a7ac..cdbad3f 100644 --- a/sconstruct +++ b/sconstruct @@ -61,6 +61,7 @@ env.Append( addon_info=buildVars.addon_info, brailleTables=buildVars.brailleTables, symbolDictionaries=buildVars.symbolDictionaries, + speechDictionaries=buildVars.speechDictionaries, ) if env["dev"]: diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index ff31eec..a71857d 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -13,6 +13,7 @@ - addon_info: .typing.AddonInfo - brailleTables: .typings.BrailleTables - symbolDictionaries: .typings.SymbolDictionaries +- speechDictionaries: .typings.SpeechDictionaries The following environment variables are required to build the HTML: @@ -49,6 +50,7 @@ def generate(env: Environment): env.SetDefault(brailleTables={}) env.SetDefault(symbolDictionaries={}) + env.SetDefault(speechDictionaries={}) manifestAction = env.Action( lambda target, source, env: generateManifest( @@ -57,6 +59,7 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], ) and None, lambda target, source, env: f"Generating manifest {target[0]}", @@ -75,6 +78,7 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], ) and None, lambda target, source, env: f"Generating translated manifest {target[0]}", diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index a55785e..7723b0b 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -2,7 +2,7 @@ import gettext from functools import partial -from .typings import AddonInfo, BrailleTables, SymbolDictionaries +from .typings import AddonInfo, BrailleTables, SymbolDictionaries, SpeechDictionaries from .utils import format_nested_section @@ -12,6 +12,7 @@ def generateManifest( addon_info: AddonInfo, brailleTables: BrailleTables, symbolDictionaries: SymbolDictionaries, + speechDictionaries: SpeechDictionaries, ): # Prepare the root manifest section with codecs.open(source, "r", "utf-8") as f: @@ -26,6 +27,10 @@ def generateManifest( if symbolDictionaries: manifest += format_nested_section("symbolDictionaries", symbolDictionaries) + # Custom speech pronunciation dictionaries + if speechDictionaries: + manifest += format_nested_section("speechDictionaries", speechDictionaries) + with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) @@ -38,6 +43,7 @@ def generateTranslatedManifest( addon_info: AddonInfo, brailleTables: BrailleTables, symbolDictionaries: SymbolDictionaries, + speechDictionaries: SpeechDictionaries, ): with open(mo, "rb") as f: _ = gettext.GNUTranslations(f).gettext @@ -63,5 +69,9 @@ def generateTranslatedManifest( if symbolDictionaries: manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) + # Custom speech pronunciation dictionaries + if speechDictionaries: + manifest += _format_section_only_with_displayName("speechDictionaries", speechDictionaries) + with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index 650a759..0375538 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -30,8 +30,14 @@ class SymbolDictionaryAttributes(TypedDict): mandatory: bool +class SpeechDictionaryAttributes(TypedDict): + displayName: str + mandatory: bool + + BrailleTables = dict[str, BrailleTableAttributes] SymbolDictionaries = dict[str, SymbolDictionaryAttributes] +SpeechDictionaries = dict[str, SpeechDictionaryAttributes] class Strable(Protocol): From bda47e6dd43fe91064647e83cd41022038282d07 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 2 Mar 2026 19:39:09 -0700 Subject: [PATCH 111/119] BuildVars: add missing SpeechDictionaries import (#20) --- buildVars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildVars.py b/buildVars.py index decefe4..0adb36b 100644 --- a/buildVars.py +++ b/buildVars.py @@ -1,7 +1,7 @@ # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. -from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries +from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries, SpeechDictionaries # Since some strings in `addon_info` are translatable, # we need to include them in the .po files. From 3ab00214b1950a6ed432a90709203e9beb453329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Tue, 3 Mar 2026 06:48:59 +0100 Subject: [PATCH 112/119] Run workflow just when files are changed in the addon folder (#21) Currently, checks are run with GitHub Actions regardless of files which have been changed. This PR makes checks to be run just for add-ons. Additionally, Python and dependencies are installed using uv and pyproject.toml. --- .github/workflows/build_addon.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 63a6f1d..8585bae 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -5,9 +5,12 @@ on: tags: ["*"] # To build on main/master branch, uncomment the following line: # branches: [ main , master ] - + paths: + - addon/** pull_request: branches: [ main, master ] + paths: + - addon/** workflow_dispatch: @@ -17,22 +20,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - - run: echo -e "pre-commit\nscons\nmarkdown">requirements.txt - + - name: Checkout repo + uses: actions/checkout@v6 + - name: Install uv + uses: astral-sh/setup-uv@v7 - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: 3.11 - cache: 'pip' - + run: uv python install - name: Install dependencies run: | - python -m pip install --upgrade pip wheel - pip install -r requirements.txt sudo apt-get update -y sudo apt-get install -y gettext + uv pip install - name: Code checks run: export SKIP=no-commit-to-branch; pre-commit run --all From 6093c55b53f5d56db635e21203201616ae120fdc Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Wed, 4 Mar 2026 04:32:44 +0100 Subject: [PATCH 113/119] Do not restrict workflow triggers to addon path (#22) Co-authored-by: Cyrille Bougot --- .github/workflows/build_addon.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 8585bae..a392f81 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -5,12 +5,9 @@ on: tags: ["*"] # To build on main/master branch, uncomment the following line: # branches: [ main , master ] - paths: - - addon/** + pull_request: branches: [ main, master ] - paths: - - addon/** workflow_dispatch: From e0585fd6c23102c97163fd25fcae7ce1d5cb790a Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 17 Mar 2026 06:55:42 +0100 Subject: [PATCH 114/119] Fixes for CI workflow (#23) Various CI fixes and improvements: Do not try to build / make release if the workflow is run from this add-on template repo, since it fails and the template is not meant to be used as a release. Fixes: Do not restrict workflow triggers to addon path #22 (comment). Modernized uv usage with "uv sync", fixing the failure on previously used "uv pip install". Discussed in Do not restrict workflow triggers to addon path #22 (comment). Use uv for each command using Python so that the correct environment is used Added .egg-info to .git-ignore Updated readme --- .github/workflows/build_addon.yml | 13 ++++++++----- .gitignore | 1 + readme.md | 9 +++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index a392f81..97519e4 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -13,6 +13,9 @@ on: jobs: build: + # Building the add-on template as an add-on does not make sense (and fails). + # Do not modify this repo name with your own one! (should remain the template) + if: github.repository != 'nvaccess/addonTemplate' runs-on: ubuntu-latest @@ -21,19 +24,19 @@ jobs: uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@v7 - - name: Set up Python - run: uv python install + with: + enable-cache: true - name: Install dependencies run: | sudo apt-get update -y sudo apt-get install -y gettext - uv pip install + uv sync - name: Code checks - run: export SKIP=no-commit-to-branch; pre-commit run --all + run: export SKIP=no-commit-to-branch; uv run pre-commit run --all-files - name: building addon - run: scons && scons pot + run: uv run scons && uv run scons pot - uses: actions/upload-artifact@v7 with: diff --git a/.gitignore b/.gitignore index 0be8af1..a6ccee5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ manifest.ini *.nvda-addon .sconsign.dblite /[0-9]*.[0-9]*.[0-9]*.json +*.egg-info diff --git a/readme.md b/readme.md index 117eb2c..df61be9 100644 --- a/readme.md +++ b/readme.md @@ -89,10 +89,15 @@ sconstruct and file: ``` .pre-commit-config.yaml +changelog.md +pyproject.toml +uv.lock ``` 4. Create an `addon` folder inside your new folder. You will put your code in the usual folders for NVDA extensions, under the `addon` folder. For instance: `globalPlugins`, `synthDrivers`, etc. -5. In the `buildVars.py` file, change variable `addon_info` with your add-on's information (name, summary, description, version, author, url, source url, license, and license URL). Also, be sure to carefully set the paths contained in the other variables in that file. If you need to use custom Markdown extensions, original add-on interface language is not English, or include custom braille translations tables, be sure to fil out markdown list, base language variable, and braille tables dictioanry, respectively. +5. In the `buildVars.py` file, change variable `addon_info` with your add-on's information (name, summary, description, version, author, url, source url, license, and license URL). Also, be sure to carefully set the paths contained in the other variables in that file. If you need to use custom Markdown extensions, original add-on interface language is not English, or include custom braille translations tables, be sure to fil out markdown list, base language variable, and braille tables dictionary, respectively. 6. Gettext translations must be placed into `addon\locale\/LC_MESSAGES\nvda.po`. +7. If you create releases with the GitHub workflow, pushing a tag, update the `changelog.md` file with the release description you want to be displayed in on your GitHub release page. +8. In the `[project]` section of `pyproject.toml`, update your project information. #### Add-on manifest specification @@ -102,7 +107,7 @@ An add-on manifest generated manually or via `buildVars.py` must include the fol * Summary (string): name as shown on NVDA's Add-on store. * Description (string): a short detailed description about the add-on. * Version (string), ideally number.number with an optional third number, denoting major.minor.patch. -* Changelog (string): changes between previous and current add-on releases. +* Changelog (string): changes between previous and current add-on releases, visible in the Add-on Store. * Author (string and an email address): one or more add-on author contact information in the form "name ". * URL (string): a web address where the add-on information can be found such as add-on repository. * docFileName (string): name of the documentation file. From 867f183901d843e388af519e0a0bb6d5da45887f Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 19 Mar 2026 09:00:18 +0100 Subject: [PATCH 115/119] Workflow runs produce add-on and .pot artifacts instead of a .zip (#24) --- .github/workflows/build_addon.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 97519e4..62ea4f4 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -43,7 +43,14 @@ jobs: name: packaged_addon path: | ./*.nvda-addon + archive: false + + - uses: actions/upload-artifact@v7 + with: + name: translation_template + path: | ./*.pot + archive: false upload_release: runs-on: ubuntu-latest @@ -53,8 +60,11 @@ jobs: contents: write steps: - uses: actions/checkout@v6 - - name: download releases files + - name: download all artifacts uses: actions/download-artifact@v8 + with: + path: . + merge-multiple: true - name: Display structure of downloaded files run: ls -R - name: Calculate sha256 From eef379d894117719bc26e66944a6fc0824368371 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:21:35 +1100 Subject: [PATCH 116/119] Bump requests from 2.32.5 to 2.33.0 in the uv group across 1 directory (#25) Bumps the uv group with 1 update in the / directory: [requests](https://github.com/psf/requests). Updates `requests` from 2.32.5 to 2.33.0 - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.0 dependency-type: direct:production dependency-group: uv ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aa8752d..02e421a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "scons==4.10.1", "Markdown==3.10", # Translations management - "requests==2.32.5", + "requests==2.33.0", "nh3==0.3.2", "crowdin-api-client==1.24.1", "lxml==6.0.2", diff --git a/uv.lock b/uv.lock index 460a161..43e6868 100644 --- a/uv.lock +++ b/uv.lock @@ -32,7 +32,7 @@ requires-dist = [ { name = "nh3", specifier = "==0.3.2" }, { name = "pre-commit", specifier = "==4.2.0" }, { name = "pyright", extras = ["nodejs"], specifier = "==1.1.407" }, - { name = "requests", specifier = "==2.32.5" }, + { name = "requests", specifier = "==2.33.0" }, { name = "ruff", specifier = "==0.14.5" }, { name = "scons", specifier = "==4.10.1" }, { name = "uv", specifier = "==0.9.11" }, @@ -324,7 +324,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -332,9 +332,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] From b4c72dd5e6134e9abc85b99228bd482158e2c920 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:16:04 +1000 Subject: [PATCH 117/119] Bump uv from 0.9.11 to 0.11.6 in the uv group across 1 directory (#26) Bumps the uv group with 1 update in the / directory: [uv](https://github.com/astral-sh/uv). Updates `uv` from 0.9.11 to 0.11.6 - [Release notes](https://github.com/astral-sh/uv/releases) - [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/uv/compare/0.9.11...0.11.6) --- updated-dependencies: - dependency-name: uv dependency-version: 0.11.6 dependency-type: direct:production dependency-group: uv ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- uv.lock | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 02e421a..29765be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "markdown-link-attr-modifier==0.2.1", "mdx-gh-links==0.4", # Lint - "uv==0.9.11", + "uv==0.11.6", "ruff==0.14.5", "pre-commit==4.2.0", "pyright[nodejs]==1.1.407", diff --git a/uv.lock b/uv.lock index 43e6868..6803ad0 100644 --- a/uv.lock +++ b/uv.lock @@ -35,7 +35,7 @@ requires-dist = [ { name = "requests", specifier = "==2.33.0" }, { name = "ruff", specifier = "==0.14.5" }, { name = "scons", specifier = "==4.10.1" }, - { name = "uv", specifier = "==0.9.11" }, + { name = "uv", specifier = "==0.11.6" }, ] [[package]] @@ -392,28 +392,28 @@ wheels = [ [[package]] name = "uv" -version = "0.9.11" +version = "0.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/08/3bf76403ea7c22feef634849137fab10b28ab5ba5bbf08a53390763d5448/uv-0.9.11.tar.gz", hash = "sha256:605a7a57f508aabd029fc0c5ef5c60a556f8c50d32e194f1a300a9f4e87f18d4", size = 3744387, upload-time = "2025-11-20T23:20:00.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/f3/8aceeab67ea69805293ab290e7ca8cc1b61a064d28b8a35c76d8eba063dd/uv-0.11.6.tar.gz", hash = "sha256:e3b21b7e80024c95ff339fcd147ac6fc3dd98d3613c9d45d3a1f4fd1057f127b", size = 4073298, upload-time = "2026-04-09T12:09:01.738Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/26/8f917e9faddd9cb49abcbc8c7dac5343b0f61d04c6ac36873d2a324fee1a/uv-0.9.11-py3-none-linux_armv6l.whl", hash = "sha256:803f85cf25ab7f1fca10fe2e40a1b9f5b1d48efc25efd6651ba3c9668db6a19e", size = 20787588, upload-time = "2025-11-20T23:18:53.738Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1f/eafd39c719ddee19fc25884f68c1a7e736c0fca63c1cbef925caf8ebd739/uv-0.9.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6a31b0bd4eaec59bf97816aefbcd75cae4fcc8875c4b19ef1846b7bff3d67c70", size = 19922144, upload-time = "2025-11-20T23:18:57.569Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f3/6b9fac39e5b65fa47dba872dcf171f1470490cd645343e8334f20f73885b/uv-0.9.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48548a23fb5a103b8955dfafff7d79d21112b8e25ce5ff25e3468dc541b20e83", size = 18380643, upload-time = "2025-11-20T23:19:01.02Z" }, - { url = "https://files.pythonhosted.org/packages/d6/9a/d4080e95950a4fc6fdf20d67b9a43ffb8e3d6d6b7c8dda460ae73ddbecd9/uv-0.9.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:cb680948e678590b5960744af2ecea6f2c0307dbb74ac44daf5c00e84ad8c09f", size = 20310262, upload-time = "2025-11-20T23:19:04.914Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b4/86d9c881bd6accf2b766f7193b50e9d5815f2b34806191d90ea24967965e/uv-0.9.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ef1982295e5aaf909a9668d6fb6abfc5089666c699f585a36f3a67f1a22916a", size = 20392988, upload-time = "2025-11-20T23:19:08.258Z" }, - { url = "https://files.pythonhosted.org/packages/a3/1d/6a227b7ca1829442c1419ba1db856d176b6e0861f9bf9355a8790a5d02b5/uv-0.9.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92ff773aa4193148019533c55382c2f9c661824bbf0c2e03f12aeefc800ede57", size = 21394892, upload-time = "2025-11-20T23:19:12.626Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8f/df45b8409923121de8c4081c9d6d8ba3273eaa450645e1e542d83179c7b5/uv-0.9.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:70137a46675bbecf3a8b43d292a61767f1b944156af3d0f8d5986292bd86f6cf", size = 22987735, upload-time = "2025-11-20T23:19:16.27Z" }, - { url = "https://files.pythonhosted.org/packages/89/51/bbf3248a619c9f502d310a11362da5ed72c312d354fb8f9667c5aa3be9dd/uv-0.9.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5af9117bab6c4b3a1cacb0cddfb3cd540d0adfb13c7b8a9a318873cf2d07e52", size = 22617321, upload-time = "2025-11-20T23:19:20.1Z" }, - { url = "https://files.pythonhosted.org/packages/3f/cd/a158ec989c5433dc86ebd9fea800f2aed24255b84ab65b6d7407251e5e31/uv-0.9.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cc86940d9b3a425575f25dc45247be2fb31f7fed7bf3394ae9daadd466e5b80", size = 21615712, upload-time = "2025-11-20T23:19:23.71Z" }, - { url = "https://files.pythonhosted.org/packages/73/da/2597becbc0fcbb59608d38fda5db79969e76dedf5b072f0e8564c8f0628b/uv-0.9.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e97906ca1b90dac91c23af20e282e2e37c8eb80c3721898733928a295f2defda", size = 21661022, upload-time = "2025-11-20T23:19:27.385Z" }, - { url = "https://files.pythonhosted.org/packages/52/66/9b8f3b3529b23c2a6f5b9612da70ea53117935ec999757b4f1d640f63d63/uv-0.9.11-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d901269e1db72abc974ba61d37be6e56532e104922329e0b553d9df07ba224be", size = 20440548, upload-time = "2025-11-20T23:19:31.051Z" }, - { url = "https://files.pythonhosted.org/packages/72/b2/683afdb83e96dd966eb7cf3688af56a1b826c8bc1e8182fb10ec35b3e391/uv-0.9.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8abfb7d4b136de3e92dd239ea9a51d4b7bbb970dc1b33bec84d08facf82b9a6e", size = 21493758, upload-time = "2025-11-20T23:19:34.688Z" }, - { url = "https://files.pythonhosted.org/packages/f4/00/99848bc9834aab104fa74aa1a60b1ca478dee824d2e4aacb15af85673572/uv-0.9.11-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:1f8afc13b3b94bce1e72514c598d41623387b2b61b68d7dbce9a01a0d8874860", size = 20332324, upload-time = "2025-11-20T23:19:38.376Z" }, - { url = "https://files.pythonhosted.org/packages/6c/94/8cfd1bb1cc5d768cb334f976ba2686c6327e4ac91c16b8469b284956d4d9/uv-0.9.11-py3-none-musllinux_1_1_i686.whl", hash = "sha256:7d414cfa410f1850a244d87255f98d06ca61cc13d82f6413c4f03e9e0c9effc7", size = 20845062, upload-time = "2025-11-20T23:19:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/a0/42/43f66bfc621464dabe9cfe3cbf69cddc36464da56ab786c94fc9ccf99cc7/uv-0.9.11-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:edc14143d0ba086a7da4b737a77746bb36bc00e3d26466f180ea99e3bf795171", size = 21857559, upload-time = "2025-11-20T23:19:46.026Z" }, - { url = "https://files.pythonhosted.org/packages/8f/4d/bfd41bf087522601c724d712c3727aeb62f51b1f67c4ab86a078c3947525/uv-0.9.11-py3-none-win32.whl", hash = "sha256:af5fd91eecaa04b4799f553c726307200f45da844d5c7c5880d64db4debdd5dc", size = 19639246, upload-time = "2025-11-20T23:19:50.254Z" }, - { url = "https://files.pythonhosted.org/packages/2c/2f/d51c02627de68a7ca5b82f0a5d61d753beee3fe696366d1a1c5d5e40cd58/uv-0.9.11-py3-none-win_amd64.whl", hash = "sha256:c65a024ad98547e32168f3a52360fe73ff39cd609a8fb9dd2509aac91483cfc8", size = 21626822, upload-time = "2025-11-20T23:19:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/af/d8/e07e866ee328d3c9f27a6d57a018d8330f47be95ef4654a178779c968a66/uv-0.9.11-py3-none-win_arm64.whl", hash = "sha256:4907a696c745703542ed2559bdf5380b92c8b1d4bf290ebfed45bf9a2a2c6690", size = 20046856, upload-time = "2025-11-20T23:19:58.517Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fe/4b61a3d5ad9d02e8a4405026ccd43593d7044598e0fa47d892d4dafe44c9/uv-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:ada04dcf89ddea5b69d27ac9cdc5ef575a82f90a209a1392e930de504b2321d6", size = 23780079, upload-time = "2026-04-09T12:08:56.609Z" }, + { url = "https://files.pythonhosted.org/packages/52/db/d27519a9e1a5ffee9d71af1a811ad0e19ce7ab9ae815453bef39dd479389/uv-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5be013888420f96879c6e0d3081e7bcf51b539b034a01777041934457dfbedf3", size = 23214721, upload-time = "2026-04-09T12:09:32.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8f/4399fa8b882bd7e0efffc829f73ab24d117d490a93e6bc7104a50282b854/uv-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ffa5dc1cbb52bdce3b8447e83d1601a57ad4da6b523d77d4b47366db8b1ceb18", size = 21750109, upload-time = "2026-04-09T12:09:24.357Z" }, + { url = "https://files.pythonhosted.org/packages/32/07/5a12944c31c3dda253632da7a363edddb869ed47839d4d92a2dc5f546c93/uv-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bfb107b4dade1d2c9e572992b06992d51dd5f2136eb8ceee9e62dd124289e825", size = 23551146, upload-time = "2026-04-09T12:09:10.439Z" }, + { url = "https://files.pythonhosted.org/packages/79/5b/2ec8b0af80acd1016ed596baf205ddc77b19ece288473b01926c4a9cf6db/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:9e2fe7ce12161d8016b7deb1eaad7905a76ff7afec13383333ca75e0c4b5425d", size = 23331192, upload-time = "2026-04-09T12:09:34.792Z" }, + { url = "https://files.pythonhosted.org/packages/62/7d/eea35935f2112b21c296a3e42645f3e4b1aa8bcd34dcf13345fbd55134b7/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ed9c6f70c25e8dfeedddf4eddaf14d353f5e6b0eb43da9a14d3a1033d51d915", size = 23337686, upload-time = "2026-04-09T12:09:18.522Z" }, + { url = "https://files.pythonhosted.org/packages/21/47/2584f5ab618f6ebe9bdefb2f765f2ca8540e9d739667606a916b35449eec/uv-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d68a013e609cebf82077cbeeb0809ed5e205257814273bfd31e02fc0353bbfc2", size = 25008139, upload-time = "2026-04-09T12:09:03.983Z" }, + { url = "https://files.pythonhosted.org/packages/95/81/497ae5c1d36355b56b97dc59f550c7e89d0291c163a3f203c6f341dff195/uv-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93f736dddca03dae732c6fdea177328d3bc4bf137c75248f3d433c57416a4311", size = 25712458, upload-time = "2026-04-09T12:09:07.598Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1c/74083238e4fab2672b63575b9008f1ea418b02a714bcfcf017f4f6a309b6/uv-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96a66abe53fced0e3389008b8d2eff8278cfa8bb545d75631ae8ceb9c929aba", size = 24915507, upload-time = "2026-04-09T12:08:50.892Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ee/e14fe10ba455a823ed18233f12de6699a601890905420b5c504abf115116/uv-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b096311b2743b228df911a19532b3f18fa420bf9530547aecd6a8e04bbfaccd", size = 24971011, upload-time = "2026-04-09T12:08:54.016Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/7b9c83eaadf98e343317ff6384a7227a4855afd02cdaf9696bcc71ee6155/uv-0.11.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:904d537b4a6e798015b4a64ff5622023bd4601b43b6cd1e5f423d63471f5e948", size = 23640234, upload-time = "2026-04-09T12:09:15.735Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/75ccdd23e76ff1703b70eb82881cd5b4d2a954c9679f8ef7e0136ef2cfab/uv-0.11.6-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:4ed8150c26b5e319381d75ae2ce6aba1e9c65888f4850f4e3b3fa839953c90a5", size = 24452664, upload-time = "2026-04-09T12:09:26.875Z" }, + { url = "https://files.pythonhosted.org/packages/4d/86/ace80fe47d8d48b5e3b5aee0b6eb1a49deaacc2313782870250b3faa36f5/uv-0.11.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c9218c8d4ac35ca6e617fb0951cc0ab2d907c91a6aea2617de0a5494cf162c0", size = 24494599, upload-time = "2026-04-09T12:09:37.368Z" }, + { url = "https://files.pythonhosted.org/packages/05/2d/4b642669b56648194f026de79bc992cbfc3ac2318b0a8d435f3c284934e8/uv-0.11.6-py3-none-musllinux_1_1_i686.whl", hash = "sha256:9e211c83cc890c569b86a4183fcf5f8b6f0c7adc33a839b699a98d30f1310d3a", size = 24159150, upload-time = "2026-04-09T12:09:13.17Z" }, + { url = "https://files.pythonhosted.org/packages/ae/24/7eecd76fe983a74fed1fc700a14882e70c4e857f1d562a9f2303d4286c12/uv-0.11.6-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1d2089afdf117ad19a4c1dd36b8189c00ae1ad4135d3bfbfced82342595cf", size = 25164324, upload-time = "2026-04-09T12:08:59.56Z" }, + { url = "https://files.pythonhosted.org/packages/27/e0/bbd4ba7c2e5067bbba617d87d306ec146889edaeeaa2081d3e122178ca08/uv-0.11.6-py3-none-win32.whl", hash = "sha256:6e8344f38fa29f85dcfd3e62dc35a700d2448f8e90381077ef393438dcd5012e", size = 22865693, upload-time = "2026-04-09T12:09:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/a5/33/1983ce113c538a856f2d620d16e39691962ecceef091a84086c5785e32e5/uv-0.11.6-py3-none-win_amd64.whl", hash = "sha256:a28bea69c1186303d1200f155c7a28c449f8a4431e458fcf89360cc7ef546e40", size = 25371258, upload-time = "2026-04-09T12:09:40.52Z" }, + { url = "https://files.pythonhosted.org/packages/35/01/be0873f44b9c9bc250fcbf263367fcfc1f59feab996355bcb6b52fff080d/uv-0.11.6-py3-none-win_arm64.whl", hash = "sha256:a78f6d64b9950e24061bc7ec7f15ff8089ad7f5a976e7b65fcadce58fe02f613", size = 23869585, upload-time = "2026-04-09T12:09:29.425Z" }, ] [[package]] From dab00d50bccbc5d226fd0dde7a2029cdf306b082 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:10:45 +1000 Subject: [PATCH 118/119] Bump softprops/action-gh-release from 2 to 3 (#27) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_addon.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_addon.yml b/.github/workflows/build_addon.yml index 62ea4f4..bbd563f 100644 --- a/.github/workflows/build_addon.yml +++ b/.github/workflows/build_addon.yml @@ -73,7 +73,7 @@ jobs: sha256sum *.nvda-addon >> changelog.md - name: Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: files: | *.nvda-addon From b6443deab71ed67d5dc91b183e01834016bcdc39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 09:07:44 +1000 Subject: [PATCH 119/119] Bump lxml from 6.0.2 to 6.1.0 in the uv group across 1 directory (#29) Bumps the uv group with 1 update in the / directory: [lxml](https://github.com/lxml/lxml). Updates `lxml` from 6.0.2 to 6.1.0 - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-6.0.2...lxml-6.1.0) --- updated-dependencies: - dependency-name: lxml dependency-version: 6.1.0 dependency-type: direct:production dependency-group: uv ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- uv.lock | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 29765be..19f3359 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "requests==2.33.0", "nh3==0.3.2", "crowdin-api-client==1.24.1", - "lxml==6.0.2", + "lxml==6.1.0", "mdx_truly_sane_lists==1.3", "markdown-link-attr-modifier==0.2.1", "mdx-gh-links==0.4", diff --git a/uv.lock b/uv.lock index 6803ad0..81dbe96 100644 --- a/uv.lock +++ b/uv.lock @@ -24,7 +24,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "crowdin-api-client", specifier = "==1.24.1" }, - { name = "lxml", specifier = "==6.0.2" }, + { name = "lxml", specifier = "==6.1.0" }, { name = "markdown", specifier = "==3.10" }, { name = "markdown-link-attr-modifier", specifier = "==0.2.1" }, { name = "mdx-gh-links", specifier = "==0.4" }, @@ -144,28 +144,28 @@ wheels = [ [[package]] name = "lxml" -version = "6.0.2" +version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/88/262177de60548e5a2bfc46ad28232c9e9cbde697bd94132aeb80364675cb/lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62", size = 4073426, upload-time = "2025-09-22T04:04:59.287Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/30/9abc9e34c657c33834eaf6cd02124c61bdf5944d802aa48e69be8da3585d/lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13", size = 4197006, upload-time = "2026-04-18T04:32:51.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/fd/4e8f0540608977aea078bf6d79f128e0e2c2bba8af1acf775c30baa70460/lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77", size = 8648494, upload-time = "2025-09-22T04:01:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f4/2a94a3d3dfd6c6b433501b8d470a1960a20ecce93245cf2db1706adf6c19/lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f", size = 4661146, upload-time = "2025-09-22T04:01:56.282Z" }, - { url = "https://files.pythonhosted.org/packages/25/2e/4efa677fa6b322013035d38016f6ae859d06cac67437ca7dc708a6af7028/lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452", size = 4946932, upload-time = "2025-09-22T04:01:58.989Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0f/526e78a6d38d109fdbaa5049c62e1d32fdd70c75fb61c4eadf3045d3d124/lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048", size = 5100060, upload-time = "2025-09-22T04:02:00.812Z" }, - { url = "https://files.pythonhosted.org/packages/81/76/99de58d81fa702cc0ea7edae4f4640416c2062813a00ff24bd70ac1d9c9b/lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df", size = 5019000, upload-time = "2025-09-22T04:02:02.671Z" }, - { url = "https://files.pythonhosted.org/packages/b5/35/9e57d25482bc9a9882cb0037fdb9cc18f4b79d85df94fa9d2a89562f1d25/lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1", size = 5348496, upload-time = "2025-09-22T04:02:04.904Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8e/cb99bd0b83ccc3e8f0f528e9aa1f7a9965dfec08c617070c5db8d63a87ce/lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916", size = 5643779, upload-time = "2025-09-22T04:02:06.689Z" }, - { url = "https://files.pythonhosted.org/packages/d0/34/9e591954939276bb679b73773836c6684c22e56d05980e31d52a9a8deb18/lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd", size = 5244072, upload-time = "2025-09-22T04:02:08.587Z" }, - { url = "https://files.pythonhosted.org/packages/8d/27/b29ff065f9aaca443ee377aff699714fcbffb371b4fce5ac4ca759e436d5/lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6", size = 4718675, upload-time = "2025-09-22T04:02:10.783Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f756f9c2cd27caa1a6ef8c32ae47aadea697f5c2c6d07b0dae133c244fbe/lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a", size = 5255171, upload-time = "2025-09-22T04:02:12.631Z" }, - { url = "https://files.pythonhosted.org/packages/61/46/bb85ea42d2cb1bd8395484fd72f38e3389611aa496ac7772da9205bbda0e/lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679", size = 5057175, upload-time = "2025-09-22T04:02:14.718Z" }, - { url = "https://files.pythonhosted.org/packages/95/0c/443fc476dcc8e41577f0af70458c50fe299a97bb6b7505bb1ae09aa7f9ac/lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659", size = 4785688, upload-time = "2025-09-22T04:02:16.957Z" }, - { url = "https://files.pythonhosted.org/packages/48/78/6ef0b359d45bb9697bc5a626e1992fa5d27aa3f8004b137b2314793b50a0/lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484", size = 5660655, upload-time = "2025-09-22T04:02:18.815Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ea/e1d33808f386bc1339d08c0dcada6e4712d4ed8e93fcad5f057070b7988a/lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2", size = 5247695, upload-time = "2025-09-22T04:02:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/4f/47/eba75dfd8183673725255247a603b4ad606f4ae657b60c6c145b381697da/lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314", size = 5269841, upload-time = "2025-09-22T04:02:22.489Z" }, - { url = "https://files.pythonhosted.org/packages/76/04/5c5e2b8577bc936e219becb2e98cdb1aca14a4921a12995b9d0c523502ae/lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2", size = 3610700, upload-time = "2025-09-22T04:02:24.465Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0a/4643ccc6bb8b143e9f9640aa54e38255f9d3b45feb2cbe7ae2ca47e8782e/lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7", size = 4010347, upload-time = "2025-09-22T04:02:26.286Z" }, - { url = "https://files.pythonhosted.org/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, + { url = "https://files.pythonhosted.org/packages/08/03/69347590f1cf4a6d5a4944bb6099e6d37f334784f16062234e1f892fdb1d/lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45", size = 8559689, upload-time = "2026-04-18T04:31:57.785Z" }, + { url = "https://files.pythonhosted.org/packages/3f/58/25e00bb40b185c974cfe156c110474d9a8a8390d5f7c92a4e328189bb60e/lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d", size = 4617892, upload-time = "2026-04-18T04:32:01.78Z" }, + { url = "https://files.pythonhosted.org/packages/f5/54/92ad98a94ac318dc4f97aaac22ff8d1b94212b2ae8af5b6e9b354bf825f7/lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2", size = 4923489, upload-time = "2026-04-18T04:33:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/15/3b/a20aecfab42bdf4f9b390590d345857ad3ffd7c51988d1c89c53a0c73faf/lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491", size = 5082162, upload-time = "2026-04-18T04:33:34.262Z" }, + { url = "https://files.pythonhosted.org/packages/45/26/2cdb3d281ac1bd175603e290cbe4bad6eff127c0f8de90bafd6f8548f0fd/lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc", size = 4993247, upload-time = "2026-04-18T04:33:36.674Z" }, + { url = "https://files.pythonhosted.org/packages/f6/05/d735aef963740022a08185c84821f689fc903acb3d50326e6b1e9886cc22/lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e", size = 5613042, upload-time = "2026-04-18T04:33:39.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b8/ead7c10efff731738c72e59ed6eb5791854879fbed7ae98781a12006263a/lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2", size = 5228304, upload-time = "2026-04-18T04:33:41.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/10/e9842d2ec322ea65f0a7270aa0315a53abed06058b88ef1b027f620e7a5f/lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9", size = 5341578, upload-time = "2026-04-18T04:33:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/89/54/40d9403d7c2775fa7301d3ddd3464689bfe9ba71acc17dfff777071b4fdc/lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe", size = 4700209, upload-time = "2026-04-18T04:33:47.552Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/bbdcc2cf45dfc7dfffef4fd97e5c47b15919b6a365247d95d6f684ef5e82/lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88", size = 5232365, upload-time = "2026-04-18T04:33:50.249Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/b06875665e53aaba7127611a7bed3b7b9658e20b22bc2dd217a0b7ab0091/lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181", size = 5043654, upload-time = "2026-04-18T04:33:52.71Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9c/e71a069d09641c1a7abeb30e693f828c7c90a41cbe3d650b2d734d876f85/lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24", size = 4769326, upload-time = "2026-04-18T04:33:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/cc/06/7a9cd84b3d4ed79adf35f874750abb697dec0b4a81a836037b36e47c091a/lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e", size = 5635879, upload-time = "2026-04-18T04:33:58.509Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/9d57916befc1e54c451712c7ee48e9e74e80ae4d03bdce49914e0aee42cd/lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495", size = 5224048, upload-time = "2026-04-18T04:34:00.943Z" }, + { url = "https://files.pythonhosted.org/packages/99/75/90c4eefda0c08c92221fe0753db2d6699a4c628f76ff4465ec20dea84cc1/lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33", size = 5250241, upload-time = "2026-04-18T04:34:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/5e/73/16596f7e4e38fa33084b9ccbccc22a15f82a290a055126f2c1541236d2ff/lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62", size = 3596938, upload-time = "2026-04-18T04:31:56.206Z" }, + { url = "https://files.pythonhosted.org/packages/8e/63/981401c5680c1eb30893f00a19641ac80db5d1e7086c62cb4b13ed813038/lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16", size = 3995728, upload-time = "2026-04-18T04:31:58.763Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e8/c358a38ac3e541d16a1b527e4e9cb78c0419b0506a070ace11777e5e8404/lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d", size = 3658372, upload-time = "2026-04-18T04:32:03.629Z" }, ] [[package]]