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, 2023
Tags: powershell | python
Laptop with code on the screen

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.


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.