You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The package is available for Python 3.11 and newer.
22
22
23
-
## Concepts
23
+
## Usage
24
24
25
-
Before using the package, there are some concepts you may need to understand for the most optimal use of the package.
25
+
### Handlers
26
26
27
-
### Key
27
+
Handlers are the validation functions used to validate the value of keys in the input data.
28
28
29
-
A key is the name of a field in a TOML file, such as `name`, `person.name`, etc. Keys must conform to the TOML specification, which means keys are either **snake_case** or **SCREAMING_SNAKE_CASE**. For the validator, keys may also include wildcards, such as `*name`, `person.*`, etc.
29
+
#### Types
30
30
31
-
### Handler
31
+
A handler must be one of `type`, `Callable`. This means any object of type `type` is valid, and and `Callable`, such as `lambda` functions as well as named functions are valid.
32
32
33
-
A _handler_ is a function that is called for a certain key. Handlers can be of the following types:
33
+
#### Parameters
34
34
35
-
- Types, such as `int`, `str`, `float`.
36
-
- Tuples/Lists of types, such as `(int, str)`, `[int, str]`.
37
-
- Anonymous functions (`lambda`)
38
-
- Named functions (Callable objects, such as `def my_handler(key, value): ...`)
35
+
The handler will dynamically be passed either the `key` and/or `value` argument based of what parameters are defined. No parameters are also okay.
39
36
40
-
The following argument configurations are supported:
If the handler has any other parameters than `key` or `value`, the validator will raise a `TOMLHandlerError`.
43
+
If a handler accepts any parameters which are not `key` or `value`, a `TOMLHandlerError` will be raised.
48
44
49
-
Handlers may return any type, but is is recommended to use the return type as an error message if the value is invalid. The validator considers a `None` return value a successful validation.
45
+
#### Return Types
46
+
47
+
A handler returns an error, meaning _nullish_ values tell the validator that the test passes. The reason for this design is that the handler may return error messages or any value your program needs.
50
48
51
49
### Schema
52
50
53
-
The schema is used to give the validator default handlers and an ability to make sure certain keys exist. The schema is defined in the `TOMLSchema` class, and is passed to the `TOMLValidator` class. To create a schema, you pass a dictionary with the keys and their respective allowed types.
51
+
A schema is an _optional_ structure used to add functionality to the validator, this includes validation for missing keys and default handlers for keys.
52
+
53
+
#### Keys
54
+
55
+
Keys follow the TOML specification, meaning keys must be in either `snake_case` or `SCREAMING_SNAKE_CASE`. This project adds some special notation in the form of suffixing a key with `?` to make it optional, adding `[]` to the end to make the key an array and wildcard regex pattern support. The importance of keys are based of specificity, so `my.key` would dominate both `my.*` and `*`.
56
+
57
+
This means the following keys are examples of valid keys:
All keys can be written in dot-notation, meaning a deeply nested object/array can be written in a simpler form. For example:
65
+
66
+
```py
58
67
{
59
-
"single_type": str,
60
-
"list_of_strings": [str],
61
-
"mixed_list:" [str, int],
62
-
"multiple_types": (int, float),
63
-
"optional?": str,
64
-
"nested": {
65
-
"key": str
68
+
"very": {
69
+
"deeply": {
70
+
"nested": {
71
+
"object": {
72
+
"key": str
73
+
}
74
+
}
75
+
}
66
76
}
67
77
}
68
78
```
69
79
70
-
When a schema is defined, the validator will also check if values are missing and if their types are correct. If a handler is defined for a key, the validator will use the handler instead of the type defined in the schema.
80
+
can be written as `"very.deeply.nested.object.key": str`. This notation also supports optionality and arrays. This would work by just suffixing the word with `?` and if an array, suffix the `?` with `[]`.
81
+
82
+
#### Defining a Schema
83
+
84
+
In order to define a new schema, you can use the following code as reference:
_Note: When a nested array includes dictionaries with different structures, they will be merged. If the merge fails, a `TOMLSchemaMergeError` will be raised._
71
118
72
119
### Validator
73
120
74
-
The validator is the core of the package. It is used to validate a TOML file. A schema is optionally passed to the validator, and handlers are added using the `add_handler` method. Once you feel ready, you can call the `validate` method with the data you want to validate as an argument to get a dictionary of errors.
121
+
The validator defines the blueprint for how data should be validated. This is defined in the optional schema, or handlers can be manually added using the `add_handler(key, fn)` method. Handlers, like keys, are prioritized based of the key priority.
122
+
123
+
#### Examples
75
124
76
-
Currently, there are two type of error structures, for type errors and all other errors.
125
+
##### Basic
77
126
78
-
Type errors are structured as follows:
127
+
This examples includes the most basic use case, where a default handler is defined manually:
_`expected_type` and `actual_type` can be either `type` or `tuple[type]`_
136
+
##### With a Schema
137
+
138
+
This example includes a schema, assume the schema is populated with the structure and handlers you require.
85
139
86
-
All other errors have a slightly simpler structure:
140
+
```py
141
+
from tomlval import TOMLValidator, TOMLSchema
87
142
88
-
```python
89
-
"key": (message, value)
143
+
schema = TOMLSchema({...})
144
+
validator = TOMLValidator(schema)
90
145
```
91
146
92
-
The point of the validator is to parse the data and get the errors in a clean and easy way. **What you do with the errors is up to you.**
147
+
##### Customizing a Defined Schema
93
148
94
-
## Example
149
+
This example includes a case where you might have defined a _shared_ schema somewhere in your code but you need to customize specific keys:
95
150
96
-
Here is a full example of how to use the validator.
151
+
```py
152
+
from tomlval import TOMLValidator
153
+
from .schema import schema
97
154
98
-
```python
99
-
import pathlib
100
-
import tomllib
101
-
import datetime
102
-
from tomlval import TOMLValidator, TOMLSchema
155
+
defvalidate_age(value):
156
+
if value <=0:
157
+
return"value-to-low"
158
+
returnNone
103
159
104
-
# Load data from file
105
-
path = pathlib.Path("data.toml")
160
+
validator = TOMLValidator(schema)
161
+
validator.add_handler("user.age", validate_age)
162
+
```
106
163
107
-
with path.open("rb") asfile:
108
-
data_file = tomllib.load(file)
164
+
##### Customizing The Default Callbacks
109
165
110
-
# Use a dictionary
111
-
data_dict = {
112
-
"first_name": "John",
113
-
"last_name": "Doe",
114
-
"age": 25
115
-
}
166
+
For some people, it might not be the best option to return an error message, and instead some other value might be preferred or even a more verbose error message. In this case, the `on_missing` and `on_type_mismatch` callbacks are changed:
returnf"The argument '{key}' expected type '{expected.__name__}', got '{got.__name__}'"
177
+
178
+
validator = TOMLValidator(
179
+
schema,
180
+
on_missing=on_missing,
181
+
on_type_mismatch=on_type_mismatch
182
+
)
183
+
```
184
+
185
+
### Validation
186
+
187
+
Now that you have defined your schema and validator, the validator is now ready to be used on TOML data.
188
+
189
+
In order to use the validator, the `validate(data)` method is used. It accepts any dictionary as an argument and outputs a flat dictionary of all keys in dot-notation with each key's respective error value.
131
190
132
-
schema = TOMLSchema(structure) # If the struture is invalid, a TOMLSchemaError is raised
191
+
#### Examples
192
+
193
+
##### Validate File
194
+
195
+
This example shows a use-case where a TOML file is validated.
196
+
197
+
```py
198
+
import tomllib
199
+
from datetime import datetime
200
+
from pathlib import Path
201
+
from tomlval import TOMLSchema, TOMLValidator
202
+
203
+
# Read file
204
+
file_path = Path("example.toml")
205
+
with file_path.open("rb") asfile:
206
+
data = tomllib.load(file)
207
+
208
+
# Define schema
209
+
schema = TOMLSchema({
210
+
"*_name": str,
211
+
"age": lambdavalue: "invalid-age"if age <=0elseNone,
212
+
"birthday": datetime,
213
+
"*": lambda: "invalid-key"
214
+
})
133
215
134
216
# Define validator
135
217
validator = TOMLValidator(schema)
136
218
137
-
# Add handlers
138
-
validator.add_handler("*_name", lambdakey: Noneif key in ["first_name", "last_name"] else"invalid-key")
139
-
validator.add_handler("age", lambdavalue: Noneif18< value <100else"invalid-age")
140
-
validator.add_handler("*", lambda: "invalid-key")
141
-
142
-
# Validate the data
143
-
errors_file = validator.validate(data_file)
144
-
errors_dict = validator.validate(data_dict)
219
+
# Validate data
220
+
errors = validator.validate(data)
145
221
```
146
222
147
-
## Future Plans
223
+
##### Validate Dictionary
224
+
225
+
Instead of loading a file, you might have pre-loaded TOML-data in the form of a dictionary.
148
226
149
-
Future plans are found in the [TODO](TODO.md) file.
227
+
```py
228
+
import tomllib
229
+
from datetime import datetime
230
+
from pathlib import Path
231
+
from tomlval import TOMLSchema, TOMLValidator
232
+
from .data import data
233
+
234
+
# Define schema
235
+
schema = TOMLSchema({
236
+
"*_name": str,
237
+
"age": lambdavalue: "invalid-age"if age <=0elseNone,
238
+
"birthday": datetime,
239
+
"*": lambda: "invalid-key"
240
+
})
241
+
242
+
# Define validator
243
+
validator = TOMLValidator(schema)
244
+
245
+
# Validate data
246
+
errors = validator.validate(data)
247
+
```
150
248
151
249
## License
152
250
153
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
251
+
This project is licensed under the MIT License - seea the [LICENSE](LICENSE) file for details.
0 commit comments