Skip to main content
Resources · 9 steps

Python Dev Setup for University Coursework

Set this up once, reference it forever. The same 9 steps cover CS50P on a Windows laptop, DATA 100 on an M2 Mac, and CSE 163 on a Linux lab box: install Python 3, pick a project folder structure, create a venv, install packages with pip, configure VS Code with the right extensions, wire up pytest, run your code, format with ruff, and ignore the right files in git. Every command below is copy-pasteable. If something errors halfway through, the next section names the fix.

Step 1

Install Python 3 on Windows, macOS, or Linux

Pick a Python version your course supports. Python 3.10, 3.11, and 3.12 are all course-safe in 2026. Use Python 3.13 only when your syllabus names it explicitly, because pandas 2.1 and a few sklearn 1.3 wheels lag behind the latest interpreter by 4 to 8 months.

Windows

Two paths. The python.org installer is the simplest, and winget is the fastest on Windows 11.

# Option A: official installer
# Download from python.org/downloads
# Check "Add Python to PATH" on the first dialog

# Option B: winget (Windows 11, PowerShell)
winget install Python.Python.3.12

# Verify
python --version

macOS

The system Python on macOS is 3.9 and locked. Install a fresh one with Homebrew, or the python.org installer if Homebrew is not on the laptop.

# Homebrew
brew install python@3.12

# Verify
python3 --version

# Alias once if you want bare "python"
echo 'alias python=python3' >> ~/.zshrc
source ~/.zshrc

Linux (Ubuntu, Debian, Fedora)

Ubuntu 22.04 ships 3.10. Ubuntu 24.04 ships 3.12. For a different version, install pyenv and pin per project.

# Ubuntu / Debian
sudo apt update
sudo apt install python3 python3-venv python3-pip

# Fedora
sudo dnf install python3 python3-pip

# Verify
python3 --version

The one-line check

Open a terminal and run python --version (Windows) or python3 --version (macOS, Linux). If the response prints 3.10 or higher, install is done. If the response is command not found on macOS, the PATH does not include the Homebrew prefix yet; run echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc and reopen the terminal. On Windows, a missing Add Python to PATH tick during install is the usual cause; run the installer again and pick Modify.

Step 2

Project folder structure

There is no single correct Python project layout. The folder structure depends on the type of assignment. For most university coursework, a simple structure is enough.

Standard assignment layout

This covers the majority of CS and data science courses.

project/
├── src/             # Source code
├── tests/           # Test files
├── .venv/           # Virtual environment
├── requirements.txt # Dependencies
├── README.md        # Project documentation
└── .gitignore       # Git ignore rules

src/ holds the Python source files for the project.

tests/ holds pytest test files, typically named with the test_ prefix.

.venv/ is the virtual environment. It stores the Python interpreter and all project-specific packages. Never commit it to git.

requirements.txt lists the packages required to run the project. Autograders on Gradescope read this file.

README.md describes the project, setup steps, and how to run it.

.gitignore keeps caches, virtual environments, and editor files out of the repository.

Single script

Small beginner assignments often need only one Python file.

project/
├── main.py          # Python script
├── .venv/           # Virtual environment
└── requirements.txt # Package dependencies

Flask web project

Web assignments separate HTML templates, static files, and application logic. Below is an MVC layout for a Flask app.

project/
├── app/
│   ├── __init__.py
│   ├── models/
│   ├── views/
│   ├── controllers/
│   ├── templates/
│   └── static/
├── tests/
├── .venv/
├── config.py
├── requirements.txt
├── .gitignore
├── run.py
└── README.md

Data science project

Data science and machine learning projects add datasets, Jupyter notebooks, and trained model artefacts.

project/
├── notebooks/       # Jupyter notebooks
├── data/            # Datasets
├── models/          # Trained ML models
├── src/             # Python source code
├── .venv/
├── requirements.txt
├── .gitignore
└── README.md
Step 3

VS Code extensions for Python coursework

Three extensions turn VS Code from a text editor into a Python-aware environment. Open the Extensions panel with Ctrl+Shift+X (Windows, Linux) or Cmd+Shift+X (macOS), then install each one below.

Python (Official Extension by Microsoft)

The Python extension adds full Python support to VS Code. It lets you run code, debug programs, manage virtual environments, run tests, and work with Jupyter notebooks. Without it, VS Code treats .py files as plain text.

VS Code Extensions marketplace showing the Microsoft Python extension page with the Install button

Pylance (Official Extension by Microsoft)

Pylance works alongside the Python extension and provides the language intelligence layer. Install it for autocomplete, parameter hints, auto-imports, and go-to-definition across your project. The type checking mode (off / basic / strict) is set in Python > Analysis: Type Checking Mode. The basic level catches the common bugs without blocking progress on assignments that don't yet have full type annotations.

Key features: autocomplete, parameter suggestions, auto-imports, go-to-definition, find references, rename symbols across a project, hover documentation, and DocString generation.

VS Code Extensions marketplace showing the Pylance extension page with language intelligence features listed

Ruff (charliermarsh.ruff)

Ruff is a fast linter and formatter for Python written in Rust. A linter analyzes source code to flag potential bugs, unused imports, and style violations before the code runs. A formatter restructures code for consistency (indentation, spacing, line lengths, import ordering), following PEP 8 without changing behavior. The VS Code extension runs both on save, so the file is clean before you commit it.

Key features: style issue detection, unused import and variable detection, automatic formatting, import sorting, and quick fixes for common problems.

VS Code Extensions marketplace showing the Ruff extension page with linting and formatting features listed

Turn on type checking

Pylance ships with three modes: off, basic, strict. Set Python > Analysis: Type Checking Mode to basic. Strict mode is satisfying once you trust your types; basic catches the bugs.

Drop this into .vscode/settings.json

{
  "python.defaultInterpreterPath": ".venv/bin/python",
  "python.terminal.activateEnvironment": true,
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    }
  },
  "python.analysis.typeCheckingMode": "basic",
  "python.testing.pytestEnabled": true,
  "python.testing.pytestArgs": ["tests"],
  "files.exclude": {
    "**/__pycache__": true,
    "**/.pytest_cache": true
  }
}

On Windows, swap .venv/bin/python for .venv\Scripts\python.exe. Commit this file to the repo so anyone cloning the project gets the same editor behavior; the .gitignore snippet at the bottom keeps everything except settings.json out.

Step 4

Virtual environments with venv

A venv isolates the packages a single assignment depends on. Without one, every pip install drops a library into the system Python, which collides with the version another course expects. The stdlib venv module ships with Python 3.3+ and is the right default for student work.

Project A isolation

System Python
│
├── Project A (.venv)
│   ├── Flask 2.3
│   └── NumPy 1.26
│
└── Project B (.venv)
    ├── Flask 3.0
    └── Pandas 2.2

Each project carries its own interpreter copy and package set. TensorFlow 2.10 stays in one assignment's .venv without breaking the other that needs the latest release.

Create the venv from the terminal

# Anywhere inside your assignment folder
cd ~/Courses/data100/hw3
python -m venv .venv

The folder name .venv is the modern convention. VS Code auto-detects it. Pin to .venv and skip the bikeshedding.

Create the venv from VS Code

Open your project folder in VS Code. Press Ctrl+Shift+P (Windows, Linux) or Cmd+Shift+P (macOS) to open the Command Palette. Type Create Environment and select it.

VS Code Command Palette showing the Python: Create Environment command selected

Select Quick Create to create a virtual environment for the project.

VS Code Quick Create option dialog for creating a new virtual environment

A .venv folder appears in the project root. Inside its bin/ folder (called Scripts/ on Windows), the environment contains its own Python executable and pip. The Python version used to create the environment is the version it runs on.

.venv folder tree in VS Code Explorer showing the bin directory with its own python and pip executables

Select the interpreter in VS Code

Open the Command Palette and type Select Interpreter.

VS Code Command Palette showing the Python: Select Interpreter command

Pick the interpreter from the .venv folder created above.

VS Code interpreter selection list with the .venv Python interpreter entry highlighted

At the bottom-right corner of VS Code, the Python indicator confirms the selected interpreter. Hover over it to see the full path. It points inside .venv/.

VS Code status bar showing the Python interpreter indicator with the .venv path

Activate on macOS or Linux

source .venv/bin/activate

# Prompt changes:
# (.venv) you@laptop hw3 %

# Confirm you are now inside the venv
which python
# /Users/you/Courses/data100/hw3/.venv/bin/python

Activate on Windows

# PowerShell
.venv\Scripts\Activate.ps1

# cmd.exe
.venv\Scripts\activate.bat

# Prompt changes:
# (.venv) PS C:\Courses\hw3>

# If PowerShell blocks the script:
# Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

Deactivate when you switch projects

deactivate

# Prompt returns to normal. The .venv folder
# stays on disk, ready for next time.

The deactivate command works on every OS once the venv is active. The prompt loses the (.venv) prefix and Python falls back to the system interpreter. Never commit the .venv folder to git; the .gitignore step at the bottom handles that.

Step 5

pip and requirements.txt

With the venv active, every pip install lands inside .venv/lib/python3.12/site-packages/. The system Python stays untouched. Pin the dependency set in a requirements.txt so the next student, the TA, and the autograder all install identical versions.

The four pip commands students reach for daily

# Install one package
pip install pandas

# Install several at known versions
pip install pandas==2.2.0 numpy==1.26.4 matplotlib==3.8.2

# Snapshot the current environment
pip freeze > requirements.txt

# Recreate the environment on another machine
pip install -r requirements.txt

Install packages from VS Code terminal

Open a terminal in VS Code by selecting Terminal > New Terminal from the menu bar.

VS Code menu bar with Terminal > New Terminal highlighted

With the virtual environment active, run pip to install packages. VS Code opens new terminals with the selected venv already activated when python.terminal.activateEnvironment is set to true in settings.json.

Terminal inside VS Code running pip install pandas with the .venv activated

What a requirements.txt looks like

# requirements.txt
pandas==2.2.0
numpy==1.26.4
matplotlib==3.8.2
scikit-learn==1.4.0
pytest==8.0.0
ruff==0.3.4

Pin the exact version with ==. If your professor accepts >= ranges, fine, but the strict pin is what prevents the 11 p.m. surprise where pandas updates on the TA's machine and your DataFrame.append call breaks.

Save and restore the environment

# See all installed packages
pip list

# Save installed packages into requirements.txt
pip freeze > requirements.txt

# Install packages from requirements.txt
pip install -r requirements.txt
Terminal showing pip freeze output written to requirements.txt with pinned package versions

When sharing a project with a professor, classmate, or Gradescope autograder, the requirements.txt file is what lets them recreate the same environment. Code that runs locally but fails on the grading server is usually a missing or mismatched package.

requirements.txt vs pyproject.toml

requirements.txt is the right default for a single assignment. One file, one job: list packages. The pyproject.toml format wraps build metadata, entry points, and tool config; it shines for shippable libraries, not for hw3. Use pyproject.toml only if your course names it. The CS50P, DATA 100, and CSE 163 graders all read requirements.txt.

Step 6

pytest setup for assignment tests

pytest is the default test runner for every Python course that takes testing seriously. CS50P uses it. DATA 100 lab tests run through it. Gradescope runs it under the hood for most Python autograders.

Install pytest and lay out the test folder

# Inside the venv
pip install pytest

# Suggested folder structure
hw3/
├── .venv/
├── src/
│   └── analysis.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   └── test_analysis.py
└── requirements.txt

pytest discovers tests automatically based on naming conventions. Test files use the test_ prefix (e.g. test_linked_list.py, test_sorting.py). Test functions inside those files also start with test_: for example test_append() or test_pop(). Following this convention means pytest finds and runs everything without extra configuration.

A runnable tests/test_example.py

# tests/test_example.py
import pytest
from src.analysis import normalize_grade

def test_normalize_grade_handles_perfect_score():
    assert normalize_grade(100) == 1.0

def test_normalize_grade_handles_zero():
    assert normalize_grade(0) == 0.0

def test_normalize_grade_rejects_negative():
    with pytest.raises(ValueError):
        normalize_grade(-5)

@pytest.mark.parametrize(
    "raw,expected",
    [
        (75, 0.75),
        (50, 0.50),
        (88, 0.88),
    ],
)
def test_normalize_grade_scales_correctly(raw, expected):
    assert normalize_grade(raw) == expected

Linked-list example

Data structure assignments follow the same pattern. Here the project implements a linked list and the test file is named test_linked_list.py.

# tests/test_linked_list.py
from src.linked_list import LinkedList

def test_append():
    ll = LinkedList()
    ll.append(10)
    assert ll.head.data == 10

def test_length():
    ll = LinkedList()
    ll.append(1)
    ll.append(2)
    ll.append(3)
    assert ll.length() == 3

Run the tests

# Run all tests
pytest

# Verbose output
pytest -v

# Run a specific test file
pytest tests/test_linked_list.py

# Run a single test by name
pytest tests/test_linked_list.py::test_length

# Stop after first failure
pytest -x

# Show local variables when a test fails
pytest -l

conftest.py for shared fixtures

# tests/conftest.py
import pytest
import pandas as pd

@pytest.fixture
def sample_grades():
    return pd.DataFrame({
        "student_id": [1, 2, 3, 4],
        "raw_score": [88, 72, 95, 60],
    })

# In tests/test_analysis.py
def test_class_mean(sample_grades):
    assert sample_grades["raw_score"].mean() == 78.75

A fixture declared in conftest.py is auto-discovered by every test in the same folder. Use it for any object three or more tests want to share: a sample DataFrame, a temp file, a fake database session.

Step 7

Running Python code

Before running a program from the terminal, make sure the virtual environment is activated. The active prompt shows the (.venv) prefix.

Run from the terminal

# With the venv activated
python main.py

Run from VS Code

VS Code adds a Run Python File button in the top-right corner of the editor. Clicking it runs the currently open file using the selected interpreter. When a virtual environment is active for the workspace, VS Code uses it automatically, no terminal activation needed.

VS Code editor showing the Run Python File button in the top-right corner of a Python file
Step 8

Formatting and linting with ruff

ruff is one tool that replaces 5: flake8, black, isort, pyupgrade, and pylint. Written in Rust, it runs 10x to 100x faster than the legacy tools. Default config matches PEP 8 out of the box, which is exactly what most professors grade against.

Install and run ruff

# Inside the venv
pip install ruff

# Find style issues (does not edit files)
ruff check .

# Auto-fix what can be auto-fixed
ruff check . --fix

# Format every .py file
ruff format .

# Check formatting without rewriting
ruff format . --check

Formatting with ruff

Run ruff format . --diff first to preview what the formatter would change before it rewrites any files.

# Check to see what ruff would format
ruff format . --diff
Terminal showing ruff format --diff output with proposed formatting changes highlighted as a diff
# Format every .py file
ruff format .
Terminal showing ruff format result listing files that were reformatted

Linting with ruff

# Find style issues (does not edit files)
ruff check .
Terminal showing ruff check output listing style issues and potential errors found in Python files
# Auto-fix what can be auto-fixed
ruff check . --fix
Terminal showing ruff check --fix output with fixes applied and one unsafe fix remaining that must be resolved manually

Ruff does not automatically fix every issue. When a change is flagged as unsafe (for example, removing a variable that alters program behavior), ruff leaves the fix to you. To apply unsafe fixes explicitly:

# Fix everything, including unsafe items
ruff check . --fix --unsafe-fixes

Minimal ruff.toml at the project root

# ruff.toml
line-length = 100
target-version = "py312"

[lint]
select = [
    "E",    # pycodestyle errors
    "F",    # pyflakes
    "I",    # isort
    "B",    # flake8-bugbear
    "UP",   # pyupgrade
    "SIM",  # flake8-simplify
]
ignore = [
    "E501",  # line-too-long handled by formatter
]

[format]
quote-style = "double"
indent-style = "space"

target-version = "py312" tells ruff which interpreter to lint against. Set it to "py310" or "py311" if your course pins an older interpreter. Skip the ruff.toml entirely and the defaults still work fine; the file is for the moment a professor says 120-character lines and you need to widen line-length.

Step 9

.gitignore for Python coursework

Without a .gitignore, the first git add . pushes __pycache__/, the entire .venv/, and possibly your MOSS-flagged temp files into the homework repo. The snippet below is the safe baseline.

Copy-pasteable .gitignore

# Byte-compiled and cached
__pycache__/
*.py[cod]
*$py.class

# Virtual environments
.venv/
venv/
env/
ENV/

# Test and coverage artefacts
.pytest_cache/
.coverage
.coverage.*
htmlcov/
.tox/

# Type checker caches
.mypy_cache/
.pyright/
.ruff_cache/

# Notebooks
.ipynb_checkpoints/

# Editor / OS
.DS_Store
Thumbs.db
.idea/
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json

# Local config and secrets
.env
.env.local
*.local

The .vscode/* + !.vscode/settings.json pair ignores everything in .vscode except the shared editor config from step 3. The .env line keeps API keys and database URLs out of the repo; once an environment file lands on GitHub, the keys are public.

The end-to-end smoke test

Open a fresh terminal, run the 7 commands below, and a clean Python project exists.

mkdir hw3 && cd hw3
python -m venv .venv
source .venv/bin/activate    # Windows: .venv\Scripts\activate
pip install pandas pytest ruff
pip freeze > requirements.txt
git init
echo ".venv/" > .gitignore

Open VS Code with code ., accept the prompt to select the venv interpreter, and steps 1 through 9 are done. The first test file lands under tests/; the first assignment script lands under src/; ruff format . handles the rest.

Setup taking longer than the assignment?

Send your brief and your Python version. A named developer quotes in 15 minutes, writes tested code against your requirements.txt, and runs it on your data. Pay 50% to start, 50% after the code runs.