Skip to content

Repository files navigation

SPOC

title-image

License: MIT Language GitHub PyPI Downloads

Install

Python 3.12+

pip install spoc

The generated project imports spoc at runtime, so install it where you run the project from — installation guide.

Documentation

Read the docs — tutorials, how-to guides, and the full API reference.

A good path in: your first projectnames & the registrybuild a framework.

Links

Build a framework in 30 lines

Say what kinds of things your app has:

import spoc

framework = spoc.Framework("models", "commands", "views")

model = framework.kind("models")
command = framework.kind("commands")
view = framework.kind("views")

Tag your code with them, anywhere in the project:

from framework import model


@model
class Post:
    """Registers as models:blog.post."""
from framework import command


@command
def publish(title: str = "Hello, SPOC") -> str:
    """Registers as commands:blog.publish."""
    return f"published {title!r}"
from framework import view


@view
def posts_api() -> list[str]:
    """Registers as views:blog.posts_api."""
    return []
[spoc.apps]
development = ["apps.blog"]
from pathlib import Path

from framework import framework

framework.start(Path(__file__).resolve().parent)

That's it — no registry to wire up, no list to keep in sync. Ask SPOC what it found:

spoc check
spoc list
spoc stubs
OK: /path/to/blog checks out clean
commands:blog.publish
models:blog.post
views:blog.posts_api
wrote framework.pyi (3 identifiers)

SPOC turns your application's declarations into one typed, inspectable registry.

check dry-boots your project and reports what's wrong before anything runs. list reads the shelf. stubs writes real autocomplete for every name you just typed — so framework.objects.models.blog.post completes in your editor before you've written a single test. Add a fourth decorated function tomorrow; all three commands see it with no edit to any of these files.

See it work without writing a file

pip install spoc
spoc init myproject --template starter
cd myproject && python main.py --help
usage: myproject [-h] {core.add,core.items} ...

positional arguments:
  {core.add,core.items}
    core.add            Add an item to the store.
    core.items          List the items in the store.

options:
  -h, --help            show this help message and exit

Nobody wrote that command list. It was derived from what the generated app registered — the same trick as above, scaffolded for you. uvx spoc init myproject works with nothing installed at all.

What you get

  • One name per component, always. Class names derive their own. Ask for a name that isn't there and the error says which segment was wrong and what would have matched — never a silent None.
  • Autocomplete, with no code changes. spoc stubs writes a type stub beside your entry point: names complete as you type, components come back as their real types, typos become editor errors. spoc projection emits the same registry as JSON for tools in any language.
  • A lifecycle you can reason about. Modules start in dependency order and stop in reverse, sync or async.
  • Problems found before runtime. spoc check dry-boots and reports config errors, cycles, collisions, and sync/async mismatches. spoc list and spoc explain read the registry from your terminal.
  • Tests in the box. spoc.testing gives isolated framework scopes and an app-tree builder, arriving as ready-made pytest fixtures.
  • Zero dependencies. dependencies = [], enforced. Optional data-format codecs live behind extras (pip install "spoc[full]").

SPOC never runs your components. FastAPI still serves your HTTP, Typer still parses your argv, Celery still runs your jobs. SPOC only answers what does this app contain, and under what name — how architecture, names, and lifecycle work underneath is in the docs.

Should you use it?

Yes, if…

  • one codebase feeds several surfaces — HTTP and CLI and workers — and each re-discovers the same components its own way.
  • you are shipping a framework other people write apps against.
  • things must start in dependency order and stop in reverse.
  • a mistyped component name should be an editor error, not a None at 3am.

No, if…

  • it's one app, one surface, a handful of modules. Imports are cheaper. SPOC pays off above a complexity threshold, not below it.
  • you are already on Django. Its app registry is a structural model.
  • you want something that runs your components. SPOC only names and orders them.

Weighing it against imports, entry points, pluggy, or a DI container specifically? Why not just…? has the one-line answer for each.

About

Python (S.P.O.C)

Resources

Contributing

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages