Using WeasyPrint with AWS Lambdas and Python

I've recently found quite a bit of joy with WeasyPrint. A Python package which produces top quality PDF documents simply by using HTML and CSS. My particular setup had WeasyPrint deployed in an AWS Lambda, where we took a previous stored JSON result, transformed it using Jinga into an HTML template, and finally stored the output on S3. But you don't want to hear about that right? Lets get to the juicy details of getting it up and running on AWS.

First things first:

The lambda layer - WeasyPrint is BIG - I hit size limits with deployment, so having a separate layer built helped keep the size down.

The following repo Cloud Print Utils was a useful resource in building the lambda layer out. This was a key part of the puzzle. Once you've output your lambda layer zip file, the rest is just hooking it up to serverless for deployment.

Once the lambda layer was built out I referenced like below inside serverless.yml - a layer builder was created in Github actions which pushed the artifact up.

configValidationMode: warn // serverless deployment complains without this
provider:
  name: aws
  runtime: python3.12 // WeasyPrint needs 3.12
  timeout: 30
  tracing:
    lambda: true


functions:
  generatePdf:
    handler: handler.generate_pdf
    layers:
      - { Ref: WeasyPrintLambdaLayer }

layers:
  weasyPrint:
    name: lambda-weasyprint-layer
    description: WeasyPrint and its dependencies
    package:
      artifact: layers/weasyprint.zip

The lambda itself was relatively simple. The crux of it was this method. Return the PDF as bytes so it can be written to S3 via an S3Client. Weasyprint does the heavy lifting here.

 def generate_pdf(self, html_content: str, css_path: str) -> bytes:
        try:
            css = CSS(filename=css_path)
            html = HTML(string=html_content, base_url=self.static_dir)
            return html.write_pdf(stylesheets=[css])
        except Exception as e:
            raise