porringer 0.2.1.dev88__tar.gz → 0.2.1.dev89__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 (223) hide show
  1. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/PKG-INFO +3 -3
  2. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/tool_based.py +14 -0
  3. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pip/plugin.py +34 -3
  4. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/pytest/tests.py +73 -2
  5. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/pyproject.toml +8 -7
  6. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/conftest.py +29 -0
  7. porringer-0.2.1.dev89/tests/integration/test_bare_environment.py +49 -0
  8. porringer-0.2.1.dev89/tests/unit/plugins/pip/conftest.py +7 -0
  9. porringer-0.2.1.dev89/tests/unit/plugins/pip/test_auxiliary_tools.py +96 -0
  10. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_backend_resolver.py +2 -2
  11. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/LICENSE.md +0 -0
  12. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/README.md +0 -0
  13. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/__init__.py +0 -0
  14. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/api.py +0 -0
  15. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/__init__.py +0 -0
  16. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/backend.py +0 -0
  17. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/builder.py +0 -0
  18. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/cache.py +0 -0
  19. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/__init__.py +0 -0
  20. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/__init__.py +0 -0
  21. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/action_builder.py +0 -0
  22. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/discovery.py +0 -0
  23. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/execution.py +0 -0
  24. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/phase.py +0 -0
  25. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/presence.py +0 -0
  26. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/resolution.py +0 -0
  27. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/core/wsl_overlay.py +0 -0
  28. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/manifest.py +0 -0
  29. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/package.py +0 -0
  30. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/plugin.py +0 -0
  31. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/self.py +0 -0
  32. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/command/sync.py +0 -0
  33. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/resolver.py +0 -0
  34. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/backend/schema.py +0 -0
  35. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/__init__.py +0 -0
  36. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/__init__.py +0 -0
  37. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/cache.py +0 -0
  38. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/check.py +0 -0
  39. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/download.py +0 -0
  40. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/package.py +0 -0
  41. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/plugin.py +0 -0
  42. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/schema.py +0 -0
  43. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/self.py +0 -0
  44. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/command/sync.py +0 -0
  45. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/entry.py +0 -0
  46. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/console/schema.py +0 -0
  47. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/__init__.py +0 -0
  48. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/path.py +0 -0
  49. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/__init__.py +0 -0
  50. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/environment.py +0 -0
  51. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/manifest.py +0 -0
  52. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/plugin_manager.py +0 -0
  53. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/project_environment.py +0 -0
  54. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/python_environment.py +0 -0
  55. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/runtime.py +0 -0
  56. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/plugin_schema/scm.py +0 -0
  57. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/schema.py +0 -0
  58. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/core/transport.py +0 -0
  59. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/__init__.py +0 -0
  60. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/apt/__init__.py +0 -0
  61. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/apt/plugin.py +0 -0
  62. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/brew/__init__.py +0 -0
  63. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/brew/plugin.py +0 -0
  64. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/bun/__init__.py +0 -0
  65. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/bun/plugin.py +0 -0
  66. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/bun_project/__init__.py +0 -0
  67. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/bun_project/plugin.py +0 -0
  68. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/deno/__init__.py +0 -0
  69. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/deno/plugin.py +0 -0
  70. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/deno_project/__init__.py +0 -0
  71. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/deno_project/plugin.py +0 -0
  72. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/git/__init__.py +0 -0
  73. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/git/plugin.py +0 -0
  74. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/npm/__init__.py +0 -0
  75. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/npm/plugin.py +0 -0
  76. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/npm_project/__init__.py +0 -0
  77. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/npm_project/plugin.py +0 -0
  78. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pdm/__init__.py +0 -0
  79. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pdm/plugin.py +0 -0
  80. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pim/__init__.py +0 -0
  81. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pim/plugin.py +0 -0
  82. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pip/__init__.py +0 -0
  83. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pipx/__init__.py +0 -0
  84. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pipx/plugin.py +0 -0
  85. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pnpm/__init__.py +0 -0
  86. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pnpm/plugin.py +0 -0
  87. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pnpm_project/__init__.py +0 -0
  88. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pnpm_project/plugin.py +0 -0
  89. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/poetry/__init__.py +0 -0
  90. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/poetry/plugin.py +0 -0
  91. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pyenv/__init__.py +0 -0
  92. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/pyenv/plugin.py +0 -0
  93. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/uv/__init__.py +0 -0
  94. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/uv/plugin.py +0 -0
  95. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/uv_project/__init__.py +0 -0
  96. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/uv_project/plugin.py +0 -0
  97. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/winget/__init__.py +0 -0
  98. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/winget/plugin.py +0 -0
  99. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/wsl/__init__.py +0 -0
  100. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/wsl/transport.py +0 -0
  101. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/wsl/utility.py +0 -0
  102. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/yarn_project/__init__.py +0 -0
  103. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/plugin/yarn_project/plugin.py +0 -0
  104. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/py.typed +0 -0
  105. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/__init__.py +0 -0
  106. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/cache.py +0 -0
  107. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/check.py +0 -0
  108. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/config.py +0 -0
  109. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/download.py +0 -0
  110. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/execution.py +0 -0
  111. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/manifest.py +0 -0
  112. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/plugin.py +0 -0
  113. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/schema/progress.py +0 -0
  114. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/__init__.py +0 -0
  115. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/environment.py +0 -0
  116. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/plugin_manager.py +0 -0
  117. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/project_environment.py +0 -0
  118. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/scm.py +0 -0
  119. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/mock/subprocess.py +0 -0
  120. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/pytest/__init__.py +0 -0
  121. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/pytest/plugin.py +0 -0
  122. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/pytest/shared.py +0 -0
  123. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/test/pytest/variants.py +0 -0
  124. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/utility/__init__.py +0 -0
  125. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/utility/download.py +0 -0
  126. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/utility/exception.py +0 -0
  127. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/utility/py.typed +0 -0
  128. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/porringer/utility/utility.py +0 -0
  129. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/__init__.py +0 -0
  130. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/__init__.py +0 -0
  131. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/api.py +0 -0
  132. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/http.py +0 -0
  133. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/manifests.py +0 -0
  134. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/mock_plugins.py +0 -0
  135. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/fixtures/packages.py +0 -0
  136. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/__init__.py +0 -0
  137. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/__init__.py +0 -0
  138. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/git/__init__.py +0 -0
  139. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/git/test_scm.py +0 -0
  140. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/pip/__init__.py +0 -0
  141. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/pip/test_environment.py +0 -0
  142. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/pipx/__init__.py +0 -0
  143. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/pipx/test_environment.py +0 -0
  144. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/winget/__init__.py +0 -0
  145. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/plugins/winget/test_environment.py +0 -0
  146. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/test_bootstrap_presence.py +0 -0
  147. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/test_example_bootstrap.py +0 -0
  148. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/test_example_presence.py +0 -0
  149. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/integration/test_frozen_app_presence.py +0 -0
  150. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/__init__.py +0 -0
  151. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/__init__.py +0 -0
  152. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/apt/__init__.py +0 -0
  153. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/apt/test_environment.py +0 -0
  154. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/brew/__init__.py +0 -0
  155. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/brew/test_environment.py +0 -0
  156. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/bun/__init__.py +0 -0
  157. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/bun/test_environment.py +0 -0
  158. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/bun_project/__init__.py +0 -0
  159. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/bun_project/test_environment.py +0 -0
  160. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/deno/__init__.py +0 -0
  161. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/deno/test_environment.py +0 -0
  162. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/deno_project/__init__.py +0 -0
  163. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/deno_project/test_environment.py +0 -0
  164. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/git/__init__.py +0 -0
  165. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/git/test_clone_detection.py +0 -0
  166. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/git/test_scm.py +0 -0
  167. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/npm/__init__.py +0 -0
  168. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/npm/test_environment.py +0 -0
  169. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/npm_project/__init__.py +0 -0
  170. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/npm_project/test_environment.py +0 -0
  171. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pdm/__init__.py +0 -0
  172. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pdm/test_environment.py +0 -0
  173. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pim/__init__.py +0 -0
  174. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pim/test_environment.py +0 -0
  175. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pip/__init__.py +0 -0
  176. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pip/test_environment.py +0 -0
  177. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pipx/__init__.py +0 -0
  178. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pipx/test_environment.py +0 -0
  179. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pnpm/__init__.py +0 -0
  180. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pnpm/test_environment.py +0 -0
  181. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pnpm_project/__init__.py +0 -0
  182. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pnpm_project/test_environment.py +0 -0
  183. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/poetry/__init__.py +0 -0
  184. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/poetry/test_environment.py +0 -0
  185. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pyenv/__init__.py +0 -0
  186. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/pyenv/test_environment.py +0 -0
  187. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/uv/__init__.py +0 -0
  188. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/uv/test_environment.py +0 -0
  189. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/uv_project/__init__.py +0 -0
  190. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/uv_project/test_environment.py +0 -0
  191. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/yarn_project/__init__.py +0 -0
  192. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/plugins/yarn_project/test_environment.py +0 -0
  193. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_bootstrap_cross_platform.py +0 -0
  194. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_cache.py +0 -0
  195. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_check.py +0 -0
  196. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_check_updates.py +0 -0
  197. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_cli.py +0 -0
  198. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_command_plugin.py +0 -0
  199. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_command_self.py +0 -0
  200. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_concurrency_and_client.py +0 -0
  201. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_deferred_resolution.py +0 -0
  202. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_event_loop_safety.py +0 -0
  203. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_extras_introspection.py +0 -0
  204. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_frozen_app_detection.py +0 -0
  205. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_frozen_presence_resolution.py +0 -0
  206. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_manifest.py +0 -0
  207. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_package_filtering.py +0 -0
  208. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_package_ref.py +0 -0
  209. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_package_relation.py +0 -0
  210. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_path_sync.py +0 -0
  211. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_plugin_filtering.py +0 -0
  212. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_plugin_manager.py +0 -0
  213. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_presence_detection.py +0 -0
  214. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_project_directory.py +0 -0
  215. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_project_root.py +0 -0
  216. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_runtime_context_seeding.py +0 -0
  217. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_runtime_propagation.py +0 -0
  218. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_runtime_updates.py +0 -0
  219. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_sub_action_progress.py +0 -0
  220. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_uninstall.py +0 -0
  221. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_update_detection.py +0 -0
  222. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_upgrade.py +0 -0
  223. {porringer-0.2.1.dev88 → porringer-0.2.1.dev89}/tests/unit/test_wsl.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: porringer
3
- Version: 0.2.1.dev88
3
+ Version: 0.2.1.dev89
4
4
  Author-Email: Synodic Software <contact@synodic.software>
5
5
  License: MIT
6
6
  Project-URL: homepage, https://github.com/synodic/porringer
@@ -8,10 +8,10 @@ Project-URL: repository, https://github.com/synodic/porringer
8
8
  Requires-Python: >=3.14
9
9
  Requires-Dist: typer[all]>=0.24.1
10
10
  Requires-Dist: pydantic>=2.12.5
11
- Requires-Dist: platformdirs>=4.9.4
11
+ Requires-Dist: platformdirs>=4.9.6
12
12
  Requires-Dist: userpath>=1.9.2
13
13
  Requires-Dist: packaging>=26.0
14
- Requires-Dist: aiohttp>=3.13.3
14
+ Requires-Dist: aiohttp>=3.13.5
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  <p align="center">
@@ -19,6 +19,7 @@ import logging
19
19
  import re
20
20
  import shutil
21
21
  import subprocess
22
+ from collections.abc import Sequence
22
23
  from pathlib import Path
23
24
  from typing import Any, Self
24
25
 
@@ -39,6 +40,19 @@ class ToolBasedPlugin(Plugin):
39
40
  considered available.
40
41
  """
41
42
 
43
+ @classmethod
44
+ def auxiliary_tools(cls) -> Sequence[str]:
45
+ """Return optional CLI tools that may be invoked as post-action side effects.
46
+
47
+ Override to declare tools the plugin calls via ``shutil.which``
48
+ outside its primary ``tool_name()``. The test framework uses
49
+ this to verify that the plugin tolerates each tool being absent.
50
+
51
+ Returns:
52
+ Tool names (empty by default).
53
+ """
54
+ return ()
55
+
42
56
  def with_transport(self, transport: Transport) -> Self:
43
57
  """Create a new instance of this plugin using a different transport."""
44
58
  parameters = PluginParameters(distribution=self._distribution, transport=transport)
@@ -5,7 +5,7 @@ import json
5
5
  import logging
6
6
  import re
7
7
  import shutil
8
- from collections.abc import Callable
8
+ from collections.abc import Callable, Sequence
9
9
  from pathlib import Path
10
10
  from typing import override
11
11
 
@@ -44,6 +44,12 @@ class PIPEnvironment(PythonEnvironment):
44
44
  the underlying Python installation.
45
45
  """
46
46
 
47
+ @classmethod
48
+ @override
49
+ def auxiliary_tools(cls) -> Sequence[str]:
50
+ """Pip may invoke ``pymanager`` to refresh global aliases."""
51
+ return ('pymanager',)
52
+
47
53
  @classmethod
48
54
  @override
49
55
  def tool_name(cls) -> str:
@@ -127,6 +133,10 @@ class PIPEnvironment(PythonEnvironment):
127
133
  When a progress_callback is provided, streams stderr line-by-line to
128
134
  report download and install phases. Otherwise falls back to the simple
129
135
  `run_command` path for zero overhead.
136
+
137
+ On Windows, if the Python Install Manager (pymanager) is available,
138
+ runs ``pymanager install --refresh`` after a successful install to
139
+ regenerate global aliases for newly installed entry points.
130
140
  """
131
141
  logger = logging.getLogger('porringer.pip.install')
132
142
  args = list(
@@ -139,9 +149,30 @@ class PIPEnvironment(PythonEnvironment):
139
149
 
140
150
  if params.progress_callback is None:
141
151
  # Fast path — no streaming needed
142
- return await self._install_simple(args, params.package, logger)
152
+ result = await self._install_simple(args, params.package, logger)
153
+ else:
154
+ result = await self._install_with_progress(args, params, logger)
143
155
 
144
- return await self._install_with_progress(args, params, logger)
156
+ if not params.dry:
157
+ await self._refresh_pymanager_aliases(logger)
158
+
159
+ return result
160
+
161
+ @staticmethod
162
+ async def _refresh_pymanager_aliases(logger: logging.Logger) -> None:
163
+ """Best-effort refresh of pymanager global aliases after pip install.
164
+
165
+ No-op when pymanager is absent. Failures never block the install.
166
+ """
167
+ if shutil.which('pymanager') is None:
168
+ return
169
+
170
+ try:
171
+ result = await run_command(['pymanager', 'install', '--refresh'])
172
+ if result.returncode != 0:
173
+ logger.warning('pymanager install --refresh exited with code %d', result.returncode)
174
+ except Exception as exc:
175
+ logger.warning('Failed to refresh pymanager aliases: %s', exc)
145
176
 
146
177
  @staticmethod
147
178
  async def _install_simple(args: list[str], package: PackageRef, logger: logging.Logger) -> Package | None:
@@ -1,15 +1,19 @@
1
1
  """Implementation of tests that should be overridden in plugins"""
2
2
 
3
3
  import shutil
4
- from abc import ABCMeta
4
+ from abc import ABCMeta, abstractmethod
5
+ from collections.abc import Generator
6
+ from typing import NamedTuple
7
+ from unittest.mock import AsyncMock
5
8
 
6
9
  from packaging.version import Version
7
10
 
8
11
  import pytest
9
- from porringer.core.plugin_schema.environment import Environment
12
+ from porringer.core.plugin_schema.environment import Environment, PackageParameters
10
13
  from porringer.core.plugin_schema.project_environment import ProjectEnvironment
11
14
  from porringer.core.plugin_schema.runtime import RuntimeProvider
12
15
  from porringer.core.plugin_schema.scm import ScmEnvironment
16
+ from porringer.core.plugin_schema.tool_based import ToolBasedPlugin
13
17
  from porringer.core.schema import Distribution, PackageRef, PluginParameters
14
18
  from porringer.test.pytest.shared import (
15
19
  EnvironmentTests,
@@ -183,3 +187,70 @@ class RuntimeProviderUnitTests[T: Environment](EnvironmentUnitTests[T], RuntimeP
183
187
  kind = plugin_type.provided_runtime_kind()
184
188
  assert isinstance(kind, str)
185
189
  assert len(kind) > 0
190
+
191
+
192
+ class InstallContext(NamedTuple):
193
+ """Context returned by the ``install_context`` fixture."""
194
+
195
+ plugin: Environment
196
+ params: PackageParameters
197
+ mock_run: AsyncMock
198
+ scenario: str
199
+
200
+
201
+ class AuxiliaryToolTests[T: ToolBasedPlugin](metaclass=ABCMeta):
202
+ """Mixin that tests auxiliary-tool interactions declared via ``_auxiliary_tools``.
203
+
204
+ Concrete test classes must provide:
205
+
206
+ * ``fixture_install_context`` — a fixture yielding an
207
+ :class:`InstallContext` whose ``scenario`` field indicates which
208
+ auxiliary-tool configuration is active.
209
+
210
+ Expected scenarios (the fixture should be parametrized over these):
211
+
212
+ * ``"tools_absent"`` — ``shutil.which`` returns ``None`` for
213
+ auxiliary tools; ``run_command`` is not expected to be called
214
+ for them.
215
+ * ``"tools_present"`` — ``shutil.which`` returns a fake path;
216
+ ``run_command`` mock returns success.
217
+ * ``"tools_fail_exception"`` — ``shutil.which`` returns a fake
218
+ path; ``run_command`` raises ``OSError`` for auxiliary tools.
219
+ * ``"tools_fail_nonzero"`` — ``shutil.which`` returns a fake path;
220
+ ``run_command`` returns non-zero for auxiliary tools.
221
+
222
+ The mixin is intentionally separate from ``EnvironmentUnitTests`` so
223
+ that plugins without auxiliary tools don't inherit meaningless tests.
224
+ """
225
+
226
+ @abstractmethod
227
+ @pytest.fixture(name='install_context')
228
+ def fixture_install_context(self) -> Generator[InstallContext]:
229
+ """Yield an ``InstallContext`` with install internals mocked."""
230
+ raise NotImplementedError('Override this fixture')
231
+
232
+ @staticmethod
233
+ async def test_install_succeeds(install_context: InstallContext) -> None:
234
+ """install() succeeds regardless of auxiliary tool availability."""
235
+ plugin, params, _mock_run, _scenario = install_context
236
+ result = await plugin.install(params)
237
+ # Install should never fail due to auxiliary tool issues
238
+ assert result is not None
239
+
240
+ @staticmethod
241
+ async def test_auxiliary_tools_called_only_when_present(
242
+ install_context: InstallContext,
243
+ ) -> None:
244
+ """Auxiliary tools are invoked when present, skipped when absent."""
245
+ plugin, params, mock_run, scenario = install_context
246
+ aux = type(plugin).auxiliary_tools()
247
+ if not aux:
248
+ pytest.skip('No auxiliary tools declared')
249
+
250
+ await plugin.install(params)
251
+
252
+ called_args = [str(c) for c in mock_run.call_args_list]
253
+ if scenario == 'tools_absent':
254
+ assert not any(tool in arg for arg in called_args for tool in aux)
255
+ else:
256
+ assert any(tool in arg for arg in called_args for tool in aux)
@@ -10,12 +10,12 @@ requires-python = ">=3.14"
10
10
  dependencies = [
11
11
  "typer[all]>=0.24.1",
12
12
  "pydantic>=2.12.5",
13
- "platformdirs>=4.9.4",
13
+ "platformdirs>=4.9.6",
14
14
  "userpath>=1.9.2",
15
15
  "packaging>=26.0",
16
- "aiohttp>=3.13.3",
16
+ "aiohttp>=3.13.5",
17
17
  ]
18
- version = "0.2.1.dev88"
18
+ version = "0.2.1.dev89"
19
19
 
20
20
  [project.license]
21
21
  text = "MIT"
@@ -56,17 +56,17 @@ porringer = "porringer.console.entry:app"
56
56
 
57
57
  [dependency-groups]
58
58
  lint = [
59
- "ruff>=0.15.7",
60
- "pyrefly>=0.57.1",
59
+ "ruff>=0.15.10",
60
+ "pyrefly>=0.60.2",
61
61
  ]
62
62
  test = [
63
- "pytest>=9.0.2",
63
+ "pytest>=9.0.3",
64
64
  "pytest-cov>=7.1.0",
65
65
  "pytest-mock>=3.15.1",
66
66
  "pytest-asyncio>=1.3.0",
67
67
  ]
68
68
  docs = [
69
- "zensical>=0.0.28",
69
+ "zensical>=0.0.32",
70
70
  ]
71
71
 
72
72
  [tool.pytest.ini_options]
@@ -78,6 +78,7 @@ testpaths = [
78
78
  markers = [
79
79
  "fresh_plugins: invalidate the plugin discovery cache before this test",
80
80
  "mock_packages: use a cached package list instead of real subprocess calls",
81
+ "bare_environment: simulate a bare system with only the primary tool on PATH",
81
82
  ]
82
83
 
83
84
  [tool.ruff]
@@ -1,5 +1,6 @@
1
1
  """Shared pytest configuration and fixtures."""
2
2
 
3
+ import shutil
3
4
  import sys
4
5
  import tempfile
5
6
  from collections.abc import Generator
@@ -55,6 +56,10 @@ def pytest_configure(config: pytest.Config) -> None:
55
56
  'markers',
56
57
  'frozen_app: simulate a frozen (PyInstaller) application environment',
57
58
  )
59
+ config.addinivalue_line(
60
+ 'markers',
61
+ 'bare_environment: simulate a bare system with only the primary tool on PATH',
62
+ )
58
63
 
59
64
 
60
65
  def pytest_runtest_setup(item: pytest.Item) -> None:
@@ -104,6 +109,30 @@ def frozen_context(*, which_result: str | None = None) -> Generator[None]:
104
109
  p.stop()
105
110
 
106
111
 
112
+ @contextmanager
113
+ def bare_environment_context(*, allowed_tools: set[str] | None = None) -> Generator[None]:
114
+ """Context manager that hides all CLI tools except *allowed_tools*.
115
+
116
+ Patches ``shutil.which`` globally so that any tool not in
117
+ *allowed_tools* appears absent. This catches undeclared
118
+ auxiliary-tool dependencies.
119
+
120
+ Args:
121
+ allowed_tools: Tool names that should remain discoverable.
122
+ When ``None``, **all** tools are hidden.
123
+ """
124
+ allowed = allowed_tools or set()
125
+ original_which = shutil.which
126
+
127
+ def _restricted_which(name: str, *args, **kwargs) -> str | None:
128
+ if name in allowed:
129
+ return original_which(name, *args, **kwargs)
130
+ return None
131
+
132
+ with patch('shutil.which', side_effect=_restricted_which):
133
+ yield
134
+
135
+
107
136
  @pytest.fixture(autouse=True)
108
137
  def _apply_mock_packages(
109
138
  request: pytest.FixtureRequest,
@@ -0,0 +1,49 @@
1
+ """Integration tests that verify plugins tolerate a bare environment.
2
+
3
+ Every available plugin is dry-run installed under a restricted PATH
4
+ where only the plugin's primary tool is discoverable. This catches
5
+ undeclared auxiliary-tool dependencies that would fail on a clean
6
+ machine.
7
+
8
+ Safety guarantees (no system mutation):
9
+
10
+ 1. ``dry_run=True`` — the sync engine skips all mutating actions
11
+ (install, clone, post-sync commands). Only read-only subprocess
12
+ calls happen (e.g. ``git rev-parse`` for presence detection,
13
+ ``pip list`` for package queries).
14
+ 2. ``bare_environment_context`` — restricts ``shutil.which`` so that
15
+ even if a mutating code path were accidentally reached, most tools
16
+ would appear absent and the operation would be skipped or fail
17
+ before spawning a process.
18
+ 3. ``session_api`` — uses an isolated temporary directory tree for
19
+ configuration and cache (no writes to user/system porringer dirs).
20
+ """
21
+
22
+ from pathlib import Path
23
+
24
+ import pytest
25
+
26
+ from porringer.api import API
27
+ from porringer.schema import SetupParameters
28
+ from tests.conftest import bare_environment_context
29
+
30
+ _BOOTSTRAP_DIR = Path(__file__).resolve().parents[2] / 'examples' / 'python-bootstrap'
31
+
32
+
33
+ @pytest.mark.bare_environment
34
+ @pytest.mark.fresh_plugins
35
+ class TestBareEnvironmentDryRun:
36
+ """Dry-run the bootstrap example under a bare PATH."""
37
+
38
+ @staticmethod
39
+ async def test_dry_run_succeeds_under_bare_environment(session_api: API) -> None:
40
+ """Dry-run with only primary tools on PATH should not crash."""
41
+ setup_params = SetupParameters(paths=_BOOTSTRAP_DIR, dry_run=True)
42
+
43
+ with bare_environment_context(allowed_tools={'pip', 'python', 'git'}):
44
+ results = await session_api.sync.run(setup_params)
45
+
46
+ assert len(results.manifest_results) >= 1
47
+ for manifest in results.manifest_results:
48
+ # Every action should resolve without raising
49
+ assert manifest.actions is not None
@@ -0,0 +1,7 @@
1
+ """Shared fixtures for pip plugin tests."""
2
+
3
+ from packaging.version import Version
4
+
5
+ from porringer.core.schema import Distribution, PluginParameters
6
+
7
+ _MOCK_PARAMS = PluginParameters(distribution=Distribution(version=Version('0.0.0')))
@@ -0,0 +1,96 @@
1
+ """Tests for auxiliary-tool interactions in the pip plugin."""
2
+
3
+ from collections.abc import Generator
4
+ from typing import override
5
+ from unittest.mock import AsyncMock, patch
6
+
7
+ import pytest
8
+ from packaging.version import Version
9
+
10
+ from porringer.core.plugin_schema.environment import PackageParameters
11
+ from porringer.core.schema import Distribution, Package, PackageRef, PluginParameters
12
+ from porringer.plugin.pip.plugin import PIPEnvironment
13
+ from porringer.test.pytest.tests import AuxiliaryToolTests, InstallContext
14
+ from porringer.utility.utility import CommandResult
15
+
16
+ _MOCK_PARAMS = PluginParameters(distribution=Distribution(version=Version('0.0.0')))
17
+
18
+ _SCENARIOS = ['tools_absent', 'tools_present', 'tools_fail_exception', 'tools_fail_nonzero']
19
+
20
+
21
+ def _make_context(scenario: str) -> Generator[InstallContext]:
22
+ """Build an InstallContext for the given scenario."""
23
+ env = PIPEnvironment(_MOCK_PARAMS)
24
+ params = PackageParameters(package=PackageRef(name='test-pkg'))
25
+
26
+ # Determine shutil.which behavior
27
+ which_return = None if scenario == 'tools_absent' else r'C:\Program Files\pymanager\pymanager.exe'
28
+
29
+ # Determine run_command behavior for auxiliary tool calls
30
+ if scenario == 'tools_fail_exception':
31
+
32
+ async def run_side_effect(args, **kwargs):
33
+ if args == ['pymanager', 'install', '--refresh']:
34
+ raise OSError('pymanager crashed')
35
+ return CommandResult(returncode=0, stdout='', stderr='')
36
+
37
+ mock_run = AsyncMock(side_effect=run_side_effect)
38
+ elif scenario == 'tools_fail_nonzero':
39
+
40
+ async def run_side_effect_nonzero(args, **kwargs):
41
+ if args == ['pymanager', 'install', '--refresh']:
42
+ return CommandResult(returncode=1, stdout='', stderr='something went wrong')
43
+ return CommandResult(returncode=0, stdout='', stderr='')
44
+
45
+ mock_run = AsyncMock(side_effect=run_side_effect_nonzero)
46
+ else:
47
+ mock_run = AsyncMock(return_value=CommandResult(returncode=0, stdout='', stderr=''))
48
+
49
+ with (
50
+ patch.object(
51
+ PIPEnvironment,
52
+ '_install_simple',
53
+ new_callable=AsyncMock,
54
+ return_value=Package(name='test-pkg', version=None),
55
+ ),
56
+ patch.object(env, 'install_command', return_value=['python', '-m', 'pip', 'install', 'test-pkg']),
57
+ patch('porringer.plugin.pip.plugin.run_command', mock_run),
58
+ patch('porringer.plugin.pip.plugin.shutil.which', return_value=which_return),
59
+ ):
60
+ yield InstallContext(plugin=env, params=params, mock_run=mock_run, scenario=scenario)
61
+
62
+
63
+ class TestPymanagerRefresh(AuxiliaryToolTests[PIPEnvironment]):
64
+ """Auxiliary-tool tests for pip's pymanager refresh integration."""
65
+
66
+ @override
67
+ @pytest.fixture(name='install_context', params=_SCENARIOS)
68
+ def fixture_install_context(self, request: pytest.FixtureRequest) -> Generator[InstallContext]:
69
+ """Yield an InstallContext parametrized over auxiliary-tool scenarios."""
70
+ yield from _make_context(request.param)
71
+
72
+ @staticmethod
73
+ async def test_refresh_not_called_on_dry_run() -> None:
74
+ """Dry-run install does not trigger refresh even with pymanager available."""
75
+ env = PIPEnvironment(_MOCK_PARAMS)
76
+ params = PackageParameters(package=PackageRef(name='test-pkg'), dry=True)
77
+
78
+ with (
79
+ patch.object(
80
+ PIPEnvironment,
81
+ '_install_simple',
82
+ new_callable=AsyncMock,
83
+ return_value=Package(name='test-pkg', version=None),
84
+ ),
85
+ patch.object(env, 'install_command', return_value=['python', '-m', 'pip', 'install', 'test-pkg']),
86
+ patch(
87
+ 'porringer.plugin.pip.plugin.shutil.which',
88
+ return_value=r'C:\Program Files\pymanager\pymanager.exe',
89
+ ),
90
+ patch('porringer.plugin.pip.plugin.run_command', new_callable=AsyncMock) as mock_run,
91
+ ):
92
+ mock_run.return_value = CommandResult(returncode=0, stdout='', stderr='')
93
+ result = await env.install(params)
94
+
95
+ assert result is not None
96
+ mock_run.assert_not_called()
@@ -6,7 +6,7 @@ The resolver selects a plugin per (PluginKind, ecosystem) pair using:
6
6
  """
7
7
 
8
8
  from pathlib import Path
9
- from typing import Self, cast
9
+ from typing import Self
10
10
 
11
11
  from packaging.version import Version
12
12
 
@@ -36,7 +36,7 @@ class _StubPlugin:
36
36
 
37
37
  def with_transport(self, transport: Transport) -> Self:
38
38
  parameters = PluginParameters(distribution=self._distribution, transport=transport)
39
- return cast(Self, type(self)(parameters))
39
+ return type(self)(parameters)
40
40
 
41
41
  @staticmethod
42
42
  def ecosystem() -> Ecosystem | None: