py-env-studio 2.1.3__tar.gz → 2.1.4__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.
- {py_env_studio-2.1.3/py_env_studio.egg-info → py_env_studio-2.1.4}/PKG-INFO +3 -3
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/PYPI_README.md +2 -2
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/README.md +18 -21
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/commands.py +36 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/config.ini +1 -1
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/__init__.py +9 -9
- py_env_studio-2.1.4/py_env_studio/core/runtime_mismatch.py +133 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/runtime_toggle.py +79 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/engine.py +20 -19
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/models.py +1 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/main_window.py +123 -4
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/vulneribility_scanner.py +1 -1
- {py_env_studio-2.1.3 → py_env_studio-2.1.4/py_env_studio.egg-info}/PKG-INFO +3 -3
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio.egg-info/SOURCES.txt +2 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/pyproject.toml +1 -1
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_app_updates.py +3 -3
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_cli_commands.py +40 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_mcp_consistency.py +13 -13
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_mcp_server.py +26 -26
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_project_intelligence.py +7 -7
- py_env_studio-2.1.4/tests/test_runtime_mismatch.py +155 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_template_engine.py +62 -1
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/LICENSE +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/__main__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/app_updates.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/auto_resolve.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/bootstrap.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/configuration.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/database.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/dependency_preview.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/env_manager.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/environment_lock.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/integration.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/config.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/context.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/errors.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/schemas/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/schemas/responses.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/server.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/analysis.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/environments.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/packages.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/project.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/mcp/tools/security.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/package_manager.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/package_update_monitor.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/pip_tools.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/plugins/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/plugins/base.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/plugins/exceptions.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/plugins/manager.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_contract/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_contract/models.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_contract/repository.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_contract/service.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_intelligence/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_intelligence/models.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_intelligence/service.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/project_launcher.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/py_tonic.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/runtime.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/runtime_cache.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/runtime_providers.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/environment_locks.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/environments.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/migration.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/package_updates.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/project_contract.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/runtime_cache.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/schema_core.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/schema_runtime_cache.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/schema/vulnerability.sql +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/setup_state.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/strategies.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/builtin_templates.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/community.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/content_filters.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/github_import.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/registry.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/user_template_store.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/validator.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/templates/workflow.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/tool_probe.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/tools.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/uv_tools.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/core/windows_apps.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/main.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/about.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/activate-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/available-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/create-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/delete-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/export.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/install.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/list-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/logo.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/packages.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/pes-default-transparrent.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/pes-icon-default.ico +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/pes-icon-default.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/pes-transparrent-icon-default.ico +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/requirements.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/selected-env.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/uninstall.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/static/icons/update.png +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/status_bar.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/ui/ui_tasks.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/__init__.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/app_icon.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/app_logging.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/db_status.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/handlers.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/progress.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/version_utils.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio/utils/vulneribility_insights.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio.egg-info/dependency_links.txt +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio.egg-info/entry_points.txt +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio.egg-info/requires.txt +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/py_env_studio.egg-info/top_level.txt +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/setup.cfg +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_all_features.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_app_logging.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_cli_progress.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_community_templates.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_configuration_service.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_dependency_preview.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_environment_lock.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_github_template_import.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_package_update_monitor.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_plugin_events.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_plugin_import.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_plugin_load.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_plugin_manager.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_project_contract.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_project_openers.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_project_runtime.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_runtime_cache.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_runtime_providers.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_schema_templates.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_status_progress.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_template_registry.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_template_workflow.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_ui_backend.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_user_template_store.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_uv_packages.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_vulnerability_insights_charts.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_vulnerability_insights_update.py +0 -0
- {py_env_studio-2.1.3 → py_env_studio-2.1.4}/tests/test_window_icon.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-env-studio
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: A comprehensive cross-platform GUI and CLI tool for managing Python virtual environments and packages with advanced features including environment lifecycle management, package operations, vulnerability scanning, security insights, project templates, Py-Tonic interactive learning mode, an extensible plugin system for custom functionality, and a local read-only MCP control plane (beta) for AI coding agents.
|
|
5
5
|
Author-email: Py Env Studio Team <contact.shaikh.wasim@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -109,7 +109,7 @@ Perfect for:
|
|
|
109
109
|
- 🔌 Extensible **plugin system** with 18 lifecycle hooks
|
|
110
110
|
- 📟 Full CLI with progress gauge and `-v` / `-q` / `--no-progress` controls
|
|
111
111
|
- 🧪 **Beta:** local, read-only **MCP control plane** for AI coding agents
|
|
112
|
-
(stdio transport, 8 `
|
|
112
|
+
(stdio transport, 8 `pes_*` tools, opt-out via `enabled = false`)
|
|
113
113
|
|
|
114
114
|
## 📥 Installation
|
|
115
115
|
|
|
@@ -161,7 +161,7 @@ Setup steps for VS Code and other clients:
|
|
|
161
161
|
|
|
162
162
|
- Documentation home: [https://py-env-studio.readthedocs.io/](https://py-env-studio.readthedocs.io/)
|
|
163
163
|
- Usage manual: [https://py-env-studio.readthedocs.io/en/latest/getting-started/usage.html](https://py-env-studio.readthedocs.io/en/latest/getting-started/usage.html)
|
|
164
|
-
- Release notes: [https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.
|
|
164
|
+
- Release notes: [https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.4.html](https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.4.html)
|
|
165
165
|
- Source code: [https://github.com/pyenvstudio/py-env-studio](https://github.com/pyenvstudio/py-env-studio)
|
|
166
166
|
- Issue tracker: [https://github.com/pyenvstudio/py-env-studio/issues](https://github.com/pyenvstudio/py-env-studio/issues)
|
|
167
167
|
|
|
@@ -44,7 +44,7 @@ Perfect for:
|
|
|
44
44
|
- 🔌 Extensible **plugin system** with 18 lifecycle hooks
|
|
45
45
|
- 📟 Full CLI with progress gauge and `-v` / `-q` / `--no-progress` controls
|
|
46
46
|
- 🧪 **Beta:** local, read-only **MCP control plane** for AI coding agents
|
|
47
|
-
(stdio transport, 8 `
|
|
47
|
+
(stdio transport, 8 `pes_*` tools, opt-out via `enabled = false`)
|
|
48
48
|
|
|
49
49
|
## 📥 Installation
|
|
50
50
|
|
|
@@ -96,7 +96,7 @@ Setup steps for VS Code and other clients:
|
|
|
96
96
|
|
|
97
97
|
- Documentation home: [https://py-env-studio.readthedocs.io/](https://py-env-studio.readthedocs.io/)
|
|
98
98
|
- Usage manual: [https://py-env-studio.readthedocs.io/en/latest/getting-started/usage.html](https://py-env-studio.readthedocs.io/en/latest/getting-started/usage.html)
|
|
99
|
-
- Release notes: [https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.
|
|
99
|
+
- Release notes: [https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.4.html](https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.4.html)
|
|
100
100
|
- Source code: [https://github.com/pyenvstudio/py-env-studio](https://github.com/pyenvstudio/py-env-studio)
|
|
101
101
|
- Issue tracker: [https://github.com/pyenvstudio/py-env-studio/issues](https://github.com/pyenvstudio/py-env-studio/issues)
|
|
102
102
|
|
|
@@ -29,26 +29,23 @@ Perfect for:
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
-
## 🆕 What's new in v2.1.
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
progress gauge and GUI status gauge, and consistent application icons.
|
|
50
|
-
|
|
51
|
-
→ [Full v2.1.2 release notes](docs/releases/v2.1.2.md)
|
|
32
|
+
## 🆕 What's new in v2.1.4
|
|
33
|
+
|
|
34
|
+
- 🔗 **Project ↔ Environment association** — creating an environment from a
|
|
35
|
+
project template now registers it with the project by default, with an
|
|
36
|
+
explicit opt-out that creates an unregistered environment.
|
|
37
|
+
- 🗂️ **Project environment panel** (Project menu) — view the environment
|
|
38
|
+
associated with a project, switch the association to another PES
|
|
39
|
+
environment, or unregister it without deleting the environment.
|
|
40
|
+
- 🛡️ **Runtime mismatch guard** — PES compares the project's registered
|
|
41
|
+
environment with the runtime it can reliably observe, stays silent on a
|
|
42
|
+
match, and surfaces mismatches through `pes status`, `pes on`, and `pes run`
|
|
43
|
+
(warning once per unchanged mismatch).
|
|
44
|
+
- 👁️ **Conservative runtime detection** — PES only reports runtime information
|
|
45
|
+
it can observe; it does not infer which interpreter VS Code or PyCharm
|
|
46
|
+
selected, does not switch IDE interpreters, and needs no IDE extension.
|
|
47
|
+
|
|
48
|
+
→ [Full v2.1.4 release notes](docs/releases/v2.1.4.md)
|
|
52
49
|
|
|
53
50
|
---
|
|
54
51
|
|
|
@@ -105,7 +102,7 @@ PyEnvStudio now features a powerful plugin system that allows developers to exte
|
|
|
105
102
|
- **Easy Development** - Simple plugin API with lifecycle management
|
|
106
103
|
- **Examples Included** - Full working sample plugin with documentation
|
|
107
104
|
|
|
108
|
-
See the [v2.1.
|
|
105
|
+
See the [v2.1.4 release notes](docs/releases/v2.1.4.md) and the [plugin development guide](docs/plugins/development.md).
|
|
109
106
|
|
|
110
107
|
### Additional implemented capabilities
|
|
111
108
|
|
|
@@ -20,6 +20,12 @@ from py_env_studio.core.runtime_toggle import (
|
|
|
20
20
|
init_project,
|
|
21
21
|
list_registered_projects,
|
|
22
22
|
)
|
|
23
|
+
from py_env_studio.core.runtime_mismatch import (
|
|
24
|
+
RuntimeMatchState,
|
|
25
|
+
clear_mismatch_notification,
|
|
26
|
+
detect_runtime_mismatch,
|
|
27
|
+
notify_mismatch_once,
|
|
28
|
+
)
|
|
23
29
|
from py_env_studio.utils.app_logging import configure_logging
|
|
24
30
|
from py_env_studio.utils.progress import TaskProgress
|
|
25
31
|
|
|
@@ -147,6 +153,7 @@ def handle_init(_: argparse.Namespace) -> None:
|
|
|
147
153
|
|
|
148
154
|
|
|
149
155
|
def handle_on(_: argparse.Namespace) -> None:
|
|
156
|
+
_notify_runtime_mismatch()
|
|
150
157
|
_print_runtime_result(enable_runtime())
|
|
151
158
|
|
|
152
159
|
|
|
@@ -166,6 +173,34 @@ def handle_status(_: argparse.Namespace) -> None:
|
|
|
166
173
|
print(f"Environment Path: {status.get('environment_path', 'N/A')}")
|
|
167
174
|
print(f"Python Version: {status['python_version']}")
|
|
168
175
|
|
|
176
|
+
root = Path(status["project_root"]) if status.get("project_root") else None
|
|
177
|
+
runtime = detect_runtime_mismatch(root)
|
|
178
|
+
if runtime.registered_environment_id:
|
|
179
|
+
print("Registered environment:")
|
|
180
|
+
print(f" {runtime.registered_environment_id}")
|
|
181
|
+
print(f" Python {runtime.registered_python_version or 'Unknown'}")
|
|
182
|
+
else:
|
|
183
|
+
print("Registered environment: None")
|
|
184
|
+
if runtime.current_python_executable:
|
|
185
|
+
print("Current runtime:")
|
|
186
|
+
print(f" {runtime.current_python_executable}")
|
|
187
|
+
print(f" Python {runtime.current_python_version or 'Unknown'}")
|
|
188
|
+
state_label = (
|
|
189
|
+
"ENVIRONMENT_MISMATCH"
|
|
190
|
+
if runtime.state is RuntimeMatchState.MISMATCH
|
|
191
|
+
else runtime.state.value
|
|
192
|
+
)
|
|
193
|
+
print(f"Status: {state_label}")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _notify_runtime_mismatch() -> None:
|
|
197
|
+
runtime = detect_runtime_mismatch()
|
|
198
|
+
if runtime.state is RuntimeMatchState.MISMATCH:
|
|
199
|
+
if notify_mismatch_once(runtime):
|
|
200
|
+
print("Environment mismatch detected")
|
|
201
|
+
elif runtime.state is RuntimeMatchState.MATCH and runtime.project_path:
|
|
202
|
+
clear_mismatch_notification(runtime.project_path)
|
|
203
|
+
|
|
169
204
|
|
|
170
205
|
def handle_list_projects(_: argparse.Namespace) -> None:
|
|
171
206
|
projects = list_registered_projects()
|
|
@@ -188,6 +223,7 @@ def handle_run(args: argparse.Namespace) -> None:
|
|
|
188
223
|
|
|
189
224
|
# No gauge here: the child process owns the terminal and its output must
|
|
190
225
|
# not be interleaved with redrawn status lines.
|
|
226
|
+
_notify_runtime_mismatch()
|
|
191
227
|
logger.info("Running %s in the managed environment", list(args.args))
|
|
192
228
|
result = execute_in_managed_env(Path.cwd(), list(args.args))
|
|
193
229
|
if result:
|
|
@@ -6,7 +6,7 @@ from . import analysis, environments, packages, project, security
|
|
|
6
6
|
|
|
7
7
|
TOOL_DEFINITIONS = [
|
|
8
8
|
{
|
|
9
|
-
"name": "
|
|
9
|
+
"name": "pes_list_environments",
|
|
10
10
|
"description": "List Python environments known to Py Env Studio.",
|
|
11
11
|
"inputSchema": {
|
|
12
12
|
"type": "object",
|
|
@@ -16,7 +16,7 @@ TOOL_DEFINITIONS = [
|
|
|
16
16
|
"handler": environments.list_environments,
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
"name": "
|
|
19
|
+
"name": "pes_get_environment",
|
|
20
20
|
"description": "Return detailed information about one environment.",
|
|
21
21
|
"inputSchema": {
|
|
22
22
|
"type": "object",
|
|
@@ -27,7 +27,7 @@ TOOL_DEFINITIONS = [
|
|
|
27
27
|
"handler": environments.get_environment,
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
"name": "
|
|
30
|
+
"name": "pes_list_packages",
|
|
31
31
|
"description": "List installed packages in an environment (pip/uv aware).",
|
|
32
32
|
"inputSchema": {
|
|
33
33
|
"type": "object",
|
|
@@ -38,7 +38,7 @@ TOOL_DEFINITIONS = [
|
|
|
38
38
|
"handler": packages.list_packages,
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
"name": "
|
|
41
|
+
"name": "pes_get_project_context",
|
|
42
42
|
"description": "Authoritative project/environment/runtime context for the current project.",
|
|
43
43
|
"inputSchema": {
|
|
44
44
|
"type": "object",
|
|
@@ -48,7 +48,7 @@ TOOL_DEFINITIONS = [
|
|
|
48
48
|
"handler": project.get_project_context,
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
"name": "
|
|
51
|
+
"name": "pes_get_environment_status",
|
|
52
52
|
"description": "Concise machine-readable environment status.",
|
|
53
53
|
"inputSchema": {
|
|
54
54
|
"type": "object",
|
|
@@ -59,7 +59,7 @@ TOOL_DEFINITIONS = [
|
|
|
59
59
|
"handler": environments.get_environment_status,
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
|
-
"name": "
|
|
62
|
+
"name": "pes_scan_vulnerabilities",
|
|
63
63
|
"description": "Return cached vulnerability findings for an environment (no network scan).",
|
|
64
64
|
"inputSchema": {
|
|
65
65
|
"type": "object",
|
|
@@ -70,7 +70,7 @@ TOOL_DEFINITIONS = [
|
|
|
70
70
|
"handler": security.scan_vulnerabilities,
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
"name": "
|
|
73
|
+
"name": "pes_get_dependency_information",
|
|
74
74
|
"description": "Expose local dependency information (pip show based, offline).",
|
|
75
75
|
"inputSchema": {
|
|
76
76
|
"type": "object",
|
|
@@ -84,7 +84,7 @@ TOOL_DEFINITIONS = [
|
|
|
84
84
|
"handler": project.get_dependency_information,
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
|
-
"name": "
|
|
87
|
+
"name": "pes_analyze_project",
|
|
88
88
|
"description": "Analyze a Python project using Py Env Studio and return authoritative environment, Python runtime, package manager, dependency, outdated-package, vulnerability, runtime, and PES project configuration information. This operation is read-only and does not modify the project or environment.",
|
|
89
89
|
"inputSchema": {
|
|
90
90
|
"type": "object",
|
|
@@ -94,7 +94,7 @@ TOOL_DEFINITIONS = [
|
|
|
94
94
|
"handler": analysis.analyze_project,
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
|
-
"name": "
|
|
97
|
+
"name": "pes_check_consistency",
|
|
98
98
|
"description": "Before modifying any Python project's dependencies or running its tests, call this to determine which interpreter, environment, lockfile, and manifest the project actually uses, and whether they agree.",
|
|
99
99
|
"inputSchema": {
|
|
100
100
|
"type": "object",
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Conservative comparison of a project's PES environment and a known runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from hashlib import sha256
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from . import env_manager, runtime_toggle
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RuntimeMatchState(str, Enum):
|
|
16
|
+
MATCH = "MATCH"
|
|
17
|
+
MISMATCH = "MISMATCH"
|
|
18
|
+
REGISTERED_ENVIRONMENT_UNAVAILABLE = "REGISTERED_ENVIRONMENT_UNAVAILABLE"
|
|
19
|
+
NO_PROJECT_ASSOCIATION = "NO_PROJECT_ASSOCIATION"
|
|
20
|
+
UNKNOWN = "UNKNOWN"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class DetectedRuntime:
|
|
25
|
+
"""Runtime facts supplied by PES or a future integration adapter."""
|
|
26
|
+
|
|
27
|
+
executable: str | None
|
|
28
|
+
python_version: str | None
|
|
29
|
+
reliable: bool = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class RuntimeMismatch:
|
|
34
|
+
project_path: str | None
|
|
35
|
+
project_name: str | None
|
|
36
|
+
registered_environment_id: str | None
|
|
37
|
+
registered_python_executable: str | None
|
|
38
|
+
registered_python_version: str | None
|
|
39
|
+
current_python_executable: str | None
|
|
40
|
+
current_python_version: str | None
|
|
41
|
+
state: RuntimeMatchState
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _process_runtime() -> DetectedRuntime:
|
|
45
|
+
"""Return the interpreter running PES, not an IDE's selected interpreter."""
|
|
46
|
+
version = sys.version_info
|
|
47
|
+
return DetectedRuntime(
|
|
48
|
+
executable=sys.executable or None,
|
|
49
|
+
python_version=f"{version.major}.{version.minor}.{version.micro}",
|
|
50
|
+
reliable=bool(sys.executable),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _canonical_path(path: str) -> str:
|
|
55
|
+
return os.path.normcase(str(Path(path).resolve()))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def detect_runtime_mismatch(
|
|
59
|
+
project_root: Path | str | None = None,
|
|
60
|
+
current_runtime: DetectedRuntime | None = None,
|
|
61
|
+
) -> RuntimeMismatch:
|
|
62
|
+
"""Compare registered and current executables without inferring IDE state."""
|
|
63
|
+
root = Path(project_root).resolve() if project_root is not None else runtime_toggle.get_project_root()
|
|
64
|
+
metadata = runtime_toggle.load_project_metadata(root) if root else {}
|
|
65
|
+
runtime = current_runtime if current_runtime is not None else _process_runtime()
|
|
66
|
+
env_id = metadata.get("environment_id") or None
|
|
67
|
+
|
|
68
|
+
registered_executable = None
|
|
69
|
+
registered_version = None
|
|
70
|
+
state = RuntimeMatchState.UNKNOWN
|
|
71
|
+
if not env_id:
|
|
72
|
+
state = RuntimeMatchState.NO_PROJECT_ASSOCIATION
|
|
73
|
+
else:
|
|
74
|
+
info = env_manager.get_environment_info(str(env_id))
|
|
75
|
+
if info is None:
|
|
76
|
+
state = RuntimeMatchState.REGISTERED_ENVIRONMENT_UNAVAILABLE
|
|
77
|
+
else:
|
|
78
|
+
registered_executable = str(info.get("python_executable") or "") or None
|
|
79
|
+
registered_version = info.get("python_version") or metadata.get("python_version") or None
|
|
80
|
+
if not registered_executable or not Path(registered_executable).is_file():
|
|
81
|
+
state = RuntimeMatchState.REGISTERED_ENVIRONMENT_UNAVAILABLE
|
|
82
|
+
elif not runtime.reliable or not runtime.executable or not Path(runtime.executable).is_file():
|
|
83
|
+
state = RuntimeMatchState.UNKNOWN
|
|
84
|
+
else:
|
|
85
|
+
same_executable = _canonical_path(registered_executable) == _canonical_path(runtime.executable)
|
|
86
|
+
if same_executable:
|
|
87
|
+
if runtime.python_version and registered_version and runtime.python_version != registered_version:
|
|
88
|
+
state = RuntimeMatchState.MISMATCH
|
|
89
|
+
elif runtime.python_version:
|
|
90
|
+
state = RuntimeMatchState.MATCH
|
|
91
|
+
else:
|
|
92
|
+
state = RuntimeMatchState.MISMATCH
|
|
93
|
+
|
|
94
|
+
return RuntimeMismatch(
|
|
95
|
+
project_path=str(root) if root else None,
|
|
96
|
+
project_name=metadata.get("project_name") if metadata else (root.name if root else None),
|
|
97
|
+
registered_environment_id=str(env_id) if env_id else None,
|
|
98
|
+
registered_python_executable=registered_executable,
|
|
99
|
+
registered_python_version=str(registered_version) if registered_version else None,
|
|
100
|
+
current_python_executable=runtime.executable,
|
|
101
|
+
current_python_version=runtime.python_version,
|
|
102
|
+
state=state,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def notify_mismatch_once(result: RuntimeMismatch) -> bool:
|
|
107
|
+
"""Persist a per-project mismatch signature and report only new mismatches."""
|
|
108
|
+
if result.state is not RuntimeMatchState.MISMATCH or not result.project_path:
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
signature_data = "\0".join(
|
|
112
|
+
str(value or "")
|
|
113
|
+
for value in (
|
|
114
|
+
result.registered_environment_id,
|
|
115
|
+
result.registered_python_executable,
|
|
116
|
+
result.registered_python_version,
|
|
117
|
+
result.current_python_executable,
|
|
118
|
+
result.current_python_version,
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
signature = sha256(signature_data.encode("utf-8")).hexdigest()
|
|
122
|
+
root = Path(result.project_path)
|
|
123
|
+
if runtime_toggle.get_last_mismatch_signature(root) == signature:
|
|
124
|
+
return False
|
|
125
|
+
runtime_toggle.set_last_mismatch_signature(root, signature)
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def clear_mismatch_notification(project_root: Path | str) -> None:
|
|
130
|
+
"""Allow a later recurrence to notify after a project returns to a match."""
|
|
131
|
+
root = Path(project_root).resolve()
|
|
132
|
+
if runtime_toggle.get_last_mismatch_signature(root):
|
|
133
|
+
runtime_toggle.set_last_mismatch_signature(root, "")
|
|
@@ -104,6 +104,7 @@ def save_project_metadata(project_root: Path, metadata: dict) -> None:
|
|
|
104
104
|
parser["runtime"] = {
|
|
105
105
|
"enabled": str(metadata.get("runtime_enabled", False)).lower(),
|
|
106
106
|
"auto_init": str(metadata.get("auto_init", True)).lower(),
|
|
107
|
+
"last_mismatch_signature": metadata.get("last_mismatch_signature", ""),
|
|
107
108
|
}
|
|
108
109
|
with open(metadata_path, "w", encoding="utf-8") as f:
|
|
109
110
|
parser.write(f)
|
|
@@ -112,6 +113,20 @@ def save_project_metadata(project_root: Path, metadata: dict) -> None:
|
|
|
112
113
|
raise
|
|
113
114
|
|
|
114
115
|
|
|
116
|
+
def get_last_mismatch_signature(project_root: Path) -> str:
|
|
117
|
+
"""Read notification suppression state without expanding public metadata."""
|
|
118
|
+
parser = ConfigParser()
|
|
119
|
+
parser.read(get_project_metadata_path(project_root), encoding="utf-8")
|
|
120
|
+
return parser.get("runtime", "last_mismatch_signature", fallback="")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def set_last_mismatch_signature(project_root: Path, signature: str) -> None:
|
|
124
|
+
"""Persist a mismatch notification signature in the existing project file."""
|
|
125
|
+
metadata = load_project_metadata(project_root)
|
|
126
|
+
metadata["last_mismatch_signature"] = signature
|
|
127
|
+
save_project_metadata(project_root, metadata)
|
|
128
|
+
|
|
129
|
+
|
|
115
130
|
def load_registry() -> dict:
|
|
116
131
|
"""Load the global registry."""
|
|
117
132
|
import json
|
|
@@ -142,6 +157,70 @@ def save_registry(registry: dict) -> None:
|
|
|
142
157
|
raise
|
|
143
158
|
|
|
144
159
|
|
|
160
|
+
def associate_environment_with_project(
|
|
161
|
+
project_root: Path,
|
|
162
|
+
environment_id: str,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Associate an existing PES environment with a project."""
|
|
165
|
+
from . import env_manager
|
|
166
|
+
|
|
167
|
+
project_root = Path(project_root).resolve()
|
|
168
|
+
env_info = env_manager.get_environment_info(environment_id)
|
|
169
|
+
if env_info is None:
|
|
170
|
+
raise FileNotFoundError(f"Environment '{environment_id}' is unavailable")
|
|
171
|
+
|
|
172
|
+
metadata = load_project_metadata(project_root)
|
|
173
|
+
metadata.update(
|
|
174
|
+
{
|
|
175
|
+
"project_name": metadata.get("project_name", project_root.name),
|
|
176
|
+
"project_root": str(project_root),
|
|
177
|
+
"environment_id": environment_id,
|
|
178
|
+
"environment_path": env_info.get("path", ""),
|
|
179
|
+
"python_version": env_info.get("python_version") or "",
|
|
180
|
+
"package_manager": env_info.get("package_manager", "pip"),
|
|
181
|
+
"runtime_enabled": metadata.get("runtime_enabled", True),
|
|
182
|
+
"auto_init": metadata.get("auto_init", True),
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
save_project_metadata(project_root, metadata)
|
|
186
|
+
|
|
187
|
+
registry = load_registry()
|
|
188
|
+
project_name = metadata["project_name"]
|
|
189
|
+
project_entry = registry.setdefault(project_name, {})
|
|
190
|
+
project_entry.update(
|
|
191
|
+
{
|
|
192
|
+
"project_root": str(project_root),
|
|
193
|
+
"environment_id": environment_id,
|
|
194
|
+
"environment_path": metadata["environment_path"],
|
|
195
|
+
"python_version": metadata["python_version"],
|
|
196
|
+
"runtime_enabled": metadata["runtime_enabled"],
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
save_registry(registry)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def unregister_project_environment(project_root: Path) -> None:
|
|
203
|
+
"""Remove a project's environment association without deleting the environment."""
|
|
204
|
+
project_root = Path(project_root).resolve()
|
|
205
|
+
metadata = load_project_metadata(project_root)
|
|
206
|
+
metadata.update(
|
|
207
|
+
{
|
|
208
|
+
"project_name": metadata.get("project_name", project_root.name),
|
|
209
|
+
"project_root": str(project_root),
|
|
210
|
+
"environment_id": "",
|
|
211
|
+
"environment_path": "",
|
|
212
|
+
"last_mismatch_signature": "",
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
save_project_metadata(project_root, metadata)
|
|
216
|
+
|
|
217
|
+
registry = load_registry()
|
|
218
|
+
project_entry = registry.get(metadata["project_name"])
|
|
219
|
+
if project_entry and Path(project_entry.get("project_root", "")).resolve() == project_root:
|
|
220
|
+
project_entry.update({"environment_id": "", "environment_path": "", "python_version": ""})
|
|
221
|
+
save_registry(registry)
|
|
222
|
+
|
|
223
|
+
|
|
145
224
|
def generate_environment_id(project_name: str) -> str:
|
|
146
225
|
"""Generate a unique environment ID for a project."""
|
|
147
226
|
import hashlib
|
|
@@ -37,6 +37,11 @@ class TemplateEngine:
|
|
|
37
37
|
def __init__(self, registry: TemplateRegistry | None = None) -> None:
|
|
38
38
|
self.registry = registry or get_default_registry()
|
|
39
39
|
|
|
40
|
+
@staticmethod
|
|
41
|
+
def environment_name_for_project(project_name: str) -> str:
|
|
42
|
+
"""Return the generated environment name for a project name."""
|
|
43
|
+
return f"{project_name.strip().lower().replace(' ', '-')}-env".replace("_", "-")[:45]
|
|
44
|
+
|
|
40
45
|
def list_templates(self):
|
|
41
46
|
return self.registry.list_all()
|
|
42
47
|
|
|
@@ -98,6 +103,7 @@ class TemplateEngine:
|
|
|
98
103
|
project_path=project_path,
|
|
99
104
|
template=template,
|
|
100
105
|
log_callback=log_callback,
|
|
106
|
+
associate_with_project=request.associate_environment_with_project,
|
|
101
107
|
)
|
|
102
108
|
deps = list(template.runtime_dependencies)
|
|
103
109
|
if request.install_dev_dependencies:
|
|
@@ -214,9 +220,10 @@ class TemplateEngine:
|
|
|
214
220
|
self._log(log_callback, "Initializing git repository")
|
|
215
221
|
subprocess.run(["git", "init"], cwd=project_path, check=False, capture_output=True, text=True)
|
|
216
222
|
|
|
217
|
-
def _create_and_configure_environment(
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
def _create_and_configure_environment(
|
|
224
|
+
self, request, project_path, template, log_callback, associate_with_project=True
|
|
225
|
+
):
|
|
226
|
+
env_name = self.environment_name_for_project(request.project_name)
|
|
220
227
|
self._log(log_callback, f"Creating virtual environment: {env_name}")
|
|
221
228
|
|
|
222
229
|
python_path = self._resolve_python_interpreter(request.python_version)
|
|
@@ -233,22 +240,16 @@ class TemplateEngine:
|
|
|
233
240
|
log_callback=log_callback,
|
|
234
241
|
)
|
|
235
242
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"package_manager": env_manager.get_preferred_package_manager(),
|
|
247
|
-
"runtime_enabled": True,
|
|
248
|
-
"auto_init": True,
|
|
249
|
-
},
|
|
250
|
-
)
|
|
251
|
-
self._log(log_callback, "Configured project interpreter metadata")
|
|
243
|
+
if associate_with_project:
|
|
244
|
+
from ..runtime_toggle import associate_environment_with_project
|
|
245
|
+
|
|
246
|
+
associate_environment_with_project(
|
|
247
|
+
Path(project_path),
|
|
248
|
+
env_name,
|
|
249
|
+
)
|
|
250
|
+
self._log(log_callback, "Associated environment with project")
|
|
251
|
+
else:
|
|
252
|
+
self._log(log_callback, "Created environment without associating it with the project")
|
|
252
253
|
return env_name
|
|
253
254
|
|
|
254
255
|
def _resolve_python_interpreter(self, requested_version: str) -> str | None:
|