Skip to content

Creating projects

uv supports creating a project with uv init.

When creating projects, uv supports two basic templates: applications and libraries. By default, uv will create a project for an application. The --lib flag can be used to create a project for a library instead.

Target directory

uv will create a project in the working directory, or, in a target directory by providing a name, e.g., uv init foo. If there's already a project in the target directory, i.e., if there's a pyproject.toml, uv will exit with an error.

Applications

Application projects are suitable for web servers, scripts, and command-line interfaces.

Applications are the default target for uv init, but can also be specified with the --app flag.

$ uv init example-app

The project includes a pyproject.toml, a sample file (hello.py), a readme, and a Python version pin file (.python-version).

$ tree example-app
example-app
├── .python-version
├── README.md
├── hello.py
└── pyproject.toml

The pyproject.toml includes basic metadata. It does not include a build system, it is not a package and will not be installed into the environment:

pyproject.toml
[project]
name = "example-app"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []

The sample file defines a main function with some standard boilerplate:

hello.py
def main():
    print("Hello from example-app!")


if __name__ == "__main__":
    main()

Python files can be executed with uv run:

$ uv run hello.py
Hello from example-project!

Packaged applications

Many use-cases require a package. For example, if you are creating a command-line interface that will be published to PyPI or if you want to define tests in a dedicated directory.

The --package flag can be used to create a packaged application:

$ uv init --package example-pkg

The source code is moved into a src directory with a module directory and an __init__.py file:

$ tree example-pkg
example-pkg
├── .python-version
├── README.md
├── pyproject.toml
└── src
    └── example_packaged_app
        └── __init__.py

A build system is defined, so the project will be installed into the environment:

pyproject.toml
[project]
name = "example-pkg"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []

[project.scripts]
example-pkg = "example_packaged_app:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Tip

The --build-backend option can be used to request an alternative build system.

A command definition is included:

pyproject.toml
[project]
name = "example-pkg"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []

[project.scripts]
example-pkg = "example_packaged_app:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

The command can be executed with uv run:

$ uv run --directory example-pkg example-pkg
Hello from example-pkg!

Libraries

A library provides functions and objects for other projects to consume. Libraries are intended to be built and distributed, e.g., by uploading them to PyPI.

Libraries can be created by using the --lib flag:

$ uv init --lib example-lib

Note

Using --lib implies --package. Libraries always require a packaged project.

As with a packaged application, a src layout is used. A py.typed marker is included to indicate to consumers that types can be read from the library:

$ tree example-lib
example-lib
├── .python-version
├── README.md
├── pyproject.toml
└── src
    └── example_lib
        ├── py.typed
        └── __init__.py

Note

A src layout is particularly valuable when developing libraries. It ensures that the library is isolated from any python invocations in the project root and that distributed library code is well separated from the rest of the project source.

A build system is defined, so the project will be installed into the environment:

pyproject.toml
[project]
name = "example-lib"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = []

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Tip

You can select a different build backend template by using --build-backend with hatchling, flit-core, pdm-backend, setuptools, maturin, or scikit-build-core. An alternative backend is required if you want to create a library with extension modules.

The created module defines a simple API function:

__init__.py
def hello() -> str:
    return "Hello from example-lib!"

And you can import and execute it using uv run:

$ uv run --directory example-lib python -c "import example_lib; print(example_lib.hello())"
Hello from example-lib!

Projects with extension modules

Most Python projects are "pure Python", meaning they do not define modules in other languages like C, C++, FORTRAN, or Rust. However, projects with extension modules are often used for performance sensitive code.

Creating a project with an extension module requires an choosing an alternative build backend. uv supports creating projects with the following build backends that support building extension modules:

Using maturin is recommended for combining Rust and Python:

$ uv init --build-backend maturin example-ext

Note

Using --build-backend implies --package.

The project contains a Cargo.toml and a lib.rs file in addition to the typical Python project files:

$ tree example-ext
example-ext
├── .python-version
├── Cargo.toml
├── README.md
├── pyproject.toml
└── src
    ├── lib.rs
    └── example_ext
        ├── __init__.py
        └── _core.pyi

The Rust library defines a simple function:

src/lib.rs
use pyo3::prelude::*;

#[pyfunction]
fn hello_from_bin() -> String {
    "Hello from example-ext!".to_string()
}

#[pymodule]
fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(hello_from_bin, m)?)?;
    Ok(())
}

And the Python module imports it:

src/example_ext/__init__.py
from example_ext._core import hello_from_bin

def main() -> None:
    print(hello_from_bin())

The command can be executed with uv run:

$ uv run --directory example-ext example-ext
Hello from example-ext!

Important

Changes to the extension code in lib.rs or main.cpp will require running --reinstall to rebuild them.