logging-objects-with-schema 0.2.0__tar.gz → 0.3.1__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.
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/PKG-INFO +22 -4
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/README.md +19 -3
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/pyproject.toml +12 -5
- logging_objects_with_schema-0.3.1/src/logging_objects_with_schema/py.typed +0 -0
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/src/logging_objects_with_schema/schema_applier.py +1 -1
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/src/logging_objects_with_schema/schema_loader.py +165 -35
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/LICENSE +0 -0
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/src/logging_objects_with_schema/__init__.py +0 -0
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/src/logging_objects_with_schema/errors.py +0 -0
- {logging_objects_with_schema-0.2.0 → logging_objects_with_schema-0.3.1}/src/logging_objects_with_schema/schema_logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logging-objects-with-schema
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Proxy logging wrapper that validates extra fields against a JSON schema.
|
|
5
5
|
Keywords: logging,schema,validation,structured-logging,structured-logs
|
|
6
6
|
Author: Dmitrii Safronov
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
17
|
Classifier: Intended Audience :: Developers
|
|
18
18
|
Classifier: Topic :: System :: Logging
|
|
19
|
+
Classifier: Typing :: Typed
|
|
19
20
|
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
20
21
|
Requires-Dist: pytest-cov>=4.0.0 ; extra == 'dev'
|
|
21
22
|
Requires-Dist: black>=23.0.0 ; extra == 'dev'
|
|
@@ -25,6 +26,7 @@ Requires-Dist: flake8-pyproject>=1.2.3 ; extra == 'dev'
|
|
|
25
26
|
Requires-Dist: mypy>=1.18.1 ; extra == 'dev'
|
|
26
27
|
Requires-Dist: bandit>=1.8.6 ; extra == 'dev'
|
|
27
28
|
Requires-Dist: pre-commit>=3.5.0 ; extra == 'dev'
|
|
29
|
+
Requires-Dist: vulture>=2.0.0 ; extra == 'dev'
|
|
28
30
|
Requires-Python: >=3.10
|
|
29
31
|
Project-URL: Issues, https://github.com/disafronov/python-logging-objects-with-schema/issues
|
|
30
32
|
Project-URL: Source, https://github.com/disafronov/python-logging-objects-with-schema
|
|
@@ -213,11 +215,27 @@ An example of a valid empty schema (no leaves, no problems):
|
|
|
213
215
|
|
|
214
216
|
**Schema structure:**
|
|
215
217
|
|
|
216
|
-
- **Inner nodes**: Objects
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
- **Inner nodes**: Objects that contain child nodes (any fields that are objects).
|
|
219
|
+
Inner nodes cannot have `type` or `source` as strings (they can only have
|
|
220
|
+
these fields as objects, which are child nodes). Inner nodes must have at
|
|
221
|
+
least one child node.
|
|
222
|
+
- **Leaf nodes**: Objects with `type` and `source` fields as strings (properties).
|
|
223
|
+
A valid leaf must have both `type` and `source` present and non-empty. Leaf
|
|
224
|
+
nodes cannot have child nodes (any fields that are objects).
|
|
225
|
+
- **Node validation rules**:
|
|
226
|
+
- A node cannot be both a leaf and an inner node (cannot have both properties
|
|
227
|
+
and children simultaneously).
|
|
228
|
+
- A node cannot be empty (must be either a leaf with properties or an inner
|
|
229
|
+
node with children).
|
|
230
|
+
- Fields `type`, `source`, and `item_type` can be used as child node names
|
|
231
|
+
in inner nodes (as objects), but then they are treated as children, not
|
|
232
|
+
as leaf properties.
|
|
219
233
|
- **`type`**: One of `"str"`, `"int"`, `"float"`, `"bool"`, or `"list"`.
|
|
234
|
+
Must be a string for leaf nodes.
|
|
220
235
|
- **`source`**: The name of the field in `extra` from which the value is taken.
|
|
236
|
+
Must be a string for leaf nodes.
|
|
237
|
+
- **`item_type`**: Optional field for list-typed leaves. Must be a string if
|
|
238
|
+
present. See "List-typed fields" section below.
|
|
221
239
|
- **Root key restrictions**: Root keys cannot conflict with standard `logging`
|
|
222
240
|
module fields (e.g., `name`, `levelno`, `pathname`). Such conflicts cause
|
|
223
241
|
schema validation to fail.
|
|
@@ -180,11 +180,27 @@ An example of a valid empty schema (no leaves, no problems):
|
|
|
180
180
|
|
|
181
181
|
**Schema structure:**
|
|
182
182
|
|
|
183
|
-
- **Inner nodes**: Objects
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
- **Inner nodes**: Objects that contain child nodes (any fields that are objects).
|
|
184
|
+
Inner nodes cannot have `type` or `source` as strings (they can only have
|
|
185
|
+
these fields as objects, which are child nodes). Inner nodes must have at
|
|
186
|
+
least one child node.
|
|
187
|
+
- **Leaf nodes**: Objects with `type` and `source` fields as strings (properties).
|
|
188
|
+
A valid leaf must have both `type` and `source` present and non-empty. Leaf
|
|
189
|
+
nodes cannot have child nodes (any fields that are objects).
|
|
190
|
+
- **Node validation rules**:
|
|
191
|
+
- A node cannot be both a leaf and an inner node (cannot have both properties
|
|
192
|
+
and children simultaneously).
|
|
193
|
+
- A node cannot be empty (must be either a leaf with properties or an inner
|
|
194
|
+
node with children).
|
|
195
|
+
- Fields `type`, `source`, and `item_type` can be used as child node names
|
|
196
|
+
in inner nodes (as objects), but then they are treated as children, not
|
|
197
|
+
as leaf properties.
|
|
186
198
|
- **`type`**: One of `"str"`, `"int"`, `"float"`, `"bool"`, or `"list"`.
|
|
199
|
+
Must be a string for leaf nodes.
|
|
187
200
|
- **`source`**: The name of the field in `extra` from which the value is taken.
|
|
201
|
+
Must be a string for leaf nodes.
|
|
202
|
+
- **`item_type`**: Optional field for list-typed leaves. Must be a string if
|
|
203
|
+
present. See "List-typed fields" section below.
|
|
188
204
|
- **Root key restrictions**: Root keys cannot conflict with standard `logging`
|
|
189
205
|
module fields (e.g., `name`, `levelno`, `pathname`). Such conflicts cause
|
|
190
206
|
schema validation to fail.
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "logging-objects-with-schema"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
description = "Proxy logging wrapper that validates extra fields against a JSON schema."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -27,7 +27,8 @@ classifiers = [
|
|
|
27
27
|
"Programming Language :: Python :: 3.14",
|
|
28
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
29
29
|
"Intended Audience :: Developers",
|
|
30
|
-
"Topic :: System :: Logging"
|
|
30
|
+
"Topic :: System :: Logging",
|
|
31
|
+
"Typing :: Typed"
|
|
31
32
|
]
|
|
32
33
|
|
|
33
34
|
[[project.authors]]
|
|
@@ -44,7 +45,8 @@ classifiers = [
|
|
|
44
45
|
"flake8-pyproject>=1.2.3",
|
|
45
46
|
"mypy>=1.18.1",
|
|
46
47
|
"bandit>=1.8.6",
|
|
47
|
-
"pre-commit>=3.5.0"
|
|
48
|
+
"pre-commit>=3.5.0",
|
|
49
|
+
"vulture>=2.0.0"
|
|
48
50
|
]
|
|
49
51
|
|
|
50
52
|
[project.urls]
|
|
@@ -93,8 +95,7 @@ warn_unreachable = true
|
|
|
93
95
|
strict_equality = true
|
|
94
96
|
|
|
95
97
|
[tool.bandit]
|
|
96
|
-
|
|
97
|
-
exclude_dirs = [ ".venv", "__pycache__", ".git", "htmlcov" ]
|
|
98
|
+
exclude_dirs = [ ".venv", "__pycache__", ".git", "htmlcov", "tests" ]
|
|
98
99
|
|
|
99
100
|
[tool.coverage.run]
|
|
100
101
|
source = [ "src" ]
|
|
@@ -115,3 +116,9 @@ skip_covered = false
|
|
|
115
116
|
|
|
116
117
|
[tool.coverage.html]
|
|
117
118
|
directory = "htmlcov"
|
|
119
|
+
|
|
120
|
+
[tool.vulture]
|
|
121
|
+
min_confidence = 80
|
|
122
|
+
exclude = [ "__pycache__" ]
|
|
123
|
+
ignore_names = [ "cls" ]
|
|
124
|
+
paths = [ "src" ]
|
|
File without changes
|
|
@@ -16,7 +16,7 @@ import threading
|
|
|
16
16
|
from collections.abc import Iterable, Mapping, MutableMapping
|
|
17
17
|
from dataclasses import dataclass
|
|
18
18
|
from pathlib import Path
|
|
19
|
-
from typing import Any
|
|
19
|
+
from typing import Any, Literal
|
|
20
20
|
|
|
21
21
|
from .errors import _SchemaProblem
|
|
22
22
|
|
|
@@ -177,24 +177,31 @@ def _check_cached_found_file_path() -> Path | None:
|
|
|
177
177
|
When a schema file was found, its absolute path is cached as CWD-independent.
|
|
178
178
|
This function checks if the cached path still exists on disk.
|
|
179
179
|
|
|
180
|
+
Note: This function must be called while holding _path_cache_lock.
|
|
181
|
+
|
|
180
182
|
Returns:
|
|
181
183
|
Cached path if file still exists, None if file was deleted (cache invalidated).
|
|
182
184
|
"""
|
|
183
185
|
global _resolved_schema_path
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return None
|
|
188
|
-
|
|
189
|
-
# Schema file was found, absolute path doesn't depend on CWD
|
|
190
|
-
# Return it if it still exists
|
|
191
|
-
if _resolved_schema_path.exists():
|
|
192
|
-
return _resolved_schema_path
|
|
187
|
+
if _resolved_schema_path is None:
|
|
188
|
+
return None
|
|
193
189
|
|
|
194
|
-
|
|
195
|
-
|
|
190
|
+
# Check if this is actually a found file cache (not missing file cache)
|
|
191
|
+
# by checking _cached_cwd. We don't need 'global' here since we only read it.
|
|
192
|
+
if _cached_cwd is not None:
|
|
193
|
+
# This path was cached as missing file, not found file
|
|
196
194
|
return None
|
|
197
195
|
|
|
196
|
+
# Schema file was found, absolute path doesn't depend on CWD
|
|
197
|
+
# Return it if it still exists
|
|
198
|
+
if _resolved_schema_path.exists():
|
|
199
|
+
return _resolved_schema_path
|
|
200
|
+
|
|
201
|
+
# Cached file no longer exists; invalidate cache so caller will re-search
|
|
202
|
+
_resolved_schema_path = None
|
|
203
|
+
return None
|
|
204
|
+
|
|
198
205
|
|
|
199
206
|
def _check_cached_missing_file_path() -> Path | None:
|
|
200
207
|
"""Check if cached path for a missing file is still valid.
|
|
@@ -202,26 +209,27 @@ def _check_cached_missing_file_path() -> Path | None:
|
|
|
202
209
|
When a schema file was not found, a path based on CWD is cached.
|
|
203
210
|
This function checks if CWD has changed since caching.
|
|
204
211
|
|
|
212
|
+
Note: This function must be called while holding _path_cache_lock.
|
|
213
|
+
|
|
205
214
|
Returns:
|
|
206
215
|
Cached path if CWD unchanged, None if CWD changed (cache invalidated).
|
|
207
216
|
"""
|
|
208
217
|
global _resolved_schema_path, _cached_cwd
|
|
209
218
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return None
|
|
219
|
+
if _resolved_schema_path is None or _cached_cwd is None:
|
|
220
|
+
return None
|
|
213
221
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
+
# Cached path is based on CWD when file was not found,
|
|
223
|
+
# check if CWD changed
|
|
224
|
+
current_cwd = _get_current_working_directory()
|
|
225
|
+
if current_cwd != _cached_cwd:
|
|
226
|
+
# CWD changed, invalidate cache and re-search from new CWD
|
|
227
|
+
_resolved_schema_path = None
|
|
228
|
+
_cached_cwd = None
|
|
229
|
+
return None
|
|
222
230
|
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
# CWD unchanged, return cached path
|
|
232
|
+
return _resolved_schema_path
|
|
225
233
|
|
|
226
234
|
|
|
227
235
|
def _cache_and_return_found_path(found_path: Path) -> Path:
|
|
@@ -383,11 +391,99 @@ def _is_empty_or_none(value: Any) -> bool:
|
|
|
383
391
|
return value is None or (isinstance(value, str) and value.strip() == "")
|
|
384
392
|
|
|
385
393
|
|
|
394
|
+
def _determine_node_type_and_validate(
|
|
395
|
+
value_dict: dict[str, Any],
|
|
396
|
+
path: tuple[str, ...],
|
|
397
|
+
key: str,
|
|
398
|
+
problems: list[_SchemaProblem],
|
|
399
|
+
) -> tuple[Literal["leaf", "inner"] | None, bool]:
|
|
400
|
+
"""Determine node type (leaf/inner) and validate node structure.
|
|
401
|
+
|
|
402
|
+
A node can be either:
|
|
403
|
+
- A leaf node: has 'type' and 'source' as strings (properties), no children
|
|
404
|
+
- An inner node: has children (any fields that are objects), no leaf properties
|
|
405
|
+
|
|
406
|
+
A node cannot have both properties and children, and cannot be empty.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
value_dict: Dictionary containing node data.
|
|
410
|
+
path: Current path in the schema tree.
|
|
411
|
+
key: Current key being processed.
|
|
412
|
+
problems: List to collect validation problems. Validation errors are
|
|
413
|
+
automatically appended to this list when an invalid node is detected.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Tuple of (node_type, is_valid) where:
|
|
417
|
+
- node_type: "leaf" for valid leaf nodes, "inner" for valid inner nodes,
|
|
418
|
+
or None if the node is invalid
|
|
419
|
+
- is_valid: True if node is valid, False if there are validation errors
|
|
420
|
+
|
|
421
|
+
When is_valid is False:
|
|
422
|
+
- node_type is always None
|
|
423
|
+
- A validation problem has been added to the problems list
|
|
424
|
+
- The caller should skip processing this node (e.g., use continue)
|
|
425
|
+
"""
|
|
426
|
+
type_value = value_dict.get("type")
|
|
427
|
+
source_value = value_dict.get("source")
|
|
428
|
+
item_type_value = value_dict.get("item_type")
|
|
429
|
+
|
|
430
|
+
# Check if node has leaf properties
|
|
431
|
+
# Leaf properties are: type (required, string), source (required, string),
|
|
432
|
+
# item_type (optional, string). If type/source/item_type are objects,
|
|
433
|
+
# they are children, not properties
|
|
434
|
+
has_leaf_properties = (
|
|
435
|
+
isinstance(type_value, str)
|
|
436
|
+
or isinstance(source_value, str)
|
|
437
|
+
or isinstance(item_type_value, str)
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# Check if node has children (any field that is an object/Mapping)
|
|
441
|
+
# Children can have ANY names, including type, source, item_type - this is
|
|
442
|
+
# valid for inner nodes. If type, source, or item_type are objects,
|
|
443
|
+
# they count as children
|
|
444
|
+
has_children = any(
|
|
445
|
+
isinstance(field_value, Mapping) for field_value in value_dict.values()
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Validate node structure
|
|
449
|
+
if has_leaf_properties and has_children:
|
|
450
|
+
# Node cannot have both properties and children
|
|
451
|
+
problems.append(
|
|
452
|
+
_SchemaProblem(
|
|
453
|
+
f"Invalid node at {_format_path(path, key)}: "
|
|
454
|
+
f"node cannot have both properties (type/source as strings) "
|
|
455
|
+
f"and children (object fields)"
|
|
456
|
+
),
|
|
457
|
+
)
|
|
458
|
+
return (None, False)
|
|
459
|
+
|
|
460
|
+
if not has_leaf_properties and not has_children:
|
|
461
|
+
# Node must be either a leaf or have children
|
|
462
|
+
problems.append(
|
|
463
|
+
_SchemaProblem(
|
|
464
|
+
f"Invalid node at {_format_path(path, key)}: "
|
|
465
|
+
f"node must be either a leaf (with type/source as strings) "
|
|
466
|
+
f"or have children (object fields)"
|
|
467
|
+
),
|
|
468
|
+
)
|
|
469
|
+
return (None, False)
|
|
470
|
+
|
|
471
|
+
# Node is valid - determine type
|
|
472
|
+
if has_leaf_properties:
|
|
473
|
+
return ("leaf", True)
|
|
474
|
+
else: # has_children
|
|
475
|
+
return ("inner", True)
|
|
476
|
+
|
|
477
|
+
|
|
386
478
|
def _is_leaf_node(value_dict: dict[str, Any]) -> bool:
|
|
387
479
|
"""Check if a schema node is a leaf node.
|
|
388
480
|
|
|
389
|
-
A leaf node is identified by having
|
|
390
|
-
|
|
481
|
+
A leaf node is identified by having at least one of 'type' or 'source' fields
|
|
482
|
+
that is a string. If 'type' or 'source' are themselves objects (not strings),
|
|
483
|
+
they are child nodes, not leaf properties.
|
|
484
|
+
|
|
485
|
+
Inner nodes have either no 'type'/'source' fields, or have these fields as
|
|
486
|
+
objects (child nodes) rather than strings.
|
|
391
487
|
|
|
392
488
|
We use `.get()` with `is not None` check instead of `in` operator because:
|
|
393
489
|
- A field might be present but have a None value (which indicates an error)
|
|
@@ -400,7 +496,19 @@ def _is_leaf_node(value_dict: dict[str, Any]) -> bool:
|
|
|
400
496
|
Returns:
|
|
401
497
|
True if the node is a leaf, False if it's an inner node.
|
|
402
498
|
"""
|
|
403
|
-
|
|
499
|
+
type_value = value_dict.get("type")
|
|
500
|
+
source_value = value_dict.get("source")
|
|
501
|
+
|
|
502
|
+
# If either field is an object (Mapping), this is an inner node, not a leaf
|
|
503
|
+
if isinstance(type_value, Mapping) or isinstance(source_value, Mapping):
|
|
504
|
+
return False
|
|
505
|
+
|
|
506
|
+
# Check if at least one field is present and is a string
|
|
507
|
+
if isinstance(type_value, str) or isinstance(source_value, str):
|
|
508
|
+
return True
|
|
509
|
+
|
|
510
|
+
# Neither field is present as a string - this is an inner node
|
|
511
|
+
return False
|
|
404
512
|
|
|
405
513
|
|
|
406
514
|
def _validate_and_create_leaf(
|
|
@@ -424,8 +532,10 @@ def _validate_and_create_leaf(
|
|
|
424
532
|
leaf_source = value_dict.get("source")
|
|
425
533
|
|
|
426
534
|
# This is supposed to be a leaf - validate required fields first.
|
|
427
|
-
|
|
428
|
-
|
|
535
|
+
# Type must be a string (not None, not empty, and not other types like bool/int)
|
|
536
|
+
type_invalid = _is_empty_or_none(leaf_type) or not isinstance(leaf_type, str)
|
|
537
|
+
# Source must be a string (not None, not empty, and not other types like bool/int)
|
|
538
|
+
source_invalid = _is_empty_or_none(leaf_source) or not isinstance(leaf_source, str)
|
|
429
539
|
|
|
430
540
|
if type_invalid:
|
|
431
541
|
problems.append(
|
|
@@ -446,6 +556,9 @@ def _validate_and_create_leaf(
|
|
|
446
556
|
if type_invalid or source_invalid:
|
|
447
557
|
return None
|
|
448
558
|
|
|
559
|
+
# Note: Check for children is done in _determine_node_type_and_validate()
|
|
560
|
+
# before this function is called, so we don't need to check here.
|
|
561
|
+
|
|
449
562
|
# Convert to string before lookup to handle cases where the JSON parser
|
|
450
563
|
# might return non-string types (though this shouldn't happen with valid JSON).
|
|
451
564
|
# This ensures type safety and consistent behavior.
|
|
@@ -463,7 +576,11 @@ def _validate_and_create_leaf(
|
|
|
463
576
|
# to ensure element homogeneity (e.g. list[str], list[int]).
|
|
464
577
|
if expected_type is list:
|
|
465
578
|
item_type_name = value_dict.get("item_type")
|
|
466
|
-
|
|
579
|
+
# Item type must be a string (not None, not empty, and not other types
|
|
580
|
+
# like bool/int)
|
|
581
|
+
item_type_invalid = _is_empty_or_none(item_type_name) or not isinstance(
|
|
582
|
+
item_type_name, str
|
|
583
|
+
)
|
|
467
584
|
if item_type_invalid:
|
|
468
585
|
problems.append(
|
|
469
586
|
_SchemaProblem(
|
|
@@ -556,12 +673,25 @@ def _compile_schema_tree(
|
|
|
556
673
|
# might be a read-only Mapping (e.g., from JSON parsing).
|
|
557
674
|
value_dict = dict(value)
|
|
558
675
|
|
|
559
|
-
|
|
676
|
+
# Determine node type and validate structure
|
|
677
|
+
# (checks for mixed nodes, empty nodes, etc.)
|
|
678
|
+
node_type, is_valid = _determine_node_type_and_validate(
|
|
679
|
+
value_dict, path, key, problems
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
if not is_valid:
|
|
683
|
+
# Node has validation errors, skip processing but continue with other nodes
|
|
684
|
+
continue
|
|
685
|
+
|
|
686
|
+
if node_type == "leaf":
|
|
687
|
+
# Process as leaf node
|
|
560
688
|
leaf = _validate_and_create_leaf(value_dict, path, key, problems)
|
|
561
689
|
if leaf is not None:
|
|
562
690
|
yield leaf
|
|
563
|
-
else:
|
|
564
|
-
#
|
|
691
|
+
else: # node_type == "inner"
|
|
692
|
+
# Process as inner node - recurse into children
|
|
693
|
+
# Note: node_type can only be "leaf" or "inner" when is_valid is True.
|
|
694
|
+
# If is_valid is False, we already continue above.
|
|
565
695
|
for child_leaf in _compile_schema_tree(value_dict, path + (key,), problems):
|
|
566
696
|
yield child_leaf
|
|
567
697
|
|
|
@@ -678,8 +808,8 @@ def _compile_schema_internal(
|
|
|
678
808
|
given path and forbidden keys set has been observed (including the cases
|
|
679
809
|
when it is missing or invalid), subsequent calls always return the cached
|
|
680
810
|
result without re-reading or re-compiling the schema. To pick up on-disk
|
|
681
|
-
changes to the schema, the application must restart the process.
|
|
682
|
-
|
|
811
|
+
changes to the schema, the application must restart the process. The schema
|
|
812
|
+
is cached process-wide and is thread-safe.
|
|
683
813
|
|
|
684
814
|
Args:
|
|
685
815
|
forbidden_keys: Additional forbidden root keys to check against.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|