coding.sgit.ai / the house style / Python

Python

217,266 lines across 3,999 files. The most distinctive style in the estate, and the only one of the five languages with a written rule set behind it.

1. The complete example — everything in eleven lines

This file is quoted verbatim and entire. Nine conventions are visible in it, and the whole style follows from them.

sg_compute/catalog/schemas/Schema__Caller__IP.pyverbatim and entire · 01__python.md
# ═══════════════════════════════════════════════════════════════════════════════
# SG/Compute — Schema__Caller__IP
# Response schema for GET /catalog/caller-ip.
# ═══════════════════════════════════════════════════════════════════════════════

from osbot_utils.type_safe.Type_Safe                                          import Type_Safe

from sg_compute.primitives.Safe_Str__IP__Address                             import Safe_Str__IP__Address


class Schema__Caller__IP(Type_Safe):
    ip : Safe_Str__IP__Address = Safe_Str__IP__Address()

Apache-2.0 — quoted from the estate's own source, not covered by this site's CC BY 4.0. Counted 2026-08-24.

  1. A ═══ banner header — product name, class name, one line of purpose.
  2. The filename is the class name. Schema__Caller__IP.pyclass Schema__Caller__IP.
  3. One class per file.
  4. Fully-qualified imports from the per-class path — never a package re-export.
  5. import keywords aligned to a column.
  6. Type_Safe as the base class.
  7. A constrained primitive (Safe_Str__IP__Address) rather than str.
  8. The annotation colon aligned, with an instance as the default.
  9. No docstring. The banner does that job.

2. Naming

Prefix families — what the class is

PrefixFilesMeaning
Schema__614Pure data. No methods (rule 4)
Safe_Str__290A regex-constrained string type
Enum__185A fixed value set. Never a Literal (rule 3)
Cli__82A Typer command group
Routes__59A FastAPI route class. No logic (rule 19)
Safe_Int__18A bounded integer type
Section__15A shell fragment generator — see Bash
Fast_API__12An app assembly

Suffix families — what the class does

__Builder 97 · __Helper 86 · __Client 77 · __Service 46 · __Mapper 39 · __Detector 23 · __Loader 17 · __Registry 14 · __Writer 14 · __Parser 10 · __Runner 9 · __Manager 7 · __Factory 4 · __Watchdog 2 · __Poller 1

The double-underscore rule

__ is the word separator inside a compound name; _ separates words within one term. Schema__Image__Build__Request reads as Schema · Image · Build · Request. Safe_Str__IP__Address keeps Safe_Str as one term. 3,871 of 3,999 filenames contain __.

Rule 20 handles the one hard case: the spec uses names like SGraph-AI with a hyphen, which is not a legal Python identifier. Class and module names use SGraph_AI; repo roots and test filenames may keep the hyphen.

Why __init__.py stays empty

Rule 22, and it holds at 299/302 = 99%. Callers import from the fully-qualified per-class path; nothing is ever re-exported, so there is exactly one import path to any class. The rule carries its own warning, learned the hard way:

Never commit an empty __init__.py in a folder that shares a name with a sibling .py module: Python's import system prefers the package, and every import under the module breaks.

3. Type_Safe — runtime validation instead of static typing

Type_Safe comes from osbot-utils, Apache-2.0, under the owasp-sbot organisation. The convention is the estate's; the mechanism is the dependency's, and on a site about attribution discipline that distinction is worth stating plainly.

Rule 1 is that all classes extend it — no plain Python classes. The tooling rule is blunter: never use Pydantic, no Literals. Of 1,034 classes in the new tree:

BaseCount
Type_Safe506
TestCase169
Safe_Str78
str, Enum69
Fast_API__Routes (a Type_Safe descendant)46
Type_Safe__List44
Schema__Step__Base (a Type_Safe descendant)25
Safe_Int / Safe_UInt / Enum13 / 6 / 8

Do not publish “48% extend Type_Safe”. It is true and it is misleading. Excluding tests and enums, essentially every class is in the Type_Safe lineage — the rule holds, and the naive percentage does not show it. A style guide that reports the flattering-looking number is doing the same thing in reverse.

What it buys: attributes are validated at construction, so a value that exists is a value that is valid. There is no separate validation layer, no schema-parse step at the boundary, and no isinstance checking scattered through the code. It is also why the absence of a type-checker matters less here than it would elsewhere — though not as little as it first appears. Q5: does runtime type safety replace static analysis, or defer it? →

4. Constrained primitives — the type is the validation

Rule 2 is zero raw primitives — no str, int, float, list or dict as attributes. One directory alone holds 27 of them, and the pattern is uniform:

sg_compute/primitives/Safe_Str__IP__Address.pyverbatim and entire · 01__python.md
# ═══════════════════════════════════════════════════════════════════════════════
# SG/Compute — Safe_Str__IP__Address
# IPv4 address string, e.g. "1.2.3.4". Empty = not yet assigned.
# ═══════════════════════════════════════════════════════════════════════════════

import re

from osbot_utils.type_safe.primitives.core.Safe_Str                         import Safe_Str
from osbot_utils.type_safe.primitives.core.enums.Enum__Safe_Str__Regex_Mode import Enum__Safe_Str__Regex_Mode


class Safe_Str__IP__Address(Safe_Str):
    max_length        = 45                                                   # covers IPv4 + IPv6
    regex             = re.compile(r'^[0-9a-fA-F.:]*$')
    regex_mode        = Enum__Safe_Str__Regex_Mode.MATCH
    strict_validation = True
    allow_empty       = True

Apache-2.0 — quoted from the estate's own source, not covered by this site's CC BY 4.0. Counted 2026-08-24.

Five class attributes, all aligned, and a trailing comment carrying the reasoning (# covers IPv4 + IPv6) — which is this estate's substitute for a docstring.

The domain vocabulary is visible in the file list itself: Safe_Str__AWS__Region, Safe_Str__Docker__Image, Safe_Str__Instance__Type, Safe_Str__Node__Name, Safe_Str__Pod__Name, Safe_Str__SSM__Path, Safe_Str__Spec__Id, Safe_Int__Port, Safe_Int__Max__Hours, Safe_Int__Exit__Code.

Naming a type is how a domain concept gets recorded.

A value of type Safe_Str__SSM__Path is a valid SSM path everywhere it appears, forever, without a single check written at a call site. And the name tells a reader — or a generator — what the value is, without a comment, a docstring or a lookup.

5. Layout and formatting

Banners. # ═ × 79, three content lines (product — class name — purpose), # ═ × 79. Present on 3,120 of 3,999 files across the repo, and on 992 of 992 class-defining files in the new tree — 100%.

Alignment. Schema attribute colons are 100% aligned. Import keywords are 39%321 of 817 files with two or more from X import Y lines have every import at a single column. That is the estate's least consistent formatting rule and the easiest to automate. Why alignment at all →

Comments. Rule 8 is inline comments only — no docstrings, ever, and compliance is 989/992 = 99.7%. Trailing comments carry the reasoning; the banner carries the purpose.

Private methods. Rule 9 is no underscore prefix for private methods, and compliance is 895/992 = 91% (97 violations). The JavaScript uses _private universally, against the same rule. Either the rule is Python-only and widely ignored, or it is being ignored in two languages, and the rule set does not say which. Q2 →

6. Testing

Four rules, quoted in full on the rules page. The first is the strongest opinion in the whole set:

No mocks. No patches.

The alternative is real in-memory composition, which is only affordable because Type_Safe objects are cheap to build. The type system and the testing philosophy are the same decision. 169 classes extend TestCase, so tests are class-based, and 4,785 tests run in 81 seconds — which is the evidence that the no-mocks position is affordable rather than aspirational.

Whether it generalises outside an estate with a cheap in-memory composition path is a genuinely open question, and this site does not assume it does. Q6 →

7. Responsibility boundaries — rules that name a single owner

Rules 16 to 19 are the most transferable thing in the rule set. Each names exactly one owner for a capability:

Rule 16 is described as being enforced by a CI guard that fails the build if a raw browser.new_context( appears outside Page__Factory. That is the model: a boundary rule with a test behind it. The other three have no such test — and the guard behind rule 16 is not listed in the rule set's own table of CI guards, which is a discrepancy this site can flag but not resolve. What is claimed versus what is listed →

8. The dependencies

The osbot-* family, all Apache-2.0, all under the owasp-sbot GitHub organisation: osbot-utils (the source of Type_Safe and Safe_Str, 885 mentions across the corpus), osbot-aws, osbot-fast-api, osbot-fast-api-serverless, memory_fs, mgraph-db, mgraph-ai-service-cache.

Two hard rules govern their use — never use Pydantic, no Literals, and never use boto3 directly, with a narrow documented exception for a Lambda Function URL two-statement permission fix.

Note that the exception is documented rather than silent. That is a convention in itself and worth naming: a banned thing with one written carve-out beats a banned thing with quiet violations, because the first is a rule you can enforce and the second is a rule you can only argue about. Rule 21's registry carve-out is the same shape.