Recommended resources
The freely available resources this lesson will direct you to.
Hello! This course begins with the engineering habits that make Python code usable beyond one machine or one notebook. Before building application logic, you need a project that you can install, import, and recreate. That foundation will matter throughout the course, from API work to the local coding assistant you will eventually build.
In this lesson, you’ll set up a small project with a private Python environment, a src/ package layout, and a dependency manifest. Plan on about 40–45 minutes, including the setup.
Keep the environment separate from the project
Suppose two projects need different releases of the same library. Installing everything into one system-wide Python makes their requirements compete. A virtual environment gives each project its own interpreter entry point and installed packages. It is not a second copy of your source code: you keep the source in the project and recreate the environment when needed.
Watch Tech With Tim’s Python Virtual Environments – Full Tutorial for Beginners for a demonstration of creating an environment, installing a package, and recording installed versions.
Python Virtual Environments - Full Tutorial for Beginners
Tech With Tim demonstrates the isolation and installation workflow that you will use shortly.
Watch isolation and creation for the reason to use an environment and how to create one. Continue with activation and installation; notice that installed packages belong to the active environment. Finally, watch recreating dependencies to see what a versioned requirements file can do. The video names its environment env; this lesson uses .venv. Its pip freeze workflow is useful, but later we will keep project requirements and a snapshot of installed versions distinct.
The Python documentation adds an important qualification: the environment directory is disposable, not portable. Copying .venv to another machine is not a reliable way to reproduce a project; recording what to install is.
venv — Creation of virtual environments
Read the opening of Python’s venv documentation to establish what is isolated and why the environment should stay out of version control.
In the introductory text before “Creating virtual environments,” read the isolation explanation. Then, under “A virtual environment is (amongst other things),” read the source-control guidance. Focus on the distinction between files you commit and an environment you recreate.
Give the source code a stable home
For this course, use repo_assistant as an illustrative Python import name. Your initial project should look like this:
repo-assistant/
├── .gitignore
├── README.md
├── pyproject.toml
└── src/
└── repo_assistant/
└── __init__.py
The outer directory is the project root, where you run setup commands. src/repo_assistant/ is the importable package. The empty __init__.py marks it as a regular Python package. Keeping code under src/ helps expose an installation mistake: merely being in the project root should not be enough to import the package; installing it should be.
The Python Packaging User Guide explains the package directory and __init__.py. Its example layout differs from our src/ layout, but the purpose of the package directory is the same.
Packaging Python Projects - Python Packaging User Guide
Read the Python Packaging User Guide’s introductory layout guidance for the relationship between a package directory and its modules.
In “A simple project,” start just after “Create the following file structure locally.” Read the directory guidance and the following paragraph on __init__.py, including why it is included. Stop before “Creating the package files.” In our version, the importable directory sits one level deeper, inside src/.
The supplied project-tree image shows what a project can look like after installation and building, rather than what you need to create by hand:

You will create src/, the package, and pyproject.toml. Directories such as dist/ and *.egg-info/ may appear later as tooling artifacts; do not mistake them for application source.
Describe the project and its dependencies
pyproject.toml is the project’s dependency manifest and packaging configuration. Its [project] section states what the project is and which libraries it needs. Its [build-system] section tells an installer which backend can prepare the project for installation. You do not need to publish anything to use this arrangement.
Read the relevant parts of the Packaging User Guide before writing the file:
Packaging Python Projects - Python Packaging User Guide
Use the Python Packaging User Guide to connect the two sections of pyproject.toml to their jobs: building the project and declaring its requirements.
In “Choosing a build backend,” read from the paragraph beginning “Tools like pip and build” through what the backend controls, then inspect the Setuptools [build-system] example and the explanations of requires and build-backend. In “Configuring metadata,” inspect the [project] example and the explanations for name, version, readme, and requires-python; the name explanation begins the relevant bullets. Finish with the paragraph just before “Creating README.md,” which mentions dependencies. You do not need the publishing-specific author, URL, or license fields for this local exercise.
Create the directory structure above in an editor, leaving src/repo_assistant/__init__.py empty. Put a short description in README.md, such as # Repository assistant prototype. Then create this pyproject.toml at the project root:
[build-system]
requires = ["setuptools>=77.0.3"]
build-backend = "setuptools.build_meta"
[project]
name = "repo-assistant"
version = "0.1.0"
description = "A local repository assistant prototype"
readme = "README.md"
requires-python = ">=3.11,<4"
dependencies = [
"httpx==0.28.1",
]
[tool.setuptools.packages.find]
where = ["src"]
There are three names worth distinguishing. repo-assistant is the project’s distribution name in package metadata. repo_assistant is the Python import name supplied by the directory under src/. httpx is a third-party dependency; you are installing it now to make the dependency workflow concrete, not calling an API yet. That comes later.
The final table tells Setuptools where to find packages. Without the src/-specific configuration, the directory layout and the installer’s expectations might not agree.
A declaration such as httpx==0.28.1 pins that direct dependency. It does not, by itself, pin every package that httpx installs in turn. We will address that distinction after the first installation.
Create, install, and verify the environment
From a terminal in the project root, check that you have Python 3.11 or newer:
python --version
If your system uses python3 for Python 3, use python3 in the creation command instead. On Windows, python is commonly the appropriate command. Create the environment:
python -m venv .venv
Activate it using the command for your shell:
macOS/Linux bash or zsh: source .venv/bin/activate
Windows PowerShell: .\.venv\Scripts\Activate.ps1
Windows Command Prompt: .venv\Scripts\activate.bat
Activation makes the environment’s python the one your shell finds first. If PowerShell blocks its activation script, you can instead invoke .\.venv\Scripts\python.exe explicitly for the Python commands below; you do not need to change a system-wide execution policy. The prompt may show (.venv), but verify the interpreter rather than relying only on that visual cue:
python -c "import sys; print(sys.prefix != sys.base_prefix)"
It should print True. Now install your project into that environment in editable mode:
python -m pip install -e .
The dot means “the project in this directory.” The -e means that edits to Python source under src/ are reflected without repeatedly reinstalling the project. It also installs the dependency declared in pyproject.toml.
Watch the focused installation demonstration in Real Python’s Packaging Your Python Code With pyproject.toml. Its package layout is not the src/ layout used here; concentrate on why the installer needs the project root and what editable mode changes.
Packaging Your Python Code With pyproject.toml | Complete Code Conversation
Real Python demonstrates installing a local project with python -m pip install -e . and checking that its package can be imported.
Watch editable installation. Notice why . identifies the directory containing pyproject.toml, and how the editable install makes project code available through the environment. Apply that mechanism to src/repo_assistant/ rather than copying the video's directory layout.
Verify both the environment and the installation:
python -c "import sys, repo_assistant, httpx; print(sys.prefix != sys.base_prefix, httpx.__version__)"
python -m pip check
The first command should print True and 0.28.1; pip check should report no broken requirements. If repo_assistant cannot be imported, check that you ran the install from the directory containing pyproject.toml, that __init__.py is in src/repo_assistant/, and that the environment’s Python is active.
Before committing the project, create .gitignore:
.venv/
.venv-check/
__pycache__/
*.py[cod]
*.egg-info/
build/
dist/
Commit pyproject.toml, README.md, .gitignore, and your source files when you begin using Git. Do not commit .venv/.
Make a fresh installation more predictable
Your manifest expresses the project’s requirements. To record the exact installed package versions from this environment, also capture a snapshot:
python -m pip freeze --exclude-editable > requirements.lock
The --exclude-editable option keeps the local editable project path out of the snapshot. Inspect requirements.lock: it should contain pinned versions for httpx and packages it requires. Keep this file under version control alongside pyproject.toml; regenerate it when you intentionally change dependencies.
The two files have different jobs:
pyproject.tomlsays what the project requires and how to install the project.requirements.lockrecords the installed third-party versions chosen in this environment.
To check that the recorded setup works, create a second, empty environment. First run deactivate, then create .venv-check with your system’s Python 3.11-or-newer command (python or python3) and activate it using the corresponding path. From the project root, run:
python -m pip install -r requirements.lock
python -m pip install --no-deps -e .
python -m pip check
python -c "import repo_assistant, httpx; print(httpx.__version__)"
Here, --no-deps prevents the editable project install from changing the third-party versions you just installed from the snapshot. This is a practical repeatable setup, not a promise of byte-for-byte identical results on every operating system and Python version: the snapshot may need to be regenerated or maintained separately for another platform. Keep the Python version requirement visible in pyproject.toml, and verify a clean install whenever dependencies change.
What you have established
You now have source code in an importable src/ package, an isolated environment that can be discarded, and committed files that describe how to reinstall the project. The crucial test was not whether .venv/ existed; it was whether a fresh environment could install the recorded dependencies and import your package.
In the next lesson, you will put actual application behavior inside that package by implementing typed functions that validate and transform nested JSON.