From 8a194a542f70388205c7cdba84409101d5fd84da Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Tue, 4 Feb 2025 13:41:37 +0000 Subject: [PATCH 01/26] rudimentary publish workflow --- template/.github/workflows/publish.yaml.jinja | 91 +++++++++++++++++++ template/pyproject.toml.jinja | 3 + 2 files changed, 94 insertions(+) create mode 100644 template/.github/workflows/publish.yaml.jinja diff --git a/template/.github/workflows/publish.yaml.jinja b/template/.github/workflows/publish.yaml.jinja new file mode 100644 index 00000000..35628a7e --- /dev/null +++ b/template/.github/workflows/publish.yaml.jinja @@ -0,0 +1,91 @@ +{% raw %}name: Publish to PyPI + +on: + workflow_dispatch: + +env: + PYTHONUNBUFFERED: True + PRE_COMMIT_HOME: ${{ github.workspace }}/.precommit_cache + +permissions: + id-token: write + contents: write # needed for mutex + +jobs: + lint: + name: Pre-commit + runs-on: {% endraw %}{{ gha_linux_runner }}{% raw %} + steps: + - name: Checkout code + uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %} + + - name: Install latest versions of python packages + uses: ./.github/actions/install_deps_uv + with: + python-version: {% endraw %}{{ python_version }}{% raw %} + + - name: Set up mutex # Github concurrency management is horrible, things get arbitrarily cancelled if queued up. So using mutex until github fixes itself. When multiple jobs are modifying cache at once, weird things can happen. possible issue is https://github.com/actions/toolkit/issues/658 + if: ${{ runner.os != 'Windows' }} # we're just gonna have to YOLO on Windows, because this action doesn't support it yet https://github.com/ben-z/gh-action-mutex/issues/14 + uses: ben-z/gh-action-mutex@{% endraw %}{{ gha_mutex }}{% raw %} + with: + branch: mutex-venv-{% endraw %}{{ gha_linux_runner }}{% raw %}-py{% endraw %}{{ python_version }}{% raw %} + timeout-minutes: 30 # this is the amount of time this action will wait to attempt to acquire the mutex lock before failing, e.g. if other jobs are queued up in front of it + + - name: Cache Pre-commit hooks + uses: actions/cache@{% endraw %}{{ gha_cache }}{% raw %} + env: + cache-name: cache-pre-commit-hooks + with: + path: ${{ env.PRE_COMMIT_HOME }} + key: {% endraw %}{{ gha_linux_runner }}{% raw %}-py{% endraw %}{{ python_version }}{% raw %}-build-${{ env.cache-name }}-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + {% endraw %}{{ gha_linux_runner }}{% raw %}-py{% endraw %}{{ python_version }}{% raw %}-build-${{ env.cache-name }}- + + - name: Run pre-commit + run: pre-commit run -a + + test: + needs: [ lint ] + strategy: + matrix: + os: + - "{% endraw %}{{ gha_linux_runner }}{% raw %}" +{% endraw %}{% if use_windows_in_ci %} - {{ gha_windows_runner }}{% endif %}{% raw %} + python-version: +{% endraw %}{% for item in python_ci_versions %} + - {{ item }} +{% endfor %}{% raw %} + include: + - os: "{% endraw %}{{ gha_linux_runner }}{% raw %}" + python-version: "{% endraw %}{{ python_ci_versions[0] }}{% raw %}" + JOB_MATCHING_DEV_ENV: true + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %} + + - name: Install python tooling + uses: ./.github/actions/install_deps_uv + with: + python-version: ${{ matrix.python-version }} + + - name: Unit test + run: uv run pytest --durations=5 + + build: + needs: [ test ] + runs-on: {% endraw %}{{ gha_linux_runner }}{% raw %} + + steps: + - name: Checkout code + uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %} + + - name: Install python tooling + uses: ./.github/actions/install_deps_uv + with: + python-version: {% endraw %}{{ python_version }}{% raw %} + + - name: Build package + run: uv build --no-sources diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 315c262c..90e180cc 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -32,6 +32,9 @@ dev = [ "sphinx=={% endraw %}{{ sphinx_version }}{% raw %}", ] +[tool.setuptools] +license-files = [] # kludge until this bug is fixed https://github.com/pypa/setuptools/issues/4759 + [tool.uv] package = true From f12fffe1ede514a389f400a139bef937ed3f9d2a Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Tue, 4 Feb 2025 13:42:31 +0000 Subject: [PATCH 02/26] endraw --- template/.github/workflows/publish.yaml.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/.github/workflows/publish.yaml.jinja b/template/.github/workflows/publish.yaml.jinja index 35628a7e..59f8a554 100644 --- a/template/.github/workflows/publish.yaml.jinja +++ b/template/.github/workflows/publish.yaml.jinja @@ -88,4 +88,4 @@ jobs: python-version: {% endraw %}{{ python_version }}{% raw %} - name: Build package - run: uv build --no-sources + run: uv build --no-sources{% endraw %} From f90ba464339c007413c2053071f8d1c700a3a0e7 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 19:27:06 +0000 Subject: [PATCH 03/26] add frozen question --- copier.yml | 5 +++++ tests/copier_data/data1.yaml | 1 + tests/copier_data/data2.yaml | 1 + 3 files changed, 7 insertions(+) diff --git a/copier.yml b/copier.yml index 83b20bc3..095bd886 100644 --- a/copier.yml +++ b/copier.yml @@ -52,6 +52,11 @@ create_docs: help: Do you want to create documentation for this project? default: yes +is_frozen_executable: + type: bool + help: Is this project a frozen executable (instead of a PyPI-compatible library)? + default: no + # Additional Settings _min_copier_version: "9.4" diff --git a/tests/copier_data/data1.yaml b/tests/copier_data/data1.yaml index 63b5c2b0..0ad3528c 100644 --- a/tests/copier_data/data1.yaml +++ b/tests/copier_data/data1.yaml @@ -17,3 +17,4 @@ package_name: foo-bar primary_author: George Washington full_repo_url: https://www.github.com/foobar create_docs: no +is_frozen_executable: yes diff --git a/tests/copier_data/data2.yaml b/tests/copier_data/data2.yaml index 04b6b418..926b7edc 100644 --- a/tests/copier_data/data2.yaml +++ b/tests/copier_data/data2.yaml @@ -22,3 +22,4 @@ package_name: the-amazing-library primary_author: John Williams full_repo_url: https://www.github.com/baz create_docs: yes +is_frozen_executable: no From 1a6b03dd148a23e1c1e17df91c862482570473dc Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 19:28:52 +0000 Subject: [PATCH 04/26] copier --- .copier-answers.yml | 2 +- .devcontainer/docker-compose.yml | 1 + .devcontainer/install-ci-tooling.sh | 2 +- .../install_deps_uv/install-ci-tooling.ps1 | 2 +- .pre-commit-config.yaml | 6 +- copier.yml | 4 + extensions/context.py | 10 +- pyrightconfig.json | 215 +++++++++--------- .../.devcontainer/docker-compose.yml.jinja | 1 + .../install_deps_uv/install-ci-tooling.ps1 | 2 +- template/.pre-commit-config.yaml | 6 +- template/pyrightconfig.json | 215 +++++++++--------- tests/copier_data/data1.yaml | 3 + tests/copier_data/data2.yaml | 3 + 14 files changed, 242 insertions(+), 230 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index bde40a8c..a0fd1ed4 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.5-36-gb56f9e4 +_commit: v0.0.5-53-ga068a8e _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables python_ci_versions: diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index f40c1e7f..0a78e182 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -15,6 +15,7 @@ services: - "55874:2222" environment: - AWS_PROFILE=localstack + - AWS_DEFAULT_REGION=us-east-1 volumes: diff --git a/.devcontainer/install-ci-tooling.sh b/.devcontainer/install-ci-tooling.sh index e5a669c0..7c9a8019 100644 --- a/.devcontainer/install-ci-tooling.sh +++ b/.devcontainer/install-ci-tooling.sh @@ -2,7 +2,7 @@ # can pass in the full major.minor.patch version of python as an optional argument set -ex -curl -LsSf https://astral.sh/uv/0.5.26/install.sh | sh +curl -LsSf https://astral.sh/uv/0.5.28/install.sh | sh uv --version # TODO: add uv autocompletion to the shell https://docs.astral.sh/uv/getting-started/installation/#shell-autocompletion diff --git a/.github/actions/install_deps_uv/install-ci-tooling.ps1 b/.github/actions/install_deps_uv/install-ci-tooling.ps1 index c03f9bf0..a8643825 100644 --- a/.github/actions/install_deps_uv/install-ci-tooling.ps1 +++ b/.github/actions/install_deps_uv/install-ci-tooling.ps1 @@ -3,7 +3,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" -irm https://astral.sh/uv/0.5.26/install.ps1 | iex +irm https://astral.sh/uv/0.5.28/install.ps1 | iex # Add uv to path (in github runner) $env:Path = "C:\Users\runneradmin\.local\bin;$env:Path" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 407a40bf..1d96875c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: # Reformatting (should generally come before any file format or other checks, because reformatting can change things) - repo: https://github.com/crate-ci/typos - rev: b1b019a4372e6055cd23b7a8e299c56f9d522a94 # frozen: dictgen-v0.3.1 + rev: e59f226fadcb05a1440bd8b35ee994a9d21bf03b # frozen: typos-dict-v0.12.4 hooks: - id: typos - repo: https://github.com/pre-commit/pre-commit-hooks @@ -162,7 +162,7 @@ repos: description: Runs hadolint to lint Dockerfiles - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 73413df07b4ab0bf103ca1ae73c7cec5c0ace593 # frozen: v0.9.2 + rev: 871f3bcae4fe473cd7109c3a068db975dc035e3b # frozen: v0.9.4 hooks: - id: ruff name: ruff-src @@ -175,7 +175,7 @@ repos: - id: ruff-format - repo: https://github.com/pylint-dev/pylint - rev: c28580be76fe1ec55a6cac41833c0bd68070d2f7 # frozen: v3.3.3 + rev: c2b01c3d0b6410d4c93eb1f32fb28f2116b1e68f # frozen: v3.3.4 hooks: - id: pylint name: pylint diff --git a/copier.yml b/copier.yml index 095bd886..0fa43252 100644 --- a/copier.yml +++ b/copier.yml @@ -3,6 +3,10 @@ repo_name: type: str help: What is the name of the repository? +repo_org_name: + type: str + help: What's the organization or username that owns this repository (look in the URL)? + description: type: str help: What is the description of this repository? diff --git a/extensions/context.py b/extensions/context.py index 331f6064..82226fed 100644 --- a/extensions/context.py +++ b/extensions/context.py @@ -10,7 +10,7 @@ class ContextUpdater(ContextHook): @override def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: - context["uv_version"] = "0.5.26" + context["uv_version"] = "0.5.28" context["pre_commit_version"] = "4.0.1" context["pyright_version"] = "1.1.393" context["pytest_version"] = "8.3.4" @@ -19,11 +19,13 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["copier_version"] = "9.4.1" context["copier_templates_extension_version"] = "0.3.0" context["sphinx_version"] = "8.1.3" - context["pulumi_version"] = "3.147.0" - context["pulumi_aws_version"] = "6.66.3" - context["pulumi_aws_native_version"] = "1.24.0" + context["pulumi_version"] = "3.149.0" + context["pulumi_aws_version"] = "6.67.0" + context["pulumi_aws_native_version"] = "1.25.0" context["pulumi_command_version"] = "1.0.1" context["boto3_version"] = "1.36.3" + context["ephemeral_pulumi_deploy_version"] = "0.0.2" + context["pydantic_version"] = "2.10.6" context["gha_checkout"] = "v4.2.2" context["gha_setup_python"] = "v5.3.0" diff --git a/pyrightconfig.json b/pyrightconfig.json index ef6d2f5e..d369f774 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,110 +1,109 @@ { - "exclude": [ - // excludes managed by this repository + "exclude": [ + // excludes managed by this repository - - // excludes managed by template - "**/node_modules", - "**/.precommit_cache", - "**/.ruff_cache", - "**/.npm_cache", - "**/.pipx_cache", - "**/__pycache__", - "**/vendor_files", - "**/.venv", - "**/venv" - ], - "strictListInference": true, - "strictDictionaryInference": true, - "strictSetInference": true, - "analyzeUnannotatedFunctions": true, - "strictParameterNoneValue": true, - "enableTypeIgnoreComments": true, - "deprecateTypingAliases": true, - "reportGeneralTypeIssues": true, - "reportPropertyTypeMismatch": true, - "reportFunctionMemberAccess": true, - "reportMissingImports": true, - "reportMissingModuleSource": true, - "reportInvalidTypeForm": true, - "reportMissingTypeStubs": false, - "reportImportCycles": true, - "reportUnusedClass": true, - "reportUnusedFunction": true, - "reportUnusedVariable": true, - "reportWildcardImportFromLibrary": true, - "reportAbstractUsage": true, - "reportArgumentType": true, - "reportAssertTypeFailure": true, - "reportAssignmentType": true, - "reportAttributeAccessIssue": true, - "reportCallIssue": true, - "reportInconsistentOverload": true, - "reportIndexIssue": true, - "reportInvalidTypeArguments": true, - "reportNoOverloadImplementation": true, - "reportOperatorIssue": true, - "reportOptionalSubscript": true, - "reportOptionalMemberAccess": true, - "reportOptionalCall": true, - "reportOptionalIterable": true, - "reportOptionalContextManager": true, - "reportOptionalOperand": true, - "reportRedeclaration": true, - "reportReturnType": true, - "reportTypedDictNotRequiredAccess": true, - "reportUntypedFunctionDecorator": true, - "reportUntypedClassDecorator": true, - "reportUntypedBaseClass": true, - "reportUntypedNamedTuple": true, - "reportPrivateUsage": false, // already covered by ruff rule SLF001 - "reportTypeCommentUsage": true, - "reportPrivateImportUsage": true, - "reportConstantRedefinition": true, - "reportDeprecated": true, - "reportIncompatibleMethodOverride": true, - "reportIncompatibleVariableOverride": true, - "reportInconsistentConstructor": true, - "reportOverlappingOverload": true, - "reportPossiblyUnboundVariable": true, - "reportMissingSuperCall": true, - "reportUninitializedInstanceVariable": true, - "reportInvalidStringEscapeSequence": true, - "reportUnknownParameterType": true, - "reportUnknownArgumentType": true, - "reportUnknownLambdaType": true, - "reportUnknownVariableType": true, - "reportUnknownMemberType": true, - "reportMissingParameterType": true, - "reportMissingTypeArgument": true, - "reportInvalidTypeVarUse": true, - "reportCallInDefaultInitializer": true, - "reportUnnecessaryIsInstance": true, - "reportUnnecessaryCast": true, - "reportUnnecessaryComparison": true, - "reportUnnecessaryContains": true, - "reportAssertAlwaysTrue": true, - "reportSelfClsParameterName": true, - "reportImplicitStringConcatenation": true, - "reportUndefinedVariable": true, - "reportUnboundVariable": true, - "reportInvalidStubStatement": true, - "reportIncompleteStub": true, - "reportUnsupportedDunderAll": true, - "reportUnusedCallResult": true, - "reportUnusedCoroutine": true, - "reportUnusedExcept": true, - "reportUnusedExpression": true, - "reportUnnecessaryTypeIgnoreComment": true, - "reportMatchNotExhaustive": true, - "reportImplicitOverride": true, - "reportShadowedImports": true, - "pythonPlatform": "Linux", - "executionEnvironments": [ - { - "root": "tests", - // reportUninitializedInstanceVariable doesn't make any sense for test cases. See https://github.com/microsoft/pyright/discussions/4834#discussioncomment-8496931 - "reportUninitializedInstanceVariable": false, - } - ] - } + // excludes managed by template + "**/node_modules", + "**/.precommit_cache", + "**/.ruff_cache", + "**/.npm_cache", + "**/.pipx_cache", + "**/__pycache__", + "**/vendor_files", + "**/.venv", + "**/venv" + ], + "strictListInference": true, + "strictDictionaryInference": true, + "strictSetInference": true, + "analyzeUnannotatedFunctions": true, + "strictParameterNoneValue": true, + "enableTypeIgnoreComments": true, + "deprecateTypingAliases": true, + "reportGeneralTypeIssues": true, + "reportPropertyTypeMismatch": true, + "reportFunctionMemberAccess": true, + "reportMissingImports": true, + "reportMissingModuleSource": true, + "reportInvalidTypeForm": true, + "reportMissingTypeStubs": false, + "reportImportCycles": true, + "reportUnusedClass": true, + "reportUnusedFunction": true, + "reportUnusedVariable": false, // already covered by ruff rule F841 + "reportWildcardImportFromLibrary": true, + "reportAbstractUsage": true, + "reportArgumentType": true, + "reportAssertTypeFailure": true, + "reportAssignmentType": true, + "reportAttributeAccessIssue": true, + "reportCallIssue": true, + "reportInconsistentOverload": true, + "reportIndexIssue": true, + "reportInvalidTypeArguments": true, + "reportNoOverloadImplementation": true, + "reportOperatorIssue": true, + "reportOptionalSubscript": true, + "reportOptionalMemberAccess": true, + "reportOptionalCall": true, + "reportOptionalIterable": true, + "reportOptionalContextManager": true, + "reportOptionalOperand": true, + "reportRedeclaration": true, + "reportReturnType": true, + "reportTypedDictNotRequiredAccess": true, + "reportUntypedFunctionDecorator": true, + "reportUntypedClassDecorator": true, + "reportUntypedBaseClass": true, + "reportUntypedNamedTuple": true, + "reportPrivateUsage": false, // already covered by ruff rule SLF001 + "reportTypeCommentUsage": true, + "reportPrivateImportUsage": true, + "reportConstantRedefinition": true, + "reportDeprecated": true, + "reportIncompatibleMethodOverride": true, + "reportIncompatibleVariableOverride": true, + "reportInconsistentConstructor": true, + "reportOverlappingOverload": true, + "reportPossiblyUnboundVariable": true, + "reportMissingSuperCall": true, + "reportUninitializedInstanceVariable": true, + "reportInvalidStringEscapeSequence": true, + "reportUnknownParameterType": true, + "reportUnknownArgumentType": true, + "reportUnknownLambdaType": true, + "reportUnknownVariableType": true, + "reportUnknownMemberType": true, + "reportMissingParameterType": true, + "reportMissingTypeArgument": true, + "reportInvalidTypeVarUse": true, + "reportCallInDefaultInitializer": true, + "reportUnnecessaryIsInstance": true, + "reportUnnecessaryCast": true, + "reportUnnecessaryComparison": true, + "reportUnnecessaryContains": true, + "reportAssertAlwaysTrue": true, + "reportSelfClsParameterName": true, + "reportImplicitStringConcatenation": true, + "reportUndefinedVariable": true, + "reportUnboundVariable": true, + "reportInvalidStubStatement": true, + "reportIncompleteStub": true, + "reportUnsupportedDunderAll": true, + "reportUnusedCallResult": true, + "reportUnusedCoroutine": true, + "reportUnusedExcept": true, + "reportUnusedExpression": true, + "reportUnnecessaryTypeIgnoreComment": true, + "reportMatchNotExhaustive": true, + "reportImplicitOverride": true, + "reportShadowedImports": true, + "pythonPlatform": "Linux", + "executionEnvironments": [ + { + "root": "tests", + // reportUninitializedInstanceVariable doesn't make any sense for test cases. See https://github.com/microsoft/pyright/discussions/4834#discussioncomment-8496931 + "reportUninitializedInstanceVariable": false + } + ] +} diff --git a/template/.devcontainer/docker-compose.yml.jinja b/template/.devcontainer/docker-compose.yml.jinja index 2d91c02d..c2f4e1b7 100644 --- a/template/.devcontainer/docker-compose.yml.jinja +++ b/template/.devcontainer/docker-compose.yml.jinja @@ -15,6 +15,7 @@ services: - "{% endraw %}{{ ssh_port_number }}{% raw %}:2222" environment: - AWS_PROFILE=localstack + - AWS_DEFAULT_REGION={% endraw %}{{ aws_region_for_stack }}{% raw %} volumes: diff --git a/template/.github/actions/install_deps_uv/install-ci-tooling.ps1 b/template/.github/actions/install_deps_uv/install-ci-tooling.ps1 index c03f9bf0..a8643825 100644 --- a/template/.github/actions/install_deps_uv/install-ci-tooling.ps1 +++ b/template/.github/actions/install_deps_uv/install-ci-tooling.ps1 @@ -3,7 +3,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" -irm https://astral.sh/uv/0.5.26/install.ps1 | iex +irm https://astral.sh/uv/0.5.28/install.ps1 | iex # Add uv to path (in github runner) $env:Path = "C:\Users\runneradmin\.local\bin;$env:Path" diff --git a/template/.pre-commit-config.yaml b/template/.pre-commit-config.yaml index 407a40bf..1d96875c 100644 --- a/template/.pre-commit-config.yaml +++ b/template/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: # Reformatting (should generally come before any file format or other checks, because reformatting can change things) - repo: https://github.com/crate-ci/typos - rev: b1b019a4372e6055cd23b7a8e299c56f9d522a94 # frozen: dictgen-v0.3.1 + rev: e59f226fadcb05a1440bd8b35ee994a9d21bf03b # frozen: typos-dict-v0.12.4 hooks: - id: typos - repo: https://github.com/pre-commit/pre-commit-hooks @@ -162,7 +162,7 @@ repos: description: Runs hadolint to lint Dockerfiles - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 73413df07b4ab0bf103ca1ae73c7cec5c0ace593 # frozen: v0.9.2 + rev: 871f3bcae4fe473cd7109c3a068db975dc035e3b # frozen: v0.9.4 hooks: - id: ruff name: ruff-src @@ -175,7 +175,7 @@ repos: - id: ruff-format - repo: https://github.com/pylint-dev/pylint - rev: c28580be76fe1ec55a6cac41833c0bd68070d2f7 # frozen: v3.3.3 + rev: c2b01c3d0b6410d4c93eb1f32fb28f2116b1e68f # frozen: v3.3.4 hooks: - id: pylint name: pylint diff --git a/template/pyrightconfig.json b/template/pyrightconfig.json index ef6d2f5e..d369f774 100644 --- a/template/pyrightconfig.json +++ b/template/pyrightconfig.json @@ -1,110 +1,109 @@ { - "exclude": [ - // excludes managed by this repository + "exclude": [ + // excludes managed by this repository - - // excludes managed by template - "**/node_modules", - "**/.precommit_cache", - "**/.ruff_cache", - "**/.npm_cache", - "**/.pipx_cache", - "**/__pycache__", - "**/vendor_files", - "**/.venv", - "**/venv" - ], - "strictListInference": true, - "strictDictionaryInference": true, - "strictSetInference": true, - "analyzeUnannotatedFunctions": true, - "strictParameterNoneValue": true, - "enableTypeIgnoreComments": true, - "deprecateTypingAliases": true, - "reportGeneralTypeIssues": true, - "reportPropertyTypeMismatch": true, - "reportFunctionMemberAccess": true, - "reportMissingImports": true, - "reportMissingModuleSource": true, - "reportInvalidTypeForm": true, - "reportMissingTypeStubs": false, - "reportImportCycles": true, - "reportUnusedClass": true, - "reportUnusedFunction": true, - "reportUnusedVariable": true, - "reportWildcardImportFromLibrary": true, - "reportAbstractUsage": true, - "reportArgumentType": true, - "reportAssertTypeFailure": true, - "reportAssignmentType": true, - "reportAttributeAccessIssue": true, - "reportCallIssue": true, - "reportInconsistentOverload": true, - "reportIndexIssue": true, - "reportInvalidTypeArguments": true, - "reportNoOverloadImplementation": true, - "reportOperatorIssue": true, - "reportOptionalSubscript": true, - "reportOptionalMemberAccess": true, - "reportOptionalCall": true, - "reportOptionalIterable": true, - "reportOptionalContextManager": true, - "reportOptionalOperand": true, - "reportRedeclaration": true, - "reportReturnType": true, - "reportTypedDictNotRequiredAccess": true, - "reportUntypedFunctionDecorator": true, - "reportUntypedClassDecorator": true, - "reportUntypedBaseClass": true, - "reportUntypedNamedTuple": true, - "reportPrivateUsage": false, // already covered by ruff rule SLF001 - "reportTypeCommentUsage": true, - "reportPrivateImportUsage": true, - "reportConstantRedefinition": true, - "reportDeprecated": true, - "reportIncompatibleMethodOverride": true, - "reportIncompatibleVariableOverride": true, - "reportInconsistentConstructor": true, - "reportOverlappingOverload": true, - "reportPossiblyUnboundVariable": true, - "reportMissingSuperCall": true, - "reportUninitializedInstanceVariable": true, - "reportInvalidStringEscapeSequence": true, - "reportUnknownParameterType": true, - "reportUnknownArgumentType": true, - "reportUnknownLambdaType": true, - "reportUnknownVariableType": true, - "reportUnknownMemberType": true, - "reportMissingParameterType": true, - "reportMissingTypeArgument": true, - "reportInvalidTypeVarUse": true, - "reportCallInDefaultInitializer": true, - "reportUnnecessaryIsInstance": true, - "reportUnnecessaryCast": true, - "reportUnnecessaryComparison": true, - "reportUnnecessaryContains": true, - "reportAssertAlwaysTrue": true, - "reportSelfClsParameterName": true, - "reportImplicitStringConcatenation": true, - "reportUndefinedVariable": true, - "reportUnboundVariable": true, - "reportInvalidStubStatement": true, - "reportIncompleteStub": true, - "reportUnsupportedDunderAll": true, - "reportUnusedCallResult": true, - "reportUnusedCoroutine": true, - "reportUnusedExcept": true, - "reportUnusedExpression": true, - "reportUnnecessaryTypeIgnoreComment": true, - "reportMatchNotExhaustive": true, - "reportImplicitOverride": true, - "reportShadowedImports": true, - "pythonPlatform": "Linux", - "executionEnvironments": [ - { - "root": "tests", - // reportUninitializedInstanceVariable doesn't make any sense for test cases. See https://github.com/microsoft/pyright/discussions/4834#discussioncomment-8496931 - "reportUninitializedInstanceVariable": false, - } - ] - } + // excludes managed by template + "**/node_modules", + "**/.precommit_cache", + "**/.ruff_cache", + "**/.npm_cache", + "**/.pipx_cache", + "**/__pycache__", + "**/vendor_files", + "**/.venv", + "**/venv" + ], + "strictListInference": true, + "strictDictionaryInference": true, + "strictSetInference": true, + "analyzeUnannotatedFunctions": true, + "strictParameterNoneValue": true, + "enableTypeIgnoreComments": true, + "deprecateTypingAliases": true, + "reportGeneralTypeIssues": true, + "reportPropertyTypeMismatch": true, + "reportFunctionMemberAccess": true, + "reportMissingImports": true, + "reportMissingModuleSource": true, + "reportInvalidTypeForm": true, + "reportMissingTypeStubs": false, + "reportImportCycles": true, + "reportUnusedClass": true, + "reportUnusedFunction": true, + "reportUnusedVariable": false, // already covered by ruff rule F841 + "reportWildcardImportFromLibrary": true, + "reportAbstractUsage": true, + "reportArgumentType": true, + "reportAssertTypeFailure": true, + "reportAssignmentType": true, + "reportAttributeAccessIssue": true, + "reportCallIssue": true, + "reportInconsistentOverload": true, + "reportIndexIssue": true, + "reportInvalidTypeArguments": true, + "reportNoOverloadImplementation": true, + "reportOperatorIssue": true, + "reportOptionalSubscript": true, + "reportOptionalMemberAccess": true, + "reportOptionalCall": true, + "reportOptionalIterable": true, + "reportOptionalContextManager": true, + "reportOptionalOperand": true, + "reportRedeclaration": true, + "reportReturnType": true, + "reportTypedDictNotRequiredAccess": true, + "reportUntypedFunctionDecorator": true, + "reportUntypedClassDecorator": true, + "reportUntypedBaseClass": true, + "reportUntypedNamedTuple": true, + "reportPrivateUsage": false, // already covered by ruff rule SLF001 + "reportTypeCommentUsage": true, + "reportPrivateImportUsage": true, + "reportConstantRedefinition": true, + "reportDeprecated": true, + "reportIncompatibleMethodOverride": true, + "reportIncompatibleVariableOverride": true, + "reportInconsistentConstructor": true, + "reportOverlappingOverload": true, + "reportPossiblyUnboundVariable": true, + "reportMissingSuperCall": true, + "reportUninitializedInstanceVariable": true, + "reportInvalidStringEscapeSequence": true, + "reportUnknownParameterType": true, + "reportUnknownArgumentType": true, + "reportUnknownLambdaType": true, + "reportUnknownVariableType": true, + "reportUnknownMemberType": true, + "reportMissingParameterType": true, + "reportMissingTypeArgument": true, + "reportInvalidTypeVarUse": true, + "reportCallInDefaultInitializer": true, + "reportUnnecessaryIsInstance": true, + "reportUnnecessaryCast": true, + "reportUnnecessaryComparison": true, + "reportUnnecessaryContains": true, + "reportAssertAlwaysTrue": true, + "reportSelfClsParameterName": true, + "reportImplicitStringConcatenation": true, + "reportUndefinedVariable": true, + "reportUnboundVariable": true, + "reportInvalidStubStatement": true, + "reportIncompleteStub": true, + "reportUnsupportedDunderAll": true, + "reportUnusedCallResult": true, + "reportUnusedCoroutine": true, + "reportUnusedExcept": true, + "reportUnusedExpression": true, + "reportUnnecessaryTypeIgnoreComment": true, + "reportMatchNotExhaustive": true, + "reportImplicitOverride": true, + "reportShadowedImports": true, + "pythonPlatform": "Linux", + "executionEnvironments": [ + { + "root": "tests", + // reportUninitializedInstanceVariable doesn't make any sense for test cases. See https://github.com/microsoft/pyright/discussions/4834#discussioncomment-8496931 + "reportUninitializedInstanceVariable": false + } + ] +} diff --git a/tests/copier_data/data1.yaml b/tests/copier_data/data1.yaml index 0ad3528c..637778e7 100644 --- a/tests/copier_data/data1.yaml +++ b/tests/copier_data/data1.yaml @@ -1,12 +1,15 @@ # Data managed by upstream template repo_name: the_best_repo +repo_org_name: great-company description: Doing amazing things ssh_port_number: 12345 use_windows_in_ci: false aws_identity_center_id: d-9145c20053 +aws_org_home_region: us-west-2 aws_production_account_id: 123456789012 use_staging_environment: false +aws_region_for_stack: us-east-2 # Data added based on the specifics of this template diff --git a/tests/copier_data/data2.yaml b/tests/copier_data/data2.yaml index 926b7edc..b953a200 100644 --- a/tests/copier_data/data2.yaml +++ b/tests/copier_data/data2.yaml @@ -1,15 +1,18 @@ # Data managed by upstream template repo_name: another_repo +repo_org_name: initech description: Doing crazy things! So many things! ssh_port_number: 54321 use_windows_in_ci: true aws_identity_center_id: d-9145c20053 +aws_org_home_region: us-east-1 aws_production_account_id: 123456789012 use_staging_environment: true aws_staging_account_id: 210987654321 aws_test_account_id: 211735654321 aws_development_account_id: 000321456870 +aws_region_for_stack: us-west-1 From 3b9fe32497b6deb00c5ddd8b7647cfe038f4a6a7 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:40:21 +0000 Subject: [PATCH 05/26] entrypoint --- ...f is_frozen_executable %}entrypoint.py{% endif %}.jinja | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja diff --git a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja new file mode 100644 index 00000000..bc7d9b27 --- /dev/null +++ b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja @@ -0,0 +1,7 @@ +{% raw %}import sys + +from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import main + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])){% endraw %} From 92ed05347fd3e6d30b4ff805c8708a5279e5c2b5 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:42:30 +0000 Subject: [PATCH 06/26] move entrypoint --- .../{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename template/src/{{{ package_name.replace('-', '_') }} => }/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja (100%) diff --git a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja similarity index 100% rename from template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja rename to template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja From cbc3a75c969dbe9c5abe453a00076c56cf0cd5df Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:43:55 +0000 Subject: [PATCH 07/26] add main --- .../{% if is_frozen_executable %}main.py{% endif %}.jinja | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja diff --git a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja new file mode 100644 index 00000000..da4c78f7 --- /dev/null +++ b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja @@ -0,0 +1,2 @@ +{% raw %}def main(argv: list[str])->int: + pass{% endraw %} From d5b4d091ab09e8eee59185c956611c59db59b0ea Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:47:20 +0000 Subject: [PATCH 08/26] lint --- .../{% if is_frozen_executable %}main.py{% endif %}.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja index da4c78f7..529e41e0 100644 --- a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja +++ b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja @@ -1,2 +1,2 @@ -{% raw %}def main(argv: list[str])->int: - pass{% endraw %} +{% raw %}def main(argv: list[str]) -> int: # noqa: ARG001 # args will be used after instantiation + return 0{% endraw %} From 3543692af2c3925836ccbc480128fb4b50bdbcdf Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:49:17 +0000 Subject: [PATCH 09/26] whitespace --- .../{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja | 1 - 1 file changed, 1 deletion(-) diff --git a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja index bc7d9b27..1723549e 100644 --- a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja +++ b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja @@ -2,6 +2,5 @@ from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import main - if __name__ == "__main__": sys.exit(main(sys.argv[1:])){% endraw %} From a807127c7e61191fbf5c86aa1e2fee855b3135be Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 20:53:54 +0000 Subject: [PATCH 10/26] coverage --- ...if is_frozen_executable %}entrypoint.py{% endif %}.jinja | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja index 1723549e..1065dbb9 100644 --- a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja +++ b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja @@ -1,6 +1,8 @@ -{% raw %}import sys +{% raw %}import sys # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable itself -from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import main +from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import ( + main, +) # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable itself if __name__ == "__main__": sys.exit(main(sys.argv[1:])){% endraw %} From 88124ed166dea91a13c82c5cc3cb8732fb49aa36 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 21:03:35 +0000 Subject: [PATCH 11/26] Words --- ...% if is_frozen_executable %}entrypoint.py{% endif %}.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja index 1065dbb9..b51bcc94 100644 --- a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja +++ b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja @@ -1,8 +1,8 @@ -{% raw %}import sys # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable itself +{% raw %}import sys # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import ( main, -) # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable itself +) # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable if __name__ == "__main__": sys.exit(main(sys.argv[1:])){% endraw %} From 802109d7b391193e2d75f10cc1ac0b50db225e0d Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 21:09:14 +0000 Subject: [PATCH 12/26] add pyinstaller --- template/pyproject.toml.jinja | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 90e180cc..ea83530f 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -29,7 +29,8 @@ dev = [ "pytest>={% endraw %}{{ pytest_version }}{% raw %}", "pytest-cov>={% endraw %}{{ pytest_cov_version }}{% raw %}", "pytest-randomly>={% endraw %}{{ pytest_randomly_version }}{% raw %}", - "sphinx=={% endraw %}{{ sphinx_version }}{% raw %}", +{% endraw %}{% if create_docs %}{% raw %} "sphinx=={% endraw %}{{ sphinx_version }}{% raw %}",{% endraw %}{% endif %}{% raw %} +{% endraw %}{% if is_frozen_executable %}{% raw %} "pyinstaller=={% endraw %}{{ pyinstaller_version }}{% raw %}",{% endraw %}{% endif %}{% raw %} ] [tool.setuptools] From 92ce05712b157237b648830a2ef3cf60e5924da6 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 21:10:43 +0000 Subject: [PATCH 13/26] context --- .copier-answers.yml | 2 +- extensions/context.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index a0fd1ed4..fc85e877 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.5-53-ga068a8e +_commit: v0.0.5-54-g714d21d _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables python_ci_versions: diff --git a/extensions/context.py b/extensions/context.py index 82226fed..2a6b993b 100644 --- a/extensions/context.py +++ b/extensions/context.py @@ -26,6 +26,7 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["boto3_version"] = "1.36.3" context["ephemeral_pulumi_deploy_version"] = "0.0.2" context["pydantic_version"] = "2.10.6" + context["pyinstaller_version"] = "6.12.0" context["gha_checkout"] = "v4.2.2" context["gha_setup_python"] = "v5.3.0" From f79acf4b8175d34f2ee00d8daee3f232984f0c63 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 21:29:14 +0000 Subject: [PATCH 14/26] shared devcontanier --- template/.devcontainer/devcontainer.json.jinja | 1 + 1 file changed, 1 insertion(+) diff --git a/template/.devcontainer/devcontainer.json.jinja b/template/.devcontainer/devcontainer.json.jinja index 19df7b3e..8b3d0200 100644 --- a/template/.devcontainer/devcontainer.json.jinja +++ b/template/.devcontainer/devcontainer.json.jinja @@ -10,6 +10,7 @@ "ghcr.io/devcontainers/features/python:1": { // https://github.com/devcontainers/features/tree/main/src/python "version": "{% endraw %}{{ python_version }}{% raw %}", +{% endraw %}{% if is_frozen_executable %}{% raw %} "enableShared": true,{% endraw %}{% endif %}{% raw %} "installTools": false, "optimize": true } From bfd7b5c7bddac83f789b7b806d80197c0e580cca Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 22:21:11 +0000 Subject: [PATCH 15/26] build cI --- template/.github/workflows/ci.yaml.jinja | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/template/.github/workflows/ci.yaml.jinja b/template/.github/workflows/ci.yaml.jinja index 577db62c..66a79beb 100644 --- a/template/.github/workflows/ci.yaml.jinja +++ b/template/.github/workflows/ci.yaml.jinja @@ -89,6 +89,14 @@ jobs: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} slug: {% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %} +{% endraw %}{% if is_frozen_executable %}{% raw %} - name: Build executable + run: uv run pyinstaller pyinstaller.spec --log-level=DEBUG + + - name: Upload executable artifact + uses: actions/upload-artifact@{% endraw %}{{ gha_upload_artifact }}{% raw %} + with: + name: exe-${{ runner.os }}-${{ matrix.python-version }} + path: dist/{% endraw %}{% endif %}{% raw %} {% endraw %}{% if create_docs %}{% raw %} build-docs: needs: [ lint ] From ee97d942b822ca0ea23d00b609a26893c0ca6276 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 22:25:21 +0000 Subject: [PATCH 16/26] pyinstaller typos --- template/_typos.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/_typos.toml b/template/_typos.toml index 8f351161..13e4f4f5 100644 --- a/template/_typos.toml +++ b/template/_typos.toml @@ -10,7 +10,7 @@ astroid = "astroid" [files] extend-exclude = [ # Files specific to this repository - + "pyinstaller.spec", # pyinstaller spells some things oddly...don't mess with it # Files managed by the base template ".copier-answers.yml", # this is an autogenerated file, and sometimes the commit sha gets confused as being a word From 4914efb539a2bb43d65c8371baf2d6e39a7364f9 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Mon, 10 Feb 2025 22:27:05 +0000 Subject: [PATCH 17/26] error on no files --- template/.github/workflows/ci.yaml.jinja | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/template/.github/workflows/ci.yaml.jinja b/template/.github/workflows/ci.yaml.jinja index 66a79beb..5ada1b47 100644 --- a/template/.github/workflows/ci.yaml.jinja +++ b/template/.github/workflows/ci.yaml.jinja @@ -96,7 +96,8 @@ jobs: uses: actions/upload-artifact@{% endraw %}{{ gha_upload_artifact }}{% raw %} with: name: exe-${{ runner.os }}-${{ matrix.python-version }} - path: dist/{% endraw %}{% endif %}{% raw %} + path: dist/ + if-no-files-found: error{% endraw %}{% endif %}{% raw %} {% endraw %}{% if create_docs %}{% raw %} build-docs: needs: [ lint ] From 50a1080ab38bae8c4c54867397fe33ada2099064 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 11:42:13 +0000 Subject: [PATCH 18/26] change to entrypoint --- template/pyproject.toml.jinja | 2 +- ...% if is_frozen_executable %}entrypoint.py{% endif %}.jinja | 4 ++-- .../{% if is_frozen_executable %}main.py{% endif %}.jinja | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index ea83530f..65a7c830 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -30,7 +30,7 @@ dev = [ "pytest-cov>={% endraw %}{{ pytest_cov_version }}{% raw %}", "pytest-randomly>={% endraw %}{{ pytest_randomly_version }}{% raw %}", {% endraw %}{% if create_docs %}{% raw %} "sphinx=={% endraw %}{{ sphinx_version }}{% raw %}",{% endraw %}{% endif %}{% raw %} -{% endraw %}{% if is_frozen_executable %}{% raw %} "pyinstaller=={% endraw %}{{ pyinstaller_version }}{% raw %}",{% endraw %}{% endif %}{% raw %} +{% endraw %}{% if is_frozen_executable %}{% raw %} "pyinstaller>={% endraw %}{{ pyinstaller_version }}{% raw %}",{% endraw %}{% endif %}{% raw %} ] [tool.setuptools] diff --git a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja index b51bcc94..8544d614 100644 --- a/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja +++ b/template/src/{% if is_frozen_executable %}entrypoint.py{% endif %}.jinja @@ -1,8 +1,8 @@ {% raw %}import sys # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable from {% endraw %}{{ package_name.replace('-', '_') }}{% raw %}.main import ( - main, + entrypoint, ) # pragma: no cover # we can't unit test the entrypoint itself. It is tested in the E2E test of the executable if __name__ == "__main__": - sys.exit(main(sys.argv[1:])){% endraw %} + sys.exit(entrypoint(sys.argv[1:])){% endraw %} diff --git a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja index 529e41e0..c46cf98e 100644 --- a/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja +++ b/template/src/{{ package_name.replace('-', '_') }}/{% if is_frozen_executable %}main.py{% endif %}.jinja @@ -1,2 +1,2 @@ -{% raw %}def main(argv: list[str]) -> int: # noqa: ARG001 # args will be used after instantiation +{% raw %}def entrypoint(argv: list[str]) -> int: # noqa: ARG001 # args will be used after instantiation return 0{% endraw %} From 82ced3475cceca3330f1899bcb7817365030b072 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 11:50:31 +0000 Subject: [PATCH 19/26] pytest update --- .copier-answers.yml | 2 +- pytest.ini | 1 + template/pytest.ini | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index fc85e877..0a7b1c88 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.5-54-g714d21d +_commit: v0.0.5-55-gfb88b5b _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables python_ci_versions: diff --git a/pytest.ini b/pytest.ini index 689062a4..7ee31078 100644 --- a/pytest.ini +++ b/pytest.ini @@ -20,6 +20,7 @@ filterwarnings = error::pytest.PytestUnraisableExceptionWarning # stackoverflow.com/questions/71642742/make-pytest-fail-on-resourcewarning-unclosed-files + error::pytest.PytestUnhandledThreadExceptionWarning xfail_strict=true diff --git a/template/pytest.ini b/template/pytest.ini index 689062a4..7ee31078 100644 --- a/template/pytest.ini +++ b/template/pytest.ini @@ -20,6 +20,7 @@ filterwarnings = error::pytest.PytestUnraisableExceptionWarning # stackoverflow.com/questions/71642742/make-pytest-fail-on-resourcewarning-unclosed-files + error::pytest.PytestUnhandledThreadExceptionWarning xfail_strict=true From cc57974cb2dd1bb9638b5067785c89b3551bda91 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 11:52:06 +0000 Subject: [PATCH 20/26] fewer badges --- template/README.md.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/README.md.jinja b/template/README.md.jinja index 0ae8fbd0..82ba6bf5 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -3,11 +3,11 @@ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv) [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/) [![Actions status]({% endraw %}{{ full_repo_url }}{% raw %}/actions/workflows/ci.yaml/badge.svg?branch=main)]({% endraw %}{{ full_repo_url }}{% raw %}/actions) -[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url={% endraw %}{{ full_repo_url }}{% raw %}) +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url={% endraw %}{{ full_repo_url }}{% raw %}){% endraw %}{% if not is_frozen_executable %}{% raw %} [![PyPI Version](https://img.shields.io/pypi/v/{% endraw %}{{ package_name }}{% raw %}.svg)](https://pypi.org/project/{% endraw %}{{ package_name }}{% raw %}/) [![Downloads](https://pepy.tech/badge/{% endraw %}{{ package_name }}{% raw %})](https://pepy.tech/project/{% endraw %}{{ package_name }}{% raw %}) [![Python Versions](https://img.shields.io/pypi/pyversions/{% endraw %}{{ package_name }}{% raw %}.svg)](https://pypi.org/project/{% endraw %}{{ package_name }}{% raw %}/) -[![Codecov](https://codecov.io/gh/{% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %}/branch/main/graph/badge.svg)](https://codecov.io/gh/{% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %}) +{% endraw %}{% endif %}{% raw %}[![Codecov](https://codecov.io/gh/{% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %}/branch/main/graph/badge.svg)](https://codecov.io/gh/{% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %}) [![Documentation Status](https://readthedocs.org/projects/{% endraw %}{{ package_name }}{% raw %}/badge/?version=latest)](https://{% endraw %}{{ package_name }}{% raw %}.readthedocs.io/en/latest/?badge=latest) # Usage From b1d6fd8352055463f7af611698f6d336ac3c50d8 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 13:42:19 +0000 Subject: [PATCH 21/26] exe ci --- .github/workflows/ci.yaml | 1 + copier.yml | 6 ++++++ tests/copier_data/data1.yaml | 1 + tests/copier_data/data2.yaml | 1 + tests/copier_data/data3.yaml | 24 ++++++++++++++++++++++++ 5 files changed, 33 insertions(+) create mode 100644 tests/copier_data/data3.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c1d7f483..49f29bb9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -69,6 +69,7 @@ jobs: copier: [ '--data-file tests/copier_data/data1.yaml', '--data-file tests/copier_data/data2.yaml', + '--data-file tests/copier_data/data3.yaml', ] runs-on: ${{ matrix.os }} diff --git a/copier.yml b/copier.yml index 0fa43252..30beae9e 100644 --- a/copier.yml +++ b/copier.yml @@ -61,6 +61,12 @@ is_frozen_executable: help: Is this project a frozen executable (instead of a PyPI-compatible library)? default: no +use_windows_in_exe_ci: + type: bool + help: Should CI in the instantiated template also use Windows runners for building and testing the executable? + default: no + when: is_frozen_executable + # Additional Settings _min_copier_version: "9.4" diff --git a/tests/copier_data/data1.yaml b/tests/copier_data/data1.yaml index 637778e7..20b2ff42 100644 --- a/tests/copier_data/data1.yaml +++ b/tests/copier_data/data1.yaml @@ -21,3 +21,4 @@ primary_author: George Washington full_repo_url: https://www.github.com/foobar create_docs: no is_frozen_executable: yes +use_windows_in_exe_ci: yes diff --git a/tests/copier_data/data2.yaml b/tests/copier_data/data2.yaml index b953a200..770ea185 100644 --- a/tests/copier_data/data2.yaml +++ b/tests/copier_data/data2.yaml @@ -26,3 +26,4 @@ primary_author: John Williams full_repo_url: https://www.github.com/baz create_docs: yes is_frozen_executable: no +use_windows_in_exe_ci: no diff --git a/tests/copier_data/data3.yaml b/tests/copier_data/data3.yaml new file mode 100644 index 00000000..178e0665 --- /dev/null +++ b/tests/copier_data/data3.yaml @@ -0,0 +1,24 @@ +# Data managed by upstream template +repo_name: the_best_repo +repo_org_name: great-company +description: Doing amazing things +ssh_port_number: 12345 +use_windows_in_ci: false + +aws_identity_center_id: d-9145c20053 +aws_org_home_region: us-west-2 +aws_production_account_id: 123456789012 +use_staging_environment: false +aws_region_for_stack: us-east-2 + + +# Data added based on the specifics of this template + +python_ci_versions: + - 3.12.7 +package_name: foo-bar +primary_author: George Washington +full_repo_url: https://www.github.com/foobar +create_docs: no +is_frozen_executable: yes +use_windows_in_exe_ci: no From 26fbdd9145cd188ff096389e41a9c0ea58ef011a Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 13:47:03 +0000 Subject: [PATCH 22/26] rework CI --- template/.github/workflows/ci.yaml.jinja | 26 ++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/template/.github/workflows/ci.yaml.jinja b/template/.github/workflows/ci.yaml.jinja index 5ada1b47..b2276429 100644 --- a/template/.github/workflows/ci.yaml.jinja +++ b/template/.github/workflows/ci.yaml.jinja @@ -89,15 +89,33 @@ jobs: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} slug: {% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %} -{% endraw %}{% if is_frozen_executable %}{% raw %} - name: Build executable - run: uv run pyinstaller pyinstaller.spec --log-level=DEBUG +{% endraw %}{% if is_frozen_executable %}{% raw %} executable: + needs: [ test ] + strategy: + matrix: + os: + - "{% endraw %}{{ gha_linux_runner }}{% raw %}" +{% endraw %}{% if use_windows_in_exe_ci %} - {{ gha_windows_runner }}{% endif %}{% raw %} + python-version: +{% endraw %}{% for item in python_ci_versions %} + - {{ item }} +{% endfor %}{% raw %} + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %} + - name: Build executable + run: uv run pyinstaller pyinstaller.spec --log-level=DEBUG - name: Upload executable artifact uses: actions/upload-artifact@{% endraw %}{{ gha_upload_artifact }}{% raw %} with: name: exe-${{ runner.os }}-${{ matrix.python-version }} path: dist/ if-no-files-found: error{% endraw %}{% endif %}{% raw %} + + {% endraw %}{% if create_docs %}{% raw %} build-docs: needs: [ lint ] @@ -126,10 +144,10 @@ jobs: required-check: runs-on: {% endraw %}{{ gha_linux_runner }}{% raw %} - needs: [ test{% endraw %}{% if create_docs %}, build-docs{% endif %}{% raw %} ] + needs: [ test{% endraw %}{% if create_docs %}, build-docs{% endif %}{% if is_frozen_executable %}, executable{% endif %}{% raw %} ] if: always() steps: - name: fail if prior job failure - if: needs.test.result != 'success'{% endraw %}{% if create_docs %} || needs.build-docs.result != 'success'{% endif %}{% raw %} + if: needs.test.result != 'success'{% endraw %}{% if create_docs %} || needs.build-docs.result != 'success'{% endif %}{% if is_frozen_executable %} || needs.executable.result != 'success'{% endif %}{% raw %} run: | exit 1{% endraw %} From 33764e29492fc12134088e3b723ed2922a6f2db9 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 13:49:01 +0000 Subject: [PATCH 23/26] fix CI --- template/.github/workflows/ci.yaml.jinja | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/template/.github/workflows/ci.yaml.jinja b/template/.github/workflows/ci.yaml.jinja index b2276429..1d1e6ff6 100644 --- a/template/.github/workflows/ci.yaml.jinja +++ b/template/.github/workflows/ci.yaml.jinja @@ -75,7 +75,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Unit test - run: uv run pytest --cov-report=xml --durations=5 + run: uv run pytest tests/unit --cov-report=xml --durations=5 - name: Upload coverage to Codecov # only upload coverage from fastest job @@ -106,6 +106,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %} + - name: Install python tooling + uses: ./.github/actions/install_deps_uv + with: + python-version: ${{ matrix.python-version }} - name: Build executable run: uv run pyinstaller pyinstaller.spec --log-level=DEBUG - name: Upload executable artifact From e0924bf7b7e7cb98d0ff3d7df337a199763776a3 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 14:10:06 +0000 Subject: [PATCH 24/26] aws profle --- .copier-answers.yml | 2 +- .devcontainer/create-aws-profile.sh | 11 +++++++++-- template/.devcontainer/create-aws-profile.sh | 11 +++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 0a7b1c88..a0b0d751 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.5-55-gfb88b5b +_commit: v0.0.5-56-gde686d3 _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables python_ci_versions: diff --git a/.devcontainer/create-aws-profile.sh b/.devcontainer/create-aws-profile.sh index 447019f1..8f93c4ba 100644 --- a/.devcontainer/create-aws-profile.sh +++ b/.devcontainer/create-aws-profile.sh @@ -1,12 +1,19 @@ -#!/bin/bash +#!/bin/sh set -ex mkdir -p ~/.aws + +if [ "$GITHUB_ACTIONS" = "true" ]; then + LOCALSTACK_ENDPOINT_URL="http://localhost:4566" +else + LOCALSTACK_ENDPOINT_URL="http://localstack:4566" +fi + cat >> ~/.aws/config <> ~/.aws/credentials <> ~/.aws/config <> ~/.aws/credentials < Date: Wed, 12 Feb 2025 14:57:40 +0000 Subject: [PATCH 25/26] artifact name --- template/.github/workflows/ci.yaml.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/.github/workflows/ci.yaml.jinja b/template/.github/workflows/ci.yaml.jinja index 1d1e6ff6..82d26e02 100644 --- a/template/.github/workflows/ci.yaml.jinja +++ b/template/.github/workflows/ci.yaml.jinja @@ -115,7 +115,7 @@ jobs: - name: Upload executable artifact uses: actions/upload-artifact@{% endraw %}{{ gha_upload_artifact }}{% raw %} with: - name: exe-${{ runner.os }}-${{ matrix.python-version }} + name: exe-${{ matrix.os }}-${{ matrix.python-version }} path: dist/ if-no-files-found: error{% endraw %}{% endif %}{% raw %} From f196940d706522bf3f13f013b6fbe036f0f5a874 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Wed, 12 Feb 2025 14:58:14 +0000 Subject: [PATCH 26/26] copier --- .copier-answers.yml | 2 +- extensions/context.py | 2 +- pyproject.toml | 2 +- uv.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index a0b0d751..3f4c44e4 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.5-56-gde686d3 +_commit: v0.0.5-57-g045b3f3 _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables python_ci_versions: diff --git a/extensions/context.py b/extensions/context.py index 2a6b993b..319ea604 100644 --- a/extensions/context.py +++ b/extensions/context.py @@ -12,7 +12,7 @@ class ContextUpdater(ContextHook): def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["uv_version"] = "0.5.28" context["pre_commit_version"] = "4.0.1" - context["pyright_version"] = "1.1.393" + context["pyright_version"] = "1.1.394" context["pytest_version"] = "8.3.4" context["pytest_randomly_version"] = "3.16.0" context["pytest_cov_version"] = "6.0.0" diff --git a/pyproject.toml b/pyproject.toml index f0c80017..0cbc2726 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ "pytest>=8.3.4", "pytest-cov>=6.0.0", "pytest-randomly>=3.16.0", - "pyright[nodejs]>=1.1.393", + "pyright[nodejs]>=1.1.394", "copier>=9.4.1", "copier-templates-extensions>=0.3.0" diff --git a/uv.lock b/uv.lock index 49802628..4c7fe94c 100644 --- a/uv.lock +++ b/uv.lock @@ -59,7 +59,7 @@ dependencies = [ requires-dist = [ { name = "copier", specifier = ">=9.4.1" }, { name = "copier-templates-extensions", specifier = ">=0.3.0" }, - { name = "pyright", extras = ["nodejs"], specifier = ">=1.1.393" }, + { name = "pyright", extras = ["nodejs"], specifier = ">=1.1.394" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "pytest-cov", specifier = ">=6.0.0" }, { name = "pytest-randomly", specifier = ">=3.16.0" }, @@ -347,15 +347,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.393" +version = "1.1.394" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/c1/aede6c74e664ab103673e4f1b7fd3d058fef32276be5c43572f4067d4a8e/pyright-1.1.393.tar.gz", hash = "sha256:aeeb7ff4e0364775ef416a80111613f91a05c8e01e58ecfefc370ca0db7aed9c", size = 3790430 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/e4/79f4d8a342eed6790fdebdb500e95062f319ee3d7d75ae27304ff995ae8c/pyright-1.1.394.tar.gz", hash = "sha256:56f2a3ab88c5214a451eb71d8f2792b7700434f841ea219119ade7f42ca93608", size = 3809348 } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/47/f0dd0f8afce13d92e406421ecac6df0990daee84335fc36717678577d3e0/pyright-1.1.393-py3-none-any.whl", hash = "sha256:8320629bb7a44ca90944ba599390162bf59307f3d9fb6e27da3b7011b8c17ae5", size = 5646057 }, + { url = "https://files.pythonhosted.org/packages/d6/4c/50c74e3d589517a9712a61a26143b587dba6285434a17aebf2ce6b82d2c3/pyright-1.1.394-py3-none-any.whl", hash = "sha256:5f74cce0a795a295fb768759bbeeec62561215dea657edcaab48a932b031ddbb", size = 5679540 }, ] [package.optional-dependencies]