-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpyproject.toml
More file actions
185 lines (155 loc) · 7.91 KB
/
Copy pathpyproject.toml
File metadata and controls
185 lines (155 loc) · 7.91 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
[project]
name = "exlabwizard"
version = "0.1.0"
description = "Desktop tool for standardized lab run/project directory creation, NAS sync, and LIMS integration"
readme = "README.md"
requires-python = ">=3.12"
# Runtime dependencies. Each entry cites the design-spec section that commits the dependency.
# Cross-OS conditionals use sys_platform markers; transitive backends (Cocoa on macOS, etc.) are pulled in by their parent packages.
dependencies = [
# ---- Web framework + ASGI server (Backend §4.1, §4.2, §4.6) ----
"fastapi>=0.110",
"uvicorn[standard]>=0.27",
"pydantic>=2.6", # API request/response models, config models
"httpx>=0.27", # LIMS REST client + integration test client (Backend §7.2.5, §4.10.2)
"packaging>=23", # semver comparison for the startup update notifier (§15.6, §15.8 item 3); also transitively present
# ---- Native window + tray (Frontend §2; Backend §4.1, §4.3.2, §15) ----
"nicegui>=1.4", # UI component library; mounts on FastAPI
"pywebview>=5.0", # Native window for the NiceGUI surface
"pystray>=0.19", # System-tray icon
"plyer>=2.1", # Cross-platform OS notifications (§15.7.3)
# ---- Template engine (Backend §5) ----
"copier>=9.0", # _min_copier_version pinned by spec (§5.2)
"jinja2>=3.1", # Used by Copier for .jinja-suffixed files
"jinja2-ansible-filters>=1.3", # Extra filters referenced in §5.6
# ---- Cache file I/O + schema validation (HIGH-LEVERAGE; vectorization audit recommendation #1) ----
"msgspec>=0.18", # Typed JSON Struct/encoder; replaces stdlib json on hot paths (§4.4.5, §11.3, §11.4); also handles plugin-host IPC envelopes (§6.3.2) and WebSocket frames (§4.6.2)
# ---- Atomic file locking (vectorization recommendation #2; §4.4.5) ----
"filelock>=3.13", # Cross-platform LOCK_EX/LOCK_SH; matches the spec's `with FileLock(...)` shape exactly
# ---- Async SQLite (vectorization recommendation #3; §7.1.1, §7.2.4) ----
"aiosqlite>=0.20", # Async-native access to lims_cache.db and sync_queue.db; removes the asyncio.to_thread roundtrip
# ---- YAML I/O (config.yaml round-trip + read-only manifest reads; §6.1.2, §9, §10.7.1) ----
"ruamel.yaml>=0.18", # Round-trip preserving comments + key order for config.yaml (Settings UI writes back via this)
"pyyaml>=6.0", # Read-only path for copier.yml, manifest.yml, README front matter -- faster than ruamel for these
# ---- Credential storage (Backend §7.4) ----
"keyring>=24.0", # OS keyring (Keychain / Credential Manager / Secret Service)
"cryptography>=42.0", # Fernet for encrypted-at-rest fallback when no keyring is available (§7.4.4)
"argon2-cffi>=23.1", # Argon2id KDF for the master passphrase in the encrypted-at-rest fallback (§7.4.4)
# ---- Markdown rendering (README preview, log viewer; Frontend §3.6.3, §3.6.5) ----
"markdown-it-py>=3.0", # CommonMark-compliant; YAML-front-matter aware via the front_matter plugin
# ---- Windows-only: plugin Job Object resource limits (Backend §6.3.3) ----
"pywin32>=306; sys_platform == 'win32'",
# ---- Linux-only: keyring's Secret Service backend (Backend §7.4); fallback path triggers when this fails to import ----
"secretstorage>=3.3; sys_platform == 'linux'",
]
[project.urls]
Homepage = "https://github.com/exfab/ExLabWizard"
Repository = "https://github.com/exfab/ExLabWizard"
Documentation = "https://exfab.github.io/ExLabWizard/"
[project.optional-dependencies]
# Plugin-author libraries used in worked examples (Backend §6.6, §6.7).
# These are NOT bundled with the app; plugin authors install them in their plugin's own requirements.
# Listed here so that running `uv pip install -e .[plugin-examples]` picks them up for local plugin development.
plugin-examples = [
"openpyxl>=3.1", # xlsx_field_filler worked example (§6.6)
"python-docx>=1.1", # docx_variable_replacer illustrative plugin (§6.7)
]
# Testing (Backend §4.10).
test = [
"pytest>=8.0",
"pytest-asyncio>=0.23",
"playwright>=1.40", # E2E browser automation against http://127.0.0.1:<port> (Backend §4.10.3)
"pytest-cov>=4.1", # Coverage reporting; configured under [tool.coverage.run]
"pytest-rerunfailures>=14", # Opt-in retries for timing-sensitive flakes (@pytest.mark.flaky)
# httpx is already in core deps -- used for AsyncClient(app=app) in integration tests too
]
# Build (Backend §15.1; PyInstaller --onedir with three entry points).
build = [
"pyinstaller>=6.3",
]
# Lint / static analysis / pre-commit (Phase 1B tooling).
lint = [
"ruff>=0.4",
"mypy>=1.10",
"pre-commit>=3.7",
]
# Documentation toolchain (Sphinx + pydata-sphinx-theme; see docs/conf.py).
docs = [
"sphinx>=7.3",
"pydata-sphinx-theme>=0.15",
"sphinx-autodoc-typehints>=2.0",
"sphinx-design>=0.6",
"myst-parser>=3.0",
"sphinx-copybutton>=0.5",
"sphinxext-opengraph>=0.9",
]
# Dev convenience: install everything for local development with `uv pip install -e .[dev]`.
dev = [
"exlabwizard[plugin-examples,test,build,lint,docs]",
]
[project.scripts]
# Three console entry points per Backend §15.3:
# - exlab-wizard : CLI alias the operator double-clicks (Backend §15.3.3); spawns or focuses the tray.
# - exlab-wizard-tray : Long-lived tray + server process (Backend §15.3.1); registered for autostart.
# - exlab-wizard-window: On-demand pywebview window subprocess (Backend §15.3.2); spawned by the tray.
exlab-wizard = "exlab_wizard.__main__:main"
exlab-wizard-tray = "exlab_wizard.tray.main:main"
exlab-wizard-window = "exlab_wizard.window.main:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/exlab_wizard"]
# ---------------------------------------------------------------------------
# Tool configuration (Phase 1B)
# ---------------------------------------------------------------------------
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
# Rule families:
# E/W : pycodestyle errors and warnings
# F : pyflakes
# I : import sorting (isort)
# N : pep8-naming
# B : flake8-bugbear
# UP : pyupgrade (modernize syntax for the py312 target)
# ASYNC: flake8-async (catches blocking calls in async fns; matches Backend §4.4)
# SIM : flake8-simplify
# TID : flake8-tidy-imports
# RUF : ruff-specific rules
select = ["E", "F", "W", "I", "N", "B", "UP", "ASYNC", "SIM", "TID", "RUF"]
# E501 (line-too-long) is enforced via the formatter; we sometimes need long URLs in comments.
ignore = ["E501"]
[tool.ruff.lint.per-file-ignores]
# Tests can use bare asserts and looser naming. ASYNC240 is suppressed because
# pytest-asyncio test functions use synchronous pathlib operations against
# tmp_path; this is the established pytest idiom and not a concurrency hazard.
"tests/**/*.py" = ["N802", "N803", "N806", "ASYNC230", "ASYNC240"]
[tool.ruff.format]
quote-style = "double"
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
python_files = ["test_*.py"]
[tool.coverage.run]
source = ["src/exlab_wizard"]
omit = [
"src/exlab_wizard/_worker.py",
"*/tests/*",
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
"\\.\\.\\.",
]
show_missing = true
[tool.mypy]
python_version = "3.12"
strict = false
ignore_missing_imports = true
# Src-layout: keep mypy resolvable when the package is not editable-installed
# (e.g. fresh checkout without `pip install -e .`).
mypy_path = "src"