Skip to main content

snakeboot

Spring Boot ergonomics for hexagonal Python applications built with Django, Django Ninja and wireup.

snakeboot imitates the basics of Spring Boot, not Spring itself. Each part is a handful of small classes you can read in an afternoon, and none of them knows anything about your domain.

snakeboot Spring Boot Needs
@transactional, after_commit @Transactional, @TransactionalEventListener(AFTER_COMMIT) nothing
@application_root, ComponentScan @SpringBootApplication, @ComponentScan nothing
ApplicationEventPublisher, @event_listener, @transactional_event_listener the same names wireup
@rest_controller, @route.get, @rest_controller_advice, @exception_handler @RestController, @GetMapping, @RestControllerAdvice, @ExceptionHandler snakeboot[ninja]
DjangoAtomicTransactionManager PlatformTransactionManager for JDBC snakeboot[django]
RecordingTransactionManager, declared_classes spring-test nothing

The transaction, component scan and testing packages import nothing but the standard library, so the application layer of a hexagonal application can use @transactional without importing Django.

Install

uv add "snakeboot[django,ninja]"

A tour

The application root. The component scan starts in the package of the marked class, like @SpringBootApplication. It skips Django's entry points (wsgi.py, asgi.py, migrations) and adds the components snakeboot provides itself (the event publisher).

# myapp/myapp_application.py
@application_root
class MyApplication: ...


# settings.py: the setting wireup needs anyway
WIREUP = WireupSettings(injectables=[*ComponentScan.of(MyApplication).module_names()], auto_inject_views=False)

Transactions. A use case declares its boundary; composition chooses the technology.

@injectable
class CancelRegistrationUseCase:
    @transactional
    def execute(self, request: CancelRegistrationRequestDTO) -> None:
        workshop = self._workshops.get(...)
        workshop.cancel_registration(...)
        self._workshops.save(workshop)
        self._events.publish_all(workshop.pull_events())


# composition, at start-up
TransactionManagerRegistry.install(DjangoAtomicTransactionManager())

Application events. Listeners react to facts after the commit, so a rolled-back change never reaches anyone, and a failing listener cannot undo a committed one.

@application_event_listener
class ParticipantNotificationListener:
    def __init__(self, notifications: NotificationPort) -> None:
        self._notifications = notifications

    @transactional_event_listener(WaitlistedParticipantPromoted)
    def notify_promoted(self, event: WaitlistedParticipantPromoted) -> None: ...

REST controllers. Singleton classes with constructor injection, found by the component scan. Two endpoints for the same URL, or two answers for one error, stop the start-up.

@rest_controller("/workshops", tags=["workshops"])
class WorkshopRestController:
    def __init__(self, enrollment: EnrollmentFacadePort) -> None:
        self._enrollment = enrollment

    @route.get("/{workshop_id}", response=WorkshopViewDTO)
    def get_workshop(self, request: HttpRequest, workshop_id: str) -> WorkshopViewDTO: ...


@rest_controller_advice
class EnrollmentRestErrorAdvice:
    @exception_handler(WorkshopNotFoundError, status=404)
    def answer_not_found(self, error: Exception) -> dict[str, str]:
        return error_answer(error)  # {"code": "workshop_not_found", "detail": "..."}


# urls.py
mounted = RestControllerMounter().mount(api, scan=settings.WIREUP.injectables or ())

Design rules

  • Imitate the basics, not all of Spring: no AOP, no profiles, no scheduling, no message brokers.
  • Fail at start-up, not at the first request: a broken controller, listener or root stops the application.
  • Each package knows as little as possible; uv run lint-imports enforces it.

Develop

uv sync
uv run pre-commit install
uv run pytest            # tests, including the import contracts
uv run ruff check        # Clean Code rules
uv run mypy              # strict

Metadata

Release files for snakeboot 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for snakeboot 0.1.2
File Size Uploaded Provenance Actions
snakeboot-0.1.2.tar.gz 21.9 kB Without provenance Details for snakeboot-0.1.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for snakeboot 0.1.2
File Interpreter ABI Platform Actions
snakeboot-0.1.2-py3-none-any.whl Python 3 none any Details for snakeboot-0.1.2-py3-none-any.whl

Total release size: 53.6 kB

Release files / snakeboot-0.1.2.tar.gz

Download URL snakeboot-0.1.2.tar.gz
Size 21.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ab60110cf8b18176dab9ceb44b453be10ef03362bd40c12cc4fd8cadb75da2d7
BLAKE2b-256 checksum
How to use checksums
be95fae1e6d578f82da87e1b1ef52007a7fe15619e794e579629cd63614e63e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / snakeboot-0.1.2-py3-none-any.whl

Download URL snakeboot-0.1.2-py3-none-any.whl
Size 31.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2caec57f5ffd2db69f7114a4df2de3916808bef348f15d05033702f94679724f
BLAKE2b-256 checksum
How to use checksums
600844d12ff79f5649acc0a032f89542ef43a244743bda30b8446e6e004c98e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for snakeboot 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page