pycarlo 0.10.194__py3-none-any.whl → 0.11.15__py3-none-any.whl
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.
- pycarlo/features/metadata/__init__.py +20 -3
- pycarlo/features/metadata/asset_allow_block_list.py +22 -0
- pycarlo/features/metadata/asset_filters_container.py +79 -0
- pycarlo/features/metadata/base_allow_block_list.py +137 -0
- pycarlo/features/metadata/metadata_allow_block_list.py +94 -0
- pycarlo/features/metadata/metadata_filters_container.py +12 -7
- pycarlo/lib/schema.json +52512 -46529
- pycarlo/lib/schema.py +2309 -250
- {pycarlo-0.10.194.dist-info → pycarlo-0.11.15.dist-info}/METADATA +1 -1
- {pycarlo-0.10.194.dist-info → pycarlo-0.11.15.dist-info}/RECORD +13 -10
- pycarlo/features/metadata/allow_block_list.py +0 -159
- {pycarlo-0.10.194.dist-info → pycarlo-0.11.15.dist-info}/LICENSE +0 -0
- {pycarlo-0.10.194.dist-info → pycarlo-0.11.15.dist-info}/WHEEL +0 -0
- {pycarlo-0.10.194.dist-info → pycarlo-0.11.15.dist-info}/top_level.txt +0 -0
|
@@ -20,9 +20,12 @@ pycarlo/features/circuit_breakers/service.py,sha256=TljwMOhA5igBumkpJwM22iEJGyvt
|
|
|
20
20
|
pycarlo/features/dbt/__init__.py,sha256=A2cFr8_aSY_kDw1m7jR6QkHfiBMC1cZ6O8WosF9XrRg,85
|
|
21
21
|
pycarlo/features/dbt/dbt_importer.py,sha256=eJb9Jiu7tAEb_xsLO-ycDOjjVm-LLfTtbAzlzIRxT5I,7328
|
|
22
22
|
pycarlo/features/dbt/queries.py,sha256=9o1HevRECYyGXQ0lG0LrN4iuuXCS-SWjz7NTgAAZIro,621
|
|
23
|
-
pycarlo/features/metadata/__init__.py,sha256=
|
|
24
|
-
pycarlo/features/metadata/
|
|
25
|
-
pycarlo/features/metadata/
|
|
23
|
+
pycarlo/features/metadata/__init__.py,sha256=0RDVHnwPvQcNXkeXEAxL4VJ8VWrl2P0fft_Kl2nlo7I,912
|
|
24
|
+
pycarlo/features/metadata/asset_allow_block_list.py,sha256=jXCS7HtUJhexEXZyRzyN4MT-BPSaMTKWCWiBT_l-Ijo,761
|
|
25
|
+
pycarlo/features/metadata/asset_filters_container.py,sha256=O15SC6u7HMGlViYnX7H8MRFttQrIXQd3a0oOrz7My5U,3411
|
|
26
|
+
pycarlo/features/metadata/base_allow_block_list.py,sha256=c8zd0BXkNSfQ3LkNC_A0KrO2AsYtIowc8FjbdyDyDu0,4738
|
|
27
|
+
pycarlo/features/metadata/metadata_allow_block_list.py,sha256=HzgXE0WhwDZoBHeV04e8dwe88rUSVVnOl8nFEPlb0jA,3496
|
|
28
|
+
pycarlo/features/metadata/metadata_filters_container.py,sha256=GCbk-t7Njq2IiQrre186aN3DT2ytN-8RTwBPnBmfgJA,12715
|
|
26
29
|
pycarlo/features/pii/__init__.py,sha256=w5X-oD8HWaL6fP2jt40AhlXO-MNzlVAlhRaZ5kQqAZY,247
|
|
27
30
|
pycarlo/features/pii/constants.py,sha256=XWeiikXk9AtljdWsGfl49b9zI6w8EzK8F__Euc0vQ3w,70
|
|
28
31
|
pycarlo/features/pii/pii_filterer.py,sha256=k53b_V_mddY4A17-DJ5vQKszFDlUaiP3E650JFHAeJA,6209
|
|
@@ -35,10 +38,10 @@ pycarlo/features/user/queries.py,sha256=m97RvM0oiBlrU5xmOwe_JJ5N0G0NG5hIOeyQqN2O
|
|
|
35
38
|
pycarlo/features/user/service.py,sha256=DHkhuonySaHro07NTd0YNe3cNkDk62CiRTY77dhVaMs,2890
|
|
36
39
|
pycarlo/lib/README.md,sha256=QGNeUefPzLKGyZqn5aITpcFgkC9WQTNS292BGisRFHk,139
|
|
37
40
|
pycarlo/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
pycarlo/lib/schema.json,sha256=
|
|
39
|
-
pycarlo/lib/schema.py,sha256=
|
|
40
|
-
pycarlo-0.
|
|
41
|
-
pycarlo-0.
|
|
42
|
-
pycarlo-0.
|
|
43
|
-
pycarlo-0.
|
|
44
|
-
pycarlo-0.
|
|
41
|
+
pycarlo/lib/schema.json,sha256=QPrl1YZ0WF2HNxk4EJG3PuypjUEu6z9cT-zby0oXlqY,6521200
|
|
42
|
+
pycarlo/lib/schema.py,sha256=6MMMErpEG-oFTgOfGBoZmQw5tgo4li7XFKXA-JqGY3k,2832020
|
|
43
|
+
pycarlo-0.11.15.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
44
|
+
pycarlo-0.11.15.dist-info/METADATA,sha256=IiKzQSFemktwSfQa1MtnM2A2AeKeBxnbC_y4XwQaWx0,8741
|
|
45
|
+
pycarlo-0.11.15.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
46
|
+
pycarlo-0.11.15.dist-info/top_level.txt,sha256=TIE04H4pgzGaFxAB-gvkmVAUOAoHxxFfhnEcpuQ5bF4,8
|
|
47
|
+
pycarlo-0.11.15.dist-info/RECORD,,
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
import re
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import Any, Callable, List, Optional
|
|
5
|
-
|
|
6
|
-
from dataclasses_json import config, dataclass_json
|
|
7
|
-
|
|
8
|
-
from pycarlo.common import get_logger
|
|
9
|
-
|
|
10
|
-
logger = get_logger(__name__)
|
|
11
|
-
|
|
12
|
-
# For documentation and samples check the link below:
|
|
13
|
-
# https://www.notion.so/montecarlodata/Catalog-Schema-Filtering-59edd6eff7f74c94ab6bfca75d2e3ff1
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _exclude_none_values(value: Any) -> bool:
|
|
17
|
-
return value is None
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class FilterEffectType(enum.Enum):
|
|
21
|
-
BLOCK = "block"
|
|
22
|
-
ALLOW = "allow"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class FilterType(enum.Enum):
|
|
26
|
-
EXACT_MATCH = "exact_match"
|
|
27
|
-
PREFIX = "prefix"
|
|
28
|
-
SUFFIX = "suffix"
|
|
29
|
-
SUBSTRING = "substring"
|
|
30
|
-
REGEXP = "regexp"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass_json
|
|
34
|
-
@dataclass
|
|
35
|
-
class MetadataFilter:
|
|
36
|
-
# we're using exclude=_exclude_none_values to prevent these properties to be serialized to json
|
|
37
|
-
# when None, to keep the json doc simpler
|
|
38
|
-
project: Optional[str] = field(metadata=config(exclude=_exclude_none_values), default=None)
|
|
39
|
-
dataset: Optional[str] = field(metadata=config(exclude=_exclude_none_values), default=None)
|
|
40
|
-
table_type: Optional[str] = field(metadata=config(exclude=_exclude_none_values), default=None)
|
|
41
|
-
table_name: Optional[str] = field(metadata=config(exclude=_exclude_none_values), default=None)
|
|
42
|
-
type: FilterType = FilterType.EXACT_MATCH
|
|
43
|
-
effect: FilterEffectType = FilterEffectType.BLOCK
|
|
44
|
-
|
|
45
|
-
def matches(self, force_regexp: bool = False, **kwargs: Any) -> bool:
|
|
46
|
-
"""
|
|
47
|
-
Returns True if all properties specified in kwargs match the conditions specified in
|
|
48
|
-
properties of the same name in this object.
|
|
49
|
-
Supported keys in kwargs: 'project', 'dataset', 'table', 'table_type'
|
|
50
|
-
For example kwargs={'project': 'prj_1'} will evaluate if 'prj_1' matches the condition in
|
|
51
|
-
self.project. For kwargs={'project': 'prj_1', 'dataset': 'ds_1'} will evaluate if 'prj_1'
|
|
52
|
-
matches the condition in self.project and if 'ds_1' matches the condition in self.dataset.
|
|
53
|
-
If any of the conditions (for example self.project) is None, that condition will be matched.
|
|
54
|
-
"""
|
|
55
|
-
if not kwargs:
|
|
56
|
-
raise ValueError("At least one field needs to be specified for matching")
|
|
57
|
-
|
|
58
|
-
# kwargs must match the field names in this class, if any of them do not,
|
|
59
|
-
# invalidate the filter.
|
|
60
|
-
try:
|
|
61
|
-
is_match = all(
|
|
62
|
-
self._safe_match(
|
|
63
|
-
component=getattr(self, component),
|
|
64
|
-
value=value,
|
|
65
|
-
force_regexp=force_regexp,
|
|
66
|
-
filter_type=self.type
|
|
67
|
-
if self.filter_type_target_field() == component
|
|
68
|
-
else FilterType.EXACT_MATCH,
|
|
69
|
-
)
|
|
70
|
-
for component, value in kwargs.items()
|
|
71
|
-
)
|
|
72
|
-
except AttributeError:
|
|
73
|
-
is_match = False
|
|
74
|
-
|
|
75
|
-
return is_match
|
|
76
|
-
|
|
77
|
-
def filter_type_target_field(self) -> str:
|
|
78
|
-
"""
|
|
79
|
-
The field that is evaluated using filter type. Other fields should be
|
|
80
|
-
compared using exact match.
|
|
81
|
-
"""
|
|
82
|
-
if self.table_name is not None:
|
|
83
|
-
return "table_name"
|
|
84
|
-
if self.dataset is not None:
|
|
85
|
-
return "dataset"
|
|
86
|
-
if self.project is not None:
|
|
87
|
-
return "project"
|
|
88
|
-
|
|
89
|
-
logger.exception("Invalid filter, missing target values")
|
|
90
|
-
return ""
|
|
91
|
-
|
|
92
|
-
@classmethod
|
|
93
|
-
def _safe_match(
|
|
94
|
-
cls,
|
|
95
|
-
component: Optional[str],
|
|
96
|
-
value: Optional[str],
|
|
97
|
-
force_regexp: bool,
|
|
98
|
-
filter_type: FilterType,
|
|
99
|
-
) -> bool:
|
|
100
|
-
# Field not specified on this object, e.g. self.dataset=None, which matches everything
|
|
101
|
-
if component is None:
|
|
102
|
-
return True
|
|
103
|
-
# The value in kwargs is empty, it does not match the condition.
|
|
104
|
-
if value is None:
|
|
105
|
-
return False
|
|
106
|
-
|
|
107
|
-
# Convert it in lowercase. In the normalizer we are converting identifiers
|
|
108
|
-
# (like project/dataset) to lowercase so the metadata filters may be defined with
|
|
109
|
-
# lowercase on the UI, however on Snowflake the identifiers are usually in uppercase.
|
|
110
|
-
# Therefore, we perform the evaluation case-insensitive.
|
|
111
|
-
component = component.lower()
|
|
112
|
-
value = value.lower()
|
|
113
|
-
|
|
114
|
-
if force_regexp or filter_type == FilterType.REGEXP:
|
|
115
|
-
regexp = f"^{component}$" # Anchor the regexp to be more strict about what to match.
|
|
116
|
-
return re.match(regexp, value) is not None
|
|
117
|
-
elif filter_type == FilterType.PREFIX:
|
|
118
|
-
return value.startswith(component)
|
|
119
|
-
elif filter_type == FilterType.SUFFIX:
|
|
120
|
-
return value.endswith(component)
|
|
121
|
-
elif filter_type == FilterType.SUBSTRING:
|
|
122
|
-
return component in value
|
|
123
|
-
else:
|
|
124
|
-
return component == value
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@dataclass_json
|
|
128
|
-
@dataclass
|
|
129
|
-
class AllowBlockList:
|
|
130
|
-
filters: List[MetadataFilter] = field(default_factory=list)
|
|
131
|
-
default_effect: FilterEffectType = FilterEffectType.ALLOW
|
|
132
|
-
|
|
133
|
-
@property
|
|
134
|
-
def other_effect(self) -> FilterEffectType:
|
|
135
|
-
return (
|
|
136
|
-
FilterEffectType.ALLOW
|
|
137
|
-
if self.default_effect == FilterEffectType.BLOCK
|
|
138
|
-
else FilterEffectType.BLOCK
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
def get_default_effect_filters(
|
|
142
|
-
self, condition: Optional[Callable[[MetadataFilter], bool]] = None
|
|
143
|
-
) -> List[MetadataFilter]:
|
|
144
|
-
return list(
|
|
145
|
-
filter(
|
|
146
|
-
lambda f: f.effect == self.default_effect and (condition is None or condition(f)),
|
|
147
|
-
self.filters,
|
|
148
|
-
)
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
def get_other_effect_filters(
|
|
152
|
-
self, condition: Optional[Callable[[MetadataFilter], bool]] = None
|
|
153
|
-
) -> List[MetadataFilter]:
|
|
154
|
-
return list(
|
|
155
|
-
filter(
|
|
156
|
-
lambda f: f.effect != self.default_effect and (condition is None or condition(f)),
|
|
157
|
-
self.filters,
|
|
158
|
-
)
|
|
159
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|