Skip to content

Commit 72fb5ce

Browse files
committed
✨ feat(sdk): add environment detection and dynamic icons for PyCharm 2026.1
PyCharm 2026.1 removed the generic Virtualenv icon and now requires proper SDK flavor data to display environment-specific icons (UV, Poetry, Hatch, Conda, Pipenv). Without this, all environments showed broken icon references and package managers couldn't properly detect their tooling. The plugin now detects environment types by checking filesystem markers: pyvenv.cfg for UV, conda-meta directories for Conda, .gitignore markers for Hatch, and standard cache locations (with environment variable overrides) for Poetry and Pipenv. Each detected type gets the appropriate SDK flavor data so PyCharm's package managers work correctly and display the right icons. SDK creation now properly handles PyCharm's threading model by separating name generation (which runs Python) from write-locked modifications. Only in-project virtual environments get associated with the project to avoid polluting other projects' interpreter lists. Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
1 parent 6c061f5 commit 72fb5ce

File tree

13 files changed

+744
-154
lines changed

13 files changed

+744
-154
lines changed

CHANGELOG.md

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- Environment type detection for UV, Conda, Poetry, Hatch, and Pipenv virtual environments
8+
- Dynamic icons in tree view and context menu based on detected environment type
9+
- Proper SDK flavor data for each environment type (UV, Poetry, Hatch, etc.)
10+
- Project association for in-project virtual environments
11+
- Support for configurable environment paths via environment variables (HATCH_DATA_DIR, WORKON_HOME, etc.)
12+
- Comprehensive logging for environment detection debugging
13+
14+
### Changed
15+
16+
- Updated to PyCharm 2026.1 platform API
17+
- Minimum supported version is now PyCharm 2026.1 (build 261)
18+
- Removed module-level interpreter action (kept project-level only)
19+
- SDK creation now uses proper SdkModificator API with write actions
20+
- Environment detection checks pyvenv.cfg for UV marker, .gitignore for Hatch marker, and standard cache locations
21+
22+
### Fixed
23+
24+
- Icon loading errors on PyCharm 2026.1 by removing hardcoded icon references
25+
- SDK duplicate registration errors by checking global SDK table before creating new SDKs
26+
- Threading assertions by properly wrapping SDK modifications in write actions
27+
528
## [2.2.7] - 2026-03-31
629

730
${GITHUB_EVENT_RELEASE_BODY}
@@ -16,8 +39,10 @@ ${GITHUB_EVENT_RELEASE_BODY}
1639
- Standardize .github files to .yaml suffix by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/142
1740
- Clarify the venv selection painfulness by @andrask in https://github.com/tox-dev/PyVenvManage/pull/143
1841
- 🔒 ci(workflows): add zizmor security auditing by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/154
19-
- 🐛 fix(icons): resolve NoSuchFieldError on IntelliJ 2026.1 by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/157
20-
- 🔒 fix(ci): split release workflow for proper credential scoping by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/158
42+
- 🐛 fix(icons): resolve NoSuchFieldError on IntelliJ 2026.1 by @gaborbernat in
43+
https://github.com/tox-dev/PyVenvManage/pull/157
44+
- 🔒 fix(ci): split release workflow for proper credential scoping by @gaborbernat in
45+
https://github.com/tox-dev/PyVenvManage/pull/158
2146

2247
## [2.2.5] - 2026-01-30
2348

@@ -26,13 +51,15 @@ ${GITHUB_EVENT_RELEASE_BODY}
2651
## [2.2.4] - 2026-01-30
2752

2853
- Bump version to `2.2.4-dev` by @github-actions[bot] in https://github.com/tox-dev/PyVenvManage/pull/123
29-
- Use RELEASE_TOKEN for post-release PR and auto-merge by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/124
54+
- Use RELEASE_TOKEN for post-release PR and auto-merge by @gaborbernat in
55+
https://github.com/tox-dev/PyVenvManage/pull/124
3056

3157
## [2.2.3] - 2026-01-30
3258

3359
- Bump version to `2.2.3-dev` by @github-actions[bot] in https://github.com/tox-dev/PyVenvManage/pull/120
3460
- Add auto-merge workflow for trusted contributors by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/121
35-
- Make Python dependency optional to fix marketplace verification by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/122
61+
- Make Python dependency optional to fix marketplace verification by @gaborbernat in
62+
https://github.com/tox-dev/PyVenvManage/pull/122
3663

3764
## [2.2.2] - 2026-01-29
3865

@@ -48,13 +75,16 @@ ${GITHUB_EVENT_RELEASE_BODY}
4875
## [2.2.0] - 2026-01-04
4976

5077
- Changelog update - `v2.1.2` by @github-actions[bot] in https://github.com/tox-dev/PyVenvManage/pull/78
51-
- Optimize GitHub Actions: parallelize verification and fix disk space by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/99
52-
- Refactor to modern Kotlin idioms and fix deprecated API by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/100
78+
- Optimize GitHub Actions: parallelize verification and fix disk space by @gaborbernat in
79+
https://github.com/tox-dev/PyVenvManage/pull/99
80+
- Refactor to modern Kotlin idioms and fix deprecated API by @gaborbernat in
81+
https://github.com/tox-dev/PyVenvManage/pull/100
5382
- Add cache invalidation with file watcher by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/101
5483
- Improve error UX with notifications by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/102
5584
- Add plugin settings page by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/103
5685
- Improve plugin description and documentation by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/105
57-
- Enhance project view decorations and add 100% test coverage by @gaborbernat in https://github.com/tox-dev/PyVenvManage/pull/104
86+
- Enhance project view decorations and add 100% test coverage by @gaborbernat in
87+
https://github.com/tox-dev/PyVenvManage/pull/104
5888

5989
## [2.1.2] - 2025-10-23
6090

@@ -127,22 +157,22 @@ ${GITHUB_EVENT_RELEASE_BODY}
127157

128158
- Removed the usage of the deprecated PythonSdkType.getPythonExecutable API
129159

130-
[Unreleased]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.7...HEAD
131-
[2.2.7]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.6...v2.2.7
132-
[2.2.6]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.5...v2.2.6
133-
[2.2.5]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.4...v2.2.5
134-
[2.2.4]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.3...v2.2.4
135-
[2.2.3]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.2...v2.2.3
136-
[2.2.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.1...v2.2.2
137-
[2.2.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.0...v2.2.1
138-
[2.2.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.1.2...v2.2.0
139-
[2.1.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.1.0...v2.1.2
140-
[2.1.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.0.1...v2.1.0
141-
[2.0.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.0.0...v2.0.1
142-
[2.0.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.4.0...v2.0.0
143-
[1.4.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.4...v1.4.0
144-
[1.3.4]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.3...v1.3.4
145-
[1.3.3]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.2...v1.3.3
146-
[1.3.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.1...v1.3.2
147-
[1.3.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.0...v1.3.1
148160
[1.3.0]: https://github.com/pyvenvmanage/PyVenvManage/commits/v1.3.0
161+
[1.3.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.0...v1.3.1
162+
[1.3.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.1...v1.3.2
163+
[1.3.3]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.2...v1.3.3
164+
[1.3.4]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.3...v1.3.4
165+
[1.4.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.3.4...v1.4.0
166+
[2.0.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v1.4.0...v2.0.0
167+
[2.0.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.0.0...v2.0.1
168+
[2.1.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.0.1...v2.1.0
169+
[2.1.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.1.0...v2.1.2
170+
[2.2.0]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.1.2...v2.2.0
171+
[2.2.1]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.0...v2.2.1
172+
[2.2.2]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.1...v2.2.2
173+
[2.2.3]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.2...v2.2.3
174+
[2.2.4]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.3...v2.2.4
175+
[2.2.5]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.4...v2.2.5
176+
[2.2.6]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.5...v2.2.6
177+
[2.2.7]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.6...v2.2.7
178+
[unreleased]: https://github.com/pyvenvmanage/PyVenvManage/compare/v2.2.7...HEAD

README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,42 @@
1212
**PyVenvManage** simplifies Python virtual environment management in JetBrains IDEs.
1313

1414
Managing multiple Python interpreters across different virtual environments (for testing against various Python versions
15-
with tools like `tox` or `nox`) traditionally requires navigating through multiple dialogs in PyCharm or other JetBrains IDEs.
16-
PyVenvManage streamlines this by enabling quick interpreter selection directly from the project view with just a right-click.
15+
with tools like `tox` or `nox`) traditionally requires navigating through multiple dialogs in PyCharm or other JetBrains
16+
IDEs. PyVenvManage streamlines this by enabling quick interpreter selection directly from the project view with just a
17+
right-click.
1718

1819
## Features
1920

2021
- **Quick interpreter switching**: Right-click any virtual environment folder to set it as your project or module
2122
interpreter instantly
22-
- **Visual identification**: Virtual environment folders display with a distinctive icon and customizable decoration
23-
(e.g., `.venv [3.11.5 - CPython]`) in the project view
23+
- **Smart environment detection**: Automatically detects environment types (UV, Conda, Poetry, Hatch, Pipenv,
24+
virtualenv) and sets appropriate metadata and icons
25+
- **Dynamic icons**: Tree view and context menus display environment-specific icons (UV, Conda, Poetry, Hatch, Pipenv)
26+
based on detection
27+
- **Visual identification**: Virtual environment folders display with customizable decoration (e.g.,
28+
`.venv [3.11.5 - CPython]`) in the project view
2429
- **Customizable decorations**: Configure which fields to show (Python version, implementation, system site-packages,
2530
creator tool), their order, and the format via Settings
2631
- **Multi-IDE support**: Works with PyCharm (Community and Professional), IntelliJ IDEA, GoLand, CLion, and RustRover
27-
- **Smart detection**: Automatically detects Python virtual environments by recognizing `pyvenv.cfg` files
32+
- **Smart association**: In-project virtual environments are associated with the current project; external environments
33+
(Poetry cache, Hatch cache, Pipenv virtualenvs) remain global
2834
- **Cached version display**: Python version information is cached for performance and automatically refreshed when
2935
`pyvenv.cfg` files change
3036

3137
<!-- Plugin description end -->
3238

3339
## Supported IDEs
3440

35-
Version 2025.1 or later of:
41+
Version 2026.1 or later of:
3642

3743
- PyCharm (Community and Professional)
3844
- IntelliJ IDEA (Community and Ultimate)
3945
- GoLand
4046
- CLion
4147
- RustRover
4248

49+
**Note**: Version 2.2.x supports PyCharm 2025.1 and earlier.
50+
4351
## Install
4452

4553
In your JetBrains IDE, open **Settings** -> **Plugins**, search for "PyVenv Manage", and click **Install**.
@@ -51,9 +59,9 @@ The official plugin page is at https://plugins.jetbrains.com/plugin/20536-pyvenv
5159
![usage video](anim.gif?raw=true)
5260

5361
1. Create or navigate to a Python virtual environment folder in your project
54-
2. Right-click the virtual environment folder (e.g., `venv`, `.venv`, or any folder with a `pyvenv.cfg`)
55-
3. Select **Set as Project Interpreter** or **Set as Module Interpreter**
56-
4. The interpreter is configured instantly with a confirmation notification
62+
1. Right-click the virtual environment folder (e.g., `venv`, `.venv`, or any folder with a `pyvenv.cfg`)
63+
1. Select **Set as Project Interpreter** or **Set as Module Interpreter**
64+
1. The interpreter is configured instantly with a confirmation notification
5765

5866
## Settings
5967

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ dependencies {
5656
testImplementation(libs.remoteRobot)
5757
testImplementation(libs.remoteRobotFixtures)
5858
intellijPlatform {
59-
pycharmCommunity(platformVersion)
59+
pycharm(platformVersion)
6060
bundledPlugin("PythonCore")
6161
pluginVerifier()
6262
zipSigner()
@@ -124,7 +124,7 @@ intellijPlatform {
124124
listOf(IntelliJPlatformType.fromCode(verifyIde))
125125
} else {
126126
listOf(
127-
IntelliJPlatformType.PyCharmCommunity,
127+
IntelliJPlatformType.PyCharm,
128128
IntelliJPlatformType.PyCharmProfessional,
129129
)
130130
}

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ nl.littlerobots.vcu.resolver=true
44
org.gradle.caching=true
55
org.gradle.configuration-cache=true
66
org.gradle.welcome=never
7-
platformVersion=2025.1
7+
platformVersion=2026.1
88
pluginGroup=com.github.pyvenvmanage
99
pluginName=PyVenv Manage 2
1010
pluginRepositoryUrl=https://github.com/pyvenvmanage/PyVenvManage
11-
pluginSinceBuild=251
11+
pluginSinceBuild=261
1212
pluginVersion=2.2.8-dev
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-all.zip
4-
networkTimeout=10000
4+
networkTimeout=60000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME
77
zipStorePath=wrapper/dists
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.github.pyvenvmanage
22

3-
import javax.swing.Icon
3+
import java.nio.file.Path
44

55
import com.intellij.ide.projectView.PresentationData
66
import com.intellij.ide.projectView.ProjectViewNode
77
import com.intellij.ide.projectView.ProjectViewNodeDecorator
88
import com.intellij.openapi.diagnostic.thisLogger
99
import com.intellij.ui.SimpleTextAttributes
1010

11+
import com.jetbrains.python.sdk.PythonSdkUtil
12+
13+
import com.github.pyvenvmanage.sdk.EnvironmentDetector
14+
import com.github.pyvenvmanage.sdk.SdkFactory
1115
import com.github.pyvenvmanage.settings.PyVenvManageSettings
1216

1317
class VenvProjectViewNodeDecorator : ProjectViewNodeDecorator {
@@ -23,6 +27,17 @@ class VenvProjectViewNodeDecorator : ProjectViewNodeDecorator {
2327
val settings = PyVenvManageSettings.getInstance()
2428
val venvInfo = VenvVersionCache.getInstance().getInfo(pyVenvCfgPath.toString())
2529
thisLogger().debug("VenvInfo from cache: $venvInfo")
30+
31+
val venvRoot = Path.of(pyVenvCfgPath.toString()).parent
32+
val pythonExecutable = venvRoot?.let { PythonSdkUtil.getPythonExecutable(it.toString()) }
33+
34+
if (pythonExecutable != null) {
35+
val envType = EnvironmentDetector.detectEnvironmentType(pythonExecutable)
36+
val icon = SdkFactory.getIconForEnvironmentType(envType)
37+
thisLogger().debug("Setting icon for environment type: $envType")
38+
data.setIcon(icon)
39+
}
40+
2641
venvInfo?.let { info ->
2742
data.presentableText?.let { fileName ->
2843
val decoration = settings.formatDecoration(info)
@@ -32,25 +47,6 @@ class VenvProjectViewNodeDecorator : ProjectViewNodeDecorator {
3247
data.addText(decoration, SimpleTextAttributes.GRAY_ATTRIBUTES)
3348
} ?: thisLogger().debug("No presentableText for decoration")
3449
} ?: thisLogger().debug("No venvInfo found for $pyVenvCfgPath")
35-
virtualenvIcon?.let { data.setIcon(it) }
3650
}
3751
}
38-
39-
companion object {
40-
val virtualenvIcon: Icon? by lazy {
41-
loadIcon("com.intellij.python.venv.icons.PythonVenvIcons", "VirtualEnv")
42-
?: loadIcon("com.jetbrains.python.icons.PythonIcons\$Python", "Virtualenv")
43-
}
44-
45-
private fun loadIcon(
46-
className: String,
47-
fieldName: String,
48-
): Icon? =
49-
try {
50-
val clazz = Class.forName(className)
51-
clazz.getField(fieldName).get(null) as? Icon
52-
} catch (_: Exception) {
53-
null
54-
}
55-
}
5652
}

0 commit comments

Comments
 (0)