Your First Project
Two minutes, three commands, and you'll have a running framework.
1. Generate a project
This generates the default template — the smallest project that runs, which
is the right one to learn from. The README's demo used
spoc init myproject --template starter, a fuller project with working CLI
commands; that one is walked through in The Starter.
spoc init creates a small project that runs without editing anything:
hello/
├── config/
│ └── spoc.toml # your settings — the only file SPOC reads
├── apps/
│ └── core/ # your first app
│ ├── __init__.py
│ ├── models.py # blocks of kind "models"
│ └── views.py # blocks of kind "views"
├── framework.py # your rules: which kinds exist
└── main.py # the entry point
2. Run it
Ready: 2 components registered
Installed apps: ['apps.core']
- models:core.example
- views:core.example
That's SPOC booting: it read your settings, imported your app, found two blocks, and put them on the shelf with their name tags.
Note the program says components where this page says blocks — those are
one thing. Component is the API's name for a record on the shelf; block
is the word these guides use while you're learning. From here on you'll see
both.
3. Read the three files
framework.py — your rules. One declaration says which kinds of blocks
exist. One decorator per kind is handed to your apps:
"""The entire framework definition for hello: one declaration."""
import spoc
framework = spoc.Framework("models", "views")
# One decorator per declared kind. Apps import these to declare components.
model = framework.kind("models")
view = framework.kind("views")
apps/core/models.py — a block. The decorator puts a name tag on the
class. Example in app core of kind models becomes
models:core.example — you never write that string yourself:
from framework import model
@model
class Example:
"""Registers as models:core.example."""
apps/core/views.py is the same shape for the views kind:
from framework import view
@view
class Example:
"""Registers as views:core.example."""
main.py — the start button. Nothing happens until you press it:
from pathlib import Path
from framework import framework
BASE_DIR = Path(__file__).resolve().parent
if __name__ == "__main__":
framework.start(BASE_DIR)
for component in framework.registry:
print(" -", component.identifier)
framework.shutdown()
4. Add a second app
SPOC reads your framework.py, sees the kinds are models and views, and
generates apps/blog/ with one module per kind. It never edits your settings
— it prints the exact line to add. Open config/spoc.toml and install it:
Now give the blog a real block:
from framework import model
@model
class Post:
"""Registers as models:blog.post."""
The generated apps/blog/views.py keeps its starter block — that's the
views:blog.example you're about to see on the shelf:
from framework import view
@view
class Example:
"""Registers as views:blog.example."""
5. Ask the shelf
And from Python, resolve turns a name tag back into the block. Update
main.py to ask for the post:
from pathlib import Path
from framework import framework
BASE_DIR = Path(__file__).resolve().parent
if __name__ == "__main__":
framework.start(BASE_DIR)
record = framework.resolve("models:blog.post")
print(record.object) # <class 'apps.blog.models.Post'>
print(record.kind) # models
print(record.namespace) # blog
framework.shutdown()
You now know the loop
Declare kinds → write apps → start() → resolve from the registry. Everything
else in these docs is detail on one of those four steps.
Next: the settings file, or jump to how the framework object works.