|
3 | 3 | Note: Having no "from __future__ import annotations" here is wanted (pydantic compatibility).
|
4 | 4 | """
|
5 | 5 |
|
6 |
| -from typing import Any, Callable, Dict, List, Optional |
| 6 | +from typing import Annotated, Any, Callable, Dict, List, Optional |
7 | 7 |
|
8 |
| -try: |
9 |
| - from pydantic import v1 as pydantic |
10 |
| -except ImportError: |
11 |
| - import pydantic # type: ignore |
| 8 | +import pydantic |
| 9 | +from pydantic.version import VERSION |
12 | 10 |
|
13 | 11 | from arta.utils import ParsingErrorStrategy
|
14 | 12 |
|
| 13 | +if not VERSION.startswith("1."): |
| 14 | + # ---------------------------------- |
| 15 | + # For instantiation using rules_dict |
| 16 | + class RuleRaw(pydantic.BaseModel): |
| 17 | + """Pydantic model for validating a rule.""" |
15 | 18 |
|
16 |
| -# ---------------------------------- |
17 |
| -# For instantiation using rules_dict |
18 |
| -class RuleRaw(pydantic.BaseModel): |
19 |
| - """Pydantic model for validating a rule.""" |
| 19 | + condition: Optional[Callable] |
| 20 | + condition_parameters: Optional[Dict[str, Any]] |
| 21 | + action: Callable |
| 22 | + action_parameters: Optional[Dict[str, Any]] |
20 | 23 |
|
21 |
| - condition: Optional[Callable] |
22 |
| - condition_parameters: Optional[Dict[str, Any]] |
23 |
| - action: Callable |
24 |
| - action_parameters: Optional[Dict[str, Any]] |
| 24 | + model_config = pydantic.ConfigDict(extra="forbid") |
25 | 25 |
|
26 |
| - class Config: |
27 |
| - extra = "forbid" |
| 26 | + class RulesGroup(pydantic.RootModel): # noqa |
| 27 | + """Pydantic model for validating a rules group.""" |
28 | 28 |
|
| 29 | + root: Dict[str, RuleRaw] |
29 | 30 |
|
30 |
| -class RulesGroup(pydantic.BaseModel): |
31 |
| - """Pydantic model for validating a rules group.""" |
| 31 | + class RulesDict(pydantic.RootModel): # noqa |
| 32 | + """Pydantic model for validating rules dict instanciation.""" |
32 | 33 |
|
33 |
| - __root__: Dict[str, RuleRaw] |
| 34 | + root: Dict[str, RulesGroup] |
34 | 35 |
|
| 36 | + # ---------------------------------- |
| 37 | + # For instantiation using config_path |
| 38 | + class Condition(pydantic.BaseModel): |
| 39 | + """Pydantic model for validating a condition.""" |
35 | 40 |
|
36 |
| -class RulesDict(pydantic.BaseModel): |
37 |
| - """Pydantic model for validating rules dict instanciation.""" |
| 41 | + description: str |
| 42 | + validation_function: str |
| 43 | + condition_parameters: Optional[Dict[str, Any]] = None |
38 | 44 |
|
39 |
| - __root__: Dict[str, RulesGroup] |
| 45 | + class RulesConfig(pydantic.BaseModel): |
| 46 | + """Pydantic model for validating a rule group from config file.""" |
40 | 47 |
|
| 48 | + condition: Optional[str] = None |
| 49 | + simple_condition: Optional[str] = None |
| 50 | + action: Annotated[str, pydantic.StringConstraints(to_lower=True)] # type: ignore |
| 51 | + action_parameters: Optional[Any] = None |
41 | 52 |
|
42 |
| -# ---------------------------------- |
43 |
| -# For instantiation using config_path |
44 |
| -class Condition(pydantic.BaseModel): |
45 |
| - """Pydantic model for validating a condition.""" |
| 53 | + model_config = pydantic.ConfigDict(extra="allow") |
46 | 54 |
|
47 |
| - description: str |
48 |
| - validation_function: str |
49 |
| - condition_parameters: Optional[Dict[str, Any]] |
| 55 | + class Configuration(pydantic.BaseModel): |
| 56 | + """Pydantic model for validating configuration files.""" |
50 | 57 |
|
| 58 | + conditions: Optional[Dict[str, Condition]] = None |
| 59 | + conditions_source_modules: Optional[List[str]] = None |
| 60 | + actions_source_modules: List[str] |
| 61 | + custom_classes_source_modules: Optional[List[str]] = None |
| 62 | + condition_factory_mapping: Optional[Dict[str, str]] = None |
| 63 | + rules: Dict[str, Dict[str, Dict[str, RulesConfig]]] |
| 64 | + parsing_error_strategy: Optional[ParsingErrorStrategy] = None |
51 | 65 |
|
52 |
| -class RulesConfig(pydantic.BaseModel): |
53 |
| - """Pydantic model for validating a rule group from config file.""" |
| 66 | + model_config = pydantic.ConfigDict(extra="ignore") |
54 | 67 |
|
55 |
| - condition: Optional[str] |
56 |
| - simple_condition: Optional[str] |
57 |
| - action: pydantic.constr(to_lower=True) # type: ignore |
58 |
| - action_parameters: Optional[Any] |
| 68 | + @pydantic.field_validator("rules", mode="before") # noqa |
| 69 | + def upper_key(cls, vl): # noqa |
| 70 | + """Validate and uppercase keys for RulesConfig""" |
| 71 | + for k, v in vl.items(): |
| 72 | + for kk, vv in v.items(): |
| 73 | + for key, rules in [*vv.items()]: |
| 74 | + if key != str(key).upper(): |
| 75 | + del vl[k][kk][key] |
| 76 | + vl[k][kk][str(key).upper()] = rules |
| 77 | + return vl |
59 | 78 |
|
60 |
| - class Config: |
61 |
| - extra = "allow" |
| 79 | +else: |
62 | 80 |
|
| 81 | + class BaseModelV2(pydantic.BaseModel): |
| 82 | + """Wrapper to expose missed methods used elsewhere in the code""" |
63 | 83 |
|
64 |
| -class Configuration(pydantic.BaseModel): |
65 |
| - """Pydantic model for validating configuration files.""" |
| 84 | + model_dump: Callable = pydantic.BaseModel.dict # noqa |
66 | 85 |
|
67 |
| - conditions: Optional[Dict[str, Condition]] |
68 |
| - conditions_source_modules: Optional[List[str]] |
69 |
| - actions_source_modules: List[str] |
70 |
| - custom_classes_source_modules: Optional[List[str]] |
71 |
| - condition_factory_mapping: Optional[Dict[str, str]] |
72 |
| - rules: Dict[str, Dict[str, Dict[pydantic.constr(to_upper=True), RulesConfig]]] # type: ignore |
73 |
| - parsing_error_strategy: Optional[ParsingErrorStrategy] |
| 86 | + @classmethod |
| 87 | + def model_validate(cls, obj): # noqa |
| 88 | + return cls.parse_obj(obj) # noqa |
74 | 89 |
|
75 |
| - class Config: |
76 |
| - extra = "ignore" |
| 90 | + # ---------------------------------- |
| 91 | + # For instantiation using rules_dict |
| 92 | + class RuleRaw(BaseModelV2): # type: ignore[no-redef] |
| 93 | + """Pydantic model for validating a rule.""" |
| 94 | + |
| 95 | + condition: Optional[Callable] |
| 96 | + condition_parameters: Optional[Dict[str, Any]] |
| 97 | + action: Callable |
| 98 | + action_parameters: Optional[Dict[str, Any]] |
| 99 | + |
| 100 | + class Config: |
| 101 | + extra = "forbid" |
| 102 | + |
| 103 | + class RulesGroup(pydantic.BaseModel): # type: ignore[no-redef] # noqa |
| 104 | + """Pydantic model for validating a rules group.""" |
| 105 | + |
| 106 | + __root__: Dict[str, RuleRaw] # noqa |
| 107 | + |
| 108 | + class RulesDict(BaseModelV2): # type: ignore[no-redef] # noqa |
| 109 | + """Pydantic model for validating rules dict instanciation.""" |
| 110 | + |
| 111 | + __root__: Dict[str, RulesGroup] # noqa |
| 112 | + |
| 113 | + # ---------------------------------- |
| 114 | + # For instantiation using config_path |
| 115 | + class Condition(BaseModelV2): # type: ignore[no-redef] |
| 116 | + """Pydantic model for validating a condition.""" |
| 117 | + |
| 118 | + description: str |
| 119 | + validation_function: str |
| 120 | + condition_parameters: Optional[Dict[str, Any]] |
| 121 | + |
| 122 | + class RulesConfig(BaseModelV2): # type: ignore[no-redef] |
| 123 | + """Pydantic model for validating a rule group from config file.""" |
| 124 | + |
| 125 | + condition: Optional[str] |
| 126 | + simple_condition: Optional[str] |
| 127 | + action: pydantic.constr(to_lower=True) # type: ignore |
| 128 | + action_parameters: Optional[Any] |
| 129 | + |
| 130 | + class Config: |
| 131 | + extra = "allow" |
| 132 | + |
| 133 | + class Configuration(BaseModelV2): # type: ignore[no-redef] |
| 134 | + """Pydantic model for validating configuration files.""" |
| 135 | + |
| 136 | + conditions: Optional[Dict[str, Condition]] |
| 137 | + conditions_source_modules: Optional[List[str]] |
| 138 | + actions_source_modules: List[str] |
| 139 | + custom_classes_source_modules: Optional[List[str]] |
| 140 | + condition_factory_mapping: Optional[Dict[str, str]] |
| 141 | + rules: Dict[str, Dict[str, Dict[pydantic.constr(to_upper=True), RulesConfig]]] # type: ignore |
| 142 | + parsing_error_strategy: Optional[ParsingErrorStrategy] |
| 143 | + |
| 144 | + class Config: |
| 145 | + extra = "ignore" |
0 commit comments