Modern development requires moving away from pip and requirements.txt for complex projects.
Do not cram everything into one script. Build a pipeline:
The Problem: Adding watermarks, headers, or stamps usually requires rewriting the entire PDF (I/O heavy).
The Modern Pattern: Use pypdf (v4+) with merge-after-page contexts.
from pypdf import PdfReader, PdfWriter from pypdf.generic import AnnotationBuilder
reader = PdfReader("input.pdf") writer = PdfWriter() for page in reader.pages: # Add a sticky note annotation WITHOUT rewriting the content stream annotation = AnnotationBuilder.freetext( "DRAFT", rect=(50, 550, 200, 570), font="Arial", font_size="12pt" ) page.annotations.append(annotation) writer.add_page(page)
Impact: 90% reduction in memory usage for large files.
Python 3.12’s exception groups let you handle multiple unrelated failures in one block—critical for async and batch processing.
from ExceptionGroup import ExceptionGroup # built-indef process_batch(items): errors = [] results = [] for item in items: try: results.append(risky_operation(item)) except Exception as e: errors.append(e) if errors: raise ExceptionGroup("Batch failed", errors)
try: process_batch([1, "two", 3]) except* ValueError as eg: # except* handles subgroups print(f"Value errors: eg.exceptions") except* TypeError as eg: print(f"Type errors: eg.exceptions")
Impact: No more except Exception: pass monstrosities. Your error handling now matches the complexity of modern async workflows.
Modern development requires moving away from pip and requirements.txt for complex projects.
Do not cram everything into one script. Build a pipeline:
The Problem: Adding watermarks, headers, or stamps usually requires rewriting the entire PDF (I/O heavy).
The Modern Pattern: Use pypdf (v4+) with merge-after-page contexts.
from pypdf import PdfReader, PdfWriter from pypdf.generic import AnnotationBuilder
reader = PdfReader("input.pdf") writer = PdfWriter() for page in reader.pages: # Add a sticky note annotation WITHOUT rewriting the content stream annotation = AnnotationBuilder.freetext( "DRAFT", rect=(50, 550, 200, 570), font="Arial", font_size="12pt" ) page.annotations.append(annotation) writer.add_page(page)
Impact: 90% reduction in memory usage for large files.
Python 3.12’s exception groups let you handle multiple unrelated failures in one block—critical for async and batch processing.
from ExceptionGroup import ExceptionGroup # built-indef process_batch(items): errors = [] results = [] for item in items: try: results.append(risky_operation(item)) except Exception as e: errors.append(e) if errors: raise ExceptionGroup("Batch failed", errors)
try: process_batch([1, "two", 3]) except* ValueError as eg: # except* handles subgroups print(f"Value errors: eg.exceptions") except* TypeError as eg: print(f"Type errors: eg.exceptions")
Impact: No more except Exception: pass monstrosities. Your error handling now matches the complexity of modern async workflows.