informatica-python 1.9.9__py3-none-any.whl → 1.10.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.
- informatica_python/generators/mapping_gen.py +52 -9
- informatica_python/models.py +3 -0
- informatica_python/parser.py +3 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/METADATA +9 -2
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/RECORD +9 -9
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/WHEEL +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/entry_points.txt +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/licenses/LICENSE +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.0.dist-info}/top_level.txt +0 -0
|
@@ -54,6 +54,8 @@ def _expand_mapplet_recursive(mapplet, mapplet_map, prefix, depth=0, max_depth=1
|
|
|
54
54
|
to_instance=new_to,
|
|
55
55
|
to_field=conn.to_field,
|
|
56
56
|
to_instance_type=conn.to_instance_type,
|
|
57
|
+
from_instance_group=conn.from_instance_group,
|
|
58
|
+
to_instance_group=conn.to_instance_group,
|
|
57
59
|
))
|
|
58
60
|
|
|
59
61
|
for inst in getattr(mapplet, 'instances', []):
|
|
@@ -138,6 +140,8 @@ def _inline_mapplets(mapping, folder):
|
|
|
138
140
|
to_instance=first_tx,
|
|
139
141
|
to_field=conn.to_field,
|
|
140
142
|
to_instance_type=conn.to_instance_type,
|
|
143
|
+
from_instance_group=conn.from_instance_group,
|
|
144
|
+
to_instance_group=conn.to_instance_group,
|
|
141
145
|
))
|
|
142
146
|
else:
|
|
143
147
|
rewired_connectors.append(conn)
|
|
@@ -167,6 +171,8 @@ def _inline_mapplets(mapping, folder):
|
|
|
167
171
|
to_instance=conn.to_instance,
|
|
168
172
|
to_field=conn.to_field,
|
|
169
173
|
to_instance_type=conn.to_instance_type,
|
|
174
|
+
from_instance_group=conn.from_instance_group,
|
|
175
|
+
to_instance_group=conn.to_instance_group,
|
|
170
176
|
))
|
|
171
177
|
else:
|
|
172
178
|
rewired_connectors.append(conn)
|
|
@@ -730,7 +736,11 @@ def _generate_transformation(lines, tx, connector_graph, source_dfs, transform_m
|
|
|
730
736
|
input_conns = connector_graph.get("to", {}).get(tx.name, [])
|
|
731
737
|
input_sources = set()
|
|
732
738
|
for c in input_conns:
|
|
733
|
-
|
|
739
|
+
if c.from_instance_group:
|
|
740
|
+
group_key = f"{c.from_instance}:{c.from_instance_group}"
|
|
741
|
+
input_sources.add(group_key)
|
|
742
|
+
else:
|
|
743
|
+
input_sources.add(c.from_instance)
|
|
734
744
|
|
|
735
745
|
input_df = None
|
|
736
746
|
for src in input_sources:
|
|
@@ -739,7 +749,14 @@ def _generate_transformation(lines, tx, connector_graph, source_dfs, transform_m
|
|
|
739
749
|
break
|
|
740
750
|
if not input_df:
|
|
741
751
|
for src in input_sources:
|
|
742
|
-
|
|
752
|
+
base = src.split(":")[0] if ":" in src else src
|
|
753
|
+
if base in source_dfs:
|
|
754
|
+
input_df = source_dfs[base]
|
|
755
|
+
break
|
|
756
|
+
if not input_df:
|
|
757
|
+
for src in input_sources:
|
|
758
|
+
base = src.split(":")[0] if ":" in src else src
|
|
759
|
+
input_df = f"df_{_safe_name(base)}"
|
|
743
760
|
break
|
|
744
761
|
if not input_df:
|
|
745
762
|
input_df = "df_input"
|
|
@@ -1095,9 +1112,24 @@ def _gen_lookup_transform(lines, tx, tx_safe, input_df, source_dfs, connector_gr
|
|
|
1095
1112
|
def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
1096
1113
|
lines.append(f" # Router groups:")
|
|
1097
1114
|
group_conditions = {}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1115
|
+
|
|
1116
|
+
output_groups = [
|
|
1117
|
+
g for g in tx.groups
|
|
1118
|
+
if g.type.upper() not in ("INPUT", "") and "DEFAULT" not in g.type.upper()
|
|
1119
|
+
]
|
|
1120
|
+
output_groups.sort(key=lambda g: g.order)
|
|
1121
|
+
|
|
1122
|
+
if output_groups:
|
|
1123
|
+
for g in output_groups:
|
|
1124
|
+
if g.expression and g.expression.strip():
|
|
1125
|
+
group_conditions[g.name] = g.expression
|
|
1126
|
+
else:
|
|
1127
|
+
group_conditions[g.name] = ""
|
|
1128
|
+
|
|
1129
|
+
if not group_conditions:
|
|
1130
|
+
for attr in tx.attributes:
|
|
1131
|
+
if "Group Filter Condition" in attr.name:
|
|
1132
|
+
group_conditions[attr.name] = attr.value
|
|
1101
1133
|
|
|
1102
1134
|
remaining_mask_parts = []
|
|
1103
1135
|
if group_conditions:
|
|
@@ -1108,15 +1140,21 @@ def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
|
1108
1140
|
expr_py = f"pd.Series(True, index={input_df}.index)"
|
|
1109
1141
|
mask_var = f"_router_mask_{tx_safe}_{i}"
|
|
1110
1142
|
lines.append(f" {mask_var} = {expr_py} # {gname}")
|
|
1111
|
-
|
|
1112
|
-
|
|
1143
|
+
group_df_name = f"df_{tx_safe}_{_safe_name(gname)}"
|
|
1144
|
+
lines.append(f" {group_df_name} = {input_df}[{mask_var}].copy()")
|
|
1145
|
+
source_dfs[f"{tx.name}:{gname}"] = group_df_name
|
|
1113
1146
|
remaining_mask_parts.append(f"~{mask_var}")
|
|
1147
|
+
|
|
1148
|
+
default_groups = [g for g in tx.groups if "DEFAULT" in g.type.upper()]
|
|
1149
|
+
default_name = default_groups[0].name if default_groups else "DEFAULT"
|
|
1150
|
+
|
|
1114
1151
|
if remaining_mask_parts:
|
|
1115
1152
|
lines.append(f" _router_default_mask = {' & '.join(remaining_mask_parts)}")
|
|
1116
|
-
lines.append(f" df_{tx_safe} = {input_df}[_router_default_mask].copy() # Default group")
|
|
1153
|
+
lines.append(f" df_{tx_safe} = {input_df}[_router_default_mask].copy() # Default group ({default_name})")
|
|
1117
1154
|
else:
|
|
1118
|
-
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group")
|
|
1155
|
+
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group ({default_name})")
|
|
1119
1156
|
source_dfs[tx.name] = f"df_{tx_safe}"
|
|
1157
|
+
source_dfs[f"{tx.name}:{default_name}"] = f"df_{tx_safe}"
|
|
1120
1158
|
|
|
1121
1159
|
|
|
1122
1160
|
def _gen_union_transform(lines, tx, tx_safe, input_sources, source_dfs, data_lib="pandas"):
|
|
@@ -1448,6 +1486,11 @@ def _generate_target_write(lines, tgt_name, tgt_def, connector_graph, source_dfs
|
|
|
1448
1486
|
to_conns = connector_graph.get("to", {}).get(tgt_name, [])
|
|
1449
1487
|
input_df = None
|
|
1450
1488
|
for c in to_conns:
|
|
1489
|
+
if c.from_instance_group:
|
|
1490
|
+
group_key = f"{c.from_instance}:{c.from_instance_group}"
|
|
1491
|
+
if group_key in source_dfs:
|
|
1492
|
+
input_df = source_dfs[group_key]
|
|
1493
|
+
break
|
|
1451
1494
|
if c.from_instance in source_dfs:
|
|
1452
1495
|
input_df = source_dfs[c.from_instance]
|
|
1453
1496
|
break
|
informatica_python/models.py
CHANGED
|
@@ -80,6 +80,7 @@ class GroupDef:
|
|
|
80
80
|
type: str = ""
|
|
81
81
|
description: str = ""
|
|
82
82
|
order: int = 0
|
|
83
|
+
expression: str = ""
|
|
83
84
|
fields: List[FieldDef] = field(default_factory=list)
|
|
84
85
|
|
|
85
86
|
|
|
@@ -274,6 +275,8 @@ class ConnectorDef:
|
|
|
274
275
|
to_field: str
|
|
275
276
|
to_instance: str
|
|
276
277
|
to_instance_type: str
|
|
278
|
+
from_instance_group: str = ""
|
|
279
|
+
to_instance_group: str = ""
|
|
277
280
|
|
|
278
281
|
|
|
279
282
|
@dataclass
|
informatica_python/parser.py
CHANGED
|
@@ -240,6 +240,7 @@ class InformaticaParser:
|
|
|
240
240
|
type=self._attr(elem, "TYPE"),
|
|
241
241
|
description=self._attr(elem, "DESCRIPTION"),
|
|
242
242
|
order=self._int_attr(elem, "ORDER"),
|
|
243
|
+
expression=self._attr(elem, "EXPRESSION"),
|
|
243
244
|
)
|
|
244
245
|
for fld in elem.findall("SOURCEFIELD"):
|
|
245
246
|
grp.fields.append(self._parse_source_field(fld))
|
|
@@ -580,6 +581,8 @@ class InformaticaParser:
|
|
|
580
581
|
to_field=self._attr(elem, "TOFIELD"),
|
|
581
582
|
to_instance=self._attr(elem, "TOINSTANCE"),
|
|
582
583
|
to_instance_type=self._attr(elem, "TOINSTANCETYPE"),
|
|
584
|
+
from_instance_group=self._attr(elem, "FROMINSTANCEGROUP"),
|
|
585
|
+
to_instance_group=self._attr(elem, "TOINSTANCEGROUP"),
|
|
583
586
|
)
|
|
584
587
|
|
|
585
588
|
def _parse_instance(self, elem) -> InstanceDef:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: informatica-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: Convert Informatica PowerCenter workflow XML to Python/PySpark code
|
|
5
5
|
Author: Nick
|
|
6
6
|
License: MIT
|
|
@@ -414,7 +414,14 @@ The generated `helper_functions.py` provides a complete runtime library:
|
|
|
414
414
|
|
|
415
415
|
## Changelog
|
|
416
416
|
|
|
417
|
-
### v1.
|
|
417
|
+
### v1.10.0 (Current)
|
|
418
|
+
- **Router multi-group output support**: Router transformations now properly handle `<GROUP>` elements with `EXPRESSION` attributes — generates separate filtered DataFrames for each named output group (e.g., `df_rtr_rest_type_per`, `df_rtr_rest_value_per`), not just the DEFAULT group
|
|
419
|
+
- **Connector group routing**: `FROMINSTANCEGROUP` / `TOINSTANCEGROUP` attributes on `CONNECTOR` elements are now parsed and used to wire downstream transforms/targets to the correct Router output group
|
|
420
|
+
- **GroupDef expression field**: `GroupDef` model now stores the `EXPRESSION` attribute from `<GROUP>` XML elements
|
|
421
|
+
- **Backward-compatible Router fallback**: Existing `TABLEATTRIBUTE`-based Router group conditions (older XML format) continue to work — the code checks `<GROUP>` elements first, then falls back to `TABLEATTRIBUTE` entries
|
|
422
|
+
- **223 tests** passing
|
|
423
|
+
|
|
424
|
+
### v1.9.8
|
|
418
425
|
- **NOT(expr) function-call form**: `NOT(ISNULL(x))` now correctly converts to `~(df["x"].isna())` — handles both `NOT ` (with space) and `NOT(` (without space) forms
|
|
419
426
|
- **AND/OR/NOT as field names fix**: Logical operators no longer mangled into `df["AND"]` / `df["OR"]` — conversion moved before field substitution in both `_vec_recursive` fallback and `_vectorize_simple`
|
|
420
427
|
- **Condition tokenizer word-boundary fix**: `_split_condition_tokens` no longer splits on `OR` inside field names like `DeletedIndicator` — verifies preceding character is a real word boundary
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
informatica_python/__init__.py,sha256=JFO8fVMClSWe0SR-CBseX4RaPyyC3rZBdxxjy47ZT5E,337
|
|
2
2
|
informatica_python/cli.py,sha256=gFwg0O99vKM-OLO0HoHA4emd-6qrgjMNqa9T59e4e_s,2905
|
|
3
3
|
informatica_python/converter.py,sha256=xCuWrYzDji0yN72D3QqOgZCVVM2j3k2_CvlGplCWxLU,22779
|
|
4
|
-
informatica_python/models.py,sha256=
|
|
5
|
-
informatica_python/parser.py,sha256=
|
|
4
|
+
informatica_python/models.py,sha256=lBlxJBVSyW050KAGj8OyUd11Wyfk-MA2XWBT3qEQSK4,17462
|
|
5
|
+
informatica_python/parser.py,sha256=LpX6Cg5jlqqZyLMr_VRUVvNaLdqls4aB-PdGCLbehZo,45603
|
|
6
6
|
informatica_python/generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
informatica_python/generators/config_gen.py,sha256=4tqcNKTB06kyGZIiM4yl0q97q_i3zeCHXTjuE1dNFKY,5726
|
|
8
8
|
informatica_python/generators/error_log_gen.py,sha256=2cc0rEcblydHkb9VAMXlrH7WdSQ-CNqAXcwVk3FYZeM,21319
|
|
9
9
|
informatica_python/generators/helper_gen.py,sha256=lC30hyZn6RIkbo4e_6sbqdrCfmZHWaXdr-p0tmtfILc,82376
|
|
10
|
-
informatica_python/generators/mapping_gen.py,sha256=
|
|
10
|
+
informatica_python/generators/mapping_gen.py,sha256=eBYJEGWKUPeRrdnWpJRFl6VL9N7qj5ybmKCgG7rFBDI,74554
|
|
11
11
|
informatica_python/generators/sql_gen.py,sha256=O8Y-aJz9EyFJ0DXeuISRt5yKwC3wlp2K3B0BHrmxrXw,4872
|
|
12
12
|
informatica_python/generators/workflow_gen.py,sha256=_uSlBg31ZRMhMlCYk4hWDRBPaBROrepD8_v3QGEWJxE,18089
|
|
13
13
|
informatica_python/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -15,9 +15,9 @@ informatica_python/utils/datatype_map.py,sha256=iLOYg-iBKT4rMecGbrFkTpJj4yqs5S9H
|
|
|
15
15
|
informatica_python/utils/expression_converter.py,sha256=ynprsvZGvavML3Y8C485GyjaoqQ-k67OESXHShafeTo,48244
|
|
16
16
|
informatica_python/utils/lib_adapters.py,sha256=1ZtuMbgDg9Ukf-OF_EG1L_BeeR-6JQk8Kx3WwMfvNRU,6516
|
|
17
17
|
informatica_python/utils/sql_dialect.py,sha256=_IHJbfu8a3mT_OvHpybgSfZKqz6mwVy5ItTKDRChqnU,5461
|
|
18
|
-
informatica_python-1.
|
|
19
|
-
informatica_python-1.
|
|
20
|
-
informatica_python-1.
|
|
21
|
-
informatica_python-1.
|
|
22
|
-
informatica_python-1.
|
|
23
|
-
informatica_python-1.
|
|
18
|
+
informatica_python-1.10.0.dist-info/licenses/LICENSE,sha256=77RaRDdXgey1D90YZAjXqEQdBxWfvUQqLQX3pC1qjUE,1061
|
|
19
|
+
informatica_python-1.10.0.dist-info/METADATA,sha256=eL7mk2t5wXyw9J1g6MjSXuUzByXzyX0BxPtXXX_wTAs,30427
|
|
20
|
+
informatica_python-1.10.0.dist-info/WHEEL,sha256=PovZm1ExVWmrRefZoXCfejlbKLnQI5SVIf1SWRV4QQI,97
|
|
21
|
+
informatica_python-1.10.0.dist-info/entry_points.txt,sha256=030jjTrx-1oRRQ16HZz52rdcKS8R8_llnymsTUtn_Xc,67
|
|
22
|
+
informatica_python-1.10.0.dist-info/top_level.txt,sha256=Dngg-WNteYi22XAJU2XKAQS8aZ52yM2LYC0tzxrlbVQ,19
|
|
23
|
+
informatica_python-1.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|