The handoff between machine learning researchers and software engineers is a notorious bottleneck. Researchers excel at exploring novel architectures, but rarely focus on writing production-grade APIs. Conversely, software engineers write highly robust systems but often lack the deep domain knowledge required to run complex computer vision or custom transformer models. At Higharc, Senior Research Engineer Vaidas Razgaitis solves this operational friction with a highly tactical systems-and-process approach. 1. Enforce Research Legibility with an RPT Document To bridge the knowledge gap, Razgaitis introduces a standardized document: the **Research Prototype Taxonomy (RPT)**. Mirroring traditional software engineering technical design documents, the RPT clarifies domain-specific concepts for engineers who might be unfamiliar with spatial reasoning, embedding models, or custom data structures. By laying out the "why" behind the ML tool and establishing clear type contracts between the core product repository and the ML codebase, teams remove ambiguity. Crucially, the RPT identifies the persistence layer. Since ML researchers should not spend their time optimizing database storage, this section acts as the perfect handoff point where back-end engineers can step in to build production infrastructure. 2. Decouple Code with a Python Mono Repo Higharc manages its ML code in a dedicated, Python-based mono repo composed of isolated, fully decoupled microservices. Rather than bundling all research projects together, the team aligns researchers to microservices on a near one-to-one ratio. ``` +-----------------+ | API Gateway | +--------+--------+ | +---------------------+---------------------+ | | | +--------v--------+ +--------v--------+ +--------v--------+ | Microservice A | | Microservice B | | Microservice C | | (Transformers) | | (Diffusions) | | (Vision Models) | +-----------------+ +-----------------+ +-----------------+ ``` Each microservice runs as a standalone application inside a unified Docker Bridge network, protected by an API gateway. By structuring these services around a simple layered architecture (API router, controllers, core business logic, and data layer), engineers easily wrap complex models using FastAPI and manage dependencies with Poetry or uv. 3. Slice Monolithic Prototypes Using Stacked PRs Shipping a large, monolithic research prototype into production at once is a recipe for system instability and stalled reviews. Instead, teams should decompose these prototypes along distinct structural layers, using toolchains designed for progressive deployment. Higharc relies on Graphite to manage stacked pull requests. This workflow lets domain experts asynchronously review specific slices of the codebase (such as type contracts, core business logic, or data models) without blocking overall developer momentum. This methodical decomposition makes shipping schedules highly predictable.
uv
Products
Nov 2024 • 2 videos
High activity month for uv. ArjanCodes among the most active voices, with 2 videos across 1 sources.
Dec 2024 • 2 videos
High activity month for uv. ArjanCodes among the most active voices, with 2 videos across 1 sources.
Mar 2025 • 2 videos
High activity month for uv. ArjanCodes among the most active voices, with 2 videos across 1 sources.
Apr 2025 • 1 videos
Steady coverage of uv. ArjanCodes contributed to 1 videos from 1 sources.
Jun 2025 • 1 videos
Steady coverage of uv. ArjanCodes contributed to 1 videos from 1 sources.
Jul 2025 • 1 videos
Steady coverage of uv. ArjanCodes contributed to 1 videos from 1 sources.
Aug 2025 • 1 videos
Steady coverage of uv. ArjanCodes contributed to 1 videos from 1 sources.
Oct 2025 • 2 videos
High activity month for uv. ArjanCodes among the most active voices, with 2 videos across 1 sources.
Jan 2026 • 1 videos
Steady coverage of uv. AI Engineer contributed to 1 videos from 1 sources.
Apr 2026 • 1 videos
Steady coverage of uv. ArjanCodes contributed to 1 videos from 1 sources.
Jun 2026 • 1 videos
Steady coverage of uv. AI Engineer contributed to 1 videos from 1 sources.
ArjanCodes (9 mentions) highlights uv for dependency management and modern Python development, citing its role in scalable projects, as seen in videos like "The Best Way to Share Code Between Python Apps" and "UV Just Got A Serious Upgrade."
- Jun 28, 2026
- Apr 3, 2026
- Jan 8, 2026
- Oct 24, 2025
- Oct 3, 2025
Overview of the uv Ecosystem Python developers often struggle with the fragmented nature of dependency management. While `pip` and `virtualenv` serve as the bedrock, they frequently feel sluggish and fragile in complex environments. uv solves this by providing a unified, extremely fast tool written in Rust. It acts as a comprehensive replacement for `pip`, `pip-tools`, and `poetry`, consolidating environment creation and package resolution into a single executable. With the release of version 0.8.0, it now includes a native build backend that eliminates the need for external tools like Hatch or Setuptools. Prerequisites and Tooling To follow this guide, you should have a basic understanding of Python project structures and command-line interfaces. While uv manages Python versions for you, having a terminal environment (like macOS with Homebrew or Windows) is essential. Key tools mentioned include the `pyproject.toml` configuration standard and the Rust toolchain that powers uv's performance. Code Walkthrough: The uv Workflow Starting a project with uv is instantaneous. Use the following commands to initialize and manage your environment: ```bash Initialize a new project uv init my-project cd my-project Add a dependency (e.g., httpx) uv add httpx Run your code within the isolated environment uv run main.py ``` The `uv init` command generates a `pyproject.toml` file. When you run `uv add`, the tool resolves dependencies and updates the lockfile simultaneously. Unlike traditional methods, `uv run` ensures the script executes within the context of the project's specific virtual environment without requiring manual activation. The New Default Build Backend One of the most significant upgrades in version 0.8.0 is the introduction of `uv-build`. This backend transforms your project into distributable formats like wheels or source distributions. You can explicitly define it in your configuration: ```toml [build-system] requires = ["uv-build"] build-backend = "uv_build" ``` This native integration results in builds that are 10 to 30 times faster than legacy systems. It validates metadata against modern standards automatically, ensuring your packages are ready for PyPI or internal distribution without configuration bloat. Syntax Notes and Best Practices uv follows the standard `pyproject.toml` syntax but introduces specialized flags like `--workspace`. Workspaces allow you to manage multiple related packages under a single lockfile, ensuring version consistency across a large codebase. Always use `uv lock` to regenerate your lockfile after manual edits to ensure environment reproducibility. Tips and Gotchas While uv is incredibly compatible, it is a fast-moving target with weekly releases. If you encounter resolution errors, check your cache settings; version 0.7.21 improved cache key performance significantly. For developers on ARM64 Windows, ensure you are on version 0.7.18 or higher for full architectural support.
Aug 8, 2025Overview Writing Python code is easy, but maintaining it as it grows is a different beast entirely. Without a clear architectural strategy, projects quickly devolve into "spaghetti code"—a mess of tight coupling, circular imports, and fragile dependencies. This tutorial demonstrates how to use **abstraction** to decouple your code, specifically focusing on how to transition from concrete implementations to flexible contracts. By shifting focus from *what* a specific class is to *how* it should behave, you create a system that is easier to test, extend, and understand. We will explore three primary ways to implement these contracts: Abstract Base Classes (ABCs), Protocols, and Callables. Prerequisites To follow this guide, you should have a solid grasp of Python fundamentals, including classes, functions, and basic type hinting. Familiarity with the Pillow library for image processing and the concept of dependency injection will help you grasp the architectural shifts being made. Key Libraries & Tools * **abc**: The built-in module for defining Abstract Base Classes. * **typing**: Contains `Protocol` for structural typing and `Callable` for functional abstractions. * **functools**: Specifically the `partial` function for partial argument application. * **Pillow (PIL)**: Used for the underlying image manipulation tasks. Code Walkthrough The Problem: Concrete Coupling In the original "spaghetti" version, the processing function explicitly checks the type of each filter and applies settings based on that type. This requires importing every specific filter class into the processing module. Solution 1: Abstract Base Classes (ABCs) By defining a base contract, we ensure every filter implements an `apply` method. This allows the processor to treat any filter the same way. ```python from abc import ABC, abstractmethod from PIL import Image class FilterBase(ABC): @property @abstractmethod def name(self) -> str: pass @abstractmethod def apply(self, image: Image.Image) -> Image.Image: pass ``` Solution 2: Protocols (Structural Typing) Protocols allow for "duck typing" with static type safety. Unlike ABCs, your filter classes don't need to inherit from the protocol; they just need to have the matching methods. ```python from typing import Protocol class Filter(Protocol): def apply(self, image: Image.Image) -> Image.Image: ... ``` Solution 3: Callables and Functional Design Sometimes a class is overkill. We can represent a filter as a simple `Callable` that takes an image and returns an image. To handle filters that need configuration (like intensity), we use closures or `functools.partial`. ```python from functools import partial from typing import Callable ImageFilter = Callable[[Image.Image], Image.Image] def apply_grayscale(image: Image.Image, intensity: float) -> Image.Image: # implementation logic return image.convert("L") Create a configured filter function grayscale_filter = partial(apply_grayscale, intensity=0.5) ``` Syntax Notes When using `Protocol`, the `...` (ellipsis) is the standard way to indicate a method body that exists only for type checking. For `Callable`, the syntax `Callable[[Arg1Type, Arg2Type], ReturnType]` provides precise hints for higher-order functions. Practical Examples This pattern is essential in plugin architectures. For instance, if you are building a data export tool, you can define an `Exporter` Protocol. Whether you add CSV, JSON, or SQL exports later, your main logic remains untouched because it only interacts with the abstraction. Tips & Gotchas Avoid over-engineering; if you only have two filters that never change, abstractions might be unnecessary. Beware that `functools.partial` objects do not carry the `__name__` attribute of the original function, which can break logging or debugging tools that rely on function names. Always designate a "dirty corner" in your code—usually the `main.py` file—where concrete instances are actually created and wired together.
Jul 4, 2025Overview Serverless functions allow you to run code in the cloud without managing the underlying infrastructure. Google Cloud Functions (now part of Cloud Run Functions) scale automatically based on demand, including scaling to zero when not in use. This model enables developers to focus purely on business logic while the cloud provider handles provisioning and runtime maintenance. This guide explores building these functions using the Functions Framework, managing them through Infrastructure as Code (IaC) with Pulumi, and navigating technical trade-offs. Prerequisites To follow this guide, you should have a basic understanding of Python and HTTP request handling. Familiarity with Google Cloud Platform (GCP) and terminal-based package management is recommended. You will need a GCP account with billing enabled to deploy resources. Key Libraries & Tools - **Functions Framework**: A Google-provided open-source library that lets you run your functions locally to simulate the cloud environment. - **Pulumi**: An infrastructure-as-code tool that uses standard programming languages to define cloud resources. - **uv**: A high-performance Python package installer and resolver used to manage project dependencies. - **Flask**: The underlying web framework used by Google Cloud Functions to handle routing. Code Walkthrough 1. Local Development You can create a function by defining a simple Python routine. The Functions Framework allows you to serve this locally for testing. ```python import functions_framework @functions_framework.http def hello_world(request): return "Hello, Serverless!" ``` Run this locally using the command: `functions-framework --target hello_world --port 8080`. This provides an immediate feedback loop before cloud deployment. 2. Infrastructure as Code with Pulumi To deploy, use Pulumi to define the necessary infrastructure. You must create a storage bucket to hold the source code artifact and then define the function resource itself. ```python import pulumi import pulumi_gcp as gcp Create a bucket for the code bucket = gcp.storage.Bucket("function-bucket") Define the cloud function cloud_fn = gcp.cloudfunctionsv2.Function("my-function", build_config=gcp.cloudfunctionsv2.FunctionBuildConfigArgs( runtime="python311", entry_point="hello_world", ), service_config=gcp.cloudfunctionsv2.FunctionServiceConfigArgs( max_instance_count=10, available_memory="256M", timeout_seconds=60, )) ``` 3. Simulating Sub-routes Since Google Cloud Functions typically only expose a single entry point, you can use an internal Flask app to handle complex routing within that single function. ```python from flask import Flask, request internal_app = Flask(__name__) @internal_app.route("/user/<id>") def get_user(id): return {"user_id": id} @functions_framework.http def entry_point(request): with internal_app.test_request_context(path=request.path, method=request.method): return internal_app.dispatch_request() ``` Syntax Notes When using Pulumi, you define resources as Python objects. Note the `service_config` block, which dictates how the cloud provider allocates resources. Google Cloud Functions specifically look for a `requirements.txt` file for dependency resolution; if you use uv, you must export your lockfile to this format during the build process. Practical Examples Serverless functions excel at event-driven tasks. Common use cases include processing an invoice whenever a PDF is uploaded to a storage bucket, sending a welcome email via an HTTP trigger during user registration, or performing data validation on a Pub/Sub message stream. Tips & Gotchas - **Cold Starts**: If a function hasn't been called recently, the first request may be slow as the provider spins up a new container instance. Keep dependencies light to minimize this delay. - **Security**: Never hardcode API keys. Use Google Cloud Secret Manager to store sensitive values and inject them as environment variables into your function. - **Access Control**: Be careful with the `allUsers` invoker role; it makes your function public, which could lead to unexpected costs if abused by third parties.
Jun 20, 2025Overview Managing source code effectively is the difference between a streamlined release and a chaotic debugging session. This guide explores the mechanical and strategic nuances of Git branching. By using a FastAPI web application as a concrete example, we demonstrate how to isolate new features, maintain a clean history, and choose between different integration strategies like merging and rebasing. Understanding these patterns allows you to collaborate without stepping on your teammates' toes. Prerequisites To follow this tutorial, you should have a baseline understanding of Python syntax and the basic concept of version control. You will need a terminal environment, Git installed, and a package manager like UV or pip. Familiarity with basic HTTP methods (GET) and unit testing with Pytest is also beneficial. Key Libraries & Tools * **FastAPI**: A modern, fast (high-performance) web framework for building APIs with Python. * **GitKraken**: A visual Git client that simplifies branch management and history visualization. * **UV**: An extremely fast Python package installer and resolver. * **Pytest**: A framework that makes it easy to write small, readable tests. Code Walkthrough Initializing the API We start by defining a simple root endpoint. This serves as our stable baseline on the `main` branch. ```python from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} ``` Isolating Features via Branches Instead of modifying `main` directly, create a feature branch. This keeps the production-ready code clean while you experiment. We add a new `goodbye` endpoint and a corresponding unit test to verify it works. ```python @app.get("/goodbye") def say_goodbye(name: str = "World"): return {"message": f"Goodbye {name}"} ``` Merging vs. Rebasing When it is time to bring changes back to `main`, you face a choice. A **Standard Merge** creates a new commit that ties the two histories together. This preserves the exact context of when a feature was developed but can lead to a "spaghetti" visual history. **Rebasing** offers a cleaner alternative. It takes your feature commits, sets them aside, moves your branch to the tip of the current `main`, and then reapplies your work on top. This results in a perfectly linear history. If `main` hasn't changed since you branched, Git performs a **Fast-Forward**, simply moving the branch pointer forward without creating a new commit at all. Syntax Notes * **Feature Flags**: When using Trunk-Based Development, use boolean constants to toggle code paths. This allows you to merge unfinished code safely. * **Naming Conventions**: In GitFlow, prefix branches with `feature/` or `hotfix/` to organize the repository automatically. Practical Examples Real-world teams often use **GitFlow** for structured releases where separate `develop` and `main` branches exist. Alternatively, fast-moving startups might prefer **Trunk-Based Development**, pushing directly to `main` while hiding incomplete features behind logic toggles to avoid long-lived branch conflicts. Tips & Gotchas * **Rewrite History with Caution**: Never rebase a branch that others are also working on. It changes commit hashes and will break their local environments. * **Small Commits**: Commit early and often. Smaller commits make resolving merge conflicts significantly easier. * **Test Before Integration**: Always run Pytest on your feature branch before merging to ensure you aren't introducing regressions.
Apr 11, 2025Overview Effective testing separates amateur scripts from professional software. Pytest transforms the grueling chore of verification into a streamlined, automated workflow. By adopting a minimalist approach, you reduce the friction of writing tests, ensuring your codebase remains resilient as it scales. This guide focuses on building a clean, manageable testing environment that mirrors your project structure and utilizes modern tooling. Prerequisites To follow this tutorial, you should have a solid grasp of **Python** fundamentals, specifically modules and functions. Familiarity with the command line and a code editor like VS Code is essential. Knowledge of virtual environments will help you manage dependencies without polluting your system Python installation. Key Libraries & Tools * **Pytest**: The primary framework for writing and running small, readable tests. * **uv**: A high-performance Python package installer and resolver. * **Pytest-mock**: A plugin that simplifies mocking objects and patching imports. * **Poetry**: An alternative dependency management tool for Python projects. Code Walkthrough 1. Installation Isolate your testing tools by installing them as development dependencies. Using the `uv` package manager ensures speed and reliability. ```bash uv add --test dev pytest ``` 2. VS Code Configuration To enable the visual testing interface in VS Code, you must explicitly configure your workspace settings. This allows the editor to discover tests within your designated directory. ```json { "python.testing.pytestEnabled": true, "python.testing.unittestEnabled": false, "python.testing.pytestArgs": [ "test" ] } ``` 3. Writing Unit Tests Test files must follow the `test_*.py` naming convention. Match your `test/` folder structure to your `src/` folder for easy navigation. Below is a test for a duration conversion function. ```python from datetime import timedelta from src.timestamp_utils import to_timestamp def test_negative_duration(): duration = timedelta(seconds=-10) assert to_timestamp(duration) == "0:00:00" def test_specific_time(): duration = timedelta(hours=1, minutes=2, seconds=3) assert to_timestamp(duration) == "1:02:03" ``` Syntax Notes Pytest relies on standard Python `assert` statements rather than the verbose helper methods found in the legacy `unittest` library. Functions must start with the `test_` prefix to be automatically discovered by the runner. For more complex scenarios, **Mocking** replaces real objects with fake ones to isolate the logic under test. Practical Examples Testing is vital for external API integrations, such as OpenAI. Instead of making real network calls during testing, you can **patch** the translator function to return a static string. This keeps your test suite fast and avoids unnecessary costs. Tips & Gotchas * **Filter by Keyword**: Use `pytest -k "keyword"` to run specific subsets of tests. * **Reset State**: Always ensure one test does not depend on the outcome of another; isolation is king. * **Check Performance**: Use the `--durations=10` flag to identify the slowest tests in your suite.
Mar 21, 2025Overview Most developers begin their containerization journey with a bloated Dockerfile that results in massive images and sluggish deployment pipelines. An unoptimized image often exceeds a gigabyte, dragging down CI/CD performance and increasing cloud storage costs. By moving away from heavy generic OS images like Ubuntu and adopting modern tooling, you can reduce image sizes by over 80% while significantly hardening security. Prerequisites To follow this guide, you should have a baseline understanding of Python development and Docker fundamentals. Familiarity with the terminal, basic shell commands, and the concept of virtual environments will help you navigate the more advanced multi-stage build patterns. Key Libraries & Tools - FastAPI: A high-performance web framework for building APIs with Python. - Poetry: A tool for dependency management and packaging in Python. - uv: An extremely fast Python package installer and resolver written in Rust. - Debian: The Linux distribution serving as the foundation for most official Python images. Refined Base Images and Tagging Standard Python images often include unnecessary build tools and headers. Switching to `python:3.13-slim-bookworm` provides a minimal Debian base without the bloat. Furthermore, avoid the `latest` tag; it is a rolling target that introduces unpredictability. Specific tags ensure your production environment remains consistent and reproducible. The Multi-Stage Build Pattern This technique separates the build environment from the runtime environment. You use a heavy image with compilers to install dependencies, then copy only the resulting virtual environment into a slim production image. ```dockerfile Stage 1: Builder FROM python:3.13-bookworm AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv WORKDIR /app COPY pyproject.toml . RUN uv venv && uv sync Stage 2: Runtime FROM python:3.13-slim-bookworm WORKDIR /app COPY --from=builder /app/.venv /.venv ENV PATH="/.venv/bin:$PATH" COPY ./src ./src CMD ["uvicorn", "src.main:app"] ``` Syntax Notes and Performance Using `apt-get install --no-install-recommends` prevents the installation of suggested packages that aren't strictly required. Additionally, chaining `rm -rf /var/lib/apt/lists/*` in the same `RUN` layer clears out the package index metadata, which serves no purpose in a final production image. Practical Examples In a real-world FastAPI deployment, applying these steps took an image from nearly 1GB down to roughly 150MB. By using uv instead of Poetry inside the Dockerfile, build times dropped from 36 seconds to under 9 seconds due to faster dependency resolution. Tips & Gotchas Always run your container as a non-root user to prevent attackers from gaining system-level access. Use `RUN useradd -m appuser && USER appuser`. A common mistake is copying the entire project directory (`COPY . .`), which can accidentally leak `.env` files or include heavy local artifacts like `node_modules` or `__pycache__`.
Mar 14, 2025Overview uv represents a paradigm shift in Python tooling. Developed by Astral in Rust, it acts as a high-performance replacement for `pip`, `poetry`, `pyenv`, and `virtualenv`. The primary advantage is speed; uv resolves and installs packages significantly faster than legacy tools while providing a unified interface for managing Python versions and virtual environments. Prerequisites To follow this guide, you should have a basic understanding of the Python ecosystem, including how to use the terminal and the purpose of a `pyproject.toml` file. While no specific version of Python is required to start—since uv can install Python for you—having a shell environment like Zsh or Bash is necessary. Key Libraries & Tools * **uv**: An extremely fast Python package and project manager. * **Ruff**: An extremely fast Python linter and formatter, also by Astral. * **Homebrew**: A macOS package manager used for easy installation. * **Cargo**: The Rust package manager, used if building uv from source. Code Walkthrough Installation On macOS, install via Homebrew: ```bash brew install uv ``` Alternatively, use a standalone script for any OS: ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` Project Initialization Create a new project structure with a standard `pyproject.toml` and a managed virtual environment: ```bash uv init my-project cd my-project ``` This command generates a boilerplate Git setup, a `.python-version` file, and a basic `hello.py` script. Managing Dependencies Add and remove packages seamlessly. uv automatically updates your requirements and syncs the environment: ```bash uv add pandas fast-api uv remove sql-alchemy ``` Executing Code Run scripts directly within the context of your managed environment without manually activating it: ```bash uv run hello.py ``` Syntax Notes uv uses a command structure reminiscent of Cargo or `npm`. The `uvx` command (shorthand for `uv tool run`) allows for one-off execution of CLI tools like Ruff without permanently adding them to your project dependencies. Practical Examples In monorepo environments, uv supports **Workspaces**. This allows multiple projects to share a single lockfile and virtual environment, reducing disk usage and ensuring version consistency across different microservices or internal libraries. Tips & Gotchas * **Shell Completion**: Enable tab-completion for faster terminal navigation by running `uv generate-shell-completion zsh` and adding it to your config. * **Build Systems**: Currently, uv relies on backends like `hatchling` for building packages. It does not yet include a built-in Rust-based build backend, though this is actively being developed. * **Python Versions**: Use `uv python install 3.13` to manage runtimes without needing pyenv.
Dec 13, 2024Overview: Unmasking the FastAPI Engine In an unscripted deep-dive into the internals of FastAPI, we move beyond the high-level marketing of "performance" and "developer experience" to examine the actual plumbing. FastAPI has exploded in popularity over the last few years, often cited as the modern successor to Django and Flask. However, a tactical look at the source code reveals a library that is less an independent framework and more a sophisticated, highly documented orchestration layer. The session explores the project's heavy reliance on Starlette for its ASGI server capabilities and Pydantic for data validation, questioning whether the architectural glue holding these pieces together is as elegant as the public-facing API suggests. Key Strategic Decisions: The Inheritance Gamble The most striking structural choice in the FastAPI codebase is its aggressive use of inheritance. In many core modules, such as `applications.py` and `exceptions.py`, FastAPI types are direct subclasses of Starlette components. While this allows for rapid development and full access to the underlying toolkit, it creates a tight coupling that makes the framework inherently brittle. By inheriting rather than using composition, FastAPI exposes the entire Starlette API to its users, essentially merging the two interfaces. This decision prioritizes speed of implementation over encapsulation. If Starlette introduces a breaking change, FastAPI has no buffer to shield its users, necessitating immediate and potentially complex refactors across the entire ecosystem. Performance Breakdown: Optimization vs. Abstraction When developers ask why FastAPI is faster than Django, the answer doesn't lie in revolutionary Python code within the FastAPI repo itself. The performance is largely inherited from Starlette, which is built on top of high-performance ASGI standards. FastAPI adds a layer of Pydantic for serialization and typing-extensions for metadata. This combination is powerful but comes with a massive amount of "boilerplate city" within the source. For example, the `Param` functions and `APIReady` classes involve deeply nested arguments and repetitive initializers. While this provides the end-user with a clean, typed interface, the internal maintenance cost is high. The framework spends a significant amount of its internal logic simply passing variables down long inheritance chains to satisfy the requirements of its dependencies. Critical Moments: The Documentation Paradox A critical moment in the analysis occurs when examining the `routing.py` and `applications.py` files. These files are massive, but not necessarily because of logic density. A vast percentage of the code consists of docstrings and metadata used to generate Swagger UI and ReDoc. This is the "Documentation Paradox": FastAPI's greatest feature for users—automatic, interactive docs—is also its greatest source of internal clutter. The code often lacks sufficient documentation for *developers* of the framework, even as it overflows with documentation for *users* of the framework. This creates a readability hurdle for anyone looking to contribute to the core, as the actual logic is frequently buried under hundreds of lines of string literals and type definitions. Refactoring Opportunities: Moving Toward Composition To improve the long-term health of the project, a shift away from inheritance toward composition would be a superior tactical move. By making Starlette an internal object within FastAPI rather than a parent class, the developers could create a cleaner boundary. This would allow for a more functional approach where data flows through clearly defined transformations rather than being stored in a massive `self` object with dozens of attributes. Implementing the Strategy Pattern for response serialization—replacing long `isinstance` checks with a mapping dictionary—would also significantly reduce the cyclomatic complexity of the routing logic. These changes would make the framework less brittle and easier to extend without breaking the existing public API. Future Implications: The Maintenance Burden of Compatibility The current source code reflects a heavy maintenance burden regarding Pydantic V1 and V2 compatibility. The presence of logic branches that handle different versions of validation libraries highlights the difficulty of managing a popular open-source project. While supporting older versions of Python and dependencies is noble for adoption, it leads to a fragmented codebase. Looking ahead, the framework must eventually shed this legacy weight. The eventual transition to uv for package management and a stricter adherence to modern Python 3.10+ features (like native generics) will be necessary to keep the codebase from becoming an unmanageable legacy system. Developers should appreciate FastAPI for the tool it is—a brilliant user interface—while remaining critical of the architectural shortcuts taken under the hood.
Dec 4, 2024Overview Setting up a Python development environment in VSCode often feels like a constant battle against broken imports, mismatched interpreter versions, and testing suites that refuse to discover your code. This guide moves past temporary fixes to establish a robust, professional workflow. We will focus on creating a project structure that Pylance understands and Pytest can navigate, using modern tools like UV for dependency management. Prerequisites To follow along, you should have VSCode installed and a basic understanding of the Python language. Familiarity with the terminal or command prompt is necessary for running installation commands and project initialization. Key Libraries & Tools * **UV**: A fast Python package installer and resolver written in Rust, used as a modern alternative to Poetry. * **Ruff**: An extremely fast Python linter and code formatter. * **Pylance**: The default language server for Python in VSCode, providing IntelliSense and type checking. * **Pytest**: A framework that makes it easy to write simple and scalable test suites. * **Even Better TOML**: An extension for better syntax highlighting and navigation in configuration files. Project Structure and Initialization Instead of installing packages globally, we use UV to create an isolated environment. Start by initializing your project in the terminal: ```bash uv init --no-workspace ``` A professional structure separates the logic from the metadata. Move your code into a `src` directory and include a `__init__.py` file to signal that it is a package. Your directory should look like this: ```text my_project/ └── src/ └── my_app/ └── __init__.py └── main.py └── tests/ └── test_main.py └── pyproject.toml ``` To add Pytest as a development dependency, run: ```bash uv add --dev pytest ``` Solving the Import and Test Discovery Crisis The most common headache is Pytest failing to find your modules because it doesn't know about the `src` folder. You fix this by adding a `pythonpath` setting to your `pyproject.toml` file: ```toml [tool.pytest.ini_options] pythonpath = "src" ``` However, Pylance might still show "reportMissingImports" in the editor even if tests run. You must align the editor's analysis with your runtime path by creating a `.vscode/settings.json` file: ```json { "python.analysis.extraPaths": ["./src"], "python.testing.pytestArgs": ["tests"], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true } ``` Professional VSCode Configuration Managing settings across a team requires moving beyond global user settings. Use **Folder Settings** within the `.vscode` directory to ensure every developer on the project uses the same interpreter and formatter. Recommended Extensions Share a consistent toolset by creating `.vscode/extensions.json`. When a team member opens the project, VSCode will prompt them to install the necessary tools: ```json { "recommendations": [ "ms-python.python", "charliermarsh.ruff", "tamasfe.even-better-toml" ] } ``` Syntax Notes and Conventions * **TOML Folding**: Use the Even Better TOML extension to collapse large configuration blocks in your `pyproject.toml`. * **Bundled Formatters**: If you use the Ruff extension, enable the `useBundled` setting to avoid needing a separate local installation of the binary. * **Analysis Paths**: Always use relative paths (like `./src`) in `extraPaths` to ensure settings work across different machines. Tips & Gotchas * **Priority of Settings**: Remember that **Folder Settings** override **Workspace Settings**, which in turn override **User Settings**. If your project isn't behaving, check the local `.vscode/settings.json` first. * **Syncing**: Use VSCode's built-in Settings Sync for personal preferences like themes and fonts, but keep project-specific logic (like the Python path) in the repository. * **Source Folder**: Never name your root code folder `source` or `src` without an `__init__.py` if you intend to import it as a package; Pylance needs that marker to recognize the package boundary correctly.
Nov 22, 2024Overview Modern API development often feels like a redundant exercise in data modeling. Traditionally, developers building with FastAPI find themselves writing two distinct sets of models: SQLAlchemy classes for the database schema and Pydantic models for request validation and response serialization. This "double definition" creates a maintenance burden where every change must be mirrored across both systems. SQLModel solves this by merging these two worlds, allowing you to define a single class that serves as both a database table and a data validation model. Prerequisites To follow this guide, you should be comfortable with Python (3.10+ recommended), basic REST API concepts, and the fundamentals of SQL. Familiarity with asynchronous programming and type hints will help you understand the framework's internal logic. Key Libraries & Tools - **SQLModel**: The star of the show. It sits on top of SQLAlchemy and Pydantic to unify data schemas. - **FastAPI**: The high-performance web framework used to build our API endpoints. - **Uvicorn**: An ASGI server implementation to run our application. - **uv**: A high-speed Python package installer and resolver used to manage the project environment. Code Walkthrough Converting a legacy SQLAlchemy setup to SQLModel drastically reduces the surface area of your code. Let's look at how we define a Hero model that acts as both our table and our schema. ```python from typing import Optional from sqlmodel import Field, SQLModel, create_engine, Session, select class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True, index=True) name: str secret_name: str age: Optional[int] = None ``` In this snippet, `table=True` tells SQLModel to create a database table for this class. Note how we use standard Python type hints. The `Field` function allows us to inject database-specific constraints like `primary_key` without losing Pydantic's validation power. To interact with the database in a FastAPI route, the session management becomes much cleaner: ```python @app.post("/heroes/", response_model=Hero) def create_hero(hero: Hero, session: Session = Depends(get_session)): session.add(hero) session.commit() session.refresh(hero) return hero ``` We no longer need to map a Pydantic object to a separate SQLAlchemy object. The `hero` object passed into the function is already compatible with the database session. Relationship Management SQLModel handles complex relationships, such as many-to-many links, using a dedicated Link model. This prevents data redundancy by storing associations in a join table while keeping the object-oriented interface clean. ```python class HeroMissionLink(SQLModel, table=True): hero_id: Optional[int] = Field(default=None, foreign_key="hero.id", primary_key=True) mission_id: Optional[int] = Field(default=None, foreign_key="mission.id", primary_key=True) ``` Tips & Gotchas While unifying models is efficient, it introduces a risk of **tight coupling**. If your database model is exactly the same as your API response, you might accidentally leak sensitive fields like hashed passwords or internal IDs. To prevent this, use inheritance: create a `HeroBase` for shared fields, then extend it into a `Hero` (with `table=True`) and a `HeroPublic` (for safe API responses). This gives you the best of both worlds: reduced boilerplate with explicit security boundaries.
Nov 15, 2024