mlschema 0.1.6__tar.gz → 0.2.0__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.
Files changed (102) hide show
  1. {mlschema-0.1.6 → mlschema-0.2.0}/.gitignore +6 -0
  2. {mlschema-0.1.6 → mlschema-0.2.0}/CONTRIBUTING.md +2 -2
  3. mlschema-0.2.0/PKG-INFO +450 -0
  4. mlschema-0.2.0/README.md +406 -0
  5. {mlschema-0.1.6 → mlschema-0.2.0}/SECURITY.md +3 -3
  6. {mlschema-0.1.6 → mlschema-0.2.0}/THIRD_PARTY_LICENSES.md +10 -10
  7. mlschema-0.2.0/docs/changelog.md +310 -0
  8. mlschema-0.2.0/docs/index.md +242 -0
  9. mlschema-0.2.0/docs/installation.md +228 -0
  10. mlschema-0.2.0/docs/schema-standard.md +618 -0
  11. mlschema-0.2.0/docs/usage.md +416 -0
  12. {mlschema-0.1.6 → mlschema-0.2.0}/pyproject.toml +78 -108
  13. mlschema-0.2.0/src/mlschema/__init__.py +23 -0
  14. mlschema-0.2.0/src/mlschema/core/__init__.py +41 -0
  15. mlschema-0.2.0/src/mlschema/core/app/__init__.py +13 -0
  16. mlschema-0.2.0/src/mlschema/core/app/inference.py +191 -0
  17. mlschema-0.2.0/src/mlschema/core/app/kind.py +106 -0
  18. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/domain/__init__.py +1 -2
  19. mlschema-0.2.0/src/mlschema/core/exceptions/__init__.py +27 -0
  20. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/exceptions/_base.py +9 -9
  21. mlschema-0.2.0/src/mlschema/core/exceptions/empty_dataframe.py +30 -0
  22. mlschema-0.2.0/src/mlschema/core/exceptions/field_builder.py +17 -0
  23. mlschema-0.2.0/src/mlschema/core/exceptions/field_kind.py +37 -0
  24. mlschema-0.2.0/src/mlschema/core/exceptions/field_kind_conflict.py +28 -0
  25. mlschema-0.2.0/src/mlschema/core/exceptions/field_service.py +37 -0
  26. mlschema-0.2.0/src/mlschema/core/exceptions/unknown_field_kind.py +28 -0
  27. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/util/dtypes.py +0 -1
  28. mlschema-0.2.0/src/mlschema/mls.py +5 -0
  29. mlschema-0.2.0/src/mlschema/strategies/__init__.py +23 -0
  30. mlschema-0.2.0/src/mlschema/strategies/app/__init__.py +25 -0
  31. mlschema-0.2.0/src/mlschema/strategies/app/_base.py +31 -0
  32. mlschema-0.2.0/src/mlschema/strategies/app/boolean.py +33 -0
  33. mlschema-0.2.0/src/mlschema/strategies/app/category.py +38 -0
  34. mlschema-0.2.0/src/mlschema/strategies/app/date.py +35 -0
  35. mlschema-0.2.0/src/mlschema/strategies/app/factory.py +44 -0
  36. mlschema-0.2.0/src/mlschema/strategies/app/number.py +36 -0
  37. mlschema-0.2.0/src/mlschema/strategies/app/series.py +86 -0
  38. mlschema-0.2.0/src/mlschema/strategies/app/text.py +30 -0
  39. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/__init__.py +1 -8
  40. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/field_types.py +2 -2
  41. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/series_field.py +3 -3
  42. mlschema-0.2.0/test/architecture/test_public_surface.py +103 -0
  43. mlschema-0.2.0/test/architecture/test_test_suite_structure.py +43 -0
  44. mlschema-0.2.0/test/integration/test_infer_schema_workflows.py +89 -0
  45. mlschema-0.2.0/test/integration/test_schema_output_matrix.py +67 -0
  46. mlschema-0.2.0/test/load/test_infer_schema_load.py +43 -0
  47. mlschema-0.2.0/test/unit/test_base_field_contract.py +53 -0
  48. mlschema-0.2.0/test/unit/test_builtin_builders.py +99 -0
  49. mlschema-0.2.0/test/unit/test_builtin_dtype_matrix.py +134 -0
  50. mlschema-0.2.0/test/unit/test_builtin_edge_cases.py +127 -0
  51. mlschema-0.2.0/test/unit/test_dtype_normalization.py +68 -0
  52. mlschema-0.2.0/test/unit/test_factory_and_context.py +49 -0
  53. mlschema-0.2.0/test/unit/test_field_models.py +199 -0
  54. mlschema-0.2.0/test/unit/test_inference_errors.py +120 -0
  55. mlschema-0.2.0/test/unit/test_kind_api.py +43 -0
  56. mlschema-0.1.6/PKG-INFO +0 -308
  57. mlschema-0.1.6/README.md +0 -262
  58. mlschema-0.1.6/docs/changelog.md +0 -115
  59. mlschema-0.1.6/docs/index.md +0 -91
  60. mlschema-0.1.6/docs/installation.md +0 -123
  61. mlschema-0.1.6/docs/schema-standard.md +0 -488
  62. mlschema-0.1.6/docs/usage.md +0 -247
  63. mlschema-0.1.6/src/mlschema/__init__.py +0 -36
  64. mlschema-0.1.6/src/mlschema/core/__init__.py +0 -36
  65. mlschema-0.1.6/src/mlschema/core/app/__init__.py +0 -7
  66. mlschema-0.1.6/src/mlschema/core/app/registry.py +0 -115
  67. mlschema-0.1.6/src/mlschema/core/app/service.py +0 -123
  68. mlschema-0.1.6/src/mlschema/core/app/strategy.py +0 -132
  69. mlschema-0.1.6/src/mlschema/core/domain/base_report.py +0 -28
  70. mlschema-0.1.6/src/mlschema/core/exceptions/__init__.py +0 -24
  71. mlschema-0.1.6/src/mlschema/core/exceptions/registry.py +0 -74
  72. mlschema-0.1.6/src/mlschema/core/exceptions/service.py +0 -82
  73. mlschema-0.1.6/src/mlschema/mls.py +0 -70
  74. mlschema-0.1.6/src/mlschema/strategies/__init__.py +0 -46
  75. mlschema-0.1.6/src/mlschema/strategies/app/__init__.py +0 -17
  76. mlschema-0.1.6/src/mlschema/strategies/app/boolean_strategy.py +0 -34
  77. mlschema-0.1.6/src/mlschema/strategies/app/category_strategy.py +0 -56
  78. mlschema-0.1.6/src/mlschema/strategies/app/date_strategy.py +0 -44
  79. mlschema-0.1.6/src/mlschema/strategies/app/number_strategy.py +0 -63
  80. mlschema-0.1.6/src/mlschema/strategies/app/series_strategy.py +0 -150
  81. mlschema-0.1.6/src/mlschema/strategies/app/text_strategy.py +0 -45
  82. mlschema-0.1.6/src/mlschema/strategies/domain/classifier_report.py +0 -26
  83. mlschema-0.1.6/src/mlschema/strategies/domain/regressor_report.py +0 -26
  84. mlschema-0.1.6/src/mlschema/strategies/domain/report_types.py +0 -8
  85. mlschema-0.1.6/test/integration/test_mlschema.py +0 -839
  86. mlschema-0.1.6/test/unit/strategies/test_boolean_strategy.py +0 -218
  87. mlschema-0.1.6/test/unit/strategies/test_category_strategy.py +0 -412
  88. mlschema-0.1.6/test/unit/strategies/test_date_strategy.py +0 -464
  89. mlschema-0.1.6/test/unit/strategies/test_number_strategy.py +0 -573
  90. mlschema-0.1.6/test/unit/strategies/test_text_strategy.py +0 -585
  91. mlschema-0.1.6/test/unit/test_mls.py +0 -519
  92. {mlschema-0.1.6 → mlschema-0.2.0}/AUTHORS.md +0 -0
  93. {mlschema-0.1.6 → mlschema-0.2.0}/LICENSE +0 -0
  94. {mlschema-0.1.6 → mlschema-0.2.0}/docs/reference.md +0 -0
  95. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/domain/base_field.py +0 -0
  96. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/core/util/__init__.py +0 -0
  97. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/py.typed +0 -0
  98. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/boolean_field.py +0 -0
  99. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/category_field.py +0 -0
  100. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/date_field.py +0 -0
  101. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/number_field.py +0 -0
  102. {mlschema-0.1.6 → mlschema-0.2.0}/src/mlschema/strategies/domain/text_field.py +0 -0
@@ -178,3 +178,9 @@ cython_debug/
178
178
  .vscode/
179
179
  .history/
180
180
  uv.lock
181
+
182
+ # Graphify
183
+ graphify-out/
184
+
185
+ # Codex
186
+ .codex/
@@ -329,8 +329,8 @@ What actually happens
329
329
  ## Environment
330
330
  - OS: [OS independent]
331
331
  - Python version: [e.g., 3.14.0]
332
- - MLSchema version: [e.g., 0.1.2]
333
- - pandas version: [e.g., 2.3.3]
332
+ - MLSchema version: [e.g., 0.2.0]
333
+ - pandas version: [e.g., 3.0.3]
334
334
 
335
335
  ## Additional Context
336
336
  Screenshots, error messages, stack traces, etc.
@@ -0,0 +1,450 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlschema
3
+ Version: 0.2.0
4
+ Summary: Infer validated, frontend-ready field schemas from pandas DataFrames.
5
+ Project-URL: Homepage, https://github.com/UlloaSP/mlschema
6
+ Project-URL: Documentation, https://ulloasp.github.io/mlschema/
7
+ Project-URL: Repository, https://github.com/UlloaSP/mlschema
8
+ Project-URL: Bug Tracker, https://github.com/UlloaSP/mlschema/issues
9
+ Project-URL: Source Code, https://github.com/UlloaSP/mlschema
10
+ Project-URL: Changelog, https://ulloasp.github.io/mlschema/changelog/
11
+ Project-URL: Discussions, https://github.com/UlloaSP/mlschema/discussions
12
+ Project-URL: Funding, https://github.com/sponsors/UlloaSP
13
+ Project-URL: Release Notes, https://github.com/UlloaSP/mlschema/releases
14
+ Author-email: Pablo Ulloa Santín <pablo.ulloa.santin@udc.es>
15
+ Maintainer-email: Pablo Ulloa Santín <pablo.ulloa.santin@udc.es>
16
+ License: MIT
17
+ License-File: AUTHORS.md
18
+ License-File: LICENSE
19
+ Keywords: data-contracts,data-science,data-validation,dataframe,field-schema,form-generation,frontend-forms,frontend-integration,json,json-schema,machine-learning,mlops,pandas,pydantic,schema-inference,validation
20
+ Classifier: Development Status :: 4 - Beta
21
+ Classifier: Framework :: Pydantic
22
+ Classifier: Framework :: Pydantic :: 2
23
+ Classifier: Intended Audience :: Developers
24
+ Classifier: Intended Audience :: Information Technology
25
+ Classifier: Intended Audience :: Science/Research
26
+ Classifier: License :: OSI Approved :: MIT License
27
+ Classifier: Natural Language :: English
28
+ Classifier: Operating System :: OS Independent
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: Programming Language :: Python :: 3 :: Only
31
+ Classifier: Programming Language :: Python :: 3.14
32
+ Classifier: Programming Language :: Python :: Implementation :: CPython
33
+ Classifier: Topic :: Scientific/Engineering
34
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
35
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
36
+ Classifier: Topic :: Software Development :: Code Generators
37
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
+ Classifier: Topic :: Utilities
39
+ Classifier: Typing :: Typed
40
+ Requires-Python: <3.15,>=3.14
41
+ Requires-Dist: pandas<4.0.0,>=3.0.3
42
+ Requires-Dist: pydantic<3.0.0,>=2.13.4
43
+ Description-Content-Type: text/markdown
44
+
45
+ # MLSchema
46
+
47
+ [![PyPI - Version](https://img.shields.io/pypi/v/mlschema.svg)](https://pypi.org/project/mlschema/)
48
+ [![Python Versions](https://img.shields.io/pypi/pyversions/mlschema.svg)](https://pypi.org/project/mlschema/)
49
+ [![CI](https://github.com/UlloaSP/mlschema/actions/workflows/ci.yml/badge.svg)](https://github.com/UlloaSP/mlschema/actions/workflows/ci.yml)
50
+ [![License](https://img.shields.io/github/license/UlloaSP/mlschema.svg)](https://github.com/UlloaSP/mlschema/blob/main/LICENSE)
51
+
52
+ > Turn pandas DataFrames into validated, front-end-ready field schemas.
53
+
54
+ `mlschema` is a lightweight Python SDK for deriving JSON-serialisable field contracts from tabular data. It is designed for model inputs, prediction forms, review tools, annotation workflows, dashboards, and any frontend that needs to render fields from a `pandas.DataFrame` without hand-writing the same schema twice.
55
+
56
+ It pairs naturally with [mlform](https://github.com/UlloaSP/mlform), but the generated schema is plain JSON-compatible data and can be consumed by any frontend or service layer.
57
+
58
+ ## Why MLSchema
59
+
60
+ DataFrame columns already carry useful contract information: names, dtypes, categories, nullability, dates, numeric values, and structured pairs. MLSchema turns that information into a validated field list.
61
+
62
+ Instead of maintaining separate form definitions beside the data pipeline, use `infer_schema(df)` as the baseline and refine only what is genuinely product-specific: labels, bounds, defaults, units, placeholders, UI hints, or custom field kinds.
63
+
64
+ ```python id="54fwnz"
65
+ import pandas as pd
66
+
67
+ from mlschema import infer_schema
68
+
69
+ df = pd.DataFrame(
70
+ {
71
+ "name": ["Ada", "Linus", "Grace"],
72
+ "score": [98.5, 86.0, 91.0],
73
+ "role": pd.Categorical(["engineer", "engineer", "scientist"]),
74
+ "active": [True, False, True],
75
+ }
76
+ )
77
+
78
+ schema = infer_schema(df)
79
+ ```
80
+
81
+ ```json id="mt9rru"
82
+ [
83
+ {
84
+ "kind": "text",
85
+ "label": "name",
86
+ "required": true
87
+ },
88
+ {
89
+ "kind": "number",
90
+ "label": "score",
91
+ "required": true,
92
+ "step": 0.1
93
+ },
94
+ {
95
+ "kind": "category",
96
+ "label": "role",
97
+ "required": true,
98
+ "options": ["engineer", "scientist"]
99
+ },
100
+ {
101
+ "kind": "boolean",
102
+ "label": "active",
103
+ "required": true
104
+ }
105
+ ]
106
+ ```
107
+
108
+ ## Key Features
109
+
110
+ * Function-first API: `infer_schema(df)`.
111
+ * Builtin inference for `text`, `number`, `category`, `boolean`, `date`, and two-axis `series` fields.
112
+ * Pydantic v2 validation before any schema is returned.
113
+ * JSON-serialisable field-list output for frontend and service integration.
114
+ * Field refinements through `overrides`.
115
+ * Domain-specific behaviour through custom builders.
116
+ * New frontend contracts through strict custom kinds.
117
+ * Typed public API with `py.typed`, Pyright, Ruff, pytest, and CI.
118
+
119
+ ## Requirements
120
+
121
+ * Python `>=3.14,<3.15`
122
+ * pandas `>=3.0.3,<4.0.0`
123
+ * pydantic `>=2.13.4,<3.0.0`
124
+
125
+ ## Installation
126
+
127
+ ```bash id="ejpqxw"
128
+ uv add mlschema
129
+ ```
130
+
131
+ Alternative package managers:
132
+
133
+ ```bash id="gqitqn"
134
+ pip install mlschema
135
+ ```
136
+
137
+ ```bash id="2n70wl"
138
+ poetry add mlschema
139
+ ```
140
+
141
+ Pin a version when reproducible environments matter:
142
+
143
+ ```bash id="yrqcr7"
144
+ uv add "mlschema==0.2.0"
145
+ ```
146
+
147
+ ## Quick Start
148
+
149
+ ```python id="f7024l"
150
+ import pandas as pd
151
+
152
+ from mlschema import infer_schema
153
+
154
+ df = pd.DataFrame(
155
+ {
156
+ "customer": ["Ada", "Linus", "Grace"],
157
+ "age": [42, 55, 38],
158
+ "tier": pd.Categorical(["pro", "free", "pro"], categories=["free", "pro"]),
159
+ "created": pd.date_range("2024-01-01", periods=3),
160
+ }
161
+ )
162
+
163
+ schema = infer_schema(df)
164
+ ```
165
+
166
+ The result can be returned from an API, stored as a contract, passed to a form renderer, or used in tests to detect schema drift.
167
+
168
+ MLSchema works best when DataFrame dtypes are deliberate. Numeric columns should use numeric dtypes, categorical columns should use `category`, date columns should use pandas datetime dtypes, and boolean columns should use boolean dtypes. Ambiguous object columns fall back to `text`.
169
+
170
+ ## Schema Output
171
+
172
+ The canonical output is a field list.
173
+
174
+ There is no top-level envelope by default. MLSchema returns the contract directly:
175
+
176
+ ```json id="25et3y"
177
+ [
178
+ {
179
+ "kind": "text",
180
+ "label": "customer",
181
+ "required": true
182
+ },
183
+ {
184
+ "kind": "number",
185
+ "label": "age",
186
+ "required": true,
187
+ "step": 1
188
+ },
189
+ {
190
+ "kind": "category",
191
+ "label": "tier",
192
+ "required": true,
193
+ "options": ["free", "pro"]
194
+ },
195
+ {
196
+ "kind": "date",
197
+ "label": "created",
198
+ "required": true
199
+ }
200
+ ]
201
+ ```
202
+
203
+ Each field includes:
204
+
205
+ * `kind`: the frontend discriminator.
206
+ * `label`: the human-readable label, inferred from the column name unless overridden.
207
+ * `required`: `true` when the source column contains no missing values.
208
+ * kind-specific metadata, such as `step`, `options`, `field1`, `field2`, or validation bounds.
209
+
210
+ Optional values set to `None` are omitted from the output.
211
+
212
+ ## Builtin Kinds
213
+
214
+ Builtin kinds are enabled by default and resolved in a fixed order.
215
+
216
+ | Kind | Detection | Notes |
217
+ | ---------- | ------------------------------------------------------------ | ---------------------------------------------------------------------- |
218
+ | `series` | Non-null cells are 2-element tuples, lists, or dictionaries. | Infers `field1` and `field2` recursively. |
219
+ | `boolean` | `bool`, `boolean` | Emits a boolean field contract. |
220
+ | `category` | `category` | Emits `options` from categorical categories. |
221
+ | `date` | `datetime64[ns]`, `datetime64[us]`, `datetime64` | Emits a date field contract. |
222
+ | `number` | `int64`, `int32`, `float64`, `float32` | Emits `step: 1` for integer columns and `step: 0.1` for float columns. |
223
+ | `text` | fallback | Claims columns not handled by earlier kinds. |
224
+
225
+ The order matters. `series` runs before `text` because it detects pair-shaped object cells by content. `text` runs last as the safe fallback.
226
+
227
+ ## Series Columns
228
+
229
+ A `series` field represents a two-axis value stored in a single DataFrame column, such as timestamp-value readings.
230
+
231
+ ```python id="vjf8bq"
232
+ import pandas as pd
233
+
234
+ from mlschema import infer_schema
235
+
236
+ df = pd.DataFrame(
237
+ {
238
+ "readings": [
239
+ (pd.Timestamp("2024-01-01"), 23.5),
240
+ (pd.Timestamp("2024-01-02"), 24.1),
241
+ (pd.Timestamp("2024-01-03"), 22.8),
242
+ ],
243
+ }
244
+ )
245
+
246
+ schema = infer_schema(df)
247
+ ```
248
+
249
+ ```json id="f1a308"
250
+ [
251
+ {
252
+ "kind": "series",
253
+ "label": "readings",
254
+ "required": true,
255
+ "field1": {
256
+ "kind": "date",
257
+ "label": "field1",
258
+ "required": true
259
+ },
260
+ "field2": {
261
+ "kind": "number",
262
+ "label": "field2",
263
+ "required": true,
264
+ "step": 0.1
265
+ }
266
+ }
267
+ ]
268
+ ```
269
+
270
+ Supported cell shapes are:
271
+
272
+ ```python id="iv6b5b"
273
+ (timestamp, value)
274
+ [timestamp, value]
275
+ {"timestamp": timestamp, "value": value}
276
+ ```
277
+
278
+ Nested series are rejected. Cardinality constraints such as `minPoints` and `maxPoints` can be added with `overrides`.
279
+
280
+ ## Refining Fields With Overrides
281
+
282
+ Inference provides the structural baseline. Production interfaces often need clearer labels, ranges, defaults, units, placeholders, or UI metadata.
283
+
284
+ ```python id="v9tboo"
285
+ schema = infer_schema(
286
+ df,
287
+ overrides={
288
+ "age": {
289
+ "label": "Age",
290
+ "description": "Customer age in years.",
291
+ "min": 0,
292
+ "max": 120,
293
+ "step": 1,
294
+ "unit": "years",
295
+ },
296
+ "tier": {
297
+ "label": "Plan",
298
+ "defaultValue": "pro",
299
+ },
300
+ },
301
+ )
302
+ ```
303
+
304
+ Overrides are applied after inference and before final validation. Missing columns and invalid constraints fail explicitly instead of producing a broken schema.
305
+
306
+ ## Extending MLSchema
307
+
308
+ Use a custom builder when an existing kind is correct, but the column needs domain-aware metadata.
309
+
310
+ ```python id="0r5mdy"
311
+ from pandas import Series
312
+
313
+ from mlschema import FieldContext, infer_schema
314
+
315
+ def money_builder(series: Series, ctx: FieldContext) -> dict | None:
316
+ if ctx.name != "amount_eur":
317
+ return None
318
+
319
+ return {
320
+ "kind": "number",
321
+ "label": "Amount",
322
+ "required": ctx.required,
323
+ "step": 0.01,
324
+ "unit": "EUR",
325
+ "min": 0,
326
+ }
327
+
328
+ schema = infer_schema(df, builders=[money_builder])
329
+ ```
330
+
331
+ Use a custom kind when the frontend needs a new field discriminator and a dedicated validation model.
332
+
333
+ ```python id="96x5rn"
334
+ from typing import Literal
335
+
336
+ from pandas import Series
337
+
338
+ from mlschema import BaseField, FieldContext, infer_schema, kind
339
+
340
+ class DurationField(BaseField):
341
+ kind: Literal["duration"] = "duration"
342
+ unit: Literal["seconds"] = "seconds"
343
+ minSeconds: int
344
+ maxSeconds: int
345
+
346
+ def duration_builder(series: Series, ctx: FieldContext) -> dict | None:
347
+ if ctx.dtype not in {"timedelta64[ns]", "timedelta64[us]"}:
348
+ return None
349
+
350
+ return {
351
+ "kind": "duration",
352
+ "label": ctx.name,
353
+ "required": ctx.required,
354
+ "unit": "seconds",
355
+ "minSeconds": int(series.min().total_seconds()),
356
+ "maxSeconds": int(series.max().total_seconds()),
357
+ }
358
+
359
+ schema = infer_schema(
360
+ df,
361
+ kinds=[
362
+ kind(model=DurationField, infer=duration_builder),
363
+ ],
364
+ )
365
+ ```
366
+
367
+ Resolution is predictable:
368
+
369
+ ```text id="kwtd5b"
370
+ user builders
371
+ custom kind builders
372
+ builtin builders
373
+ ```
374
+
375
+ The first builder returning a field dictionary owns the column.
376
+
377
+ ## Validation And Errors
378
+
379
+ MLSchema validates the generated contract before returning it.
380
+
381
+ Common errors include:
382
+
383
+ | Error | Meaning |
384
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
385
+ | `EmptyDataFrameError` | The input DataFrame has no rows or no columns. |
386
+ | `FieldBuilderError` | A builder returned an invalid payload, omitted `kind`, no builder matched, or an override targeted a missing column. |
387
+ | `UnknownFieldKindError` | A builder emitted a kind with no registered field model. |
388
+ | `FieldKindAlreadyRegisteredError` | Duplicate kind names were registered. |
389
+ | `FieldKindError` | `kind()` received an invalid field model. |
390
+ | `pydantic.ValidationError` | The final field payload violates its Pydantic model. |
391
+
392
+ Library exceptions are available from `mlschema.core.exceptions` and re-exported from `mlschema.core`.
393
+
394
+ ## With mlform
395
+
396
+ MLSchema focuses on inference and validation. [mlform](https://github.com/UlloaSP/mlform) can consume the generated field list to render interactive forms.
397
+
398
+ The split is intentional: Python owns the data contract; the frontend owns rendering, interaction, and submission.
399
+
400
+ ## Documentation
401
+
402
+ * Documentation: https://ulloasp.github.io/mlschema/
403
+ * Usage guide: https://ulloasp.github.io/mlschema/usage/
404
+ * Schema standard: https://ulloasp.github.io/mlschema/schema-standard/
405
+ * API reference: https://ulloasp.github.io/mlschema/reference/
406
+ * Changelog: https://ulloasp.github.io/mlschema/changelog/
407
+
408
+ ## Tooling And Quality
409
+
410
+ * MIT-licensed package distributed as wheel and sdist.
411
+ * Built with Hatchling.
412
+ * Typed with `py.typed`.
413
+ * Tested with `pytest` and `pytest-cov`.
414
+ * Checked with `ruff` and `pyright`.
415
+ * CI provided by GitHub Actions.
416
+
417
+ ## Contributing
418
+
419
+ Contributions are welcome.
420
+
421
+ Useful commands for local development:
422
+
423
+ ```bash id="2fzokc"
424
+ uv sync
425
+ uv run pre-commit install
426
+ uv run pytest
427
+ ```
428
+
429
+ Project links:
430
+
431
+ * Issues: https://github.com/UlloaSP/mlschema/issues
432
+ * Discussions: https://github.com/UlloaSP/mlschema/discussions
433
+ * Contributing guide: https://github.com/UlloaSP/mlschema/blob/main/CONTRIBUTING.md
434
+
435
+ ## Security
436
+
437
+ Please report security concerns privately by emailing `pablo.ulloa.santin@udc.es`.
438
+
439
+ The disclosure process is documented in [SECURITY.md](https://github.com/UlloaSP/mlschema/blob/main/SECURITY.md).
440
+
441
+ ## License
442
+
443
+ Released under the MIT License.
444
+
445
+ * License: https://github.com/UlloaSP/mlschema/blob/main/LICENSE
446
+ * Third-party notices: https://github.com/UlloaSP/mlschema/blob/main/THIRD_PARTY_LICENSES.md
447
+
448
+ ---
449
+
450
+ Made by [Pablo Ulloa Santin](https://github.com/UlloaSP) and contributors.