Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
165 lines (153 loc) · 6.63 KB
/
Copy pathpyproject.toml
File metadata and controls
165 lines (153 loc) · 6.63 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
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "minilake"
version = "1.9.1"
description = "A local Databricks API emulator backed by DuckDB."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [
{ name = "Rafael Sales", email = "rafael.sales@gmail.com" }
]
keywords = ["databricks", "emulator", "duckdb", "delta-lake", "unity-catalog", "spark", "mcp", "testing"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Database",
"Topic :: Software Development :: Testing",
"Topic :: Software Development :: Testing :: Mocking",
"Typing :: Typed",
]
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
# Floor raised from 2.7 to satisfy the `mcp` extra (mcp>=1.29 needs pydantic>=2.11);
# keeping a single floor avoids a resolution that silently works without the extra
# and breaks with it.
"pydantic>=2.11",
"pydantic-settings>=2.3",
# Capped below 2.0: DuckDB 2 stopped carrying E-string escape semantics across
# implicit string continuation (E'a'\n'\'b' is now a parse error), which the backtick
# rewriter in sql_statements.py mirrors from 1.x. Lift with that tokenizer change.
"duckdb>=1.0,<2",
# Runtime, not just test: the JupyterLab reverse proxy (notebook.py) and the MCP
# ASGI client (mcp/client.py) both import it, and app.py imports notebook.py
# unconditionally. Without it a plain `pip install minilake` crashes on startup.
"httpx>=0.27",
"python-multipart>=0.0.9",
"aiofiles>=24.1",
"docker>=7.1",
"pyarrow>=14.0",
"cryptography>=42.0",
"pytz>=2024.1",
]
# Keep this below `dependencies`: a `[table]` header ends the parent table, so anything
# declared after it would be parsed as a URL rather than a project field.
[project.urls]
Homepage = "https://github.com/dmux/minilake"
Documentation = "https://github.com/dmux/minilake/blob/main/docs/index.md"
Repository = "https://github.com/dmux/minilake"
Issues = "https://github.com/dmux/minilake/issues"
Changelog = "https://github.com/dmux/minilake/releases"
[project.optional-dependencies]
# MCP server (exposes minilake's capabilities to LLM agents at /mcp). Optional so the
# base install stays free of the SDK's transitive deps (jsonschema, pyjwt[crypto],
# sse-starlette, ...). Pinned to the 1.x line: 2.0 renames FastMCP -> MCPServer and
# removes mcp.server.fastmcp outright, so it is a rewrite, not a bump. The 1.30 floor
# fixes unbounded session accumulation in the StreamableHTTP transport we serve at /mcp.
mcp = ["mcp>=1.30,<2"]
# Embedded JupyterLab, proxied at /jupyter and framed by the UI's Notebooks page.
# Optional so a base install stays slim: this pulls in the whole Jupyter stack
# (jupyter-server, ipykernel, nbformat, ...). No pyspark here on purpose — notebooks
# reach real Spark through the Jobs API, in the same sibling containers a job uses,
# so the image never carries a second Spark.
# The 4.6.4 floor carries the XSS and argument-injection fixes for the 4.6 line.
notebook = ["jupyterlab>=4.6.4,<5", "databricks-sdk>=0.40"]
[dependency-groups]
dev = [
"pytest>=8.0",
"pytest-xdist>=3.6",
"pytest-asyncio>=0.24",
"mcp>=1.30,<2",
"databricks-sdk>=0.40",
"ruff>=0.6",
"pre-commit>=3.8",
# Lightweight (no JVM/Spark) real Delta Lake writer, used only to validate
# minilake's delta_scan()-based EXTERNAL table read path in tests. Actual
# users write Delta tables with real Spark (jobs or the notebook service).
"deltalake>=0.19",
"pandas>=2.0",
"pyarrow>=14.0",
]
[project.scripts]
minilake = "minilake.cli:main"
[tool.hatch.build.targets.wheel]
# The built web UI is staged into src/minilake/ui_static by `cd ui && pnpm build`
# (see ui/scripts/stage-export.mjs), so it is inside the package and needs no
# force-include — which would break the editable install in the Docker image,
# where ui/ is not part of the build context at all. A wheel built without having
# built the UI simply has no /ui mount.
packages = ["src/minilake"]
# `artifacts` overrides hatchling's default of skipping VCS-ignored files. The
# export is build output, so it is gitignored — without this the wheel builds
# happily and silently ships no UI.
artifacts = ["src/minilake/ui_static/**"]
[tool.hatch.build.targets.sdist]
# Same override as the wheel, and for a sharper reason: `uv build` (what the release
# workflow runs) builds the sdist first and then builds the wheel *from that sdist*.
# Without this the export is dropped at the sdist step and the published wheel has no
# web UI at all — silently, since nothing fails. `uv build --wheel` hides the bug by
# going straight from the source tree.
artifacts = ["src/minilake/ui_static/**"]
# The sdist is what someone builds from, so it carries the source, the docs and the
# compose files. Agent config and local tooling are not part of that.
exclude = [
"/.agents",
"/.claude",
"/.mcp.json",
"/.github",
"/htmlcov",
"/data",
"/scratchpad",
# hatchling only honours the *root* .gitignore, so everything ui/.gitignore
# covers has to be repeated here. Without this the sdist carries ui/node_modules
# — 44k files, 300+ MB — because the release workflow runs `pnpm install` before
# `uv build`. PyPI rejects anything over 100 MB, and the failure lands after the
# wheel has already been uploaded.
"/ui/node_modules",
"/ui/.next",
"/ui/out",
"/ui/public/monaco",
]
[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
addopts = "--dist=loadfile -v"
# Strict mode: async tests must be marked explicitly, so pytest-asyncio never silently
# takes over the existing sync (databricks-sdk) tests.
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "function"
markers = [
"serial: tests that cannot run in parallel (mutate global state)",
"crud: create/read/update/delete operations",
"workflow: multi-step end-to-end flows",
"error: error handling and edge cases",
"smoke: quick sanity checks",
]
[tool.ruff]
line-length = 120
target-version = "py311"
# Example notebooks are teaching material, not library code: they interleave prose and
# imports by design, which E402 flags on every cell. Ruff lints .ipynb by default.
exclude = ["src/minilake/notebooks"]
[tool.ruff.lint]
select = ["E", "F", "W", "I"]
ignore = ["E501"] # line too long (handled by formatter)