xbrlkit 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. xbrlkit-0.2.0/.gitignore +144 -0
  2. xbrlkit-0.2.0/LICENSE +21 -0
  3. xbrlkit-0.2.0/PKG-INFO +161 -0
  4. xbrlkit-0.2.0/README.md +113 -0
  5. xbrlkit-0.2.0/pyproject.toml +100 -0
  6. xbrlkit-0.2.0/pytest.ini +21 -0
  7. xbrlkit-0.2.0/ruff.toml +47 -0
  8. xbrlkit-0.2.0/tests/__init__.py +1 -0
  9. xbrlkit-0.2.0/tests/conftest.py +13 -0
  10. xbrlkit-0.2.0/tests/test_config.py +51 -0
  11. xbrlkit-0.2.0/tests/test_edgar.py +104 -0
  12. xbrlkit-0.2.0/tests/test_parity.py +129 -0
  13. xbrlkit-0.2.0/tests/test_parse.py +248 -0
  14. xbrlkit-0.2.0/tests/test_query.py +140 -0
  15. xbrlkit-0.2.0/tests/test_serialize.py +474 -0
  16. xbrlkit-0.2.0/tests/test_tavi.py +405 -0
  17. xbrlkit-0.2.0/xbrlkit/__init__.py +24 -0
  18. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/__init__.py +3 -0
  19. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/__init__.py +308 -0
  20. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/README.md +39 -0
  21. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/extractTestcase.sh +2 -0
  22. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/extractTestcase.xsl +109 -0
  23. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/runIxtSecTests.sh +16 -0
  24. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/saxon9.jar +0 -0
  25. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/testcase.xml +7117 -0
  26. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/tests.xml +848 -0
  27. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/conf/tests.xsd +50 -0
  28. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/text2num.py +110 -0
  29. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-boolballotbox.xml +66 -0
  30. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-countrynameen.xml +65 -0
  31. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-datequarterend.xml +66 -0
  32. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-durday.xml +61 -0
  33. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-durhour.xml +69 -0
  34. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-durmonth.xml +71 -0
  35. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-durweek.xml +70 -0
  36. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-durwordsen.xml +56 -0
  37. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-duryear.xml +64 -0
  38. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-edgarprovcountryen.xml +65 -0
  39. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-entityfilercategoryen.xml +63 -0
  40. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-exchnameen.xml +86 -0
  41. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-numwordsen.xml +55 -0
  42. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-stateprovnameen.xml +64 -0
  43. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/ixt-sec-yesnoballotbox.xml +66 -0
  44. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/registry/transform-registry.xml +314 -0
  45. xbrlkit-0.2.0/xbrlkit/_vendor/arelle_plugins/EDGAR/transform/transformationRegistry/schema/inlinexbrl-sec-transformation.xsd +418 -0
  46. xbrlkit-0.2.0/xbrlkit/_vendor/ontology/v1/context.jsonld +397 -0
  47. xbrlkit-0.2.0/xbrlkit/_vendor/ontology/v1/shapes.ttl +115 -0
  48. xbrlkit-0.2.0/xbrlkit/cli.py +261 -0
  49. xbrlkit-0.2.0/xbrlkit/config.py +50 -0
  50. xbrlkit-0.2.0/xbrlkit/edgar/__init__.py +13 -0
  51. xbrlkit-0.2.0/xbrlkit/edgar/client.py +186 -0
  52. xbrlkit-0.2.0/xbrlkit/edgar/download.py +102 -0
  53. xbrlkit-0.2.0/xbrlkit/edgar/rate_limit.py +33 -0
  54. xbrlkit-0.2.0/xbrlkit/model.py +190 -0
  55. xbrlkit-0.2.0/xbrlkit/namespaces.py +62 -0
  56. xbrlkit-0.2.0/xbrlkit/parse/__init__.py +18 -0
  57. xbrlkit-0.2.0/xbrlkit/parse/arelle_load.py +145 -0
  58. xbrlkit-0.2.0/xbrlkit/parse/ids.py +67 -0
  59. xbrlkit-0.2.0/xbrlkit/parse/to_model.py +571 -0
  60. xbrlkit-0.2.0/xbrlkit/py.typed +1 -0
  61. xbrlkit-0.2.0/xbrlkit/query.py +139 -0
  62. xbrlkit-0.2.0/xbrlkit/serialize/__init__.py +24 -0
  63. xbrlkit-0.2.0/xbrlkit/serialize/_kernel/__init__.py +0 -0
  64. xbrlkit-0.2.0/xbrlkit/serialize/_kernel/bundle.py +322 -0
  65. xbrlkit-0.2.0/xbrlkit/serialize/_kernel/context.py +286 -0
  66. xbrlkit-0.2.0/xbrlkit/serialize/_kernel/holon.py +213 -0
  67. xbrlkit-0.2.0/xbrlkit/serialize/_kernel/jsonld.py +627 -0
  68. xbrlkit-0.2.0/xbrlkit/serialize/classify.py +87 -0
  69. xbrlkit-0.2.0/xbrlkit/serialize/graph.py +526 -0
  70. xbrlkit-0.2.0/xbrlkit/serialize/holon.py +26 -0
  71. xbrlkit-0.2.0/xbrlkit/serialize/tavi.py +855 -0
@@ -0,0 +1,144 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ scripts/
22
+ var/
23
+ src/
24
+ wheels/
25
+ pip-wheel-metadata/
26
+ share/python-wheels/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+ MANIFEST
31
+ data/
32
+ data/input/
33
+ data/output/
34
+ notebooks/
35
+ uv.lock
36
+
37
+ # PyInstaller
38
+ # Usually these files are written by a python script from a template
39
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
40
+ *.manifest
41
+ *.spec
42
+
43
+ # Installer logs
44
+ pip-log.txt
45
+ pip-delete-this-directory.txt
46
+
47
+ # Unit test / coverage reports
48
+ htmlcov/
49
+ .tox/
50
+ .nox/
51
+ .coverage
52
+ .coverage.*
53
+ .cache
54
+ nosetests.xml
55
+ coverage.xml
56
+ *.cover
57
+ .hypothesis/
58
+ .pytest_cache/
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+
69
+ # Flask stuff:
70
+ instance/
71
+ .webassets-cache
72
+
73
+ # Scrapy stuff:
74
+ .scrapy
75
+
76
+ # Sphinx documentation
77
+ docs/_build/
78
+
79
+ # PyBuilder
80
+ target/
81
+
82
+ # Jupyter Notebook
83
+ .ipynb_checkpoints
84
+
85
+ # IPython
86
+ profile_default/
87
+ ipython_config.py
88
+
89
+ # pipenv
90
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
91
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
92
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
93
+ # install all needed dependencies.
94
+ Dockerfile
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # Environments
100
+ .env
101
+ .env*
102
+ !.env.example
103
+ .venv
104
+ env/
105
+ venv/
106
+ ENV/
107
+ env.bak/
108
+ venv.bak/
109
+
110
+
111
+ # Spyder project settings
112
+ .spyderproject
113
+ .spyproject
114
+
115
+ # Rope project settings
116
+ .ropeproject
117
+
118
+ # mkdocs documentation
119
+ /site
120
+
121
+ # mypy
122
+ .mypy_cache/
123
+ .dmypy.json
124
+ dmypy.json
125
+
126
+ # Pyre type checker
127
+ .pyre/
128
+
129
+ # MacOS
130
+ .DS_STORE
131
+
132
+ # Node.js
133
+ node_modules/
134
+ package-lock.json
135
+
136
+ # npm package files
137
+ *.tgz
138
+
139
+ # AI - Claude Code
140
+ .claude/settings.local.json
141
+ CLAUDE.local.md
142
+
143
+ .gemini/
144
+ GEMINI.md
xbrlkit-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RFS LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
xbrlkit-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: xbrlkit
3
+ Version: 0.2.0
4
+ Summary: Work with XBRL filings above Arelle: one parse, portable models
5
+ Project-URL: Repository, https://github.com/RoboFinSystems/xbrlkit
6
+ Project-URL: Homepage, https://github.com/RoboFinSystems/xbrlkit#readme
7
+ Project-URL: Bug Tracker, https://github.com/RoboFinSystems/xbrlkit/issues
8
+ Author: RFS LLC
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: arelle,edgar,financial,holon,jsonld,oim,rdf,sec,tavi,xbrl
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Framework :: Pydantic
15
+ Classifier: Framework :: Pydantic :: 2
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Financial and Insurance Industry
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Natural Language :: English
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Topic :: Office/Business :: Financial
27
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Topic :: Utilities
30
+ Classifier: Typing :: Typed
31
+ Requires-Python: >=3.10
32
+ Requires-Dist: arelle-release
33
+ Requires-Dist: httpx>=0.28
34
+ Requires-Dist: pydantic>=2.11
35
+ Requires-Dist: pyshacl
36
+ Requires-Dist: python-dotenv>=1.0
37
+ Requires-Dist: rdflib
38
+ Requires-Dist: regex
39
+ Requires-Dist: requests>=2.31
40
+ Provides-Extra: dev
41
+ Requires-Dist: basedpyright>=1.21; extra == 'dev'
42
+ Requires-Dist: build>=1.0; extra == 'dev'
43
+ Requires-Dist: pytest-asyncio>=0.26; extra == 'dev'
44
+ Requires-Dist: pytest>=8.3; extra == 'dev'
45
+ Requires-Dist: ruff>=0.12; extra == 'dev'
46
+ Requires-Dist: twine>=5.0; extra == 'dev'
47
+ Description-Content-Type: text/markdown
48
+
49
+ # xbrlkit
50
+
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
52
+
53
+ Work with XBRL filings above [Arelle](https://arelle.org): fetch a filing, parse
54
+ it **once** into a neutral typed model, and project that model into whichever
55
+ portable representation you need.
56
+
57
+ ```
58
+ EDGAR ──▶ Arelle ──▶ XbrlModel ──┬──▶ holon.jsonld (RDF / JSON-LD)
59
+ ├──▶ Tavi (compiled model)
60
+ └──▶ …
61
+ ```
62
+
63
+ Arelle stays the parser — nobody should reimplement DTS resolution. What it does
64
+ not give you is anything ergonomic to *hold*: `ModelXbrl` is a large mutable
65
+ object graph tied to a controller you have to close. `XbrlModel` is the answer to
66
+ that — stateless, single-filing, lossless, and the waist every projection hangs
67
+ off.
68
+
69
+ **The one architectural rule:** everything goes through `XbrlModel`. A feature
70
+ that reaches into Arelle's `ModelXbrl` directly is bypassing the waist, and that
71
+ is the change that turns a kit into a junk drawer.
72
+
73
+ ## Projections
74
+
75
+ | Target | Status | Notes |
76
+ | --- | --- | --- |
77
+ | **holon** (`.holon.jsonld`) | shipped | RDF/JSON-LD, renders in the [Holon Viewer](https://holon.robosystems.ai/) |
78
+ | **Tavi** (`.tavi.json`) | shipped | [Project Tavi](https://www.xbrl.org/Specification/tavi/PWD-2026-09-01/tavi-PWD-2026-09-01.html) compiled model, PWD-2026-09-01 |
79
+ | OIM (xBRL-JSON / xBRL-CSV) | planned | Arelle already emits these; a native writer is a fidelity check on `XbrlModel` |
80
+ | property graph / Parquet | planned | see `model.py` |
81
+
82
+ Tavi is a **public working draft** and its name is explicitly a working title,
83
+ so treat that projection as tracking a moving target. `--format tavi` also
84
+ writes a `.tavi.gaps.json` sidecar recording what the filing carries that the
85
+ model has nowhere to put — that file is the point of the projection, not a
86
+ by-product of it.
87
+
88
+ ## Install
89
+
90
+ ### As a package
91
+
92
+ ```bash
93
+ pip install xbrlkit
94
+ ```
95
+
96
+ Exposes the `xbrlkit` CLI (`xbrlkit build …`, `xbrlkit fetch …`, `xbrlkit query …`)
97
+ and the library — use this to consume it from another project. Set your SEC
98
+ User-Agent via the environment (see [SEC User-Agent](#sec-user-agent)).
99
+
100
+ ### From source (development)
101
+
102
+ ```bash
103
+ # Install the toolchain
104
+ brew install uv just
105
+
106
+ # Install dependencies and provision .env from the template
107
+ just install
108
+ ```
109
+
110
+ `just install` creates `.env` from `.env.example` on first run — then set your
111
+ SEC User-Agent in it.
112
+
113
+ ## SEC User-Agent
114
+
115
+ SEC EDGAR requires a descriptive `User-Agent` on every request, or it throttles
116
+ you (empty responses / HTTP 429). `just install` already created your `.env` —
117
+ set your details there:
118
+
119
+ ```bash
120
+ # .env
121
+ SEC_GOV_USER_AGENT="Your Name your@email.com"
122
+ ```
123
+
124
+ `.env` is loaded automatically by every command. Outside the `just` workflow,
125
+ `export SEC_GOV_USER_AGENT="Your Name your@email.com"` or pass `--user-agent`.
126
+
127
+ ## Usage
128
+
129
+ ```bash
130
+ # Build a holon.jsonld from a specific filing (-> ./output/)
131
+ xbrlkit build --cik 320193 --accno 0000320193-23-000106
132
+
133
+ # Fetch the latest filing for a ticker (-> ./output/)
134
+ xbrlkit fetch --ticker NVDA
135
+
136
+ # Query consolidated facts in a built holon (in-memory SPARQL)
137
+ xbrlkit query --in output/0000320193-23-000106.holon.jsonld --element us-gaap:Assets
138
+ ```
139
+
140
+ From a source checkout, `just` wraps the same CLI as a shorthand:
141
+ `just build 320193 0000320193-23-000106` and `just fetch NVDA`.
142
+
143
+ ## View & explore
144
+
145
+ Built holons render in the **RoboSystems Holon Viewer** — a browser-based reader
146
+ that renders the financial statements and lets you ask questions of the report
147
+ with AI:
148
+
149
+ - **Hosted:** <https://holon.robosystems.ai/> — open a `holon.jsonld` and explore
150
+ the statements, notes, and dimensional facts, or chat with the report.
151
+ - **Source:** <https://github.com/RoboFinSystems/robosystems-holon-viewer> — run
152
+ it locally or self-host.
153
+
154
+ The viewer reads a holon entirely client-side, so a single `holon.jsonld` is a
155
+ complete, portable, self-describing report.
156
+
157
+ ## License
158
+
159
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
160
+
161
+ MIT © 2026 RFS LLC
@@ -0,0 +1,113 @@
1
+ # xbrlkit
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ Work with XBRL filings above [Arelle](https://arelle.org): fetch a filing, parse
6
+ it **once** into a neutral typed model, and project that model into whichever
7
+ portable representation you need.
8
+
9
+ ```
10
+ EDGAR ──▶ Arelle ──▶ XbrlModel ──┬──▶ holon.jsonld (RDF / JSON-LD)
11
+ ├──▶ Tavi (compiled model)
12
+ └──▶ …
13
+ ```
14
+
15
+ Arelle stays the parser — nobody should reimplement DTS resolution. What it does
16
+ not give you is anything ergonomic to *hold*: `ModelXbrl` is a large mutable
17
+ object graph tied to a controller you have to close. `XbrlModel` is the answer to
18
+ that — stateless, single-filing, lossless, and the waist every projection hangs
19
+ off.
20
+
21
+ **The one architectural rule:** everything goes through `XbrlModel`. A feature
22
+ that reaches into Arelle's `ModelXbrl` directly is bypassing the waist, and that
23
+ is the change that turns a kit into a junk drawer.
24
+
25
+ ## Projections
26
+
27
+ | Target | Status | Notes |
28
+ | --- | --- | --- |
29
+ | **holon** (`.holon.jsonld`) | shipped | RDF/JSON-LD, renders in the [Holon Viewer](https://holon.robosystems.ai/) |
30
+ | **Tavi** (`.tavi.json`) | shipped | [Project Tavi](https://www.xbrl.org/Specification/tavi/PWD-2026-09-01/tavi-PWD-2026-09-01.html) compiled model, PWD-2026-09-01 |
31
+ | OIM (xBRL-JSON / xBRL-CSV) | planned | Arelle already emits these; a native writer is a fidelity check on `XbrlModel` |
32
+ | property graph / Parquet | planned | see `model.py` |
33
+
34
+ Tavi is a **public working draft** and its name is explicitly a working title,
35
+ so treat that projection as tracking a moving target. `--format tavi` also
36
+ writes a `.tavi.gaps.json` sidecar recording what the filing carries that the
37
+ model has nowhere to put — that file is the point of the projection, not a
38
+ by-product of it.
39
+
40
+ ## Install
41
+
42
+ ### As a package
43
+
44
+ ```bash
45
+ pip install xbrlkit
46
+ ```
47
+
48
+ Exposes the `xbrlkit` CLI (`xbrlkit build …`, `xbrlkit fetch …`, `xbrlkit query …`)
49
+ and the library — use this to consume it from another project. Set your SEC
50
+ User-Agent via the environment (see [SEC User-Agent](#sec-user-agent)).
51
+
52
+ ### From source (development)
53
+
54
+ ```bash
55
+ # Install the toolchain
56
+ brew install uv just
57
+
58
+ # Install dependencies and provision .env from the template
59
+ just install
60
+ ```
61
+
62
+ `just install` creates `.env` from `.env.example` on first run — then set your
63
+ SEC User-Agent in it.
64
+
65
+ ## SEC User-Agent
66
+
67
+ SEC EDGAR requires a descriptive `User-Agent` on every request, or it throttles
68
+ you (empty responses / HTTP 429). `just install` already created your `.env` —
69
+ set your details there:
70
+
71
+ ```bash
72
+ # .env
73
+ SEC_GOV_USER_AGENT="Your Name your@email.com"
74
+ ```
75
+
76
+ `.env` is loaded automatically by every command. Outside the `just` workflow,
77
+ `export SEC_GOV_USER_AGENT="Your Name your@email.com"` or pass `--user-agent`.
78
+
79
+ ## Usage
80
+
81
+ ```bash
82
+ # Build a holon.jsonld from a specific filing (-> ./output/)
83
+ xbrlkit build --cik 320193 --accno 0000320193-23-000106
84
+
85
+ # Fetch the latest filing for a ticker (-> ./output/)
86
+ xbrlkit fetch --ticker NVDA
87
+
88
+ # Query consolidated facts in a built holon (in-memory SPARQL)
89
+ xbrlkit query --in output/0000320193-23-000106.holon.jsonld --element us-gaap:Assets
90
+ ```
91
+
92
+ From a source checkout, `just` wraps the same CLI as a shorthand:
93
+ `just build 320193 0000320193-23-000106` and `just fetch NVDA`.
94
+
95
+ ## View & explore
96
+
97
+ Built holons render in the **RoboSystems Holon Viewer** — a browser-based reader
98
+ that renders the financial statements and lets you ask questions of the report
99
+ with AI:
100
+
101
+ - **Hosted:** <https://holon.robosystems.ai/> — open a `holon.jsonld` and explore
102
+ the statements, notes, and dimensional facts, or chat with the report.
103
+ - **Source:** <https://github.com/RoboFinSystems/robosystems-holon-viewer> — run
104
+ it locally or self-host.
105
+
106
+ The viewer reads a holon entirely client-side, so a single `holon.jsonld` is a
107
+ complete, portable, self-describing report.
108
+
109
+ ## License
110
+
111
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
112
+
113
+ MIT © 2026 RFS LLC
@@ -0,0 +1,100 @@
1
+ [project]
2
+ name = "xbrlkit"
3
+ version = "0.2.0"
4
+ description = "Work with XBRL filings above Arelle: one parse, portable models"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ authors = [
8
+ {name = "RFS LLC"},
9
+ ]
10
+ license = {text = "MIT"}
11
+ keywords = ["xbrl", "sec", "edgar", "financial", "arelle", "tavi", "oim", "holon", "jsonld", "rdf"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Environment :: Console",
15
+ "Framework :: Pydantic",
16
+ "Framework :: Pydantic :: 2",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Financial and Insurance Industry",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Natural Language :: English",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Office/Business :: Financial",
28
+ "Topic :: Office/Business :: Financial :: Accounting",
29
+ "Topic :: Software Development :: Libraries :: Python Modules",
30
+ "Topic :: Utilities",
31
+ "Typing :: Typed",
32
+ ]
33
+ dependencies = [
34
+ "arelle-release",
35
+ "httpx>=0.28",
36
+ "pydantic>=2.11",
37
+ "python-dotenv>=1.0",
38
+ "rdflib",
39
+ "pyshacl",
40
+ "requests>=2.31",
41
+ # Required by the vendored EDGAR ixt-sec transform registry
42
+ # (_vendor/arelle_plugins/EDGAR/transform).
43
+ "regex",
44
+ ]
45
+
46
+ [project.optional-dependencies]
47
+ dev = [
48
+ "basedpyright>=1.21",
49
+ "ruff>=0.12",
50
+ "pytest>=8.3",
51
+ "pytest-asyncio>=0.26",
52
+ "build>=1.0",
53
+ "twine>=5.0",
54
+ ]
55
+
56
+ [project.scripts]
57
+ xbrlkit = "xbrlkit.cli:main"
58
+
59
+ [project.urls]
60
+ Repository = "https://github.com/RoboFinSystems/xbrlkit"
61
+ Homepage = "https://github.com/RoboFinSystems/xbrlkit#readme"
62
+ "Bug Tracker" = "https://github.com/RoboFinSystems/xbrlkit/issues"
63
+
64
+ [build-system]
65
+ requires = ["hatchling"]
66
+ build-backend = "hatchling.build"
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = ["xbrlkit"]
70
+
71
+ # Keep internal dev tooling out of the sdist uploaded to PyPI. Hatchling
72
+ # defaults to "everything not in .gitignore", which would drag along
73
+ # `.claude/`, `.vscode/`, `.githooks/`, and other repo-local config. The
74
+ # wheel target above is unaffected — it only includes the package dir.
75
+ [tool.hatch.build.targets.sdist]
76
+ include = [
77
+ "/xbrlkit",
78
+ "/tests",
79
+ "/README.md",
80
+ "/LICENSE",
81
+ "/pyproject.toml",
82
+ "/pytest.ini",
83
+ "/ruff.toml",
84
+ ]
85
+
86
+ [tool.basedpyright]
87
+ include = ["xbrlkit"]
88
+ exclude = ["tests", "xbrlkit/serialize/_kernel", "xbrlkit/_vendor"]
89
+ extraPaths = ["."]
90
+ pythonVersion = "3.13"
91
+ venvPath = "."
92
+ venv = ".venv"
93
+ typeCheckingMode = "standard"
94
+ reportAttributeAccessIssue = "none"
95
+ reportArgumentType = "none"
96
+ reportGeneralTypeIssues = "none"
97
+ reportOptionalMemberAccess = "none"
98
+ reportReturnType = "none"
99
+ reportInvalidTypeForm = "none"
100
+ reportMissingImports = "none"
@@ -0,0 +1,21 @@
1
+ [pytest]
2
+ testpaths = tests
3
+ python_files = test_*.py
4
+ python_classes = Test*
5
+ python_functions = test_*
6
+
7
+ markers =
8
+ integration: marks integration tests
9
+ unit: marks unit tests
10
+ asyncio: marks tests that use asyncio
11
+
12
+ # Configure test output
13
+ console_output_style = progress
14
+ log_cli = true
15
+ log_cli_level = INFO
16
+ log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
17
+ log_cli_date_format = %Y-%m-%d %H:%M:%S
18
+
19
+ # Configure asyncio
20
+ asyncio_mode = strict
21
+ asyncio_default_fixture_loop_scope = function
@@ -0,0 +1,47 @@
1
+ exclude = [
2
+ ".bzr",
3
+ ".direnv",
4
+ ".eggs",
5
+ ".git",
6
+ ".git-rewrite",
7
+ ".hg",
8
+ ".ipynb_checkpoints",
9
+ ".mypy_cache",
10
+ ".nox",
11
+ ".pants.d",
12
+ ".pyenv",
13
+ ".pytest_cache",
14
+ ".pytype",
15
+ ".ruff_cache",
16
+ ".svn",
17
+ ".tox",
18
+ ".venv",
19
+ ".vscode",
20
+ "__pypackages__",
21
+ "_build",
22
+ "buck-out",
23
+ "build",
24
+ "dist",
25
+ "node_modules",
26
+ "site-packages",
27
+ "venv",
28
+ "xbrlkit/serialize/_kernel",
29
+ "xbrlkit/_vendor",
30
+ ]
31
+
32
+ line-length = 88
33
+ indent-width = 2
34
+ target-version = "py311"
35
+
36
+ [lint]
37
+ select = ["E4", "E7", "E9", "F"]
38
+ ignore = []
39
+ fixable = ["ALL"]
40
+ unfixable = []
41
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
42
+
43
+ [format]
44
+ quote-style = "double"
45
+ indent-style = "space"
46
+ skip-magic-trailing-comma = false
47
+ line-ending = "auto"
@@ -0,0 +1 @@
1
+ """Test package."""
@@ -0,0 +1,13 @@
1
+ """Shared pytest fixtures for the xbrlkit test suite."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+
8
+ @pytest.fixture
9
+ def sample_output_dir(tmp_path: Path) -> Path:
10
+ """Return a temporary directory for tests that write output artifacts."""
11
+ out = tmp_path / "output"
12
+ out.mkdir()
13
+ return out
@@ -0,0 +1,51 @@
1
+ """The SEC User-Agent / ``.env`` config contract.
2
+
3
+ The CLI calls ``load_dotenv()`` at startup and then builds a fresh ``Config``,
4
+ so a ``SEC_GOV_USER_AGENT`` set in a local ``.env`` reaches the EDGAR client.
5
+ These tests exercise that env -> ``Config`` path without any network access.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+
13
+ from dotenv import load_dotenv
14
+
15
+ from xbrlkit.config import DEFAULT_USER_AGENT, Config
16
+
17
+
18
+ def test_env_user_agent_overrides_default(monkeypatch):
19
+ monkeypatch.setenv("SEC_GOV_USER_AGENT", "Acme Corp ops@acme.com")
20
+ cfg = Config()
21
+ assert cfg.user_agent == "Acme Corp ops@acme.com"
22
+ assert cfg.headers["User-Agent"] == "Acme Corp ops@acme.com"
23
+
24
+
25
+ def test_default_user_agent_when_unset(monkeypatch):
26
+ monkeypatch.delenv("SEC_GOV_USER_AGENT", raising=False)
27
+ assert Config().user_agent == DEFAULT_USER_AGENT
28
+
29
+
30
+ def test_dotenv_file_populates_config(tmp_path):
31
+ """A ``.env`` loaded via ``load_dotenv`` flows into a fresh ``Config``."""
32
+ # load_dotenv mutates os.environ directly, so save/restore around it rather
33
+ # than relying on monkeypatch (which can't unwind that external mutation).
34
+ original = os.environ.pop("SEC_GOV_USER_AGENT", None)
35
+ try:
36
+ env = tmp_path / ".env"
37
+ env.write_text('SEC_GOV_USER_AGENT="Dotenv User dev@example.com"\n')
38
+ load_dotenv(dotenv_path=env, override=True)
39
+ assert Config().user_agent == "Dotenv User dev@example.com"
40
+ finally:
41
+ if original is None:
42
+ os.environ.pop("SEC_GOV_USER_AGENT", None)
43
+ else:
44
+ os.environ["SEC_GOV_USER_AGENT"] = original
45
+
46
+
47
+ def test_env_example_template_is_tracked_and_documents_user_agent():
48
+ """The tracked template must survive the ``.env*`` gitignore un-ignore."""
49
+ example = Path(__file__).resolve().parent.parent / ".env.example"
50
+ assert example.exists()
51
+ assert "SEC_GOV_USER_AGENT" in example.read_text()