Create a Modern Python Project

This guide will walk you through everything you need to know to set up a Python project repository with the most up-to-date tools.

You can set up your project in many different ways, but this is an opinionated guide based on my own experience of what works well.

This guide is based on using a Mac, but it should also work for Linux or using Windows Subsystem for Linux although some modifications may be necessary for your particular system.

Create a Git Repository

First create a new empty repo on GitHub or a similar service. You'll want to grab the SSH URL of the new git repo and then clone it and cd into it:

shell
cd ~/projects
git clone git@github.com:<username>/<project-name>.git
cd <project-name>
The HTTPS URL also works, but you'll need to enter your password every time you push to the repository. After setting up SSH you never have to enter your password to push to GitHub. If you haven't set up SSH on GitHub yet, check out [their guide.][github-ssh]

Install uv for Python and package management

You probably have a version of Python pre-installed onto your computer already, as it is included with many operating systems by default.

  • run python --version to find the version of Python you have installed.
  • run which python to find the full path of your default Python installation.

I don't recommend using the default Python installation to build your own projects, because when you collaborate on it you'll come across issues from different people having different versions of Python on their computers.

The Python core team is constantly creating awesome new features that get released in new versions, so I encourage you to use the latest version of Python available to take advantage of all of them.

I'll show you how you can use uv to manage everything Python-related: installing Python versions, managing packages, creating virtual environments, and running scripts. uv is an extremely fast Python package and project manager written in Rust that handles all of these tasks seamlessly that the Python community is quickly coalescing around.

Install uv by running the official installer script:

shell
curl -LsSf https://astral.sh/uv/install.sh | sh

You can also use Homebrew on macOS if you prefer:

shell
brew install uv

uv will automatically download and install Python versions as needed, so you don't need to worry about managing Python installations separately. When you specify a Python version for your project, uv will handle downloading the precompiled binaries if they're not already available.

Initialize your project with uv

Now that you have uv installed and your Git repository set up, you can initialize your Python project. Navigate to your cloned repository directory and run:

shell
uv init --python 3.13

The --python 3.13 flag specifies which Python version to use for this project. If you don't have Python 3.13 installed, uv will automatically download and install it for you.

This command creates several files automatically:

  • pyproject.toml - Project metadata and dependencies (similar to package.json)
  • README.md - Basic project documentation
  • src/your_project/ - Python package structure
  • .gitignore - Ignores Python-generated files and the .venv directory
  • .python-version - Specifies the Python version for the project

The generated .gitignore includes all the standard Python files you want to ignore (like __pycache__/, *.pyc, .venv/, etc.), so you don't need to create one manually.

uv automatically creates and manages a virtual environment in a .venv directory in your project. You don't need to activate or deactivate it manually - uv handles this transparently when you run commands.

Managing dependencies

To add dependencies to your project, use uv add:

shell
uv add requests
uv add pytest --dev  # for development dependencies

This automatically updates your pyproject.toml file and creates a uv.lock file that locks your dependencies to specific versions for reproducible builds.

Locking dependencies with uv lock

Whenever you add or update dependencies, uv automatically creates or updates a uv.lock file. You can also manually generate or update this lockfile:

shell
uv lock

The lockfile is crucial because it:

  • Ensures reproducibility: Everyone who works on the project gets the exact same dependency versions
  • Includes transitive dependencies: It locks not just your direct dependencies, but all the dependencies of your dependencies
  • Prevents "works on my machine" issues: The same versions are installed across development, testing, and production environments
  • Enables faster installs: uv can install directly from the lockfile without having to resolve dependencies

Important: Always commit your uv.lock file to version control. This ensures that everyone on your team (and your deployment systems) use identical dependency versions.

To install dependencies from an existing project (like when someone else clones your repo), use:

shell
uv sync

This reads the uv.lock file and installs the exact versions specified, ensuring everyone has the same environment.

Running your code

Use uv run to execute Python scripts or modules:

shell
uv run main.py
uv run -m pytest  # run pytest module

uv automatically uses the correct Python version and virtual environment for your project.

You can also use uvx to run tools in isolated environments without installing them globally:

shell
uvx ruff check  # run ruff linter
uvx black .     # run black formatter

Bonus: Standalone scripts with inline dependencies

One of uv's coolest features is the ability to create standalone Python scripts with their dependencies declared right in the file. This is perfect when you want to write a quick script without setting up a full project.

Create a script with inline dependencies using:

shell
uv init --script my-script.py

Or manually create a script like this:

python
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "requests",
#     "rich"
# ]
# ///

import requests
from rich import print

response = requests.get("https://api.github.com/user", auth=("user", "pass"))
print(response.json())

Run it with uv run my-script.py and uv will automatically create a virtual environment, install the dependencies, and execute the script. This is incredibly useful for one-off scripts, data analysis, or experimenting with new libraries.

You can also run scripts with temporary dependencies:

shell
uv run --with pandas my-data-script.py

Congratulations! You're now set up with a modern Python project using the fastest tooling available.

Run the following commands to save the configuration and push to GitHub:

shell
git add -A .
git commit -m "set up python project skeleton"
git push

Make sure to commit your uv.lock file along with your pyproject.toml - this ensures everyone gets the same dependency versions when they run uv sync.