Repository navigation
Releases
Releases · poli-page/fastapi
v0.13.0
First release on PyPI. It requires the poli-page SDK 0.13 (poli-page>=0.13,<0.14) and passes the Poli Page API contract v0.13.0. The package shares its major.minor with the Poli Page API, so its first version is 0.13.0; a 0.11.0 was prepared and never published. The Changed, Removed and Fixed entries below describe changes made while it was prepared.
Added
PoliPageSettings(BaseSettings)— pydantic-settings reader for
POLI_PAGE_API_KEY/POLI_PAGE_BASE_URL/POLI_PAGE_TIMEOUT/
POLI_PAGE_MAX_RETRIES/POLI_PAGE_RETRY_DELAY. Validates the
POLI_PAGE_TIMEOUT,POLI_PAGE_MAX_RETRIESandPOLI_PAGE_RETRY_DELAY
ranges at construction time; a key that does not match
pp_{environment}_{32 hex}logs one warning.get_poli_page_client()—@lru_cache(maxsize=1)memoised factory,
the canonicalDepends(...)target. Tests inject mocks via
app.dependency_overrides[get_poli_page_client].PoliPageDependency— class-based dependency for per-router clients
or explicit settings overrides.PdfResponse,PdfStreamResponse,PreviewResponse,
DocumentRedirectResponse— Starlette response subclasses with
correctContent-Type, RFC 5987-encodedContent-Disposition,
Cache-Control: no-store, private, andX-Content-Type-Options: nosniff. PDF responses acceptinline=Trueto flip the disposition.poli_page_exception_handler— answers aPoliPageErrorwith the
SDK'sto_payload()body and its status (503network_error, 504
timeout, 502invalid_response, 500 when none, as for
invalid_optionsandio_error); any other exception is re-raised.
setup_poli_pageregisters it by default;
app.add_exception_handler(PoliPageError, poli_page_exception_handler)
registers it by hand.poli_page_lifespan— async context manager for
FastAPI(lifespan=...)that warms the client at startup and closes
it (plus clears the factory cache) at shutdown.example-app/— runnable FastAPI demo with an interactive dashboard
atGET /exercising every SDK feature.setup_poli_page(app, *, error_handler=True): wraps the app's lifespan inpoli_page_lifespan(the app's own lifespan and startup/shutdown handlers still run) and registers the error handler, on by default. APoliPageErrora route lets through answersapplication/jsonwith the SDK'sto_payload()body,{code, message, status, requestId}, and itsstatus(503fornetwork_error,504fortimeout,502forinvalid_response,500when the error has none).error_handler=Falseturns it off.PdfStreamResponseaccepts the SDK stream as is (PdfStreamResponse(client.render.pdf_stream(input))): it enters the stream at once, so a render or download error raises in the route and reaches the error handler, and closes it when the response ends: read to the end, failed, or the client disconnected. Iterators are still accepted.app = FastAPI()+setup_poli_page(app)sets up both the lifespan and the error handler;poli_page_lifespanandpoli_page_exception_handlerstay available to wire by hand.setup_poli_pageraisesRuntimeErrorif the app has already started.- Contract tests:
example-app/tests/test_contract.pyruns the API contract's integration cases (impl.*) against the example app. CI runs them against the pinnedpoli-page/api-contractv0.13.0, includingimpl.invalid-response, and Dependabot proposes each new contract version. - Release workflow (
.github/workflows/release.yml): avX.Y.Ztag runs the test suite against the pinned API contract, publishes the wheel and sdist to PyPI through Trusted Publishing, and creates the GitHub Release. Before that, the version's major.minor must be the pinned API contract's (a stable contract tag on the pinned commit, not the comment next to it) and the requiredpoli-pageSDK's (scripts/check-release-versions.sh); the error names both versions. SeeRELEASING.md.
Changed (breaking)
PoliPageSettingsaccepts environment keys (pp_{environment}_{32 lowercase hex}, for examplepp_sandbox_…,pp_live_…,pp_staging_…). It no longer raises on a key that is notpp_test_…/pp_live_…: a missing or emptyPOLI_PAGE_API_KEYstill fails when the client is built, any other value is accepted, and a value that does not match the key format logs one warning that shows no part of the key (not even itspp_{environment}_prefix) before the API decides. An explicitPoliPageSettings(api_key=None)orapi_key=""leaves the SDK to readPOLI_PAGE_API_KEY, and that value gets the same check.- Requires the
poli-pageSDK 0.13 (poli-page>=0.13,<0.14, from PyPI), with environment API keys and no template versions: render inputs carry noversionandrender.previewis gone. A missing key raises the SDK'sPoliPageErrorwith the messageapiKey is required(codeinvalid_options). With SDK 0.13, a 2xx from the API whose body is not JSON (usually from a proxy) raisesInvalidResponseError, codeinvalid_response(it was a rawjson.JSONDecodeError), which the error handler answers502; arender_to_file()that cannot write its file raisesio_error(it was theOSErroritself), answered500with"status": null; and the PDF download is bounded by thetimeoutsetting (an idle timeout forrender.pdf_stream), a download that outlasts it raisingDOWNLOAD_FAILED. poli_page_exception_handleranswers with the SDK'sto_payload()body as is: an error with no status answers500with"status": nullin the body (it used to copy500into the body). Any exception that is not aPoliPageErroris re-raised instead of being answered as a hand-built{"code": "UNKNOWN", …}.PreviewResponseis documented forclient.documents.preview(the stored HTML of a rendered document).- Example app: no
versionin any render;GET /errors/not-found(templatedoes-not-exist,404 TEMPLATE_NOT_FOUND) replacesGET /errors/bad-version. The app is set up withsetup_poli_page(app), so that route answers through the default error handler.GET /render/streampasses the SDK stream toPdfStreamResponse, so a render error answers the handler's JSON instead of a200PDF with an empty body. The app loadsexample-app/.envinstead of a.envabove the repository. - The integration test runs only with a sandbox key (
pp_sandbox_…).
CI
- The live API job runs on
main(and on demand) against the production API with the sandbox key from thePOLI_PAGE_API_KEYsecret, and skips with a notice when the key is absent or not app_sandbox_key. It passes the optionalPOLI_PAGE_BASE_URLsecret to the integration test (unset: the SDK default).
Removed
- Example app:
GET /render/previewand its dashboard button (render.previewno longer exists in the SDK).
Fixed
PdfStreamResponsecloses the SDK stream before it runs itsbackgroundtask. After a
client disconnect, the stream's connection used to stay open until the task finished.PdfResponse/PdfStreamResponse: theContent-Dispositionfilename is now escaped per RFC 6266 / RFC 9110 (\and"as quoted-pairs, ASCII fallback included) and stripped of control characters (CR/LF, TAB, DEL, C1), so a filename can no longer break the header or inject parameters.
Compatibility
- Python 3.11, 3.12, 3.13
- FastAPI
^0.100,^0.110 - Pydantic v2 + pydantic-settings v2
poli-page>=0.13,<0.14