Automating new Python environments with PowerShell
I'm a big fan of automation. If I can automate something, I will. One thing I do often is create a new virtual environment for Python applications, which is just a simple terminal command. But then I have to update pip. Every. Single. Time.
Published: April 24, 2020 | Last Updated: August 27, 2023Tags: powershell | python
One thing I do often is create a new virtual environment for Python applications, which is just a simple terminal command.
python -m venv <name_of_env>
The Problem
The part that has become more frustrating of late is that after every time you pip install
the first a package into the environment you are presented with:
WARNING: You are using pip version 19.2.3, however version 20.0.2 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.
Urgh.
I know there is a lot of work being done on pip right now, so hopefully this problem goes away at some point. But for now, it's a little annoying to have to update pip each and every time I create a virtual environment.
Doing all this is just three short commands that I can chain together to make a long one-liner.
python -m venv <name_of_env> && <name_of_env>\scripts\activate.ps1 && python -m pip install --upgrade pip
What is this doing? Well, first it created the environment. Then it activated the environment. And it finishes with upgrading pip.
The &&
operator in PowerShell is used to chain commands together. The good thing about &&
is that if the command to the left of the operator fails, then the command to the right of the operator is not executed. So if any part of the command above fails it stops. It won't then try to activate your virtual environment if the environment fails to be created, etc.
The Automation
I'm an unashamed Windows user, and develop all of my Python on my Surface Book 2. And my shell of choice, PowerShell Core.
How I envisioned this looking was for me to just type NewEnv
(or something similar) into the terminal, and the rest to be taken care of. That's it. Why type three commands, when I can just write one very small one, right?
For me to create the NewEnv
command (cmdlet) in PowerShell, and have it available to me each time I open a new terminal, without me doing anything, I will need to create a PowerShell module which will automatically load on instantiation of a new PowerShell session.
Before we can create the module you first need to determine if you have a PowerShell profile
file created yet. Much like a .bash_profile
file, it is used when you open PowerShell to create your PowerShell environment.
We can check for the presence of this with the command:
Test-Path $profile
If it returns false
then you do not have a profile
file yet. If it returns true
, then already have one and you're good to go.
If you don't have a profile
you can create one with this command:
New-Item -ItemType File -Path $PROFILE -Force
Typically, your PowerShell profile will be stored in your Windows PowerShell
folder, which lives in your user Documents
folder. This is also where your PowerShell Modules will be stored. So, if not already present, create a folder named Modules
in your Windows PowerShell
directory.
Go ahead and open your Modules folder in your favorite code editor.
Now to make your new module's directory. For this I'm going to call mine penv
, but you can call it anything you want, as long as it does not conflict with any other default PowerShell Module.
# Inside your Windows PowerShell directory
mkdir penv
cd penv
# Now to create our main module file
New-Item penv.psm1
NOTE: Your .psm1
file MUST have the same name as your module directory.
Now let's add our function to our new .psm1
file with our chained command from before.
# Replace <name_of_env> with whatever you usually call your virtual envs.
function NewEnv {
python -m venv <name_of_env> && <name_of_env>\scripts\activate.ps1 && python -m pip install --upgrade pip
}
Do not make the mistake of calling your function in your .psm1
file. If you do, the function will run every time the module is loaded (which will be every time you start PowerShell, and you'll run into problems very quickly.) You only want it to run when you tell it to.
Now we want PowerShell to load our new module each time we load a new terminal session. Head back to your Windows PowerShell
directory and open your profile
file in your code editor. Mine's called Microsoft.PowerShell_profile.ps1
. Yours may be called something slightly different.
You may find this file to be empty or full of settings, if you have PowerShell customizations already. It doesn't matter for our purposes, so head to the bottom of the file and add import the your new module:
# Change to your module name
Import-Module penv
Save the file and you're all set!
Close any active PowerShell sessions you have open and your new module will be loaded when a new session is started. All you need to do to run it is type the name of the function
you created and hit enter. In my instance it was called NewEnv
. The command will create your new Python virtual environment (with updated pip) in the current working directory (cwd
). So be sure to cd
into you projects parent directory before running the command.
Potential pitfalls
This script assumes no other environments are present in the cwd
. This will likely break stuff if ran within a file that has a current environment with the same name already. I haven't tested it, but I think it's a fair assumption.
This script also assumes you like all your environments to have the same name. I call all my virtual environments .env
. I think this suits most people's use case too. But if you want your environments to have different names per project (like including the project name), then you'll need to add a variable to the function
to accommodate that.
This script uses the version of Python that is ran when you type python
in the terminal. If you are not sure which version you have by default, use python --version
. If you have several versions of Python installed and you want a specific version when creating your environment that is not the default version, you might need to extend this to allow an optional command line argument to select the Python version. But if you need to switch versions regularly, and you're going to that much trouble, you probably should just use a tool like pyenv instead.
If you have any questions or run into any issues with this, feel free to reach out to me on Twitter, or email me.