craft-application 4.2.1__tar.gz → 4.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. {craft_application-4.2.1/craft_application.egg-info → craft_application-4.2.3}/PKG-INFO +1 -1
  2. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/_version.py +2 -2
  3. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/application.py +54 -47
  4. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/provider.py +10 -6
  5. {craft_application-4.2.1 → craft_application-4.2.3/craft_application.egg-info}/PKG-INFO +1 -1
  6. {craft_application-4.2.1 → craft_application-4.2.3}/docs/reference/changelog.rst +23 -0
  7. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_provider.py +48 -16
  8. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/test_application.py +32 -0
  9. {craft_application-4.2.1 → craft_application-4.2.3}/.editorconfig +0 -0
  10. {craft_application-4.2.1 → craft_application-4.2.3}/.github/.jira_sync_config.yaml +0 -0
  11. {craft_application-4.2.1 → craft_application-4.2.3}/.github/ISSUE_TEMPLATE/bug.yaml +0 -0
  12. {craft_application-4.2.1 → craft_application-4.2.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  13. {craft_application-4.2.1 → craft_application-4.2.3}/.github/ISSUE_TEMPLATE/task.yaml +0 -0
  14. {craft_application-4.2.1 → craft_application-4.2.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {craft_application-4.2.1 → craft_application-4.2.3}/.github/release-drafter.yaml +0 -0
  16. {craft_application-4.2.1 → craft_application-4.2.3}/.github/release-drafter.yml +0 -0
  17. {craft_application-4.2.1 → craft_application-4.2.3}/.github/renovate.json5 +0 -0
  18. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/check-renovate.yaml +0 -0
  19. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/cla-check.yaml +0 -0
  20. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/docs.yaml +0 -0
  21. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/release-drafter.yaml +0 -0
  22. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/release-publish.yaml +0 -0
  23. {craft_application-4.2.1 → craft_application-4.2.3}/.github/workflows/tests.yaml +0 -0
  24. {craft_application-4.2.1 → craft_application-4.2.3}/.gitignore +0 -0
  25. {craft_application-4.2.1 → craft_application-4.2.3}/.pre-commit-config.yaml +0 -0
  26. {craft_application-4.2.1 → craft_application-4.2.3}/.readthedocs.yaml +0 -0
  27. {craft_application-4.2.1 → craft_application-4.2.3}/.yamllint.yaml +0 -0
  28. {craft_application-4.2.1 → craft_application-4.2.3}/HACKING.rst +0 -0
  29. {craft_application-4.2.1 → craft_application-4.2.3}/LICENSE +0 -0
  30. {craft_application-4.2.1 → craft_application-4.2.3}/README.rst +0 -0
  31. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/__init__.py +0 -0
  32. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/_config.py +0 -0
  33. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/commands/__init__.py +0 -0
  34. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/commands/base.py +0 -0
  35. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/commands/lifecycle.py +0 -0
  36. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/commands/other.py +0 -0
  37. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/errors.py +0 -0
  38. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/git/__init__.py +0 -0
  39. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/git/_errors.py +0 -0
  40. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/git/_git_repo.py +0 -0
  41. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/git/_models.py +0 -0
  42. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/grammar.py +0 -0
  43. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/__init__.py +0 -0
  44. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/errors.py +0 -0
  45. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/launchpad.py +0 -0
  46. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/__init__.py +0 -0
  47. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/base.py +0 -0
  48. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/build.py +0 -0
  49. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/code.py +0 -0
  50. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/distro.py +0 -0
  51. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/project.py +0 -0
  52. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/models/recipe.py +0 -0
  53. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/launchpad/util.py +0 -0
  54. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/misc/instance_bashrc +0 -0
  55. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/__init__.py +0 -0
  56. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/base.py +0 -0
  57. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/constraints.py +0 -0
  58. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/grammar.py +0 -0
  59. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/metadata.py +0 -0
  60. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/models/project.py +0 -0
  61. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/py.typed +0 -0
  62. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/remote/__init__.py +0 -0
  63. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/remote/errors.py +0 -0
  64. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/remote/git.py +0 -0
  65. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/remote/utils.py +0 -0
  66. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/remote/worktree.py +0 -0
  67. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/secrets.py +0 -0
  68. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/__init__.py +0 -0
  69. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/base.py +0 -0
  70. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/config.py +0 -0
  71. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/lifecycle.py +0 -0
  72. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/package.py +0 -0
  73. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/remotebuild.py +0 -0
  74. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/request.py +0 -0
  75. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/services/service_factory.py +0 -0
  76. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/__init__.py +0 -0
  77. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/callbacks.py +0 -0
  78. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/docs.py +0 -0
  79. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/error_formatting.py +0 -0
  80. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/logging.py +0 -0
  81. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/paths.py +0 -0
  82. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/platforms.py +0 -0
  83. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/repositories.py +0 -0
  84. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/retry.py +0 -0
  85. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/snap_config.py +0 -0
  86. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/string.py +0 -0
  87. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application/util/yaml.py +0 -0
  88. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application.egg-info/SOURCES.txt +0 -0
  89. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application.egg-info/dependency_links.txt +0 -0
  90. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application.egg-info/requires.txt +0 -0
  91. {craft_application-4.2.1 → craft_application-4.2.3}/craft_application.egg-info/top_level.txt +0 -0
  92. {craft_application-4.2.1 → craft_application-4.2.3}/docs/conf.py +0 -0
  93. {craft_application-4.2.1 → craft_application-4.2.3}/docs/explanation/build-plans.rst +0 -0
  94. {craft_application-4.2.1 → craft_application-4.2.3}/docs/explanation/index.rst +0 -0
  95. {craft_application-4.2.1 → craft_application-4.2.3}/docs/howto/index.rst +0 -0
  96. {craft_application-4.2.1 → craft_application-4.2.3}/docs/howto/partitions.rst +0 -0
  97. {craft_application-4.2.1 → craft_application-4.2.3}/docs/index.rst +0 -0
  98. {craft_application-4.2.1 → craft_application-4.2.3}/docs/reference/environment-variables.rst +0 -0
  99. {craft_application-4.2.1 → craft_application-4.2.3}/docs/reference/index.rst +0 -0
  100. {craft_application-4.2.1 → craft_application-4.2.3}/docs/reference/platforms.rst +0 -0
  101. {craft_application-4.2.1 → craft_application-4.2.3}/docs/tutorials/index.rst +0 -0
  102. {craft_application-4.2.1 → craft_application-4.2.3}/pyproject.toml +0 -0
  103. {craft_application-4.2.1 → craft_application-4.2.3}/setup.cfg +0 -0
  104. {craft_application-4.2.1 → craft_application-4.2.3}/tests/__init__.py +0 -0
  105. {craft_application-4.2.1 → craft_application-4.2.3}/tests/conftest.py +0 -0
  106. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/__init__.py +0 -0
  107. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/conftest.py +0 -0
  108. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/build-secrets/secret-source-folder/source-file.txt +0 -0
  109. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/build-secrets/testcraft.yaml +0 -0
  110. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/invalid_projects/build-error/testcraft.yaml +0 -0
  111. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/adoption/stderr +0 -0
  112. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/adoption/testcraft.yaml +0 -0
  113. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/basic/stderr +0 -0
  114. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/basic/testcraft.yaml +0 -0
  115. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/build-for-all/stderr +0 -0
  116. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/build-for-all/testcraft.yaml +0 -0
  117. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/environment/stderr +0 -0
  118. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/environment/testcraft.yaml +0 -0
  119. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-amd64/hello.txt +0 -0
  120. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/grammar/src/on-amd64-to-arm64/hello.txt +0 -0
  121. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/grammar/stderr +0 -0
  122. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/data/valid_projects/grammar/testcraft.yaml +0 -0
  123. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/launchpad/__init__.py +0 -0
  124. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/launchpad/conftest.py +0 -0
  125. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/launchpad/test_anonymous_access.py +0 -0
  126. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/__init__.py +0 -0
  127. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/test_lifecycle.py +0 -0
  128. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/test_provider.py +0 -0
  129. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/test_remotebuild.py +0 -0
  130. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/test_request.py +0 -0
  131. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/services/test_service_factory.py +0 -0
  132. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/test_application.py +0 -0
  133. {craft_application-4.2.1 → craft_application-4.2.3}/tests/integration/test_version.py +0 -0
  134. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/__init__.py +0 -0
  135. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/commands/__init__.py +0 -0
  136. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/commands/test_base.py +0 -0
  137. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/commands/test_lifecycle.py +0 -0
  138. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/commands/test_other.py +0 -0
  139. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/conftest.py +0 -0
  140. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/git/__init__.py +0 -0
  141. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/git/test_errors.py +0 -0
  142. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/git/test_git.py +0 -0
  143. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/__init__.py +0 -0
  144. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/conftest.py +0 -0
  145. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/models/__init__.py +0 -0
  146. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/models/test_base.py +0 -0
  147. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/models/test_code.py +0 -0
  148. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/test_launchpad.py +0 -0
  149. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/launchpad/test_util.py +0 -0
  150. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/__init__.py +0 -0
  151. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/project_models/basic_project.yaml +0 -0
  152. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/project_models/full_project.yaml +0 -0
  153. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/project_models/invalid_project.yaml +0 -0
  154. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/test_base.py +0 -0
  155. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/test_constraints.py +0 -0
  156. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/test_grammar.py +0 -0
  157. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/models/test_project.py +0 -0
  158. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/remote/__init__.py +0 -0
  159. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/remote/conftest.py +0 -0
  160. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/remote/test_errors.py +0 -0
  161. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/remote/test_utils.py +0 -0
  162. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/remote/test_worktree.py +0 -0
  163. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/__init__.py +0 -0
  164. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/conftest.py +0 -0
  165. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_config.py +0 -0
  166. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_lifecycle.py +0 -0
  167. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_package.py +0 -0
  168. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_remotebuild.py +0 -0
  169. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_repositories.py +0 -0
  170. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_request.py +0 -0
  171. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/services/test_service_factory.py +0 -0
  172. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/test_errors.py +0 -0
  173. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/test_grammar.py +0 -0
  174. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/test_nothing.py +0 -0
  175. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/test_secrets.py +0 -0
  176. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/__init__.py +0 -0
  177. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/invalid_yaml/_README +0 -0
  178. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/invalid_yaml/duplicate_second_level.yaml-invalid +0 -0
  179. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/invalid_yaml/duplicate_top_level.yaml-invalid +0 -0
  180. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/invalid_yaml/unhashable.yaml-invalid +0 -0
  181. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_docs.py +0 -0
  182. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_error_formatting.py +0 -0
  183. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_logging.py +0 -0
  184. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_paths.py +0 -0
  185. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_platforms.py +0 -0
  186. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_retry.py +0 -0
  187. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_snap_config.py +0 -0
  188. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_string.py +0 -0
  189. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/test_yaml.py +0 -0
  190. {craft_application-4.2.1 → craft_application-4.2.3}/tests/unit/util/valid_yaml/empty.yaml +0 -0
  191. {craft_application-4.2.1 → craft_application-4.2.3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: craft-application
3
- Version: 4.2.1
3
+ Version: 4.2.3
4
4
  Summary: A framework for *craft applications.
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Environment :: Console
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '4.2.1'
16
- __version_tuple__ = version_tuple = (4, 2, 1)
15
+ __version__ = version = '4.2.3'
16
+ __version_tuple__ = version_tuple = (4, 2, 3)
@@ -392,8 +392,9 @@ class Application:
392
392
  """Configure the application using any global arguments."""
393
393
 
394
394
  def _get_dispatcher(self) -> craft_cli.Dispatcher:
395
- """Configure this application. Should be called by the run method.
395
+ """Configure this application.
396
396
 
397
+ Should be called by the _run_inner method.
397
398
  Side-effect: This method may exit the process.
398
399
 
399
400
  :returns: A ready-to-run Dispatcher object
@@ -504,61 +505,67 @@ class Application:
504
505
  :param item: the name of the namespace or config item.
505
506
  :returns: the requested value.
506
507
  """
507
- return getattr(parsed_args, item, self.services.config.get(item))
508
+ arg_value = getattr(parsed_args, item, None)
509
+ if arg_value is not None:
510
+ return arg_value
511
+ return self.services.config.get(item)
508
512
 
509
- def run( # noqa: PLR0912,PLR0915 (too many branches, too many statements)
510
- self,
511
- ) -> int:
512
- """Bootstrap and run the application."""
513
- self._setup_logging()
514
- self._initialize_craft_parts()
513
+ def _run_inner(self) -> int:
514
+ """Actual run implementation."""
515
515
  dispatcher = self._get_dispatcher()
516
- craft_cli.emit.debug("Preparing application...")
517
-
518
- return_code = 1 # General error
519
- try:
520
- command = cast(
521
- commands.AppCommand,
522
- dispatcher.load_command(self.app_config),
516
+ command = cast(
517
+ commands.AppCommand,
518
+ dispatcher.load_command(self.app_config),
519
+ )
520
+ parsed_args = dispatcher.parsed_args()
521
+ platform = self.get_arg_or_config(parsed_args, "platform")
522
+ build_for = self.get_arg_or_config(parsed_args, "build_for")
523
+
524
+ # Some commands (e.g. remote build) can allow multiple platforms
525
+ # or build-fors, comma-separated. In these cases, we create the
526
+ # project using the first defined platform.
527
+ if platform and "," in platform:
528
+ platform = platform.split(",", maxsplit=1)[0]
529
+ if build_for and "," in build_for:
530
+ build_for = build_for.split(",", maxsplit=1)[0]
531
+
532
+ provider_name = command.provider_name(dispatcher.parsed_args())
533
+
534
+ craft_cli.emit.debug(f"Build plan: platform={platform}, build_for={build_for}")
535
+ self._pre_run(dispatcher)
536
+
537
+ managed_mode = command.run_managed(dispatcher.parsed_args())
538
+ if managed_mode or command.needs_project(dispatcher.parsed_args()):
539
+ self.services.project = self.get_project(
540
+ platform=platform, build_for=build_for
523
541
  )
524
- parsed_args = dispatcher.parsed_args()
525
- platform = self.get_arg_or_config(parsed_args, "platform")
526
- build_for = self.get_arg_or_config(parsed_args, "build_for")
527
542
 
528
- # Some commands (e.g. remote build) can allow multiple platforms
529
- # or build-fors, comma-separated. In these cases, we create the
530
- # project using the first defined platform.
531
- if platform and "," in platform:
532
- platform = platform.split(",", maxsplit=1)[0]
533
- if build_for and "," in build_for:
534
- build_for = build_for.split(",", maxsplit=1)[0]
543
+ self._configure_services(provider_name)
535
544
 
536
- provider_name = command.provider_name(dispatcher.parsed_args())
545
+ return_code = 1 # General error
546
+ if not managed_mode:
547
+ # command runs in the outer instance
548
+ craft_cli.emit.debug(f"Running {self.app.name} {command.name} on host")
549
+ return_code = dispatcher.run() or os.EX_OK
550
+ elif not self.is_managed():
551
+ # command runs in inner instance, but this is the outer instance
552
+ self.run_managed(platform, build_for)
553
+ return_code = os.EX_OK
554
+ else:
555
+ # command runs in inner instance
556
+ return_code = dispatcher.run() or 0
537
557
 
538
- craft_cli.emit.debug(
539
- f"Build plan: platform={platform}, build_for={build_for}"
540
- )
541
- self._pre_run(dispatcher)
558
+ return return_code
542
559
 
543
- managed_mode = command.run_managed(dispatcher.parsed_args())
544
- if managed_mode or command.needs_project(dispatcher.parsed_args()):
545
- self.services.project = self.get_project(
546
- platform=platform, build_for=build_for
547
- )
560
+ def run(self) -> int:
561
+ """Bootstrap and run the application."""
562
+ self._setup_logging()
563
+ self._initialize_craft_parts()
548
564
 
549
- self._configure_services(provider_name)
565
+ craft_cli.emit.debug("Preparing application...")
550
566
 
551
- if not managed_mode:
552
- # command runs in the outer instance
553
- craft_cli.emit.debug(f"Running {self.app.name} {command.name} on host")
554
- return_code = dispatcher.run() or os.EX_OK
555
- elif not self.is_managed():
556
- # command runs in inner instance, but this is the outer instance
557
- self.run_managed(platform, build_for)
558
- return_code = os.EX_OK
559
- else:
560
- # command runs in inner instance
561
- return_code = dispatcher.run() or 0
567
+ try:
568
+ return_code = self._run_inner()
562
569
  except craft_cli.ArgumentParsingError as err:
563
570
  print(err, file=sys.stderr) # to stderr, as argparse normally does
564
571
  craft_cli.emit.ended_ok()
@@ -98,12 +98,16 @@ class ProviderService(base.ProjectService):
98
98
  self.environment[f"{scheme.upper()}_PROXY"] = value
99
99
 
100
100
  if self._install_snap:
101
- channel = (
102
- None
103
- if util.is_running_from_snap(self._app.name)
104
- else os.getenv("CRAFT_SNAP_CHANNEL", "latest/stable")
105
- )
106
- self.snaps.append(Snap(name=self._app.name, channel=channel, classic=True))
101
+ if util.is_running_from_snap(self._app.name):
102
+ # use the aliased name of the snap when injecting
103
+ name = os.getenv("SNAP_INSTANCE_NAME", self._app.name)
104
+ channel = None
105
+ else:
106
+ # use the snap name when installing from the store
107
+ name = self._app.name
108
+ channel = os.getenv("CRAFT_SNAP_CHANNEL", "latest/stable")
109
+
110
+ self.snaps.append(Snap(name=name, channel=channel, classic=True))
107
111
 
108
112
  @contextlib.contextmanager
109
113
  def instance(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: craft-application
3
- Version: 4.2.1
3
+ Version: 4.2.3
4
4
  Summary: A framework for *craft applications.
5
5
  Classifier: Development Status :: 5 - Production/Stable
6
6
  Classifier: Environment :: Console
@@ -2,6 +2,27 @@
2
2
  Changelog
3
3
  *********
4
4
 
5
+ 4.2.3 (2024-Sep-18)
6
+ -------------------
7
+
8
+ Application
9
+ ===========
10
+
11
+ - ``get_arg_or_config`` now correctly checks the config service if the passed
12
+ namespace has ``None`` as the value of the requested item.
13
+
14
+ For a complete list of commits, check out the `4.2.3`_ release on GitHub.
15
+
16
+ 4.2.2 (2024-Sep-13)
17
+ -------------------
18
+
19
+ Application
20
+ ===========
21
+
22
+ - Add a ``_run_inner`` method to override or wrap the core run logic.
23
+
24
+ For a complete list of commits, check out the `4.2.2`_ release on GitHub.
25
+
5
26
  4.2.1 (2024-Sep-13)
6
27
  -------------------
7
28
 
@@ -255,3 +276,5 @@ For a complete list of commits, check out the `2.7.0`_ release on GitHub.
255
276
  .. _4.1.2: https://github.com/canonical/craft-application/releases/tag/4.1.2
256
277
  .. _4.2.0: https://github.com/canonical/craft-application/releases/tag/4.2.0
257
278
  .. _4.2.1: https://github.com/canonical/craft-application/releases/tag/4.2.1
279
+ .. _4.2.2: https://github.com/canonical/craft-application/releases/tag/4.2.2
280
+ .. _4.2.3: https://github.com/canonical/craft-application/releases/tag/4.2.3
@@ -84,41 +84,70 @@ def test_setup_proxy_environment(
84
84
 
85
85
 
86
86
  @pytest.mark.parametrize(
87
- ("install_snap", "environment", "snaps"),
87
+ ("environment", "snaps"),
88
88
  [
89
- (True, {}, [Snap(name="testcraft", channel="latest/stable", classic=True)]),
90
- (
91
- True,
89
+ pytest.param(
90
+ {},
91
+ [Snap(name="testcraft", channel="latest/stable", classic=True)],
92
+ id="install-from-store-default-channel",
93
+ ),
94
+ pytest.param(
92
95
  {"CRAFT_SNAP_CHANNEL": "something"},
93
96
  [Snap(name="testcraft", channel="something", classic=True)],
97
+ id="install-from-store-with-channel",
94
98
  ),
95
- (
96
- True,
97
- {"SNAP_NAME": "testcraft", "SNAP": "/snap/testcraft/x1"},
98
- [Snap(name="testcraft", channel=None, classic=True)],
99
+ pytest.param(
100
+ {
101
+ "SNAP_NAME": "testcraft",
102
+ "SNAP_INSTANCE_NAME": "testcraft_1",
103
+ "SNAP": "/snap/testcraft/x1",
104
+ },
105
+ [Snap(name="testcraft_1", channel=None, classic=True)],
106
+ id="inject-from-host",
99
107
  ),
100
- (
101
- True,
108
+ pytest.param(
102
109
  {
103
110
  "SNAP_NAME": "testcraft",
111
+ "SNAP_INSTANCE_NAME": "testcraft_1",
104
112
  "SNAP": "/snap/testcraft/x1",
105
113
  "CRAFT_SNAP_CHANNEL": "something",
106
114
  },
115
+ [Snap(name="testcraft_1", channel=None, classic=True)],
116
+ id="inject-from-host-ignore-channel",
117
+ ),
118
+ pytest.param(
119
+ # SNAP_INSTANCE_NAME may not exist if snapd < 2.43 or feature is disabled
120
+ {
121
+ "SNAP_NAME": "testcraft",
122
+ "SNAP": "/snap/testcraft/x1",
123
+ },
107
124
  [Snap(name="testcraft", channel=None, classic=True)],
125
+ id="missing-snap-instance-name",
108
126
  ),
109
- (False, {}, []),
110
- (False, {"CRAFT_SNAP_CHANNEL": "something"}, []),
111
- (
112
- False,
127
+ pytest.param(
128
+ # SNAP_INSTANCE_NAME may not exist if snapd < 2.43 or feature is disabled
113
129
  {
114
130
  "SNAP_NAME": "testcraft",
115
131
  "SNAP": "/snap/testcraft/x1",
132
+ # CRAFT_SNAP_CHANNEL should be ignored
116
133
  "CRAFT_SNAP_CHANNEL": "something",
117
134
  },
118
- [],
135
+ [Snap(name="testcraft", channel=None, classic=True)],
136
+ id="missing-snap-instance-name-ignore-snap-channel",
137
+ ),
138
+ pytest.param(
139
+ # this can happen when running testcraft from a venv in a snapped terminal
140
+ {
141
+ "SNAP_NAME": "kitty",
142
+ "SNAP_INSTANCE_NAME": "kitty",
143
+ "SNAP": "/snap/kitty/x1",
144
+ },
145
+ [Snap(name="testcraft", channel="latest/stable", classic=True)],
146
+ id="running-inside-another-snap",
119
147
  ),
120
148
  ],
121
149
  )
150
+ @pytest.mark.parametrize("install_snap", [True, False])
122
151
  def test_install_snap(
123
152
  monkeypatch,
124
153
  app_metadata,
@@ -143,7 +172,10 @@ def test_install_snap(
143
172
  )
144
173
  service.setup()
145
174
 
146
- assert service.snaps == snaps
175
+ if install_snap:
176
+ assert service.snaps == snaps
177
+ else:
178
+ assert service.snaps == []
147
179
 
148
180
 
149
181
  @pytest.mark.parametrize(
@@ -614,6 +614,38 @@ def test_run_managed_empty_plan(app, fake_project):
614
614
  app.run_managed(None, None)
615
615
 
616
616
 
617
+ @pytest.mark.parametrize(
618
+ ("parsed_args", "environ", "item", "expected"),
619
+ [
620
+ (argparse.Namespace(), {}, "build_for", None),
621
+ (argparse.Namespace(build_for=None), {}, "build_for", None),
622
+ (
623
+ argparse.Namespace(build_for=None),
624
+ {"CRAFT_BUILD_FOR": "arm64"},
625
+ "build_for",
626
+ "arm64",
627
+ ),
628
+ (
629
+ argparse.Namespace(build_for=None),
630
+ {"TESTCRAFT_BUILD_FOR": "arm64"},
631
+ "build_for",
632
+ "arm64",
633
+ ),
634
+ (
635
+ argparse.Namespace(build_for="riscv64"),
636
+ {"TESTCRAFT_BUILD_FOR": "arm64"},
637
+ "build_for",
638
+ "riscv64",
639
+ ),
640
+ ],
641
+ )
642
+ def test_get_arg_or_config(monkeypatch, app, parsed_args, environ, item, expected):
643
+ for var, content in environ.items():
644
+ monkeypatch.setenv(var, content)
645
+
646
+ assert app.get_arg_or_config(parsed_args, item) == expected
647
+
648
+
617
649
  @pytest.mark.parametrize(
618
650
  ("managed", "error", "exit_code", "message"),
619
651
  [