Typorithm · easy

Flask: application factories and isolated configuration

pythonflaskindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Flask: application factories and isolated configuration

0%

Concept and contract

Build two independent applications instead of mutating a process-wide singleton.

A Flask application owns routing, configuration and registered extensions. An application factory creates that object after it receives explicit configuration; importing a module need not open a database or start a server.

This is particularly useful for tests with different settings. A factory is not a separate worker process, and sharing mutable module-level objects can still defeat its isolation.

Mechanism and worked example

The example defines a health route inside create_app, so its closure uses the particular app being constructed.

The example defines a health route inside create_app, so its closure uses the particular app being constructed. Two test clients then request two independently configured applications.

The resulting labels must remain different when their inputs differ. TESTING changes error propagation for this owned fixture; it does not establish production transport security or make a development server suitable for serving public traffic.

Complete worked example

python

from flask import Flask

def create_app(label):
    app = Flask(__name__)
    app.config.update(TESTING=True, LABEL=label)
    @app.get('/health')
    def health():
        return {'label': app.config['LABEL']}
    return app

def inspect_apps(first, second):
    left, right = create_app(first), create_app(second)
    return [left.test_client().get('/health').json,
            right.test_client().get('/health').json]
Reasoning
The example defines a health route inside create_app, so its closure uses the particular app being constructed. Two test clients then request two independently configured applications.

Counterexamples and common mistakes

The counterexample reuses one cached application and overwrites its label for the second caller.

The counterexample reuses one cached application and overwrites its label for the second caller. Requests through the first reference then observe the second configuration.

This is an object-lifetime error, not a browser caching issue. Avoid storing per-application settings in mutable global dictionaries, and initialize extension instances through init_app when the extension supports that factory pattern.

Counterexample: a runnable framework mistake

python

from flask import Flask

def create_app(label):
    app = Flask(__name__)
    app.config.update(TESTING=True, LABEL=label)
    @app.get('/health')
    def health():
        return {'label': app.config['LABEL']}
    return app

def inspect_apps(first, second):
    left = create_app(first)
    right = left
    right.config['LABEL'] = second
    return [left.test_client().get('/health').json,
            right.test_client().get('/health').json]
Reasoning
The counterexample reuses one cached application and overwrites its label for the second caller. Requests through the first reference then observe the second configuration.

Independent practice: one bounded framework change

Add a second configuration value called REGION, passed explicitly into each factory call, and return it beside label.

Add a second configuration value called REGION, passed explicitly into each factory call, and return it beside label. Preserve the independent application objects and existing route.

Start with two distinct labels and regions, then repeat the same values to check ordinary behavior. The task introduces only one setting; environment parsing, secret retrieval and deployment configuration remain separate responsibilities.

Step 1: Pass a REGION into each factory and expose it beside label. Step 2: Extend the factory signature and its configuration together.

Practice starter: solve before reading the solution

python

from flask import Flask

def create_app(label):
    app = Flask(__name__)
    app.config.update(TESTING=True, LABEL=label)
    @app.get('/health')
    def health():
        return {'label': app.config['LABEL']}
    return app

def inspect_apps(first, second):
    # Remove this placeholder, then adapt the retained baseline:
    # Pass a REGION into each factory and expose it beside label.
    raise NotImplementedError
    left, right = create_app(first), create_app(second)
    return [left.test_client().get('/health').json,
            right.test_client().get('/health').json]
Reasoning
Pass a REGION into each factory and expose it beside label. Extend the factory signature and its configuration together.
  • Hint 1: Pass each region to its corresponding application.
  • Hint 2: Check both responses, including distinct regions.

Solution, tests and trade-offs

The reference checks compare both client responses in a fixed order, including repeated labels and an empty label.

The reference checks compare both client responses in a fixed order, including repeated labels and an empty label. Practice also compares distinct regions, so returning one hard-coded region cannot pass.

These are request-level checks over actual Flask application objects. They do not launch a server, make network requests or change any real application configuration outside the fixture.

Practice solution and acceptance checks

python

from flask import Flask

def create_app(label, region):
    app = Flask(__name__)
    app.config.update(TESTING=True, LABEL=label, REGION=region)
    @app.get('/health')
    def health():
        return {'label': app.config['LABEL'], 'region': app.config['REGION']}
    return app

def inspect_apps(first, second, first_region, second_region):
    left, right = create_app(first, first_region), create_app(second, second_region)
    return [left.test_client().get('/health').json,
            right.test_client().get('/health').json]
Reasoning
The reference checks compare both client responses in a fixed order, including repeated labels and an empty label. Practice also compares distinct regions, so returning one hard-coded region cannot pass.

Interview reasoning and next step

In a larger project the factory normally receives a settings object or mapping, registers blueprints, initializes extensions and returns the application..

In a larger project the factory normally receives a settings object or mapping, registers blueprints, initializes extensions and returns the application.

Keep the import path cheap enough for tooling to inspect it. Explain which state belongs to the application, request and session before adding background workers: those scopes have different lifetimes, and a test client does not simulate multiple worker processes.

Optional stretch — after the core task works: Move the factory into a small package and explain extension initialization.

Computer practice: https://applydjinn.com/typorithm/python-frameworks-lab-20261105.zip. Install the pinned dependencies in an isolated environment, run verify.py --track flask, then edit the retained exercise baseline and check it with --practice fl-factory. The lab uses owned in-memory databases, local framework clients and tiny CPU tensors. It never reads the working ApplyDjinn database, contacts production feeds or downloads models.

Build two independent applications instead of mutating a process-wide singleton.