Skip to lesson
Python for JS Developers

Ecosystem & ToolingLesson 2 of 2

Project Setup & Tooling

4 min

The critical difference from Node: Python does not isolate dependencies per project by default. npm always installs into the project's node_modules, but a bare pip install puts packages into whichever Python you are running, often shared by every project on your machine. Isolation is something you opt into with a virtual environment.

A virtual environment (venv) is a folder, conventionally .venv, with its own Python interpreter and installed packages. It plays the role of node_modules. The classic workflow uses only built-in tools: python -m venv .venv, then activate it (source .venv/bin/activate on macOS/Linux, .venv\Scripts\activate on Windows), then pip install requests. pip freeze > requirements.txt records the installed versions.

In 2026 most new projects use uv, a fast all-in-one tool that will feel familiar to npm and pnpm users. uv init creates a project with a pyproject.toml (the package.json of Python), uv add requests installs a dependency and creates the .venv for you, uv.lock pins exact versions like package-lock.json, and uv run main.py runs code inside the environment without activating anything. uv can even install Python itself (uv python install 3.13), replacing nvm-style version managers. pyproject.toml is the standard either way; setup.py is the legacy approach.

For code quality, ruff is the ESLint + Prettier of Python: ruff check . lints and ruff format . formats, both configured in pyproject.toml.

Packages are directories of modules. A directory with an __init__.py file is a regular package. Without __init__.py, Python 3 still imports it as a namespace package (PEP 420), but namespace packages are meant for splitting one package across several locations, so add __init__.py to your own packages. A common layout is src/your_package/, tests/, pyproject.toml, and README.md. To locate files next to your code, use Path(__file__).parent, Python's __dirname.

For debugging, call breakpoint() anywhere and Python drops into pdb, an interactive debugger in your terminal: n (next), s (step into), c (continue), p expr (print). VS Code and PyCharm offer graphical debuggers too.

Popular libraries: Django (batteries-included web framework, closer to Rails or NestJS than to Next.js), FastAPI and Flask (APIs, like Express or Fastify), requests and httpx (HTTP clients, like axios), SQLAlchemy (ORM, like Prisma or TypeORM), pandas and NumPy (data analysis and numerical computing, with no real JS counterpart).

Quick reference

JavaScriptPythonNote
node_modules/.venv/Per-project environment
package.jsonpyproject.tomlMetadata, deps, tool config
package-lock.jsonuv.lock (or requirements.txt)Pinned versions
npm inituv initCreate a project
npm install axiosuv add requestsOr pip install inside a venv
npm run / npxuv runRun inside the environment
nvm install 22uv python install 3.13Manage interpreter versions
ESLint + Prettierruff check / ruff formatLint and format
__dirnamePath(__file__).parentFolder of the current file
node --inspectbreakpoint() / python -m pdbInteractive debugger

Side by side

JavaScript
// Node.js project setup
// npm init -y          -> package.json
// npm install axios    -> node_modules/ + package-lock.json
// npx eslint . && npx prettier --write .

import axios from "axios";
import path from "node:path";

const here = __dirname; // CommonJS
const res = await axios.get("https://api.example.com/data");
console.log(res.data);
Python
# Modern setup with uv
#   uv init my-app        -> pyproject.toml
#   cd my-app
#   uv add requests       -> .venv/ + uv.lock
#   uv run main.py        -> runs inside .venv
#   uvx ruff check .      -> lint (like npx eslint)
#   uvx ruff format .     -> format (like prettier)

# Classic setup with built-in tools
#   python -m venv .venv
#   source .venv/bin/activate      (macOS/Linux)
#   .venv\Scripts\activate         (Windows)
#   pip install requests
#   pip freeze > requirements.txt

from pathlib import Path
import requests

BASE_DIR = Path(__file__).parent   # like __dirname

response = requests.get("https://api.example.com/data")
response.raise_for_status()        # error on 4xx/5xx
breakpoint()                       # inspect in pdb
print(response.json())

# Project structure:
# my-app/
#   pyproject.toml
#   uv.lock
#   src/my_app/
#     __init__.py
#     core.py
#   tests/

Exercise

Real Python, running in your browser

Set up a project and write the code (terminal commands go in comments):

Starter code
# As comments (terminal commands):
# 1. Create a project / environment (uv init, or python -m venv .venv)
# 2. Add the 'requests' package (uv add requests, or pip install requests)
# 3. Lint the code with ruff

# As Python code:
# 4. BASE_DIR = the folder of the current file (Python's __dirname)
# 5. def get_data(): GET https://api.example.com/data with requests,
#    call raise_for_status(), and return the parsed JSON
# 6. print(get_data())

(The network is simulated here, so requests.get returns fake data.)
Expected output
{'items': [1, 2, 3], 'source': 'https://api.example.com/data'}

Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment