Repository guide · architecture · v1
simonw / datasette · Architecture
A 333-file, Python-primary repository organized around a datasette package (135 files) with tests, docs, and demos. Static analysis recorded 5 entrypoints and 41 bounded paths; the one itemized in-repo path runs datasette/__main__.py to datasette/cli.py [cli], while app.py-rooted paths end at unresolved boundaries. Module roles are inferred from static call degree, not runtime behavior.
- Original repository
- simonw/datasette
- License
- Apache-2.0 · open-source license
- Analyzed revision · last verified
- b338c6f5f6b39dd3e0a341431f071d8cecb0d12b ·
Newer revision observed; parts of this guide may be outdated. The default branch moved to cec5e6b2ef5d, checked 2026-10-03. The whole guide describes revision b338c6f5f6b3. A revision diff found changes in 1 code region it cites (listed below); statements about that region may not hold at the new revision and have not been rechecked yet.
- datasette/app.py:168 · call site on the execution path changed
simonw/datasette architecture: shape, control flow, and limits
Terms used on this page
- Entrypoint record
- A file the analyzer marks as a place where execution can start. When it carries a __main__ guard excerpt, the file contains an if __name__ == "__main__": block and the excerpt shows what that block calls; without an excerpt, the file was flagged by its name only.
- Bounded static execution path
- A call chain reconstructed from the source code without running it, stopped after a fixed number of steps. It shows how far the code can be followed on paper, not what happens at run time.
- Unresolved boundary
- Where a static path stops because the next call goes into an external library or cannot be resolved without running the code. It marks the edge of this analysis, not a defect in the repository.
- Static relations
- Calls and imports found in the source. "External or unresolved" relations point outside the analyzed files.
- Module role
- The analyzer's label for a file, inferred from how many calls go in and out (for example core, entry/orchestration, leaf). It describes a position in the call graph, not the authors' design.
- Test-like files
- Files whose names or locations look like tests. This analysis counts them; it does not run them.
- Observed · inferred · author-claimed · unresolved
- How each statement is supported: read directly from the analyzed files; derived by the analyzer from them; stated by the repository's authors (metadata, README); or not established by this analysis.
- Verified
- Two uses on these pages. In an analyzer record ("verified provenance", "a verified entrypoint") it means the record was read directly from the analyzed files, which these guides call observed; for an entrypoint, the __main__ guard text is present. Since the analyzer fix of 2026-10-04, a file that only has an entrypoint-like name such as cli.py or main.py is recorded as inferred; guides analyzed before that still call such a file verified, and it is still only a guess about how the file is used. It does not mean the code was run or tested. "Last verified" is the date the guide last passed the lab's checks against the analysis record of the stated revision; it is not a review of the repository itself.
Shape at a glance
The analyzed inventory holds 333 files: datasette 135, tests 102, docs 50, demos 7, .github 17, and 22 at the repository root. The analyzer classifies the top-level directories datasette, docs, and tests as likely subsystems. The primary language is Python, and repository topics include asgi, sqlite, json, csv, and docker. Inside the datasette package, datasette/app.py, datasette/cli.py, and datasette/__main__.py are flagged as likely entrypoints. Role records reference the subdirectories datasette/views, datasette/utils, and datasette/static, so view, utility, and static-asset code appears to live there.
Entrypoints and control flow
Three files contain __main__ execution guards: datasette/__main__.py calls cli(), tests/fixtures.py calls cli(), and tests/build_small_spatialite_db.py calls generate_it to create spatialite.db. Two further entry candidates, datasette/app.py and datasette/cli.py, are flagged by filename heuristic rather than an execution guard. The reconstruction counts 5 entrypoints, 4,537 resolved static call relations, and 41 bounded execution paths, of which this packet itemizes 10. Of the 10 listed execution paths, exactly one stays inside the repository: datasette/__main__.py [__main__] calls datasette/cli.py [cli], terminating because cli is a leaf. None of the 10 listed execution paths is truncated, and none contains a cycle. The other nine listed paths all start at datasette/app.py and each ends after one step at an unresolved external boundary: Path, logging.getLogger, contextvars.ContextVar, collections.namedtuple, or Setting (the last five times).
Core modules and inferred roles
Role labels in this section are the analyzer's inference from static resolved call degree and entrypoint membership, not runtime observations; paired numbers are incoming/outgoing static call counts. Ranked by incoming calls, the leading role records are datasette/static/edit-tools.js (808/808), datasette/utils/__init__.py (519/53), datasette/utils/asgi.py (369/21), datasette/app.py (356/197), and datasette/__init__.py (348/0). Two records carry the entry/orchestration label: datasette/app.py and tests/fixtures.py (87 in / 23 out). Four records are leaf/data-boundary candidates with zero outgoing calls: datasette/__init__.py (348 in), datasette/resources.py (227 in), datasette/permissions.py (114 in), and datasette/plugins.py (108 in). Six records cover datasette/views modules — table.py (46/283), query_helpers.py (126/82), database.py (12/153), table_create_alter.py (42/94), special.py (28/82), stored_queries.py (11/99) — all service/core candidates. Three test modules hold service/core role records: tests/test_api_write.py (123/158), tests/test_queries.py (18/114), and tests/test_plugins.py (20/108). Other service/core candidates include datasette/static/table.js (83/83) and datasette/utils/sqlite.py (109/30).
Where static paths stop
Every app.py-rooted listed path stops at a module-level external target rather than repository code: Path, logging.getLogger, contextvars.ContextVar, collections.namedtuple, or one of five Setting references. By resolution status, 15,850 of 21,338 static relations are external or unresolved and 5,488 are local. Dynamic dispatch, reflection, dynamic imports, runtime dependency injection, monkey-patching, generated code, and framework wiring are not resolved, so calls through them act as boundaries. Relation extraction is capped at 2,500 relations per file and 150,000 per repository, so large files can under-report their edges.
Dependencies between parts
The analyzer recorded 21,338 static relations: 19,775 calls and 1,563 imports, split into 5,488 local and 15,850 external or unresolved. The packet's six dependency records come from package.json: runtime @codemirror/lang-sql ^6.3.3, codemirror ^6.0.1, rollup ^3.30.0, @rollup/plugin-node-resolve ^15.0.1, and @rollup/plugin-terser ^0.1.0, plus dev prettier ^3.0.0. Dependency parsing covers requirements-style manifests only; pyproject.toml dependency tables are not parsed, so the Python dependency set is not established. With 15,850 relations external or unresolved, this packet does not establish a complete module-to-module dependency map for the Python package.
What static analysis cannot show
Runtime wiring, reflection, dynamic imports, monkey-patching, generated code, framework runtime wiring, and dynamic dispatch are not resolved, so runtime connections between components are not shown. Install, run, and build inference is partial and installation commands are not verified; this asset therefore states no install or run commands. Bounded analysis inventories but does not parse files over 400 kB, minified bundles, and vendored directories, and relation extraction stops at 2,500 per file and 150,000 per repository. The packet names two skipped files, datasette/static/cm-editor-6.0.1.bundle.js and datasette/static/sql-formatter-2.3.3.min.js; their contents are not established. README-derived text is author_claimed at best and line-based extraction may capture code lines, so no README text is used as evidence in this asset. The inventory counts 102 test-like files, but what behavior they enforce at runtime is not established by this static packet.
Suggested reading order
Start with package.json and pyproject.toml, the packet's two manifest files, plus README.md; all three are classified as project-level important files. Continue with datasette/cli.py and datasette/app.py, both flagged as likely entrypoints, then datasette/__main__.py, whose guard roots the one listed in-repository path. Finish at the unresolved boundary targets — for example the Setting and logging.getLogger stops on the app.py paths — before changing code.
What this analysis could not establish
The writing model's own notes on the analysis record. Identifiers such as lim_3, exec_* or claim_… name records of that analysis; the claims table cites the same records.
- The boundaries_static entries for datasette/cli.py [install] (click.UsageError, list, run_module), tests/build_small_spatialite_db.py (sqlite3.connect and connection calls), and tests/fixtures.py (os.path, tempfile, pathlib, str, textwrap) carry no evidence IDs in this packet, so this asset makes no claims about them.
- resolved_relation_summary_sample lists concrete Python imports and calls (for example datasette/app.py importing datasette/database.py:Database and datasette/plugins.py:pm) without evidence IDs, so per-module import statements are not asserted.
- The reconstruction reports 41 bounded execution paths but only 10 are itemized; the other 31 are undescribed in this packet.
- Teaching claim claim_4d30ade3e21e states that no bounded static execution path is available, which conflicts with the 10 listed exec_* records; it was not used.
- The analyzer capability profile describes JavaScript analysis as bounded_heuristic, so role numbers for .js files (for example role_44, role_46) rest on weaker evidence than the Python ones.
- relation_counts reports 5,488 local relations while the reconstruction headline says 4,537 resolved static call relations; the packet does not explain the difference, so each figure is cited only to its own record.
- important_2 (readme.md) was excluded as a case-variant path not in the analyzed inventory; README.md (important_1) is used instead.
Claims and evidence — 35 claims, 35 supported by an independent verifier
Every substantive statement above is a claim bound to grounding IDs of the analyzed revision. Global grounding IDs are namespaced by the analysis run.
| Claim | Epistemic status | Verifier | Grounding (global IDs) |
|---|---|---|---|
| c1 The analyzed inventory holds 333 files: datasette 135, tests 102, docs 50, demos 7, .github 17, and 22 at the repository root. | inferred | supported | analysis_ef14c7222eb9f538:summary_repository |
| c2 The analyzer classifies the top-level directories datasette, docs, and tests as likely subsystems. | observed | supported | analysis_ef14c7222eb9f538:subsys_1, analysis_ef14c7222eb9f538:subsys_2, analysis_ef14c7222eb9f538:subsys_3 |
| c3 The primary language is Python, and repository topics include asgi, sqlite, json, csv, and docker. | observed | supported | analysis_ef14c7222eb9f538:meta_primary_language, analysis_ef14c7222eb9f538:meta_topics |
| c4 Inside the datasette package, datasette/app.py, datasette/cli.py, and datasette/__main__.py are flagged as likely entrypoints. | observed | supported | analysis_ef14c7222eb9f538:important_9, analysis_ef14c7222eb9f538:important_10, analysis_ef14c7222eb9f538:important_6 |
| c5 Role records reference the subdirectories datasette/views, datasette/utils, and datasette/static, so view, utility, and static-asset code appears to live there. | inferred | supported | analysis_ef14c7222eb9f538:role_73, analysis_ef14c7222eb9f538:role_52, analysis_ef14c7222eb9f538:role_44, analysis_ef14c7222eb9f538:role_46 |
| c6 Three files contain __main__ execution guards: datasette/__main__.py calls cli(), tests/fixtures.py calls cli(), and tests/build_small_spatialite_db.py calls generate_it to create spatialite.db. | observed | supported | analysis_ef14c7222eb9f538:py_entry_7cbf1557a33b, analysis_ef14c7222eb9f538:py_entry_d7853af71ceb, analysis_ef14c7222eb9f538:py_entry_f23c245ed3b2 |
| c7 Two further entry candidates, datasette/app.py and datasette/cli.py, are flagged by filename heuristic rather than an execution guard. | observed | supported | analysis_ef14c7222eb9f538:entry_1, analysis_ef14c7222eb9f538:entry_2 |
| c8 Of the 10 listed execution paths, exactly one stays inside the repository: datasette/__main__.py [__main__] calls datasette/cli.py [cli], terminating because cli is a leaf. | inferred | supported | analysis_ef14c7222eb9f538:exec_707222db7901, analysis_ef14c7222eb9f538:exec_bb193f5bffdb, analysis_ef14c7222eb9f538:exec_141532159c69, analysis_ef14c7222eb9f538:exec_6c1e96f0a6c1, analysis_ef14c7222eb9f538:exec_99dfaf114e63, analysis_ef14c7222eb9f538:exec_af5f4c46e06d, analysis_ef14c7222eb9f538:exec_2d38e0ddd5d3, analysis_ef14c7222eb9f538:exec_a33242c74a4c, analysis_ef14c7222eb9f538:exec_41785dbdb39c, analysis_ef14c7222eb9f538:exec_4a5bcac3dad4 |
| c9 The other nine listed paths all start at datasette/app.py and each ends after one step at an unresolved external boundary: Path, logging.getLogger, contextvars.ContextVar, collections.namedtuple, or Setting (the last five times). | inferred | supported | analysis_ef14c7222eb9f538:exec_bb193f5bffdb, analysis_ef14c7222eb9f538:exec_141532159c69, analysis_ef14c7222eb9f538:exec_6c1e96f0a6c1, analysis_ef14c7222eb9f538:exec_99dfaf114e63, analysis_ef14c7222eb9f538:exec_af5f4c46e06d, analysis_ef14c7222eb9f538:exec_2d38e0ddd5d3, analysis_ef14c7222eb9f538:exec_a33242c74a4c, analysis_ef14c7222eb9f538:exec_41785dbdb39c, analysis_ef14c7222eb9f538:exec_4a5bcac3dad4 |
| c10 None of the 10 listed execution paths is truncated, and none contains a cycle. | inferred | supported | analysis_ef14c7222eb9f538:exec_707222db7901, analysis_ef14c7222eb9f538:exec_bb193f5bffdb, analysis_ef14c7222eb9f538:exec_141532159c69, analysis_ef14c7222eb9f538:exec_6c1e96f0a6c1, analysis_ef14c7222eb9f538:exec_99dfaf114e63, analysis_ef14c7222eb9f538:exec_af5f4c46e06d, analysis_ef14c7222eb9f538:exec_2d38e0ddd5d3, analysis_ef14c7222eb9f538:exec_a33242c74a4c, analysis_ef14c7222eb9f538:exec_41785dbdb39c, analysis_ef14c7222eb9f538:exec_4a5bcac3dad4 |
| c11 The reconstruction counts 5 entrypoints, 4,537 resolved static call relations, and 41 bounded execution paths, of which this packet itemizes 10. | inferred | supported | analysis_ef14c7222eb9f538:architecture_reconstruction |
| c12 Role labels in this section are the analyzer's inference from static resolved call degree and entrypoint membership, not runtime observations; paired numbers are incoming/outgoing static call counts. | inferred | supported | analysis_ef14c7222eb9f538:role_44, analysis_ef14c7222eb9f538:role_52, analysis_ef14c7222eb9f538:role_5, analysis_ef14c7222eb9f538:role_54, analysis_ef14c7222eb9f538:role_1, analysis_ef14c7222eb9f538:role_73, analysis_ef14c7222eb9f538:role_92, analysis_ef14c7222eb9f538:role_41, analysis_ef14c7222eb9f538:role_69, analysis_ef14c7222eb9f538:role_46, analysis_ef14c7222eb9f538:role_66, analysis_ef14c7222eb9f538:role_62, analysis_ef14c7222eb9f538:role_74, analysis_ef14c7222eb9f538:role_137, analysis_ef14c7222eb9f538:role_133, analysis_ef14c7222eb9f538:role_35, analysis_ef14c7222eb9f538:role_71, analysis_ef14c7222eb9f538:role_72, analysis_ef14c7222eb9f538:role_82, analysis_ef14c7222eb9f538:role_36 |
| c13 Ranked by incoming calls, the leading role records are datasette/static/edit-tools.js (808/808), datasette/utils/__init__.py (519/53), datasette/utils/asgi.py (369/21), datasette/app.py (356/197), and datasette/__init__.py (348/0). | inferred | supported | analysis_ef14c7222eb9f538:role_44, analysis_ef14c7222eb9f538:role_52, analysis_ef14c7222eb9f538:role_5, analysis_ef14c7222eb9f538:role_54, analysis_ef14c7222eb9f538:role_1, analysis_ef14c7222eb9f538:role_73, analysis_ef14c7222eb9f538:role_92, analysis_ef14c7222eb9f538:role_41, analysis_ef14c7222eb9f538:role_69, analysis_ef14c7222eb9f538:role_46, analysis_ef14c7222eb9f538:role_66, analysis_ef14c7222eb9f538:role_62, analysis_ef14c7222eb9f538:role_74, analysis_ef14c7222eb9f538:role_137, analysis_ef14c7222eb9f538:role_133, analysis_ef14c7222eb9f538:role_35, analysis_ef14c7222eb9f538:role_71, analysis_ef14c7222eb9f538:role_72, analysis_ef14c7222eb9f538:role_82, analysis_ef14c7222eb9f538:role_36 |
| c14 Two records carry the entry/orchestration label: datasette/app.py and tests/fixtures.py (87 in / 23 out). | inferred | supported | analysis_ef14c7222eb9f538:role_5, analysis_ef14c7222eb9f538:role_82 |
| c15 Four records are leaf/data-boundary candidates with zero outgoing calls: datasette/__init__.py (348 in), datasette/resources.py (227 in), datasette/permissions.py (114 in), and datasette/plugins.py (108 in). | inferred | supported | analysis_ef14c7222eb9f538:role_1, analysis_ef14c7222eb9f538:role_41, analysis_ef14c7222eb9f538:role_35, analysis_ef14c7222eb9f538:role_36 |
| c16 Six records cover datasette/views modules — table.py (46/283), query_helpers.py (126/82), database.py (12/153), table_create_alter.py (42/94), special.py (28/82), stored_queries.py (11/99) — all service/core candidates. | inferred | supported | analysis_ef14c7222eb9f538:role_73, analysis_ef14c7222eb9f538:role_69, analysis_ef14c7222eb9f538:role_66, analysis_ef14c7222eb9f538:role_74, analysis_ef14c7222eb9f538:role_71, analysis_ef14c7222eb9f538:role_72 |
| c17 Three test modules hold service/core role records: tests/test_api_write.py (123/158), tests/test_queries.py (18/114), and tests/test_plugins.py (20/108). | inferred | supported | analysis_ef14c7222eb9f538:role_92, analysis_ef14c7222eb9f538:role_137, analysis_ef14c7222eb9f538:role_133 |
| c18 Other service/core candidates include datasette/static/table.js (83/83) and datasette/utils/sqlite.py (109/30). | inferred | supported | analysis_ef14c7222eb9f538:role_46, analysis_ef14c7222eb9f538:role_62 |
| c19 Every app.py-rooted listed path stops at a module-level external target rather than repository code: Path, logging.getLogger, contextvars.ContextVar, collections.namedtuple, or one of five Setting references. | inferred | supported | analysis_ef14c7222eb9f538:exec_bb193f5bffdb, analysis_ef14c7222eb9f538:exec_141532159c69, analysis_ef14c7222eb9f538:exec_6c1e96f0a6c1, analysis_ef14c7222eb9f538:exec_99dfaf114e63, analysis_ef14c7222eb9f538:exec_af5f4c46e06d, analysis_ef14c7222eb9f538:exec_2d38e0ddd5d3, analysis_ef14c7222eb9f538:exec_a33242c74a4c, analysis_ef14c7222eb9f538:exec_41785dbdb39c, analysis_ef14c7222eb9f538:exec_4a5bcac3dad4 |
| c20 By resolution status, 15,850 of 21,338 static relations are external or unresolved and 5,488 are local. | observed | supported | analysis_ef14c7222eb9f538:relation_counts |
| c21 Dynamic dispatch, reflection, dynamic imports, runtime dependency injection, monkey-patching, generated code, and framework wiring are not resolved, so calls through them act as boundaries. | observed | supported | analysis_ef14c7222eb9f538:lim_1 |
| c22 Relation extraction is capped at 2,500 relations per file and 150,000 per repository, so large files can under-report their edges. | observed | supported | analysis_ef14c7222eb9f538:lim_6 |
| c23 The analyzer recorded 21,338 static relations: 19,775 calls and 1,563 imports, split into 5,488 local and 15,850 external or unresolved. | observed | supported | analysis_ef14c7222eb9f538:relation_counts |
| c24 The packet's six dependency records come from package.json: runtime @codemirror/lang-sql ^6.3.3, codemirror ^6.0.1, rollup ^3.30.0, @rollup/plugin-node-resolve ^15.0.1, and @rollup/plugin-terser ^0.1.0, plus dev prettier ^3.0.0. | observed | supported | analysis_ef14c7222eb9f538:dep_1, analysis_ef14c7222eb9f538:dep_2, analysis_ef14c7222eb9f538:dep_3, analysis_ef14c7222eb9f538:dep_4, analysis_ef14c7222eb9f538:dep_5, analysis_ef14c7222eb9f538:dep_6 |
| c25 Dependency parsing covers requirements-style manifests only; pyproject.toml dependency tables are not parsed, so the Python dependency set is not established. | unresolved | supported | analysis_ef14c7222eb9f538:lim_3 |
| c26 With 15,850 relations external or unresolved, this packet does not establish a complete module-to-module dependency map for the Python package. | inferred | supported | analysis_ef14c7222eb9f538:relation_counts |
| c27 Runtime wiring, reflection, dynamic imports, monkey-patching, generated code, framework runtime wiring, and dynamic dispatch are not resolved, so runtime connections between components are not shown. | observed | supported | analysis_ef14c7222eb9f538:lim_1 |
| c28 Install, run, and build inference is partial and installation commands are not verified; this asset therefore states no install or run commands. | observed | supported | analysis_ef14c7222eb9f538:lim_2 |
| c29 Bounded analysis inventories but does not parse files over 400 kB, minified bundles, and vendored directories, and relation extraction stops at 2,500 per file and 150,000 per repository. | observed | supported | analysis_ef14c7222eb9f538:lim_6 |
| c30 The packet names two skipped files, datasette/static/cm-editor-6.0.1.bundle.js and datasette/static/sql-formatter-2.3.3.min.js; their contents are not established. | unresolved | supported | analysis_ef14c7222eb9f538:claim_e6126b089362, analysis_ef14c7222eb9f538:lim_6 |
| c31 README-derived text is author_claimed at best and line-based extraction may capture code lines, so no README text is used as evidence in this asset. | observed | supported | analysis_ef14c7222eb9f538:lim_4 |
| c32 The inventory counts 102 test-like files, but what behavior they enforce at runtime is not established by this static packet. | inferred | supported | analysis_ef14c7222eb9f538:summary_repository |
| c33 Start with package.json and pyproject.toml, the packet's two manifest files, plus README.md; all three are classified as project-level important files. | inferred | supported | analysis_ef14c7222eb9f538:important_1, analysis_ef14c7222eb9f538:important_3, analysis_ef14c7222eb9f538:important_4, analysis_ef14c7222eb9f538:summary_repository |
| c34 Continue with datasette/cli.py and datasette/app.py, both flagged as likely entrypoints, then datasette/__main__.py, whose guard roots the one listed in-repository path. | inferred | supported | analysis_ef14c7222eb9f538:important_10, analysis_ef14c7222eb9f538:important_9, analysis_ef14c7222eb9f538:important_6, analysis_ef14c7222eb9f538:py_entry_7cbf1557a33b, analysis_ef14c7222eb9f538:exec_707222db7901 |
| c35 Finish at the unresolved boundary targets — for example the Setting and logging.getLogger stops on the app.py paths — before changing code. | inferred | supported | analysis_ef14c7222eb9f538:exec_af5f4c46e06d, analysis_ef14c7222eb9f538:exec_141532159c69, analysis_ef14c7222eb9f538:claim_e56db45165bf |
Sources, rights and disclosure · attribution-license-templates/v0.1
Rights notice. Original repository hosted on GitHub. Repository source code, documentation, names, media, and related project materials remain subject to the rights of their respective authors, contributors, and other rights holders and to applicable repository license terms.
Platform notice. GitHub is the source hosting platform for the linked repository. GitHub and related marks are trademarks of GitHub, Inc. EVEMISS Technology is not affiliated with or endorsed by GitHub unless explicitly stated otherwise.
How this page is produced. This page was produced using revision-aware repository analysis and AI-assisted editorial tooling. Technical claims are tied to the analyzed repository revision and may be revalidated when the source repository changes.
AI-assisted analysis. Reviewed by EVEMISS Technology through human–AI collaborative review. · Report a rights concern · Back to the overview