DocmaxV3 3.0.0a7__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.
- docmaxv3-3.0.0a7/LICENSE +21 -0
- docmaxv3-3.0.0a7/PKG-INFO +196 -0
- docmaxv3-3.0.0a7/README.md +132 -0
- docmaxv3-3.0.0a7/pyproject.toml +240 -0
- docmaxv3-3.0.0a7/setup.cfg +4 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/PKG-INFO +196 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/SOURCES.txt +37 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/dependency_links.txt +1 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/entry_points.txt +2 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/requires.txt +43 -0
- docmaxv3-3.0.0a7/src/DocmaxV3.egg-info/top_level.txt +1 -0
- docmaxv3-3.0.0a7/src/docmax/__init__.py +23 -0
- docmaxv3-3.0.0a7/src/docmax/cli/__init__.py +8 -0
- docmaxv3-3.0.0a7/src/docmax/cli/main.py +99 -0
- docmaxv3-3.0.0a7/src/docmax/cli/render.py +58 -0
- docmaxv3-3.0.0a7/src/docmax/cloud_client/__init__.py +35 -0
- docmaxv3-3.0.0a7/src/docmax/cloud_client/client.py +343 -0
- docmaxv3-3.0.0a7/src/docmax/cloud_client/config.py +84 -0
- docmaxv3-3.0.0a7/src/docmax/cloud_client/errors.py +95 -0
- docmaxv3-3.0.0a7/src/docmax/cloud_client/models.py +189 -0
- docmaxv3-3.0.0a7/src/docmax/core/__init__.py +12 -0
- docmaxv3-3.0.0a7/src/docmax/core/atomic.py +282 -0
- docmaxv3-3.0.0a7/src/docmax/core/branding.py +53 -0
- docmaxv3-3.0.0a7/src/docmax/core/cancellation.py +235 -0
- docmaxv3-3.0.0a7/src/docmax/core/errors.py +472 -0
- docmaxv3-3.0.0a7/src/docmax/core/models.py +174 -0
- docmaxv3-3.0.0a7/src/docmax/core/protocols.py +107 -0
- docmaxv3-3.0.0a7/src/docmax/core/registry.py +248 -0
- docmaxv3-3.0.0a7/src/docmax/py.typed +0 -0
- docmaxv3-3.0.0a7/src/docmax/tools/__init__.py +15 -0
- docmaxv3-3.0.0a7/src/docmax/tools/merge/__init__.py +9 -0
- docmaxv3-3.0.0a7/src/docmax/tools/merge/local.py +80 -0
- docmaxv3-3.0.0a7/src/docmax/tools/merge/tool.py +35 -0
- docmaxv3-3.0.0a7/src/docmax/tools/merge/validators.py +63 -0
- docmaxv3-3.0.0a7/src/docmax/tools/ocr/__init__.py +10 -0
- docmaxv3-3.0.0a7/src/docmax/tools/ocr/cloud.py +78 -0
- docmaxv3-3.0.0a7/src/docmax/tools/ocr/local.py +96 -0
- docmaxv3-3.0.0a7/src/docmax/tools/ocr/tool.py +44 -0
- docmaxv3-3.0.0a7/src/docmax/tools/ocr/validators.py +37 -0
docmaxv3-3.0.0a7/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Punith Naidu
|
|
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.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DocmaxV3
|
|
3
|
+
Version: 3.0.0a7
|
|
4
|
+
Summary: Terminal-native document toolkit. Local-first, dual-engine, no server required.
|
|
5
|
+
Author: Punith Naidu
|
|
6
|
+
Maintainer-email: Punith Naidu <punithmedaramitta@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/megabyte44/docmax
|
|
9
|
+
Project-URL: Repository, https://github.com/megabyte44/docmax
|
|
10
|
+
Project-URL: Issues, https://github.com/megabyte44/docmax/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/megabyte44/docmax/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: pdf,ocr,document,cli,tui,terminal,conversion,batch
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Office/Business
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: typer>=0.12.0
|
|
28
|
+
Requires-Dist: rich>=13.7.0
|
|
29
|
+
Requires-Dist: pypdf>=4.2.0
|
|
30
|
+
Requires-Dist: platformdirs>=4.2.0
|
|
31
|
+
Requires-Dist: httpx>=0.27.0
|
|
32
|
+
Provides-Extra: ocr
|
|
33
|
+
Requires-Dist: pytesseract>=0.3.10; extra == "ocr"
|
|
34
|
+
Requires-Dist: pdf2image>=1.17.0; extra == "ocr"
|
|
35
|
+
Requires-Dist: opencv-python-headless>=4.9.0; extra == "ocr"
|
|
36
|
+
Requires-Dist: numpy>=1.26.0; extra == "ocr"
|
|
37
|
+
Provides-Extra: tables
|
|
38
|
+
Requires-Dist: pdfplumber>=0.11.0; extra == "tables"
|
|
39
|
+
Requires-Dist: pandas>=2.2.0; extra == "tables"
|
|
40
|
+
Requires-Dist: openpyxl>=3.1.0; extra == "tables"
|
|
41
|
+
Provides-Extra: images
|
|
42
|
+
Requires-Dist: Pillow>=10.3.0; extra == "images"
|
|
43
|
+
Requires-Dist: img2pdf>=0.5.0; extra == "images"
|
|
44
|
+
Provides-Extra: tui
|
|
45
|
+
Requires-Dist: textual>=0.60.0; extra == "tui"
|
|
46
|
+
Provides-Extra: server
|
|
47
|
+
Requires-Dist: DocmaxV3[all]; extra == "server"
|
|
48
|
+
Requires-Dist: fastapi>=0.111.0; extra == "server"
|
|
49
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == "server"
|
|
50
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "server"
|
|
51
|
+
Provides-Extra: all
|
|
52
|
+
Requires-Dist: DocmaxV3[images,ocr,tables,tui]; extra == "all"
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
|
55
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
56
|
+
Requires-Dist: pytest-timeout>=2.3.0; extra == "dev"
|
|
57
|
+
Requires-Dist: hypothesis>=6.100.0; extra == "dev"
|
|
58
|
+
Requires-Dist: respx>=0.21.0; extra == "dev"
|
|
59
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
60
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
61
|
+
Requires-Dist: import-linter>=2.0; extra == "dev"
|
|
62
|
+
Requires-Dist: pre-commit>=3.7.0; extra == "dev"
|
|
63
|
+
Dynamic: license-file
|
|
64
|
+
|
|
65
|
+
# DocMax
|
|
66
|
+
|
|
67
|
+
**A document toolkit that lives in your terminal.** Merge, split, OCR, compress,
|
|
68
|
+
convert, redact — locally, privately, with no server to run and no browser tab
|
|
69
|
+
to open.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install DocmaxV3
|
|
73
|
+
docmax merge a.pdf b.pdf -o combined.pdf
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
> **Status: early development (M0).** The architecture and safety mechanisms are
|
|
77
|
+
> in place; the tools are being rebuilt on top of them one at a time. For a
|
|
78
|
+
> working tool today, use [`docmax` 2.x](https://pypi.org/project/docmax/).
|
|
79
|
+
> See [the roadmap](#roadmap) for what lands when.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Why another PDF tool
|
|
84
|
+
|
|
85
|
+
The good self-hosted options — Stirling PDF and friends — are excellent, and
|
|
86
|
+
they all assume a browser. That means Docker, a running server, a port, and no
|
|
87
|
+
sensible way to use them over SSH or from a script.
|
|
88
|
+
|
|
89
|
+
DocMax assumes a terminal instead.
|
|
90
|
+
|
|
91
|
+
| | DocMax | Self-hosted web tools |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| Install | `pip install DocmaxV3` | Docker + a container |
|
|
94
|
+
| Interface | CLI and TUI | browser |
|
|
95
|
+
| Over SSH | works | needs port forwarding |
|
|
96
|
+
| Scripting | argv | HTTP against a running server |
|
|
97
|
+
| Your documents | stay on your machine | stay on your machine |
|
|
98
|
+
|
|
99
|
+
## Two engines, one interface
|
|
100
|
+
|
|
101
|
+
Every operation can run two ways, and the choice is yours per tool:
|
|
102
|
+
|
|
103
|
+
- **Local** — offline and private. Needs the relevant dependencies installed.
|
|
104
|
+
- **Cloud** — no local install at all. For the handful of tools whose
|
|
105
|
+
dependencies are genuinely painful.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
docmax ocr scan.pdf # picks whichever is available
|
|
109
|
+
docmax ocr scan.pdf --engine local # force local
|
|
110
|
+
docmax ocr scan.pdf --engine cloud # skip installing Tesseract
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Cloud exists for exactly one reason — to let you use a tool without installing
|
|
114
|
+
its heavy dependencies. Only five tools have it (`ocr`, `compress`, `convert`,
|
|
115
|
+
`pdfa`, `remove-bg`), because for a pure-Python operation like `merge`,
|
|
116
|
+
uploading your document would be slower, less private, and pointless.
|
|
117
|
+
|
|
118
|
+
**Nothing is ever uploaded without asking.** Consent is per-tool and remembered;
|
|
119
|
+
`offline = true` in your config disables cloud entirely regardless of flags; and
|
|
120
|
+
every upload tells you what it is sending before it sends it. The cloud endpoint
|
|
121
|
+
is configurable, so you can point DocMax at your own server instead.
|
|
122
|
+
|
|
123
|
+
## Your files are safe
|
|
124
|
+
|
|
125
|
+
This is the part most tools get wrong, so it is worth being specific.
|
|
126
|
+
|
|
127
|
+
- **Atomic writes.** Output goes to a temp file, gets validated, and is only then
|
|
128
|
+
swapped into place. A crash or Ctrl-C mid-operation leaves your destination
|
|
129
|
+
either untouched or absent — never half-written.
|
|
130
|
+
- **Your input is never the output.** `docmax merge a.pdf b.pdf -o a.pdf` is
|
|
131
|
+
refused, not silently obeyed.
|
|
132
|
+
- **Nothing is overwritten by accident.** Existing files need `--force`.
|
|
133
|
+
- **No tracebacks.** Every anticipated failure gives you a plain message and the
|
|
134
|
+
next step to take.
|
|
135
|
+
|
|
136
|
+
These are enforced by tests that run on every commit across Linux, macOS, and
|
|
137
|
+
Windows — not by good intentions. See
|
|
138
|
+
[architecture.md](docs/architecture.md#the-structural-guarantees).
|
|
139
|
+
|
|
140
|
+
## Install
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pip install DocmaxV3 # the shell and the cloud client
|
|
144
|
+
pip install "DocmaxV3[ocr]" # local OCR
|
|
145
|
+
pip install "DocmaxV3[all]" # everything
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The base install is deliberately small. Heavy dependencies arrive only when you
|
|
149
|
+
first ask for a local engine that needs them.
|
|
150
|
+
|
|
151
|
+
Some local engines also need external programs (Ghostscript, Tesseract, Pandoc,
|
|
152
|
+
Poppler):
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
docmax doctor # what's installed, what's missing, what needs it
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Roadmap
|
|
159
|
+
|
|
160
|
+
| | | |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| **M0** | Foundation — architecture, CI, safety mechanisms | ✅ done |
|
|
163
|
+
| **M1** | Core engine + `merge` as the reference implementation | in progress |
|
|
164
|
+
| **M2** | `split`, `rotate`, `reorder`, `pages`, `metadata`, `sanitize`, `get-info` | |
|
|
165
|
+
| **M3** | `compress` + a real `setup` / `doctor` | |
|
|
166
|
+
| **M4** | `watermark`, `stamp`, `protect`, `unlock`, `permissions` | |
|
|
167
|
+
| **M5** | `convert`, `to-images`, `from-images` | |
|
|
168
|
+
| **M6** | Cloud engines, `--json` everywhere, published benchmarks | |
|
|
169
|
+
| **M7** | Textual TUI + visual pickers for crop and reorder | |
|
|
170
|
+
| **M8** | OCR, done properly | |
|
|
171
|
+
| **M9** | Pipelines, resumable batch, folder watch | |
|
|
172
|
+
| **M10** | Local MCP server — drive DocMax from an AI agent, nothing leaves your machine | |
|
|
173
|
+
|
|
174
|
+
Benchmarks will be published in `benchmarks/` with real hardware and
|
|
175
|
+
methodology. No numbers appear in this README until they are measured.
|
|
176
|
+
|
|
177
|
+
## Contributing
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
git clone https://github.com/megabyte44/docmax
|
|
181
|
+
cd docmax
|
|
182
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
183
|
+
pre-commit install
|
|
184
|
+
|
|
185
|
+
pytest && ruff check . && mypy && lint-imports
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Start with [docs/architecture.md](docs/architecture.md) and the
|
|
189
|
+
[ADRs](docs/adr/) — they explain the constraints, most of which exist for a
|
|
190
|
+
specific reason.
|
|
191
|
+
|
|
192
|
+
## Licence
|
|
193
|
+
|
|
194
|
+
MIT. Every document operation is free and always will be — see
|
|
195
|
+
[ADR 0004](docs/adr/0004-open-core-boundary.md) for where the open-core line
|
|
196
|
+
sits and why.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# DocMax
|
|
2
|
+
|
|
3
|
+
**A document toolkit that lives in your terminal.** Merge, split, OCR, compress,
|
|
4
|
+
convert, redact — locally, privately, with no server to run and no browser tab
|
|
5
|
+
to open.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install DocmaxV3
|
|
9
|
+
docmax merge a.pdf b.pdf -o combined.pdf
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> **Status: early development (M0).** The architecture and safety mechanisms are
|
|
13
|
+
> in place; the tools are being rebuilt on top of them one at a time. For a
|
|
14
|
+
> working tool today, use [`docmax` 2.x](https://pypi.org/project/docmax/).
|
|
15
|
+
> See [the roadmap](#roadmap) for what lands when.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why another PDF tool
|
|
20
|
+
|
|
21
|
+
The good self-hosted options — Stirling PDF and friends — are excellent, and
|
|
22
|
+
they all assume a browser. That means Docker, a running server, a port, and no
|
|
23
|
+
sensible way to use them over SSH or from a script.
|
|
24
|
+
|
|
25
|
+
DocMax assumes a terminal instead.
|
|
26
|
+
|
|
27
|
+
| | DocMax | Self-hosted web tools |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| Install | `pip install DocmaxV3` | Docker + a container |
|
|
30
|
+
| Interface | CLI and TUI | browser |
|
|
31
|
+
| Over SSH | works | needs port forwarding |
|
|
32
|
+
| Scripting | argv | HTTP against a running server |
|
|
33
|
+
| Your documents | stay on your machine | stay on your machine |
|
|
34
|
+
|
|
35
|
+
## Two engines, one interface
|
|
36
|
+
|
|
37
|
+
Every operation can run two ways, and the choice is yours per tool:
|
|
38
|
+
|
|
39
|
+
- **Local** — offline and private. Needs the relevant dependencies installed.
|
|
40
|
+
- **Cloud** — no local install at all. For the handful of tools whose
|
|
41
|
+
dependencies are genuinely painful.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
docmax ocr scan.pdf # picks whichever is available
|
|
45
|
+
docmax ocr scan.pdf --engine local # force local
|
|
46
|
+
docmax ocr scan.pdf --engine cloud # skip installing Tesseract
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Cloud exists for exactly one reason — to let you use a tool without installing
|
|
50
|
+
its heavy dependencies. Only five tools have it (`ocr`, `compress`, `convert`,
|
|
51
|
+
`pdfa`, `remove-bg`), because for a pure-Python operation like `merge`,
|
|
52
|
+
uploading your document would be slower, less private, and pointless.
|
|
53
|
+
|
|
54
|
+
**Nothing is ever uploaded without asking.** Consent is per-tool and remembered;
|
|
55
|
+
`offline = true` in your config disables cloud entirely regardless of flags; and
|
|
56
|
+
every upload tells you what it is sending before it sends it. The cloud endpoint
|
|
57
|
+
is configurable, so you can point DocMax at your own server instead.
|
|
58
|
+
|
|
59
|
+
## Your files are safe
|
|
60
|
+
|
|
61
|
+
This is the part most tools get wrong, so it is worth being specific.
|
|
62
|
+
|
|
63
|
+
- **Atomic writes.** Output goes to a temp file, gets validated, and is only then
|
|
64
|
+
swapped into place. A crash or Ctrl-C mid-operation leaves your destination
|
|
65
|
+
either untouched or absent — never half-written.
|
|
66
|
+
- **Your input is never the output.** `docmax merge a.pdf b.pdf -o a.pdf` is
|
|
67
|
+
refused, not silently obeyed.
|
|
68
|
+
- **Nothing is overwritten by accident.** Existing files need `--force`.
|
|
69
|
+
- **No tracebacks.** Every anticipated failure gives you a plain message and the
|
|
70
|
+
next step to take.
|
|
71
|
+
|
|
72
|
+
These are enforced by tests that run on every commit across Linux, macOS, and
|
|
73
|
+
Windows — not by good intentions. See
|
|
74
|
+
[architecture.md](docs/architecture.md#the-structural-guarantees).
|
|
75
|
+
|
|
76
|
+
## Install
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install DocmaxV3 # the shell and the cloud client
|
|
80
|
+
pip install "DocmaxV3[ocr]" # local OCR
|
|
81
|
+
pip install "DocmaxV3[all]" # everything
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The base install is deliberately small. Heavy dependencies arrive only when you
|
|
85
|
+
first ask for a local engine that needs them.
|
|
86
|
+
|
|
87
|
+
Some local engines also need external programs (Ghostscript, Tesseract, Pandoc,
|
|
88
|
+
Poppler):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
docmax doctor # what's installed, what's missing, what needs it
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Roadmap
|
|
95
|
+
|
|
96
|
+
| | | |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| **M0** | Foundation — architecture, CI, safety mechanisms | ✅ done |
|
|
99
|
+
| **M1** | Core engine + `merge` as the reference implementation | in progress |
|
|
100
|
+
| **M2** | `split`, `rotate`, `reorder`, `pages`, `metadata`, `sanitize`, `get-info` | |
|
|
101
|
+
| **M3** | `compress` + a real `setup` / `doctor` | |
|
|
102
|
+
| **M4** | `watermark`, `stamp`, `protect`, `unlock`, `permissions` | |
|
|
103
|
+
| **M5** | `convert`, `to-images`, `from-images` | |
|
|
104
|
+
| **M6** | Cloud engines, `--json` everywhere, published benchmarks | |
|
|
105
|
+
| **M7** | Textual TUI + visual pickers for crop and reorder | |
|
|
106
|
+
| **M8** | OCR, done properly | |
|
|
107
|
+
| **M9** | Pipelines, resumable batch, folder watch | |
|
|
108
|
+
| **M10** | Local MCP server — drive DocMax from an AI agent, nothing leaves your machine | |
|
|
109
|
+
|
|
110
|
+
Benchmarks will be published in `benchmarks/` with real hardware and
|
|
111
|
+
methodology. No numbers appear in this README until they are measured.
|
|
112
|
+
|
|
113
|
+
## Contributing
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git clone https://github.com/megabyte44/docmax
|
|
117
|
+
cd docmax
|
|
118
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
119
|
+
pre-commit install
|
|
120
|
+
|
|
121
|
+
pytest && ruff check . && mypy && lint-imports
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Start with [docs/architecture.md](docs/architecture.md) and the
|
|
125
|
+
[ADRs](docs/adr/) — they explain the constraints, most of which exist for a
|
|
126
|
+
specific reason.
|
|
127
|
+
|
|
128
|
+
## Licence
|
|
129
|
+
|
|
130
|
+
MIT. Every document operation is free and always will be — see
|
|
131
|
+
[ADR 0004](docs/adr/0004-open-core-boundary.md) for where the open-core line
|
|
132
|
+
sits and why.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
# ============================================================================
|
|
6
|
+
# NOTE: product identity also lives in src/docmax/core/branding.py.
|
|
7
|
+
# tests/hygiene/test_branding.py asserts the two stay in sync and that brand
|
|
8
|
+
# literals do not leak into other modules.
|
|
9
|
+
# ============================================================================
|
|
10
|
+
|
|
11
|
+
[project]
|
|
12
|
+
name = "DocmaxV3"
|
|
13
|
+
version = "3.0.0a7"
|
|
14
|
+
description = "Terminal-native document toolkit. Local-first, dual-engine, no server required."
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.11"
|
|
17
|
+
authors = [{ name = "Punith Naidu" }]
|
|
18
|
+
maintainers = [{ name = "Punith Naidu", email = "punithmedaramitta@gmail.com" }]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICENSE"]
|
|
21
|
+
keywords = ["pdf", "ocr", "document", "cli", "tui", "terminal", "conversion", "batch"]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 3 - Alpha",
|
|
24
|
+
"Environment :: Console",
|
|
25
|
+
"Intended Audience :: End Users/Desktop",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Topic :: Office/Business",
|
|
32
|
+
"Topic :: Utilities",
|
|
33
|
+
"Typing :: Typed",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Base install stays light — non-negotiable #3.
|
|
37
|
+
# Everything here is pure Python and installs in seconds on every platform.
|
|
38
|
+
# Heavy deps (OpenCV, Tesseract bindings, ML models) live in extras and are
|
|
39
|
+
# imported lazily, never at module scope. See tests/hygiene/test_no_heavy_imports.py
|
|
40
|
+
dependencies = [
|
|
41
|
+
"typer>=0.12.0",
|
|
42
|
+
"rich>=13.7.0",
|
|
43
|
+
"pypdf>=4.2.0",
|
|
44
|
+
"platformdirs>=4.2.0",
|
|
45
|
+
"httpx>=0.27.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
# Each extra maps to the local engine of a specific tool group.
|
|
50
|
+
# `docmax setup` installs the external *binaries*; these are the Python bindings.
|
|
51
|
+
ocr = [
|
|
52
|
+
"pytesseract>=0.3.10",
|
|
53
|
+
"pdf2image>=1.17.0",
|
|
54
|
+
"opencv-python-headless>=4.9.0",
|
|
55
|
+
"numpy>=1.26.0",
|
|
56
|
+
]
|
|
57
|
+
tables = [
|
|
58
|
+
"pdfplumber>=0.11.0",
|
|
59
|
+
"pandas>=2.2.0",
|
|
60
|
+
"openpyxl>=3.1.0",
|
|
61
|
+
]
|
|
62
|
+
images = [
|
|
63
|
+
"Pillow>=10.3.0",
|
|
64
|
+
"img2pdf>=0.5.0",
|
|
65
|
+
]
|
|
66
|
+
tui = [
|
|
67
|
+
"textual>=0.60.0",
|
|
68
|
+
]
|
|
69
|
+
# Dependencies for the reference API server — the half a self-hoster deploys.
|
|
70
|
+
# Not in `all`: installing DocMax should never install a web framework by
|
|
71
|
+
# accident. Note this extra installs what the server *needs*, not the server
|
|
72
|
+
# itself: its code is excluded from the wheel (see packages.find below) and is
|
|
73
|
+
# run from a checkout.
|
|
74
|
+
server = [
|
|
75
|
+
"DocmaxV3[all]",
|
|
76
|
+
"fastapi>=0.111.0",
|
|
77
|
+
"uvicorn[standard]>=0.30.0",
|
|
78
|
+
"python-multipart>=0.0.9",
|
|
79
|
+
]
|
|
80
|
+
# v2 shipped a `full` extra that silently omitted opencv, which broke the entire
|
|
81
|
+
# OCR path for anyone who installed it. Self-referencing extras make that class
|
|
82
|
+
# of mistake impossible — there is only one list.
|
|
83
|
+
#
|
|
84
|
+
# Must match [project].name exactly: "docmax" is already taken on PyPI by the
|
|
85
|
+
# v2 package under this same author, so "docmax[...]" here would silently
|
|
86
|
+
# resolve to that external package instead of self-referencing, shadowing the
|
|
87
|
+
# local editable install with v2's incompatible module layout.
|
|
88
|
+
all = [
|
|
89
|
+
"DocmaxV3[ocr,tables,images,tui]",
|
|
90
|
+
]
|
|
91
|
+
dev = [
|
|
92
|
+
"pytest>=8.2.0",
|
|
93
|
+
"pytest-cov>=5.0.0",
|
|
94
|
+
"pytest-timeout>=2.3.0",
|
|
95
|
+
"hypothesis>=6.100.0",
|
|
96
|
+
"respx>=0.21.0",
|
|
97
|
+
"ruff>=0.5.0",
|
|
98
|
+
"mypy>=1.10.0",
|
|
99
|
+
"import-linter>=2.0",
|
|
100
|
+
"pre-commit>=3.7.0",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[project.scripts]
|
|
104
|
+
docmax = "docmax.cli.main:app"
|
|
105
|
+
|
|
106
|
+
[project.urls]
|
|
107
|
+
Homepage = "https://github.com/megabyte44/docmax"
|
|
108
|
+
Repository = "https://github.com/megabyte44/docmax"
|
|
109
|
+
Issues = "https://github.com/megabyte44/docmax/issues"
|
|
110
|
+
Changelog = "https://github.com/megabyte44/docmax/blob/main/CHANGELOG.md"
|
|
111
|
+
|
|
112
|
+
[tool.setuptools.packages.find]
|
|
113
|
+
where = ["src"]
|
|
114
|
+
# The server is the half a self-hoster deploys from a checkout, inside an image
|
|
115
|
+
# that also carries Ghostscript, Tesseract, and Pandoc — none of which pip can
|
|
116
|
+
# install. So it has no reason to be in the wheel, and one reason not to be:
|
|
117
|
+
# every byte here lands in the site-packages of someone who only wanted to merge
|
|
118
|
+
# two PDFs. tests/hygiene/test_wheel_excludes_server.py holds the line.
|
|
119
|
+
exclude = ["docmax.server*"]
|
|
120
|
+
|
|
121
|
+
[tool.setuptools.package-data]
|
|
122
|
+
docmax = ["py.typed"]
|
|
123
|
+
|
|
124
|
+
# ============================================================================
|
|
125
|
+
# Ruff
|
|
126
|
+
# ============================================================================
|
|
127
|
+
[tool.ruff]
|
|
128
|
+
target-version = "py311"
|
|
129
|
+
line-length = 100
|
|
130
|
+
src = ["src", "tests"]
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint]
|
|
133
|
+
select = [
|
|
134
|
+
"E", "W", # pycodestyle
|
|
135
|
+
"F", # pyflakes
|
|
136
|
+
"I", # isort
|
|
137
|
+
"N", # pep8-naming
|
|
138
|
+
"UP", # pyupgrade
|
|
139
|
+
"B", # bugbear
|
|
140
|
+
"A", # builtin shadowing
|
|
141
|
+
"C4", # comprehensions
|
|
142
|
+
"PT", # pytest style
|
|
143
|
+
"SIM", # simplify
|
|
144
|
+
"RET", # return consistency
|
|
145
|
+
"PTH", # use pathlib, not os.path
|
|
146
|
+
"S", # bandit — this project shells out; we want these loud
|
|
147
|
+
"T20", # no stray print() in library code
|
|
148
|
+
"RUF",
|
|
149
|
+
]
|
|
150
|
+
ignore = [
|
|
151
|
+
"E501", # line length is handled by the formatter
|
|
152
|
+
"S101", # assert is fine in tests
|
|
153
|
+
"RUF012", # ClassVar annotations on mutable defaults — noisy for our error classes
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
[tool.ruff.lint.per-file-ignores]
|
|
157
|
+
# Tests may assert, use fixtures loosely, and print.
|
|
158
|
+
"tests/**" = ["S", "T20", "ARG", "PLR2004"]
|
|
159
|
+
# The CLI layer is the one place that legitimately writes to stdout.
|
|
160
|
+
"src/docmax/cli/**" = ["T20"]
|
|
161
|
+
# FastAPI's dependency-injection idiom is a call in an argument default, which
|
|
162
|
+
# B008 exists to catch. It is correct everywhere else.
|
|
163
|
+
"src/docmax/server/**" = ["B008"]
|
|
164
|
+
|
|
165
|
+
[tool.ruff.lint.isort]
|
|
166
|
+
known-first-party = ["docmax"]
|
|
167
|
+
|
|
168
|
+
# ============================================================================
|
|
169
|
+
# Mypy — strict. There is no legacy code to grandfather in.
|
|
170
|
+
# ============================================================================
|
|
171
|
+
[tool.mypy]
|
|
172
|
+
python_version = "3.11"
|
|
173
|
+
strict = true
|
|
174
|
+
warn_unreachable = true
|
|
175
|
+
warn_no_return = true
|
|
176
|
+
show_error_codes = true
|
|
177
|
+
pretty = true
|
|
178
|
+
files = ["src", "tests"]
|
|
179
|
+
|
|
180
|
+
# Third-party packages without type stubs. Kept as a short, explicit list so
|
|
181
|
+
# that adding one is a visible decision rather than a blanket ignore.
|
|
182
|
+
[[tool.mypy.overrides]]
|
|
183
|
+
module = [
|
|
184
|
+
"pytesseract.*",
|
|
185
|
+
"pdf2image.*",
|
|
186
|
+
"cv2.*",
|
|
187
|
+
"img2pdf.*",
|
|
188
|
+
"pdfplumber.*",
|
|
189
|
+
"rembg.*",
|
|
190
|
+
]
|
|
191
|
+
ignore_missing_imports = true
|
|
192
|
+
|
|
193
|
+
# The server extra is not part of `dev`: the lint job type-checks the terminal
|
|
194
|
+
# tool, and a self-hosted API server should not be able to slow that down or
|
|
195
|
+
# break it. These two overrides are what that costs.
|
|
196
|
+
[[tool.mypy.overrides]]
|
|
197
|
+
module = [
|
|
198
|
+
"fastapi.*",
|
|
199
|
+
"starlette.*",
|
|
200
|
+
"uvicorn.*",
|
|
201
|
+
]
|
|
202
|
+
ignore_missing_imports = true
|
|
203
|
+
|
|
204
|
+
[[tool.mypy.overrides]]
|
|
205
|
+
module = ["docmax.server.*"]
|
|
206
|
+
# FastAPI's route decorators are untyped when the framework itself is absent.
|
|
207
|
+
disallow_untyped_decorators = false
|
|
208
|
+
|
|
209
|
+
# ============================================================================
|
|
210
|
+
# Pytest
|
|
211
|
+
# ============================================================================
|
|
212
|
+
[tool.pytest.ini_options]
|
|
213
|
+
minversion = "8.0"
|
|
214
|
+
testpaths = ["tests"]
|
|
215
|
+
addopts = [
|
|
216
|
+
"--strict-markers",
|
|
217
|
+
"--strict-config",
|
|
218
|
+
"-ra",
|
|
219
|
+
]
|
|
220
|
+
# No test may hang. Property tests feed malformed bytes to parsers; a hang is a
|
|
221
|
+
# failure mode we explicitly assert against.
|
|
222
|
+
timeout = 120
|
|
223
|
+
markers = [
|
|
224
|
+
"needs_binary(name): requires an external binary (gs, tesseract, pandoc, pdftoppm). Skipped when absent locally, REQUIRED in CI.",
|
|
225
|
+
"golden: golden-file test against the generated fixture corpus.",
|
|
226
|
+
"property: hypothesis-based fuzz test.",
|
|
227
|
+
"slow: excluded from the default run via -m 'not slow'.",
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
[tool.coverage.run]
|
|
231
|
+
source = ["docmax"]
|
|
232
|
+
branch = true
|
|
233
|
+
|
|
234
|
+
[tool.coverage.report]
|
|
235
|
+
exclude_lines = [
|
|
236
|
+
"pragma: no cover",
|
|
237
|
+
"if TYPE_CHECKING:",
|
|
238
|
+
"raise NotImplementedError",
|
|
239
|
+
"@overload",
|
|
240
|
+
]
|