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.
Files changed (66) hide show
  1. floe/__init__.py +327 -0
  2. floe/__main__.py +5 -0
  3. floe/_cli.py +302 -0
  4. floe/_cli_output.py +187 -0
  5. floe/add_entity.py +516 -0
  6. floe/checks/cast.py +160 -0
  7. floe/checks/mismatch.py +137 -0
  8. floe/checks/normalize.py +118 -0
  9. floe/checks/rows.py +116 -0
  10. floe/checks/unique.py +247 -0
  11. floe/config/__init__.py +0 -0
  12. floe/config/entity.py +479 -0
  13. floe/config/include.py +122 -0
  14. floe/config/parse.py +140 -0
  15. floe/config/profile.py +381 -0
  16. floe/config/project.py +404 -0
  17. floe/config/storage.py +179 -0
  18. floe/config/types.py +195 -0
  19. floe/config/validate.py +803 -0
  20. floe/config/vars.py +127 -0
  21. floe/config/yaml.py +263 -0
  22. floe/errors.py +13 -0
  23. floe/glob.py +300 -0
  24. floe/iceberg/catalog.py +345 -0
  25. floe/iceberg/compact.py +139 -0
  26. floe/iceberg/merge.py +297 -0
  27. floe/iceberg/sink.py +208 -0
  28. floe/iceberg/unique.py +61 -0
  29. floe/iceberg/write.py +270 -0
  30. floe/lineage/events.py +163 -0
  31. floe/lineage/observer.py +261 -0
  32. floe/maintain.py +134 -0
  33. floe/manifest.py +503 -0
  34. floe/manifest_run.py +299 -0
  35. floe/pii.py +134 -0
  36. floe/py.typed +0 -0
  37. floe/read/__init__.py +0 -0
  38. floe/read/avro.py +40 -0
  39. floe/read/csv.py +138 -0
  40. floe/read/json.py +236 -0
  41. floe/read/orc.py +42 -0
  42. floe/read/parquet.py +43 -0
  43. floe/read/xlsx.py +75 -0
  44. floe/read/xml.py +227 -0
  45. floe/report/entity.py +245 -0
  46. floe/report/summary.py +114 -0
  47. floe/run/__init__.py +175 -0
  48. floe/run/entity.py +963 -0
  49. floe/run/events.py +42 -0
  50. floe/state.py +311 -0
  51. floe/storage/__init__.py +0 -0
  52. floe/storage/archive.py +31 -0
  53. floe/storage/inputs.py +302 -0
  54. floe/storage/local.py +175 -0
  55. floe/storage/partitioning.py +116 -0
  56. floe/storage/paths.py +69 -0
  57. floe/storage/resolve.py +55 -0
  58. floe/storage/s3.py +182 -0
  59. floe/storage/types.py +33 -0
  60. floe/write/__init__.py +0 -0
  61. floe/write/outputs.py +121 -0
  62. floe_python-0.8.0.dist-info/METADATA +15 -0
  63. floe_python-0.8.0.dist-info/RECORD +66 -0
  64. floe_python-0.8.0.dist-info/WHEEL +4 -0
  65. floe_python-0.8.0.dist-info/entry_points.txt +2 -0
  66. 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})