Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python Flask If-Match: reject a stale in-memory revision before mutation

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

An If-Match precondition lets a client require a specific representation revision before a write is accepted.

Download Python source kit

Operation contract

The local test client first reads a quoted revision tag, then sends an update with that tag. The handler checks it before changing the receipt amount and increments the revision. Replaying the old tag returns 412 and leaves the new amount intact. This shows an optimistic conflict boundary without opening a network listener.

Failure and ownership boundary

This in-memory state is not durable and is shared only within this fixture. Real handlers must authenticate the caller, apply tenant scope and make the compare-and-update atomic in the persistent store. Python Flask ETag responses: preserve cache validators across conditional reads, Flask SQLite application: own the request connection and verify a clean reopen and Data & Transactions show the next layer.

Tested environment

Dependency check: this program was executed on CPython 3.14.6 with Flask==3.1.3. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.

Working program

python
from flask import Flask, jsonify, request

application = Flask(__name__)
receipt = {"minor": 125, "revision": 1}

@application.get("/receipt")
def read_receipt():
    response = jsonify(minor=receipt["minor"])
    response.headers["ETag"] = f'"{receipt["revision"]}"'
    return response

@application.put("/receipt")
def update_receipt():
    if request.headers.get("If-Match") != f'"{receipt["revision"]}"':
        return jsonify(error="stale revision"), 412
    payload = request.get_json(silent=True)
    if type(payload) is not dict or type(payload.get("minor")) is not int or not 0 <= payload["minor"] <= 1000:
        return jsonify(error="invalid amount"), 400
    receipt["minor"] = payload["minor"]
    receipt["revision"] += 1
    return jsonify(minor=receipt["minor"])

with application.test_client() as client:
    tag = client.get("/receipt").headers["ETag"]
    print("accepted:", client.put("/receipt", json={"minor": 150}, headers={"If-Match": tag}).status_code)
    print("stale:", client.put("/receipt", json={"minor": 175}, headers={"If-Match": tag}).status_code)
    print("stored:", client.get("/receipt").get_json()["minor"])

Output

Output
accepted: 200
stale: 412
stored: 150

Costs and limits

The dictionary comparison and update are tiny local operations. This fixture does not measure HTTP deployment latency or establish atomicity across workers; a database update needs a revision predicate in the write itself.

Common Mistakes

  • An ETag is not an authentication token.
  • A read-then-write check outside a transaction can race in a persistent service.

Connected lessons

Python Flask ETag responses: preserve cache validators across conditional reads, Flask SQLite application: own the request connection and verify a clean reopen, Data & Transactions.

python
flask-if-match-write
Storage details