Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
214 lines (201 loc) · 8.99 KB
/
Copy pathpyproject.toml
File metadata and controls
214 lines (201 loc) · 8.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pyrite"
version = "0.25.5"
description = "Knowledge-as-Code for you and your agents"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.11"
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Information Analysis",
]
keywords = ["knowledge-base", "knowledge-management", "mcp", "ai", "knowledge-graph", "agent-memory", "zettelkasten"]
dependencies = [
"pydantic>=2.11.0",
"python-dotenv>=1.0.1",
"PyYAML>=6.0.1",
"ruamel.yaml>=0.18.0",
"SQLAlchemy>=2.0.0",
# pyrite/server/mcp_server.py's build_sdk_server() uses the mcp SDK's
# 1.x low-level Server API (e.g. Server.list_tools() as a decorator),
# which the SDK's 2.0.0 release removed/renamed. An unbounded install
# picks up 2.0.0 and every MCP request 500s with
# AttributeError: 'Server' object has no attribute 'list_tools'.
#
# The floor is 1.3.0, not 1.0.0: build_sdk_server()'s read_resource
# adapter (#217) imports mcp.server.lowlevel.helper_types.ReadResourceContents
# and returns an Iterable of it, which is the shape
# Server.read_resource()'s decorator has expected since 1.3.0 (verified
# against PyPI's published wheels for 1.0.0 through 1.3.0rc1 -- the
# module and the class do not exist before 1.3.0rc1). An install pinned
# below that has no such module to import and resources/read fails at
# import time instead of at the call.
"mcp>=1.27.2,<2.0.0", # 1.27.2: SSE same-owner check (mcp_routes._session_owner)
]
[project.optional-dependencies]
server = [
# 0.132.0: strict_content_type (default on) -- a body without a JSON
# Content-Type is not parsed as JSON for a handler.
"fastapi>=0.132.0",
"uvicorn[standard]>=0.30.1",
"slowapi>=0.1.9",
# Used directly by pyrite/server/auth_rate_limit.py (slowapi depends on it too).
"limits>=3.1",
"httpx>=0.27.0",
"jinja2>=3.1.4",
"watchdog>=4.0.1",
"alembic>=1.13.0",
"markdownify>=0.14.1",
"bcrypt>=4.0.0",
]
cli = [
"typer[all]>=0.12.3",
"rich>=13.7.1",
]
ai = [
"openai>=1.30.0",
"anthropic>=0.30.0",
]
semantic = [
"sentence-transformers>=2.3.0", # trust_remote_code (embedding_service._load_model)
"sqlite-vec>=0.1.0",
]
postgres = [
"psycopg2-binary>=2.9.0",
"pgvector>=0.3.0",
]
dev = [
# Pinned exactly (#128): CI resolved pytest 9.1.1 on 3.12 and 9.0.2 on
# 3.13 from an unbounded `>=8.0.0` -- every worktree venv got 9.1.1, the
# main checkout 9.0.2. #81's `@classmethod` fixtures passed the
# one-interpreter PR gate under whichever version resolved there and
# broke `dev` twice on 3.13 (fixed in #129). All three are pure-Python
# wheels with no interpreter-specific build, so one version installs
# identically on 3.11/3.12/3.13; `tests/test_dev_process_config.py`
# (TestPinnedTestRunner) asserts the pin stays exact.
"pytest==9.1.1",
"pytest-cov==7.1.0",
"pytest-xdist==3.8.0",
# Diff coverage on the PR's 3.12 leg (advisory; see ci.yml's test job).
"diff-cover==10.6.0",
# Pinned exactly: tests/test_storage_invariants.py runs derandomized, and
# its strict xfails depend on the sequences this version generates.
"hypothesis==6.168.1",
"mypy>=1.9.0",
"ruff>=0.15.0,<0.16",
"build>=1.0.0",
"pre-commit>=3.6.0",
]
all = [
"pyrite[server,cli,ai,semantic,dev]",
]
[project.scripts]
pyrite = "pyrite.cli:main"
pyrite-read = "pyrite.read_cli:main"
pyrite-admin = "pyrite.admin_cli:main"
pyrite-server = "pyrite.server.api:main"
[project.urls]
Homepage = "https://github.com/pyrite-wiki/pyrite"
Documentation = "https://github.com/pyrite-wiki/pyrite#readme"
Repository = "https://github.com/pyrite-wiki/pyrite"
[tool.setuptools.packages.find]
where = ["."]
include = ["pyrite", "pyrite.*"]
[tool.pytest.ini_options]
testpaths = ["tests", "extensions"]
python_files = ["test_*.py"]
addopts = "-v --tb=short -m 'not slow and not e2e'"
markers = [
"integration: tests requiring full environment setup (DB, indexing, network)",
"api: REST API endpoint tests",
"mcp: MCP server protocol tests",
"cli: CLI command tests",
"slow: long-running performance tests (1000+ entries)",
# Smoke layer (ADR-0032 §3a "breadth"): starts a real `pyrite-server`
# subprocess or a real `pyrite mcp` stdio process and drives it as a
# client. Minutes, not seconds -- excluded from the default run (and so
# from the pre-push hook and the PR matrix) and gated on the push to dev
# in CI. Run it deliberately with `pytest tests/e2e -m e2e`.
"e2e: live-process smoke tests (real server subprocess, real client)",
"embeddings: needs write-time embedding on (loads the sentence-transformers model)",
# The local smoke set scripts/test-affected always runs (#356): storage,
# the KB service, read scoping, the REST app factory, MCP dispatch and the
# CLI entry point. Keep it under a minute at -n 4.
"core: always run by scripts/test-affected, whatever the branch changed",
# verify-red (scripts/verify_red_ci.py): a test the PR adds on purpose to
# pass without the fix too (a "still works" guard), so it is not warned about.
# It must say why: reason=... or the test's docstring; a bare marker is rejected.
"control(reason): a deliberate negative control; verify-red expects it to pass without the fix",
]
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
# ci-make-green-and-load-bearing item 6: ratchet mypy strict on the storage
# layer specifically -- 280-error field-bug history samples concentrate
# there (dual-registry drift, silent index desync). Not repo-wide --scope
# is deliberately narrow so this is achievable incrementally.
#
# Baseline as of 2026-07-03 (`mypy pyrite/storage/ --ignore-missing-imports
# --follow-imports=silent --disallow-untyped-defs`): 281 errors in 15
# files. This override does NOT fail CI yet -- mypy stays
# continue-on-error in ci.yml until the count is burned down (tracked in
# mypy-strict-ratchet-burn-down-storage-layer-errors). Turning this on
# is step 1: enforce no NEW untyped defs in storage/ going forward,
# without yet requiring the existing 281 to be fixed.
[[tool.mypy.overrides]]
module = "pyrite.storage.*"
disallow_untyped_defs = true
# ci-make-green-and-load-bearing item 5: ratchet coverage instead of just
# measuring it. Baseline as of 2026-07-04 (full suite, disk-space-healthy
# run): 71% (`pytest --cov=pyrite --cov-report=term`, 3952 passed). Set a
# few points below to tolerate normal fluctuation while still catching a
# real regression; ratchet up over time rather than setting it at the
# ceiling immediately.
[tool.coverage.report]
fail_under = 68
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
# PT (flake8-pytest-style, #128) is enabled here in `select` but effectively
# scoped to tests/: ruff has no per-file *select*, only per-file *ignore*, so
# it is turned back off for every non-test path below. It is also a no-op in
# practice for the rest of the repo -- no pyrite/, extensions/*/pyrite_*/ or
# scripts/ file uses a pytest construct (fixture/parametrize/raises) that PT
# matches -- but the per-file-ignore makes "PT applies to tests/ only" true
# by construction, not by accident of what the code happens to contain.
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "PT"]
ignore = [
"E501", # Line too long (handled by formatter)
"B008", # Function call in argument defaults (standard Typer pattern)
"B904", # raise-without-from in except (typer.Exit doesn't chain)
"E741", # Ambiguous variable name (l for link is clear in context)
"N802", # Function name should be lowercase (do_GET is HTTP convention)
]
[tool.ruff.lint.per-file-ignores]
# Test files: unused imports (fixtures, side effects), unused assignments,
# import order, and module-level imports not at top (importorskip pattern).
# Four PT (#128) rules ignored here, each judgment-required rather than
# mechanical (14 mechanical PT001/PT006/PT022 findings were fixed instead,
# not ignored):
# PT011 -- pytest.raises(ValueError) needs a match= chosen per call site
# PT012 -- raises-block-is-one-statement needs restructuring per call site
# PT018 -- splitting a compound assert changes its failure message; needs
# a human picking the message, not a mechanical split
# PT019 -- converting `_fixture` params to @pytest.mark.usefixtures touches
# every call site's signature; no autofix exists even unsafely
"tests/*" = ["F401", "F841", "E402", "I001", "PT011", "PT012", "PT018", "PT019"]
# PT is pytest-only style; turn it off everywhere that isn't a test file.
"!tests/**" = ["PT"]