Sublime Forum

Can a Project recognize its Virtual Environment?

#1

Hi all,

I’m just curious on if I can make my .sublime-project files a bit cleaner and generic.

I’m using Windows 11 Pro and Sublime Text 4 Build 4169
I’m developing in Python 3.12.1 and am using pipenv to manage my virtual environments.
I have set the environment variable WORKON_HOME to S:/.virtualenvs

Currently, I have my files arranged as such:

S:/
- .virtualenvs/
--- test1-abcdefg/
--- test2-hijklmno/
- Projects/
--- test1/
----- test1.sublime-project
----- ...
--- test2/
----- test2.sublime-project
----- ...

I currently have S:/Projects syncing to a cloud storage location. This folder will contain other folders with simple project names (ex “test1”).

The .virtualenvs directory is for holding the virtual environment information and will have folders named as <project>-<hash> (ex “test1-abcdefg”) which is generated by pipenv automatically upon environment creation. A key note is the base project name (ex “test1”) is in the directory names of the folders found in both S:/Projects and S:/.virtualenvs.

My .sublime-project consequently currently looks like this:

{
	"build_systems":
	[
		{
			"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
			"name": "Virtual Environment",
			"selector": "source.python",
			"shell_cmd": "S:/.virtualenvs/test1-abcdefg/Scripts/python.exe -u \"$file\"",
		},
	],
	"folders":
	[
		{
			"path": "."
		},
	],
	"settings":
	{
		"SublimeLinter.linters.mypy.args":
		[
			"--config-file=S:/Projects/pyproject.toml",
			"--python-executable=S:/.virtualenvs/test1-abcdefg/Scripts/python.exe",
		],
	}
}

I’d like to make the .sublime-project file a bit more generic so I don’t have to always go into each project and change the <project>-<hash> in the paths containing a virtual environment folder name. Frankly, it might let me remove the “settings” section all together by relocating that information over to the SublimeLinter settings file, but we’ll ignore that for now.

I’ll quickly mention this CLI option regarding pipenv:

S:\Projects\test1>pipenv --venv
S:\.virtualenvs\test1-abcdefg

To my knowledge - this can’t be used in a .sublime-project file, but if it could this would then be the ideal .sublime-project file for me in my opinion:

{
	"build_systems":
	[
		{
			"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
			"name": "Virtual Environment",
			"selector": "source.python",
			"shell_cmd": "$(pipenv --venv)/Scripts/python.exe -u \"$file\"",
		},
	],
	"folders":
	[
		{
			"path": "."
		},
	],
	"settings":
	{
		"SublimeLinter.linters.mypy.args":
		[
			"--config-file=S:/Projects/pyproject.toml",
			"--python-executable=$(pipenv --venv)/Scripts/python.exe",
		],
	}
}

Given I’m fairly sure this is not possible I have come here to ask for advice on whatever else can be done to make something like this work to minimize the overall management of my .sublime-project files.

Any advice would be greatly appreciated!

0 Likes

#2

If you were on Linux or macOS that "shell_cmd" would work, but since you’re on Windows you’re using the batch shell language. Untested but I think this should work:

for /f "delims=" %%a in ('pipenv --venv') do set "venv=%%a"; "%venv%\Scripts\python.exe" -u "$file"

Alternatively if you have a better shell installed you can run that, for example:

"cmd": ["C:\cygwin\bin\bash.exe", "-c", "$(pipenv --venv)/Scripts/python.exe -u \"$file\""]
0 Likes

#3

This is looking promising, but I might be missing something small here. My knowledge of the bash executable options and the sublime environment is still pretty limited.

Here is what I have in my .sublime-project file:

{
	"build_systems":
	[
		{
			"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
			"name": "Virtual Environment",
			"selector": "source.python",
			"cmd": ["C:/msys64/usr/bin/bash.exe", "-c", "$(pipenv --venv)/Scripts/python.exe -u \"$file\""],
		},
	],
	"folders":
	[
		{
			"path": "."
		},
	],
	"settings":
	{
		"SublimeLinter.linters.mypy.args":
		[
			"--config-file=S:/Projects/pyproject.toml",
			"--python-executable=S:/.virtualenvs/outlook-baNMh6cB/Scripts/python.exe",
		],
	}
}

This actually seems to get an error message that appears to be very close to what we need, but it seems like it’s missing the $file environment variable now for some reason?

The error message I get when I try to run the build tool is this:

S:\.virtualenvs\outlook-baNMh6cB\Scripts\python.exe: can't find '__main__' module in 'S:\\OneDrive\\Projects\\outlook'

However, when I tried doing this it worked:

"cmd": ["C:/msys64/usr/bin/bash.exe", "-c", "$(pipenv --venv)/Scripts/python.exe -u main.py"],

So, it’s something to do with losing the context of $file it seems?

Also, just curious, but do you think this will also work for the --python-executable flag to my linter?

0 Likes

#4

You probably need to escape the first $, for example: "\\$(pipenv --venv). I don’t use SublimeLinter; it’s up to that plugin whether that will work.

0 Likes

#5

Ah! That made the build system work! Brilliant!

My .sublime-project file now looks like this:

{
	"build_systems":
	[
		{
			"file_regex": "^[ ]*File '(...*?)', line ([0-9]*)",
			"name": "Virtual Environment",
			"selector": "source.python",
			"cmd": ["C:/msys64/usr/bin/bash.exe", "-c", "\\$(pipenv --venv)/Scripts/python.exe -u '$file'"],
		},
	],
	"folders":
	[
		{
			"path": "."
		},
	],
	"settings":
	{
		"SublimeLinter.linters.mypy.args":
		[
			"--config-file=S:/Projects/pyproject.toml",
			"--python-executable=S:/.virtualenvs/outlook-baNMh6cB/Scripts/python.exe",
		],
	}
}

So, the only thing I’d like to figure out is how to fix that --python-executable path to be dynamic if possible. I’m not sure it has to be a “SublimeLinter” specific thing since I know it can expand environment variables such as $WORKON_HOME. I’m probably mistaken on how “settings” work, but isn’t there a way to set an environment variable in that context? If there is I’m assuming it can only be set with a static value, but if I’m wrong on that then maybe we could do another clever thing to invoke the bash subsystem to set that value and then have it available to the “SublimeLinter” settings as a basic environment variable?

0 Likes

#6

Why do you need to set --python-executable? SublimeLinter should call pipenv --venv for you and then find mypy accordingly.

I typically recommend to place the virtual env inside the project, typically in a directory called .venv. (This is also the default of rye, pretty sure pipenv can do this too as it is Kenneth’s preferred choice iirc and he wrote the tool initially.) I think everything gets easier and it doesn’t look like you need multiple venvs per project. (And SublimeLinter will look for “.venv” or “.env” directories automatically too.)

0 Likes

#7

My intention was to limit bloat of projects so as to not use up a ton of unnecessary space in the cloud. So, I keep my virtual environments outside of the cloud directory and consequently out of the projects directory. My thought process is that if I need to work on a project on a different computer that I can download all necessary files via pipenv install rather than storing and syncing them in the cloud. Further, in another effort to save space I simply installed mypy globally so all projects can reference it. Consequently, this means that mypy is never actually installed in any project’s virtual environment. Thus, I am left with a single global install of mypy, but the need to tell mypy which virtual environment to use based on the project I’m currently in. Hence the --python-executable flag and this current curiosity on dynamically selecting the virtual environment from within the given project’s context whilst maintaining only a single install of mypy for all projects. Not sure if that makes sense to do, but that’s what I was aiming for anyways.

0 Likes