floe-python 0.8.0__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.
- floe/__init__.py +327 -0
- floe/__main__.py +5 -0
- floe/_cli.py +302 -0
- floe/_cli_output.py +187 -0
- floe/add_entity.py +516 -0
- floe/checks/cast.py +160 -0
- floe/checks/mismatch.py +137 -0
- floe/checks/normalize.py +118 -0
- floe/checks/rows.py +116 -0
- floe/checks/unique.py +247 -0
- floe/config/__init__.py +0 -0
- floe/config/entity.py +479 -0
- floe/config/include.py +122 -0
- floe/config/parse.py +140 -0
- floe/config/profile.py +381 -0
- floe/config/project.py +404 -0
- floe/config/storage.py +179 -0
- floe/config/types.py +195 -0
- floe/config/validate.py +803 -0
- floe/config/vars.py +127 -0
- floe/config/yaml.py +263 -0
- floe/errors.py +13 -0
- floe/glob.py +300 -0
- floe/iceberg/catalog.py +345 -0
- floe/iceberg/compact.py +139 -0
- floe/iceberg/merge.py +297 -0
- floe/iceberg/sink.py +208 -0
- floe/iceberg/unique.py +61 -0
- floe/iceberg/write.py +270 -0
- floe/lineage/events.py +163 -0
- floe/lineage/observer.py +261 -0
- floe/maintain.py +134 -0
- floe/manifest.py +503 -0
- floe/manifest_run.py +299 -0
- floe/pii.py +134 -0
- floe/py.typed +0 -0
- floe/read/__init__.py +0 -0
- floe/read/avro.py +40 -0
- floe/read/csv.py +138 -0
- floe/read/json.py +236 -0
- floe/read/orc.py +42 -0
- floe/read/parquet.py +43 -0
- floe/read/xlsx.py +75 -0
- floe/read/xml.py +227 -0
- floe/report/entity.py +245 -0
- floe/report/summary.py +114 -0
- floe/run/__init__.py +175 -0
- floe/run/entity.py +963 -0
- floe/run/events.py +42 -0
- floe/state.py +311 -0
- floe/storage/__init__.py +0 -0
- floe/storage/archive.py +31 -0
- floe/storage/inputs.py +302 -0
- floe/storage/local.py +175 -0
- floe/storage/partitioning.py +116 -0
- floe/storage/paths.py +69 -0
- floe/storage/resolve.py +55 -0
- floe/storage/s3.py +182 -0
- floe/storage/types.py +33 -0
- floe/write/__init__.py +0 -0
- floe/write/outputs.py +121 -0
- floe_python-0.8.0.dist-info/METADATA +15 -0
- floe_python-0.8.0.dist-info/RECORD +66 -0
- floe_python-0.8.0.dist-info/WHEEL +4 -0
- floe_python-0.8.0.dist-info/entry_points.txt +2 -0
- floe_python-0.8.0.dist-info/licenses/LICENSE +21 -0
floe/config/types.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Config models, ported from `crates/floe-core/src/config/types.rs` (entity-level fields)."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from floe.pii import PiiColumn
|
|
8
|
+
|
|
9
|
+
IncrementalMode = Literal["none", "archive", "file", "row"]
|
|
10
|
+
PolicySeverity = Literal["warn", "reject", "abort"]
|
|
11
|
+
SchemaEvolutionMode = Literal["strict", "add_columns"]
|
|
12
|
+
SchemaEvolutionIncompatibleAction = Literal["fail"]
|
|
13
|
+
WriteMode = Literal["overwrite", "append", "merge_scd1", "merge_scd2"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _Model(BaseModel):
|
|
17
|
+
model_config = ConfigDict(frozen=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EntityMetadata(_Model):
|
|
21
|
+
data_product: str | None
|
|
22
|
+
domain: str | None
|
|
23
|
+
owner: str | None
|
|
24
|
+
description: str | None
|
|
25
|
+
tags: list[str] | None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EntityStateConfig(_Model):
|
|
29
|
+
path: str | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PolicyConfig(_Model):
|
|
33
|
+
severity: PolicySeverity
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class NormalizeColumnsConfig(_Model):
|
|
37
|
+
enabled: bool | None
|
|
38
|
+
strategy: str | None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SchemaMismatchConfig(_Model):
|
|
42
|
+
missing_columns: str | None
|
|
43
|
+
extra_columns: str | None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SchemaEvolutionConfig(_Model):
|
|
47
|
+
mode: SchemaEvolutionMode = "strict"
|
|
48
|
+
on_incompatible: SchemaEvolutionIncompatibleAction = "fail"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ColumnConfig(_Model):
|
|
52
|
+
name: str
|
|
53
|
+
source: str | None
|
|
54
|
+
column_type: str
|
|
55
|
+
nullable: bool | None
|
|
56
|
+
unique: bool | None
|
|
57
|
+
width: int | None
|
|
58
|
+
trim: bool | None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SchemaConfig(_Model):
|
|
62
|
+
normalize_columns: NormalizeColumnsConfig | None
|
|
63
|
+
mismatch: SchemaMismatchConfig | None
|
|
64
|
+
schema_evolution: SchemaEvolutionConfig | None
|
|
65
|
+
primary_key: list[str] | None
|
|
66
|
+
unique_keys: list[list[str]] | None
|
|
67
|
+
columns: list[ColumnConfig]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SourceOptions(_Model):
|
|
71
|
+
header: bool | None = True
|
|
72
|
+
separator: str | None = ";"
|
|
73
|
+
encoding: str | None = "UTF8"
|
|
74
|
+
null_values: list[str] | None = [""]
|
|
75
|
+
recursive: bool | None = False
|
|
76
|
+
glob: str | None = None
|
|
77
|
+
json_mode: str | None = None
|
|
78
|
+
sheet: str | None = None
|
|
79
|
+
header_row: int | None = 1
|
|
80
|
+
data_row: int | None = None
|
|
81
|
+
row_tag: str | None = None
|
|
82
|
+
namespace: str | None = None
|
|
83
|
+
value_tag: str | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class SourcePartitioning(_Model): # D28
|
|
87
|
+
pattern: str
|
|
88
|
+
date_column: str
|
|
89
|
+
batch_column: str | None
|
|
90
|
+
lookback_days: int = 1 # D30: file mode lists from the watermark minus these days
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class SourceConfig(_Model):
|
|
94
|
+
format: str
|
|
95
|
+
path: str
|
|
96
|
+
storage: str | None
|
|
97
|
+
options: SourceOptions
|
|
98
|
+
cast_mode: str
|
|
99
|
+
source_ref: str | None
|
|
100
|
+
resource: str | None
|
|
101
|
+
partitioning: SourcePartitioning | None = None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SinkOptions(_Model):
|
|
105
|
+
compression: str | None
|
|
106
|
+
row_group_size: int | None
|
|
107
|
+
max_size_per_file: int | None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class MergeScd2Options(_Model):
|
|
111
|
+
current_flag_column: str | None
|
|
112
|
+
valid_from_column: str | None
|
|
113
|
+
valid_to_column: str | None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class MergeOptions(_Model):
|
|
117
|
+
ignore_columns: list[str] | None
|
|
118
|
+
compare_columns: list[str] | None
|
|
119
|
+
scd2: MergeScd2Options | None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class IcebergSinkOptions(_Model):
|
|
123
|
+
catalog: str | None
|
|
124
|
+
namespace: str | None
|
|
125
|
+
table: str | None
|
|
126
|
+
location: str | None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class IcebergPartitionField(_Model):
|
|
130
|
+
column: str
|
|
131
|
+
transform: str
|
|
132
|
+
num_buckets: int | None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class SinkTarget(_Model):
|
|
136
|
+
format: str
|
|
137
|
+
path: str
|
|
138
|
+
storage: str | None
|
|
139
|
+
options: SinkOptions | None
|
|
140
|
+
merge: MergeOptions | None
|
|
141
|
+
iceberg: IcebergSinkOptions | None
|
|
142
|
+
delta: object = None # raw: Delta is dropped (D1), validation rejects it
|
|
143
|
+
duckdb: object = None
|
|
144
|
+
partition_by: list[str] | None
|
|
145
|
+
partition_spec: list[IcebergPartitionField] | None
|
|
146
|
+
write_mode: WriteMode
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ArchiveTarget(_Model):
|
|
150
|
+
path: str
|
|
151
|
+
storage: str | None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class SinkConfig(_Model):
|
|
155
|
+
write_mode: WriteMode
|
|
156
|
+
accepted: SinkTarget
|
|
157
|
+
rejected: SinkTarget | None
|
|
158
|
+
archive: ArchiveTarget | None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class PiiConfig(_Model):
|
|
162
|
+
columns: list[PiiColumn]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class EntityConfig(_Model):
|
|
166
|
+
name: str
|
|
167
|
+
id: str
|
|
168
|
+
metadata: EntityMetadata | None
|
|
169
|
+
domain: str | None
|
|
170
|
+
incremental_mode: IncrementalMode
|
|
171
|
+
state: EntityStateConfig | None
|
|
172
|
+
source: SourceConfig
|
|
173
|
+
sink: SinkConfig
|
|
174
|
+
policy: PolicyConfig
|
|
175
|
+
# `schema` shadows a BaseModel attribute, so the field is `schema_` and the key stays `schema`.
|
|
176
|
+
schema_: SchemaConfig = Field(alias="schema")
|
|
177
|
+
pii: PiiConfig | None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def with_partition_columns(entity: EntityConfig) -> EntityConfig:
|
|
181
|
+
"""D28: the columns `source.partitioning` derives count as schema columns."""
|
|
182
|
+
p = entity.source.partitioning
|
|
183
|
+
if p is None:
|
|
184
|
+
return entity
|
|
185
|
+
date_type = "datetime" if "{HH}" in p.pattern else "date"
|
|
186
|
+
derived = [(p.date_column, date_type), (p.batch_column, "string")]
|
|
187
|
+
columns = [
|
|
188
|
+
ColumnConfig(name=n, source=n, column_type=t, nullable=False, unique=None, width=None, trim=None)
|
|
189
|
+
for n, t in derived
|
|
190
|
+
if n is not None
|
|
191
|
+
] # fmt: skip # `source` keeps the name out of normalize_columns
|
|
192
|
+
schema = entity.schema_.model_copy(
|
|
193
|
+
update={"columns": [*entity.schema_.columns, *columns]}
|
|
194
|
+
)
|
|
195
|
+
return entity.model_copy(update={"schema_": schema})
|