At Local Logic, we rely heavily on lambda functions. We currently monitor our environment with Sentry via their sdk and know that we could use, as an alternative, the sentry layer. As a good developer, I wondered what were the pros and cons. Obviously, one of them is: which option is the fastest? So, I compared them and will share the results in this post. Without further introduction, let’s dig into the experiment.
Continue reading “Measuring Sentry Impact on AWS Lambda Cold Start and Time to Handler”Author: Frank-Mich L'Heureux
bookmark_borderIt’s a Trap! Postgis Geometry with SRID 4326 is not a Geography
Long story short, you create a table with a geometry column that stores points with srid 4326 (read geographic lat/lng, or should I write lng/lat, coordinates). That should be interpreted as geography right? Not at all!
Continue reading “It’s a Trap! Postgis Geometry with SRID 4326 is not a Geography”bookmark_borderHybrid Pydantic/FastAPI Model
Short story, I have an API that doesn’t run on FastAPI, but I still use FastAPI to generate documentation in OpenAPI format.
So I have models for endpoints and I want them to work with and without FastAPI, because in their pure version FastAPI is not deployed. (To make a long story short, installing FastAPI makes the package a bit heavier to deploy, but especially the cold starts of the AWS lambdas because of the Sentry initialization delay. Sentry detects that FastAPI is present in the environment and automatically adds monitoring, which takes a good 500ms longer than when it’s missing. Measured in December 2023).
Anyway, drawing heavily on a StackOverflow answer, I produced this.
# models.py
from typing import Annotated, Any, cast
from pydantic import BaseModel, Field
from pydantic.fields import FieldInfo
try:
from fastapi import Query # type:ignore[import-not-found]
def FieldQuery(*args: Any, **kwargs: Any) -> FieldInfo: # noqa:N802
return cast(FieldInfo, Field(Query(*args, **kwargs)))
except ImportError:
def FieldQuery(*args: Any, **kwargs: Any) -> FieldInfo: # noqa:N802
return cast(FieldInfo, Field(*args, **kwargs)) # type: ignore[pydantic-field]
class QueryParams(BaseModel):
limit: Annotated[
int,
FieldQuery(
-1,
description="The limit of values to fetch. -1 means no limit.",
),
]
And for the routes:
# routes.py
from fastapi import FastAPI
from models import QueryParams
app = FastAPI()
@app.get("/things")
def get_things(
params: Annotated[QueryParams, Depends()],
) -> dict:
...
Is it perfect? With the “type: ignore”, “noqa”, “import try/except”, not at all. It looks like a big hack.
Does it work? Oh yeah!
Note: The “noqa” and “type: ignore” tags are respectively linked to ruff and mypy linters.
bookmark_borderMy Python Toolbox, 2024 Edition
I’ve been wanting to publish my favorite Python tools annually for a long time now. It’s practical for a lot of people, and it allows to see how they evolve over time and the projects I contribute to.
So here’s the first edition of this list.
Continue reading “My Python Toolbox, 2024 Edition”bookmark_borderRecipe: Migrating a WordPress Blog Over AWS Lightsail
The blog you are currently on used to be hosted on some Virtual Private Server. My hosting provider decided to quit the business and I had to leave. He kindly suggested that I look at AWS Lightsail as a migration option. I did, and this is where this blog is now hosted.
This blog is not new. It already had content. It also has customisations like plugins and themes. This post will focus on how to export your content and customisations from a former blog and import them on Lightsail. Its accuracy may vary depending on the level of customisation your blog has. Command line (CLI) experience is required.
Continue reading “Recipe: Migrating a WordPress Blog Over AWS Lightsail”bookmark_borderPrinting an Aware Date in Python
A handy code sample that I keep forgetting about.
from datetime import datetime, timezone
now = datetime.now(tz=timezone.utc)
print(now.astimezone().isoformat())
bookmark_borderFastAPI Stripe Webhook Template
A FastAPI equivalent for verify-events-came-from-stripe.
import os
from http import HTTPStatus
from typing import Annotated
import stripe
from fastapi import Depends, FastAPI, Header, HTTPException, Request
app = FastAPI()
async def get_body(request: Request) -> bytes:
return await request.body()
@app.post("/webhook", status_code=HTTPStatus.NO_CONTENT)
def post_report(
stripe_signature: Annotated[str, Header(alias="stripe-signature")],
body: bytes = Depends(get_body),
) -> None:
endpoint_secret = os.environ["ENDPOINT_SECRET"]
try:
# signature validation
event = stripe.Webhook.construct_event(body, stripe_signature, endpoint_secret)
except ValueError as e:
# Invalid payload
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST) from e
except stripe.error.SignatureVerificationError as e:
# Invalid signature
raise HTTPException(status_code=HTTPStatus.UNPROCESSABLE_ENTITY) from e
print(event)
return
A possible catch, one that I ran into, is that I had FastAPI convert the request.body
to a dictionary in the function parameters (so body: dict
). Then I serialized it to a string for the validation step… and it failed because it was no longer identical to what came in.
bookmark_borderServerless: How to share the ids of an existing API Gateway
The serverless documentation indicates how to share the ids of an API Gateway, but the technique described there seems to work only for a new deployment. (In my case, following this technique created a 2nd empty API Gateway whose ids were shared, which was, unsurprisingly, not fulfilling my needs).
Here is how to do it for an existing deployment.
Continue reading “Serverless: How to share the ids of an existing API Gateway”bookmark_borderPython Type Hinting: To hint or to cast?
During a code review, a colleague, Zachary Paden, asked me why I was calling the typing.cast
function on my variables rather than creating temporary variables just to type hint. Well, just as he didn’t know about cast, I didn’t know that this approach worked. Being the nerdz that we are, he decided to measure the performance of each approach.
bookmark_borderboto3 – From monkeypatch to moto
Today, at the suggestion of a college (Zachary Paden), I contemplated the idea of migrating my tests using python/monkeypatch to moto. Here I will share my journey and observations.
Continue reading “boto3 – From monkeypatch to moto”