Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
175 lines (160 loc) · 7.62 KB
/
Copy pathpyproject.toml
File metadata and controls
175 lines (160 loc) · 7.62 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
[build-system]
requires = ["setuptools>=77", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"
[project]
name = "dp-python-lib"
# Version is derived from the git tag by setuptools-scm (see [tool.setuptools_scm]); it is
# deliberately NOT hardcoded here, so the tag is the single source of truth at release time.
dynamic = ["version"]
description = "Python client library for the MLDP gRPC API"
readme = "README.md"
license = "BSD-3-Clause"
license-files = ["LICENSE"]
requires-python = ">=3.10"
authors = [{ name = "Osprey Distributed Control Systems" }]
keywords = ["mldp", "grpc", "data-platform", "time-series", "epics"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering",
]
dependencies = [
# Minimums match what the generated gRPC stubs hard-require at import time
# (see GRPC_GENERATED_VERSION / ValidateProtobufRuntimeVersion in src/dp_python_lib/grpc);
# keep these in sync with the stub generator when regenerating.
#
# NOTE: grpcio-tools is deliberately NOT here. It is the protoc toolchain used to *generate*
# the stubs, not to run them -- nothing under src/ imports grpc_tools. Requiring it at runtime
# would force every consumer to install the compiler. It lives in the [codegen] extra instead.
"grpcio>=1.84.0",
"protobuf>=7.35.1",
"pydantic-settings",
"PyYAML",
]
[project.urls]
Homepage = "https://github.com/osprey-dcs/dp-python-lib"
Repository = "https://github.com/osprey-dcs/dp-python-lib"
Issues = "https://github.com/osprey-dcs/dp-python-lib/issues"
Documentation = "https://github.com/osprey-dcs/dp-python-lib/blob/main/doc/cookbook/README.md"
Changelog = "https://github.com/osprey-dcs/dp-python-lib/releases"
[project.optional-dependencies]
# Pythonic query-result conversions (query_conversions): DataFrame / NumPy / Excel. Kept out of the core
# install so the gRPC client stays lightweight; imported lazily. openpyxl is the Excel engine and is NOT
# implied by pandas, so it is listed explicitly.
analysis = [
"pandas",
"numpy",
"openpyxl",
]
# Development tooling. Not required to use the library. mypy runs in CI as `mypy src/`
# (config under [tool.mypy]) and also backs the cookbook snippet checker
# (.dev/tools/check-cookbook-snippets.py), which type-checks every example in doc/cookbook
# against this package to catch wrong attribute/method names in the docs. The types-* and
# pandas-stubs packages are the third-party stubs mypy needs to check against real
# signatures rather than treating those imports as untyped.
dev = [
"mypy",
"types-PyYAML",
"types-protobuf",
"types-grpcio",
"pandas-stubs",
"pytest",
"ruff",
"build",
"twine",
]
# Toolchain for regenerating the gRPC stubs in src/dp_python_lib/grpc from the upstream
# dp-grpc protos. Not needed to use or develop against the library. mypy-protobuf is the
# protoc plugin behind --mypy_out / --mypy_grpc_out, which write the .pyi type stubs beside the
# generated .py files (osprey-dcs/dp-grpc#158); without it a regeneration produces untyped stubs.
# dp-grpc is the authority on the generator: its tools/python-stubs-requirements.in pins the
# versions and its generate-python-stubs.yml workflow sets the flags. Keep these minimums in step
# with those pins (#61).
codegen = [
"grpcio-tools>=1.84.0",
"mypy-protobuf>=5.1.0",
]
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools_scm]
# Release tags are `rel-X.Y.Z`; the default regex expects `v` or a bare number, so it is
# overridden here. Without this, setuptools-scm cannot parse the project's tags at all.
tag_regex = '^rel-(?P<version>[0-9]+\.[0-9]+\.[0-9]+.*)$'
# Only `rel-*` tags define versions. Other tags in this repo (e.g. the legacy `v1.13`) are
# ignored rather than being misread as releases.
git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "rel-*"]
# Non-tagged commits get the NEXT patch version with a .devN suffix, rather than appearing to
# be a later release than they are.
version_scheme = "guess-next-dev"
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"integration: requires a live MLDP ecosystem on localhost:50051-50053 (deselect with '-m \"not integration\"')",
]
[tool.mypy]
# No python_version: numpy's bundled stubs use the 3.12 `type` statement, and pinning 3.10
# makes mypy refuse to check anything. mypy checks against the running interpreter (3.12 in
# CI's quality job); the 3.10 unit-test leg and ruff's target-version guard 3.10 syntax.
#
# The generated gRPC package is untyped (no .pyi stubs; osprey-dcs/dp-grpc#158), so it is
# excluded from checking and its imports are resolved to Any. Both are needed: `exclude`
# alone still follows imports into it, and `follow_imports = "skip"` alone does nothing for
# files `mypy src/` names on the command line. Remove both once typed stubs are synced (#61).
#
# Neither holds against .pyi files: mypy reads a stub whenever one is present, whatever
# follow_imports says. So once a sync brings dp-grpc's .pyi stubs, the hand-written code is
# checked against real protobuf types even with this suppression in place.
exclude = ["^src/dp_python_lib/grpc/"]
[[tool.mypy.overrides]]
module = ["dp_python_lib.grpc.*"]
follow_imports = "skip"
[tool.ruff]
# 120 matches the margin this codebase was already written to: at line-length 100 there were
# 307 violations, nearly all docstrings and long string literals that the formatter will not
# rewrap; at 120 there are 3. Reflowing that much prose by hand would be a large diff with no
# readability gain.
line-length = 120
target-version = "py310"
# The gRPC stubs are generated from the upstream dp-grpc protos and regenerated wholesale;
# linting or formatting them would be overwritten on the next regeneration.
#
# Markdown is excluded because ruff would reformat the Python snippets embedded in CLAUDE.md
# and doc/cookbook/*.md. Those snippets are hand-wrapped for readability as documentation,
# and the cookbook examples are already verified independently by
# .dev/tools/check-cookbook-snippets.py, which type-checks them against this package.
extend-exclude = ["src/dp_python_lib/grpc", "*.md"]
[tool.ruff.lint]
select = [
"E", "W", # pycodestyle
"F", # pyflakes
"I", # isort
"UP", # pyupgrade -- requires-python is >=3.10, so modern syntax is available
"B", # flake8-bugbear
"SIM", # flake8-simplify
"G", # flake8-logging-format
"DTZ", # flake8-datetimez -- this library rejects naive datetimes, so they matter here
"RUF",
]
ignore = [
# The documented three-tier error handling deliberately ends with a broad `except Exception`
# as the outermost tier (see CLAUDE.md, "gRPC Error Handling"), and config loading catches
# broadly to re-raise as ValueError. This is the intended design, not an oversight.
"BLE001",
# `int(round(x))` reads as deliberate in the nanosecond arithmetic in to_timestamp(); the
# redundant cast documents the intent to land on an int.
"RUF046",
# ServiceApiClientBase and ApiResultBase inherit ABC to mark them as not-directly-instantiable
# shared-implementation bases. They intentionally have no abstract methods; adding one purely
# to satisfy this rule would be worse than the warning.
"B024",
]
[tool.ruff.lint.per-file-ignores]
# Tests legitimately catch broadly in setUpClass to skip when no live server is present,
# and assert on private/underscore internals.
"tests/**" = ["BLE001"]