# Your Python Project

Every piece of Python you write this semester lives in one folder with a record of what it needs to run. That folder is your **project**, and it sits inside your course repository, so it travels with your work to GitHub and back.

This page walks through starting your project, and it is the reference for the commands that manage it afterward.

## Why a project at all

This course grades whether your numbers are right, and the way anybody checks a number is to run the thing that produced it. A project makes that possible. It writes down which version of Python you used and which version of every package you installed, in two small files that you commit alongside your notebooks. Somebody who clones your repository types one command and has the setup you had.

## Starting a project

You do this once, in your course repository. If you do not have one yet, start with [Getting Started](/guides/getting-started/).

### Open your repository and a terminal

1. Open GitHub Desktop and check that **Current repository**, at the top left, shows your course repository: `oim7510` or `oim6301`.
2. Click **Open in Visual Studio Code**. VS Code opens with your repository's folder, and the file list on the left shows `README.md`.
3. Choose **View → Terminal**. A panel opens at the bottom, and the prompt in it ends in your repository's name.

Opening VS Code from GitHub Desktop every time keeps the terminal in the right folder. [The Terminal](/guides/terminal/) explains why, and [Where Am I Typing?](/guides/where-am-i-typing/) shows what each prompt looks like.

**Your repository must not be in OneDrive, iCloud Drive, Dropbox or Google Drive.** Those programs sync files while your code runs and can break a repository. If yours is in one of them, move it first: the steps are in the [FAQ](/faq/), under **Your repository**.

Check that the tools are there. Paste each line into the terminal and press Enter:

```
git --version
uv --version
```

Each prints a version number. If one prints `command not found` or `is not recognized`, see [The Terminal](/guides/terminal/).

### Make it a project

```
uv init --app --no-package --python 3.14
```

**The first run can take a minute or two.** If Python 3.14 is not on your machine yet, uv downloads it, about 30 MB. You never install Python yourself and you never pick it from a menu.

Each part of the command does a job:

- **`--app`** marks the project as your own analysis, which nobody installs as a package.
- **`--no-package`** turns off the packaging machinery that goes with a package. Without it, recent versions of uv build and install your own project every time you add anything, which prints a wall of output and gives you one more thing that can break.
- **`--python 3.14`** pins the version in a file called `.python-version`, so uv uses the same Python here, on your next computer, and on mine when you show me a problem. Leave it out and uv takes whatever Python your machine offers first, which on macOS can be one that shipped years ago.

The command adds `pyproject.toml`, `.python-version` and `main.py` to your repository and touches nothing else. [The project files](#the-project-files) says what the first two are for, and you may keep or delete `main.py`.

Check that it worked:

```
uv run python -c "print('it works')"
```

It prints `it works`, which tells you that uv runs, Python is installed, and your project can execute code. Any week you suspect your setup has broken, run it again before you debug anything else.

### Keep the right files out of git

**Use the course `.gitignore`.** [Step 11 of Getting Started](/guides/getting-started/#11-replace-the-gitignore) replaces GitHub's template with it. It lists `__pycache__/`, which Python writes beside your code, and `__marimo__/`, where marimo keeps every cell's output, and committing either fills your history with noise. The `.venv` folder holding your packages ignores itself, which uv arranges when it builds the folder.

### Open your first notebook

```
uv add marimo
uv run marimo edit
```

Your browser opens a page listing the notebooks in your project. There are none yet, so click **Create a new notebook** and give it a plain name.

To stop marimo, click the terminal and press `Ctrl+C`, which is `Ctrl` on macOS too. marimo asks `Are you sure you want to quit? (y/N)`, so type `y` and press Enter.

### Then set up your agent

Your agent runs on the key that Babson's AI team emails you. [Your AI Tools and Babson Key](/guides/babson-ai-key/) has the steps for marimo and for VS Code.

## The project files

`uv init` creates the first two, and uv writes the third the first time you run `uv add`, `uv run` or `uv sync`. After that, `uv add` and `uv remove` keep the first and the third in step, and `uv python pin` is the only thing that touches the second.

**`pyproject.toml`** is the one you read. It holds the version of Python your project needs and the list of packages you asked for, and it is short:

```toml
[project]
name = "<course-code>"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
    "marimo[recommended]>=0.24.2",
    "duckdb>=1.5.5",
]
```

**`.python-version`** is one line long, and on a new project it holds `3.14`. It decides which Python actually runs. The `requires-python` line in `pyproject.toml` is a floor, meaning `3.14 or newer`, while this file names the exact one. Change it with `uv python pin 3.14`. Editing it by hand to a version below the floor gives you an error that names the fix, so this is a mistake that announces itself.

**`uv.lock`** is the one you leave alone. It records the exact version of every package that got installed, including the ones your packages pulled in behind your back, each with a checksum. It is long and it is generated.

**Commit all three.** They belong to the work in the same way the notebook does.

**Never edit `uv.lock` by hand.** One wrong character and both `uv sync` and `uv run` stop with `Failed to parse uv.lock`, which leaves you unable to run anything until it is fixed. If it happens, delete the file and run `uv sync`, which builds a fresh one.

Editing `pyproject.toml` by hand is allowed, and uv catches up the next time you run something. Use `uv add` and `uv remove` anyway. They keep both files in step, and they are faster than typing a version number correctly.

## Commands you will use

| command | when |
|---|---|
| `uv init --app --no-package --python 3.14` | once, in a new repository |
| `uv add requests` | whenever your work needs a package it does not have |
| `uv run marimo edit` | every time you sit down to work |
| `uv sync` | when you open somebody else's project, or your own on a second computer |

`uv run` is the one you type most. It puts your project's packages in front of whatever comes after it, so there is no environment to activate and nothing to remember between opening the terminal and running your code.

**Open the project.** `uv run marimo edit` with no file name after it gives you a page listing every notebook in your project, subfolders included, and you click the one you want. You can pass a file name to jump straight to one, and there is rarely a reason to. Typing a file name is how you meet a `FileNotFoundError` for a file that exists, spelled slightly differently.

The keys and menus inside a notebook are in [marimo Basics](/guides/marimo-basics/).

## Where files go

**Put a file into your repository before you open it.** Your browser saves a download to its default folder, usually **Downloads**, or to wherever you saved the last one. Move it into the folder it belongs in, then check that VS Code's file list shows it there. marimo lists only the notebooks inside the folder you started it from, so a notebook left in Downloads does not appear.

| The file | Where it goes |
|---|---|
| A session notebook | `notebooks/` |
| A data file a notebook reads | `data/` |
| A handbook you only read | `handbooks/`, if you keep it at all |
| A script you run on its own, like `hello.py` | `scripts/`, run from the root folder as `uv run python scripts/hello.py` |
| A project | its own folder under `projects/`, such as `projects/mp1/` |
| The export of a notebook you publish | `docs/`, as in [Publishing a Notebook](/guides/publishing-a-notebook/) |

**Build a data path from the notebook's own folder.** A relative path is read from the folder you started marimo in. `mo.notebook_dir()` returns the folder the notebook is in, and [the data handbook](/handbooks/python/03-data/) shows how to get from there to the `data` folder.

## If you would rather use Jupyter

You do not have to. marimo is what this course uses and what the handed-out notebooks are written in. But Jupyter is not forbidden, and it lives in the same project:

```
uv add jupyterlab
uv run jupyter lab
```

Same project, same packages, no second environment to keep in step.

Know this before you try it. A marimo notebook is a `.py` file, and JupyterLab opens a `.py` file as plain text with no cells. What you want instead is the **Jupyter version** offered beside each notebook on its session page, which is an `.ipynb`. It reads and it runs, with two exceptions, because both are marimo features: the interactive controls do nothing, and a SQL cell stops with an error.

## Adding a package

```
uv add requests
uv add yfinance
uv remove yfinance
```

You can also let marimo do it. A cell that imports something your project does not have stops with `ModuleNotFoundError`, and marimo offers to install it. Accept the offer. It runs `uv add` for you, and your `pyproject.toml` gains a line exactly as if you had typed the command. Open the file afterward and look, at least the first time.

This works because you opened the notebook from inside your project. A notebook opened somewhere else installs into a temporary environment that records nothing, and the install disappears the next time anybody runs `uv sync`. Keep your notebooks in your repository and this never comes up.

## Opening your work somewhere else

On a new laptop, or on a classmate's machine during pair work:

```
uv sync
```

It reads `uv.lock` and installs precisely what that file says, down to the version. Nothing resolves, nothing upgrades, and you get the environment your commits describe.

`uv sync` also removes anything in your environment that the lock file does not list. That is deliberate. It is what keeps your environment matching your repository instead of drifting away from it over a semester.

## The Python version

Because `.python-version` is committed, your project runs on the same Python on your machine, on mine, and on a classmate's.

**Leave it at 3.14 for the semester, even after a newer Python is released.** Packages containing compiled code publish ready-built versions for a new Python on their own schedule, and until they do, installing one means compiling it on your own machine. Upgrading in the middle of the term can therefore produce an error that nobody else in the class is able to reproduce.

## What to avoid

**`uv pip install`.** It installs a package without writing it down anywhere. Your work runs, you commit, and the person who clones your repository gets a `ModuleNotFoundError`. Your own next `uv sync` deletes it too. Use `uv add`.

**`uv venv`.** Older instructions tell you to create an environment before installing anything. `uv add`, `uv run` and `uv sync` each create it for you the first time they need it.

**Anaconda and `conda`.** Most Python tutorials on the internet start there. The [Python environments guide](/guides/python-environments/) explains what those tools are and how to read a tutorial written for one.

**Hand-edited `uv.lock`.** Covered above, and worth repeating because an agent will occasionally suggest it.

## When something goes wrong

**`error: No pyproject.toml found in current directory or any parent directory`.** You are standing outside your project. Look at the terminal prompt, check the folder name at the end of it, and open the right folder through GitHub Desktop.

**`ModuleNotFoundError: No module named 'pandas'`.** Your project does not have that package. Run `uv add pandas`, or accept marimo's offer to install it.

**`error: Failed to parse uv.lock`.** Delete `uv.lock` and run `uv sync`.

**`error: The Python request from .python-version ... is incompatible with the project's Python requirement`.** Somebody edited one of the two version settings and left the other behind. Run `uv python pin 3.14`.

**marimo says `To enable code formatting, please install ruff`.** The formatter is missing, which happens in a project whose marimo was installed without `recommended`. Run `uv add "marimo[recommended]"` and reopen the notebook. Nothing else is affected while it is missing. That install is the first step of the marimo part of [Your AI Tools and Babson Key](/guides/babson-ai-key/), and the setting that uses it is [Formatting on save](/guides/marimo-basics/#formatting-on-save) in marimo Basics.

**`error: Failed to spawn: marimo`.** This prints for two different reasons, so try them in this order. Run `uv add marimo`. If that answers `No pyproject.toml found`, your Terminal is not inside your project: open VS Code from GitHub Desktop, open a new Terminal there, and run it again.

**No internet, and you need to work.** `uv sync` reads from a cache of everything uv has downloaded before, so it rebuilds your environment with no connection at all. Add `--offline` to stop it from trying to reach the network. Adding a package you have never installed still needs a connection.

**Your disk is filling up.** That cache is the reason. It grows all semester, and `uv cache dir` tells you where it is. `uv cache prune` deletes the parts nothing is using, which is the one to run first. `uv cache clean` empties it completely, which costs you the offline behavior above until each package is downloaded again.
