py-env-studio 2.1.0__tar.gz → 2.1.2__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.0/py_env_studio.egg-info → py_env_studio-2.1.2}/PKG-INFO +3 -2
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/PYPI_README.md +1 -1
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/README.md +31 -7
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/config.ini +1 -1
- py_env_studio-2.1.2/py_env_studio/core/app_updates.py +94 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/database.py +12 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/env_manager.py +25 -5
- py_env_studio-2.1.2/py_env_studio/core/environment_lock.py +704 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/tools/__init__.py +13 -0
- py_env_studio-2.1.2/py_env_studio/core/mcp/tools/analysis.py +87 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/package_manager.py +97 -8
- py_env_studio-2.1.2/py_env_studio/core/package_update_monitor.py +162 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/plugins/manager.py +110 -15
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/runtime_cache.py +7 -1
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/runtime_providers.py +95 -4
- py_env_studio-2.1.2/py_env_studio/core/schema/environment_locks.sql +37 -0
- py_env_studio-2.1.2/py_env_studio/core/schema/package_updates.sql +36 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/main_window.py +1166 -110
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/app_logging.py +42 -2
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/vulneribility_scanner.py +1 -1
- {py_env_studio-2.1.0 → py_env_studio-2.1.2/py_env_studio.egg-info}/PKG-INFO +3 -2
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio.egg-info/SOURCES.txt +40 -1
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio.egg-info/requires.txt +1 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/pyproject.toml +3 -2
- py_env_studio-2.1.2/tests/test_all_features.py +568 -0
- py_env_studio-2.1.2/tests/test_app_logging.py +183 -0
- py_env_studio-2.1.2/tests/test_app_updates.py +106 -0
- py_env_studio-2.1.2/tests/test_cli_commands.py +126 -0
- py_env_studio-2.1.2/tests/test_cli_progress.py +134 -0
- py_env_studio-2.1.2/tests/test_community_templates.py +158 -0
- py_env_studio-2.1.2/tests/test_configuration_service.py +139 -0
- py_env_studio-2.1.2/tests/test_dependency_preview.py +288 -0
- py_env_studio-2.1.2/tests/test_environment_lock.py +364 -0
- py_env_studio-2.1.2/tests/test_github_template_import.py +81 -0
- py_env_studio-2.1.2/tests/test_mcp_consistency.py +438 -0
- py_env_studio-2.1.2/tests/test_mcp_server.py +350 -0
- py_env_studio-2.1.2/tests/test_package_update_monitor.py +579 -0
- py_env_studio-2.1.2/tests/test_plugin_events.py +45 -0
- py_env_studio-2.1.2/tests/test_plugin_import.py +21 -0
- py_env_studio-2.1.2/tests/test_plugin_load.py +28 -0
- py_env_studio-2.1.2/tests/test_plugin_manager.py +205 -0
- py_env_studio-2.1.2/tests/test_project_contract.py +494 -0
- py_env_studio-2.1.2/tests/test_project_intelligence.py +631 -0
- py_env_studio-2.1.2/tests/test_project_openers.py +85 -0
- py_env_studio-2.1.2/tests/test_project_runtime.py +89 -0
- py_env_studio-2.1.2/tests/test_runtime_cache.py +234 -0
- py_env_studio-2.1.2/tests/test_runtime_providers.py +400 -0
- py_env_studio-2.1.2/tests/test_schema_templates.py +141 -0
- py_env_studio-2.1.2/tests/test_status_progress.py +192 -0
- py_env_studio-2.1.2/tests/test_template_engine.py +178 -0
- py_env_studio-2.1.2/tests/test_template_registry.py +73 -0
- py_env_studio-2.1.2/tests/test_template_workflow.py +54 -0
- py_env_studio-2.1.2/tests/test_ui_backend.py +63 -0
- py_env_studio-2.1.2/tests/test_user_template_store.py +125 -0
- py_env_studio-2.1.2/tests/test_uv_packages.py +47 -0
- py_env_studio-2.1.2/tests/test_vulnerability_insights_charts.py +256 -0
- py_env_studio-2.1.2/tests/test_vulnerability_insights_update.py +387 -0
- py_env_studio-2.1.2/tests/test_window_icon.py +297 -0
- py_env_studio-2.1.0/py_env_studio/core/mcp/tools/analysis.py +0 -20
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/LICENSE +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/__main__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/commands.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/auto_resolve.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/bootstrap.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/configuration.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/dependency_preview.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/integration.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/config.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/context.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/errors.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/schemas/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/schemas/responses.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/server.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/tools/environments.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/tools/packages.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/tools/project.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/mcp/tools/security.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/pip_tools.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/plugins/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/plugins/base.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/plugins/exceptions.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_contract/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_contract/models.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_contract/repository.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_contract/service.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_intelligence/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_intelligence/models.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_intelligence/service.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/project_launcher.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/py_tonic.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/runtime.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/runtime_toggle.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/environments.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/migration.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/project_contract.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/runtime_cache.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/schema_core.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/schema_runtime_cache.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/schema/vulnerability.sql +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/setup_state.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/strategies.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/builtin_templates.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/community.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/content_filters.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/engine.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/github_import.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/models.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/registry.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/user_template_store.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/validator.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/templates/workflow.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/tool_probe.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/tools.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/uv_tools.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/core/windows_apps.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/main.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/about.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/activate-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/available-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/create-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/delete-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/export.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/install.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/list-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/logo.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/packages.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/pes-default-transparrent.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/pes-icon-default.ico +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/pes-icon-default.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/pes-transparrent-icon-default.ico +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/requirements.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/selected-env.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/uninstall.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/static/icons/update.png +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/status_bar.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/ui/ui_tasks.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/__init__.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/app_icon.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/db_status.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/handlers.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/progress.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/version_utils.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio/utils/vulneribility_insights.py +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio.egg-info/dependency_links.txt +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio.egg-info/entry_points.txt +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/py_env_studio.egg-info/top_level.txt +0 -0
- {py_env_studio-2.1.0 → py_env_studio-2.1.2}/setup.cfg +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.2
|
|
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
|
|
@@ -54,6 +54,7 @@ Requires-Dist: pip
|
|
|
54
54
|
Requires-Dist: setuptools
|
|
55
55
|
Requires-Dist: wheel
|
|
56
56
|
Requires-Dist: platformdirs>=4.2.0
|
|
57
|
+
Requires-Dist: packaging>=24.2
|
|
57
58
|
Provides-Extra: dev
|
|
58
59
|
Requires-Dist: pytest; extra == "dev"
|
|
59
60
|
Requires-Dist: black; extra == "dev"
|
|
@@ -160,7 +161,7 @@ Setup steps for VS Code and other clients:
|
|
|
160
161
|
|
|
161
162
|
- Documentation home: [https://py-env-studio.readthedocs.io/](https://py-env-studio.readthedocs.io/)
|
|
162
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)
|
|
163
|
-
- 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.2.html](https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.2.html)
|
|
164
165
|
- Source code: [https://github.com/pyenvstudio/py-env-studio](https://github.com/pyenvstudio/py-env-studio)
|
|
165
166
|
- Issue tracker: [https://github.com/pyenvstudio/py-env-studio/issues](https://github.com/pyenvstudio/py-env-studio/issues)
|
|
166
167
|
|
|
@@ -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.2.html](https://py-env-studio.readthedocs.io/en/latest/releases/v2.1.2.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,7 +29,7 @@ Perfect for:
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
-
## 🆕 What's new in v2.1.
|
|
32
|
+
## 🆕 What's new in v2.1.2
|
|
33
33
|
|
|
34
34
|
- 🧪 **MCP control plane (beta)** — a local, read-only MCP server lets AI coding
|
|
35
35
|
agents (VS Code Copilot and other MCP clients) read authoritative
|
|
@@ -48,7 +48,7 @@ Perfect for:
|
|
|
48
48
|
- 📊 **Native dashboard charts** (`tkinter-dash`, no matplotlib), 📶 a CLI
|
|
49
49
|
progress gauge and GUI status gauge, and consistent application icons.
|
|
50
50
|
|
|
51
|
-
→ [Full v2.1.
|
|
51
|
+
→ [Full v2.1.2 release notes](docs/releases/v2.1.2.md)
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
@@ -105,7 +105,7 @@ PyEnvStudio now features a powerful plugin system that allows developers to exte
|
|
|
105
105
|
- **Easy Development** - Simple plugin API with lifecycle management
|
|
106
106
|
- **Examples Included** - Full working sample plugin with documentation
|
|
107
107
|
|
|
108
|
-
See the [v2.1.
|
|
108
|
+
See the [v2.1.2 release notes](docs/releases/v2.1.2.md) and the [plugin development guide](docs/plugins/development.md).
|
|
109
109
|
|
|
110
110
|
### Additional implemented capabilities
|
|
111
111
|
|
|
@@ -160,6 +160,9 @@ If you find **Py Env Studio** helpful, consider supporting me:
|
|
|
160
160
|
[](https://ko-fi.com/contactshaikhwasim)
|
|
161
161
|
[](https://www.paypal.me/paypalwasimshaikh)
|
|
162
162
|
|
|
163
|
+
<p align="center">
|
|
164
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/bmc-qr-code.png?raw=true" alt="Buy Me a Coffee QR Code" width="200">
|
|
165
|
+
</p>
|
|
163
166
|
|
|
164
167
|
---
|
|
165
168
|
|
|
@@ -182,13 +185,21 @@ The same aliases apply to every CLI command shown in the documentation
|
|
|
182
185
|
|
|
183
186
|
Refer usage documentation here: https://py-env-studio.readthedocs.io/en/latest/
|
|
184
187
|
|
|
188
|
+
## 📸 Screenshots
|
|
189
|
+
|
|
185
190
|
<p align="center">
|
|
186
191
|
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.environment-screen.PNG?raw=true" alt="Environment Screen" width="400">
|
|
187
|
-
|
|
188
|
-
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/
|
|
192
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.1.more_options.PNG?raw=true" alt="Environment More Options" width="400"><br>
|
|
193
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.2.1_vulneribility_scan_report.PNG?raw=true" alt="Vulnerability Scan Report" width="400">
|
|
194
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.2.2_vulneribility_scan_report.PNG?raw=true" alt="Vulnerability Scan Report Details" width="400"><br>
|
|
195
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.2.3_vulneribility_scan_report.PNG?raw=true" alt="Vulnerability Scan Report Insights" width="400">
|
|
189
196
|
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/2.0.package-screen.PNG?raw=true" alt="Package Screen" width="400"><br>
|
|
190
|
-
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/1.
|
|
191
|
-
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/
|
|
197
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/2.1.package-screen.PNG?raw=true" alt="Package Screen Details" width="400">
|
|
198
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/plugin-screen.PNG?raw=true" alt="Plugin Screen" width="400"><br>
|
|
199
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/templates.PNG?raw=true" alt="Templates Screen" width="400">
|
|
200
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/python-manager.PNG?raw=true" alt="Python Manager Screen" width="400"><br>
|
|
201
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/configuration.PNG?raw=true" alt="Configuration Screen" width="400">
|
|
202
|
+
<img src="https://github.com/pyenvstudio/py-env-studio/blob/main/screenshots/3.about-sceeen.PNG?raw=true" alt="About Screen" width="400">
|
|
192
203
|
</p>
|
|
193
204
|
|
|
194
205
|
**📁 Project Structure**
|
|
@@ -249,6 +260,7 @@ This project uses public APIs for core features:
|
|
|
249
260
|
| PyPI | Package metadata | [pypi.org](https://pypi.org) |
|
|
250
261
|
| deps.dev | Dependency data | [deps.dev](https://deps.dev) |
|
|
251
262
|
| OSV.dev | Vulnerability info | [osv.dev](https://osv.dev) |
|
|
263
|
+
| Github | Community templates | [github.com](https://github.com) |
|
|
252
264
|
|
|
253
265
|
Ensure HTTPS access to these domains.
|
|
254
266
|
APIs are public, read-only, no auth required.
|
|
@@ -260,6 +272,18 @@ APIs are public, read-only, no auth required.
|
|
|
260
272
|
We welcome contributions!
|
|
261
273
|
Feel free to fork the repository, raise issues, or submit pull requests.
|
|
262
274
|
|
|
275
|
+
## Acknowledgements
|
|
276
|
+
|
|
277
|
+
Py Env Studio is built on the work of the Python community and the broader open-source ecosystem.
|
|
278
|
+
|
|
279
|
+
**Built with:** [Python](https://www.python.org/), [CustomTkinter](https://github.com/TomSchimansky/CustomTkinter), which provides the desktop UI foundation, and [Pillow](https://python-pillow.org/) for image and icon handling.
|
|
280
|
+
|
|
281
|
+
**Integrates with:** the [Python Install Manager](https://docs.python.org/3/using/windows.html#python-install-manager) for Python runtime management; [PyPI](https://pypi.org/) and the [Python Packaging community](https://packaging.python.org/) for package distribution and metadata; and [pip](https://pip.pypa.io/) and [uv](https://docs.astral.sh/uv/) for package management.
|
|
282
|
+
|
|
283
|
+
Thanks to the maintainers and contributors behind these projects and to everyone building Python and open-source software. These acknowledgements do not imply endorsement, affiliation, or official partnership.
|
|
284
|
+
|
|
285
|
+
*Built with Python. Powered by open source.*
|
|
286
|
+
|
|
263
287
|
**⚖️ License**
|
|
264
288
|
This project is licensed under the MIT License.
|
|
265
289
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Check and apply Py Env Studio package updates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
from packaging.version import InvalidVersion, Version
|
|
14
|
+
|
|
15
|
+
from .runtime import get_runtime_config
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
PACKAGE_NAME = "py-env-studio"
|
|
20
|
+
PYPI_JSON_URL = "https://pypi.org/pypi/py-env-studio/json"
|
|
21
|
+
RELEASES_URL = "https://github.com/pyenvstudio/py-env-studio/releases/latest"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class AppUpdateStatus:
|
|
26
|
+
current_version: str
|
|
27
|
+
latest_version: str
|
|
28
|
+
update_available: bool
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_installed_app_version() -> str:
|
|
32
|
+
"""Return the installed distribution version, falling back to package config."""
|
|
33
|
+
try:
|
|
34
|
+
return version(PACKAGE_NAME)
|
|
35
|
+
except PackageNotFoundError:
|
|
36
|
+
configured_version = get_runtime_config().app_version
|
|
37
|
+
return configured_version if configured_version != "stable" else "Unknown"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def check_for_app_update(current_version: str | None = None) -> AppUpdateStatus:
|
|
41
|
+
"""Fetch PyPI's latest release metadata and compare it with this install."""
|
|
42
|
+
current = current_version or get_installed_app_version()
|
|
43
|
+
response = requests.get(
|
|
44
|
+
PYPI_JSON_URL,
|
|
45
|
+
headers={"Accept": "application/json"},
|
|
46
|
+
timeout=10,
|
|
47
|
+
)
|
|
48
|
+
response.raise_for_status()
|
|
49
|
+
try:
|
|
50
|
+
latest = str(response.json()["info"]["version"])
|
|
51
|
+
update_available = Version(latest) > Version(current)
|
|
52
|
+
except (KeyError, TypeError, InvalidVersion) as exc:
|
|
53
|
+
raise RuntimeError("PyPI returned invalid Py Env Studio version metadata") from exc
|
|
54
|
+
return AppUpdateStatus(current, latest, update_available)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_frozen_build() -> bool:
|
|
58
|
+
"""Whether this process is running from a bundled executable."""
|
|
59
|
+
return bool(getattr(sys, "frozen", False))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def install_app_update(target_version: str) -> None:
|
|
63
|
+
"""Upgrade this interpreter's Py Env Studio distribution through pip."""
|
|
64
|
+
try:
|
|
65
|
+
Version(target_version)
|
|
66
|
+
except InvalidVersion as exc:
|
|
67
|
+
raise ValueError(f"Invalid Py Env Studio version: {target_version}") from exc
|
|
68
|
+
if is_frozen_build():
|
|
69
|
+
raise RuntimeError("Bundled builds must be updated from the release download page")
|
|
70
|
+
|
|
71
|
+
command = [
|
|
72
|
+
sys.executable,
|
|
73
|
+
"-m",
|
|
74
|
+
"pip",
|
|
75
|
+
"install",
|
|
76
|
+
"--upgrade",
|
|
77
|
+
f"{PACKAGE_NAME}=={target_version}",
|
|
78
|
+
]
|
|
79
|
+
result = subprocess.run(command, capture_output=True, text=True, timeout=600)
|
|
80
|
+
if result.returncode != 0:
|
|
81
|
+
details = (result.stderr or result.stdout or "pip exited with an error").strip()
|
|
82
|
+
raise RuntimeError(f"pip could not install Py Env Studio {target_version}: {details}")
|
|
83
|
+
logger.info("Installed Py Env Studio %s", target_version)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def restart_app() -> subprocess.Popen:
|
|
87
|
+
"""Start a fresh GUI process using the same Python interpreter."""
|
|
88
|
+
command = [sys.executable, "-m", "py_env_studio"]
|
|
89
|
+
options = {"close_fds": True}
|
|
90
|
+
if os.name == "nt":
|
|
91
|
+
options["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
|
|
92
|
+
else:
|
|
93
|
+
options["start_new_session"] = True
|
|
94
|
+
return subprocess.Popen(command, **options)
|
|
@@ -42,6 +42,18 @@ class DatabaseManager:
|
|
|
42
42
|
cur.execute(sql.get("schema_core", name))
|
|
43
43
|
for statement in sql.statements("schema_runtime_cache"):
|
|
44
44
|
cur.execute(statement)
|
|
45
|
+
for name in sql.available("package_updates"):
|
|
46
|
+
if name.startswith("create_"):
|
|
47
|
+
cur.execute(sql.get("package_updates", name))
|
|
48
|
+
for name in sql.available("environment_locks"):
|
|
49
|
+
if name.startswith("create_"):
|
|
50
|
+
cur.execute(sql.get("environment_locks", name))
|
|
51
|
+
package_update_columns = {
|
|
52
|
+
row[1]
|
|
53
|
+
for row in cur.execute("PRAGMA table_info(package_update_cache)").fetchall()
|
|
54
|
+
}
|
|
55
|
+
if "outdated_packages" not in package_update_columns:
|
|
56
|
+
cur.execute(sql.get("package_updates", "add_outdated_packages_column"))
|
|
45
57
|
# project_contract.sql mixes DDL with DML templates: only the
|
|
46
58
|
# create_* statements are schema, the rest are fetched by name
|
|
47
59
|
# at the call site (see ProjectContractRepository).
|
|
@@ -183,7 +183,7 @@ def _save_env_data(data):
|
|
|
183
183
|
LOGGER.error("Failed to save env data: %s", exc)
|
|
184
184
|
|
|
185
185
|
|
|
186
|
-
def set_env_data(env_name, recent_location=None, size=None, last_scanned=None, python_version=None, package_manager=None):
|
|
186
|
+
def set_env_data(env_name, recent_location=None, size=None, last_scanned=None, python_version=None, package_manager=None, package_update_check_enabled=None):
|
|
187
187
|
data = _load_env_data()
|
|
188
188
|
entry = data.get(env_name, {})
|
|
189
189
|
|
|
@@ -197,6 +197,8 @@ def set_env_data(env_name, recent_location=None, size=None, last_scanned=None, p
|
|
|
197
197
|
entry["python_version"] = python_version
|
|
198
198
|
if package_manager is not None:
|
|
199
199
|
entry["package_manager"] = package_manager
|
|
200
|
+
if package_update_check_enabled is not None:
|
|
201
|
+
entry["package_update_check_enabled"] = package_update_check_enabled is True
|
|
200
202
|
|
|
201
203
|
data[env_name] = entry
|
|
202
204
|
_save_env_data(data)
|
|
@@ -204,7 +206,10 @@ def set_env_data(env_name, recent_location=None, size=None, last_scanned=None, p
|
|
|
204
206
|
|
|
205
207
|
def get_env_data(env_name):
|
|
206
208
|
data = _load_env_data()
|
|
207
|
-
|
|
209
|
+
entry = data.get(env_name, {})
|
|
210
|
+
if "package_update_check_enabled" not in entry:
|
|
211
|
+
entry["package_update_check_enabled"] = False
|
|
212
|
+
return entry
|
|
208
213
|
|
|
209
214
|
|
|
210
215
|
def calculate_env_size_mb(env_path):
|
|
@@ -327,7 +332,7 @@ def _is_valid_env_name(name: str) -> bool:
|
|
|
327
332
|
return True
|
|
328
333
|
|
|
329
334
|
|
|
330
|
-
def create_env(name, python_path=None, upgrade_pip=False, log_callback=None):
|
|
335
|
+
def create_env(name, python_path=None, upgrade_pip=False, log_callback=None, package_update_check_enabled=False):
|
|
331
336
|
env_path = os.path.join(VENV_DIR, name)
|
|
332
337
|
python_path = python_path or PYTHON_PATH or "python"
|
|
333
338
|
python_version = _extract_python_version(python_path) or "default"
|
|
@@ -444,7 +449,14 @@ def create_env(name, python_path=None, upgrade_pip=False, log_callback=None):
|
|
|
444
449
|
size_mb = calculate_env_size_mb(env_path)
|
|
445
450
|
detected_version = _extract_python_version(venv_python)
|
|
446
451
|
# Store which package manager was actually used to create this environment
|
|
447
|
-
set_env_data(
|
|
452
|
+
set_env_data(
|
|
453
|
+
name,
|
|
454
|
+
recent_location=env_path,
|
|
455
|
+
size=size_mb,
|
|
456
|
+
python_version=detected_version,
|
|
457
|
+
package_manager=package_manager,
|
|
458
|
+
package_update_check_enabled=package_update_check_enabled,
|
|
459
|
+
)
|
|
448
460
|
|
|
449
461
|
LOGGER.info(
|
|
450
462
|
"Created environment at: %s with Python: %s using %s (%.1fs)",
|
|
@@ -570,6 +582,13 @@ def delete_env(name, log_callback=None):
|
|
|
570
582
|
shutil.rmtree(env_path)
|
|
571
583
|
LOGGER.info("Deleted environment: %s", name)
|
|
572
584
|
|
|
585
|
+
try:
|
|
586
|
+
from .package_update_monitor import PackageUpdateMonitor
|
|
587
|
+
|
|
588
|
+
PackageUpdateMonitor().invalidate_cache(name)
|
|
589
|
+
except Exception as exc:
|
|
590
|
+
LOGGER.warning("Could not invalidate package-update cache for '%s': %s", name, exc)
|
|
591
|
+
|
|
573
592
|
data = _load_env_data()
|
|
574
593
|
if name in data:
|
|
575
594
|
del data[name]
|
|
@@ -586,7 +605,8 @@ def delete_env(name, log_callback=None):
|
|
|
586
605
|
|
|
587
606
|
|
|
588
607
|
def get_env_python(env_name):
|
|
589
|
-
|
|
608
|
+
executable = "python.exe" if os.name == "nt" else "python"
|
|
609
|
+
return os.path.join(VENV_DIR, env_name, "Scripts" if os.name == "nt" else "bin", executable)
|
|
590
610
|
|
|
591
611
|
|
|
592
612
|
def activate_env(env_name, directory=None, open_with="vscode", open_in_venv_cwd=False, log_callback=None):
|