informatica-python 1.9.9__py3-none-any.whl → 1.10.1__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 +65 -13
- informatica_python/models.py +3 -0
- informatica_python/parser.py +3 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.dist-info}/METADATA +10 -2
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.dist-info}/RECORD +9 -9
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.dist-info}/WHEEL +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.dist-info}/entry_points.txt +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.dist-info}/licenses/LICENSE +0 -0
- {informatica_python-1.9.9.dist-info → informatica_python-1.10.1.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"
|
|
@@ -946,10 +963,11 @@ def _gen_joiner_transform(lines, tx, tx_safe, input_df, input_sources, source_df
|
|
|
946
963
|
to_field = conn.to_field
|
|
947
964
|
port_to_col[to_field] = conn.from_field
|
|
948
965
|
port_to_col[to_field.lower()] = conn.from_field
|
|
966
|
+
src_key = f"{conn.from_instance}:{conn.from_instance_group}" if conn.from_instance_group else conn.from_instance
|
|
949
967
|
if to_field in master_fields or to_field.lower() in master_fields_lower:
|
|
950
|
-
master_src =
|
|
968
|
+
master_src = src_key
|
|
951
969
|
elif to_field in detail_fields or to_field.lower() in detail_fields_lower:
|
|
952
|
-
detail_src =
|
|
970
|
+
detail_src = src_key
|
|
953
971
|
|
|
954
972
|
if left_keys and right_keys and port_to_col:
|
|
955
973
|
left_keys = [port_to_col.get(k, port_to_col.get(k.lower(), k)) for k in left_keys]
|
|
@@ -967,9 +985,17 @@ def _gen_joiner_transform(lines, tx, tx_safe, input_df, input_sources, source_df
|
|
|
967
985
|
detail_src = s
|
|
968
986
|
break
|
|
969
987
|
|
|
988
|
+
def _resolve_src(key):
|
|
989
|
+
if key in source_dfs:
|
|
990
|
+
return source_dfs[key]
|
|
991
|
+
base = key.split(":")[0] if ":" in key else key
|
|
992
|
+
if base in source_dfs:
|
|
993
|
+
return source_dfs[base]
|
|
994
|
+
return f"df_{_safe_name(base)}"
|
|
995
|
+
|
|
970
996
|
if master_src and detail_src:
|
|
971
|
-
df_master =
|
|
972
|
-
df_detail =
|
|
997
|
+
df_master = _resolve_src(master_src)
|
|
998
|
+
df_detail = _resolve_src(detail_src)
|
|
973
999
|
|
|
974
1000
|
lines.append(f" # Join ({join_type}): {join_condition or 'auto'}")
|
|
975
1001
|
if left_keys and right_keys:
|
|
@@ -1095,9 +1121,24 @@ def _gen_lookup_transform(lines, tx, tx_safe, input_df, source_dfs, connector_gr
|
|
|
1095
1121
|
def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
1096
1122
|
lines.append(f" # Router groups:")
|
|
1097
1123
|
group_conditions = {}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1124
|
+
|
|
1125
|
+
output_groups = [
|
|
1126
|
+
g for g in tx.groups
|
|
1127
|
+
if g.type.upper() not in ("INPUT", "") and "DEFAULT" not in g.type.upper()
|
|
1128
|
+
]
|
|
1129
|
+
output_groups.sort(key=lambda g: g.order)
|
|
1130
|
+
|
|
1131
|
+
if output_groups:
|
|
1132
|
+
for g in output_groups:
|
|
1133
|
+
if g.expression and g.expression.strip():
|
|
1134
|
+
group_conditions[g.name] = g.expression
|
|
1135
|
+
else:
|
|
1136
|
+
group_conditions[g.name] = ""
|
|
1137
|
+
|
|
1138
|
+
if not group_conditions:
|
|
1139
|
+
for attr in tx.attributes:
|
|
1140
|
+
if "Group Filter Condition" in attr.name:
|
|
1141
|
+
group_conditions[attr.name] = attr.value
|
|
1101
1142
|
|
|
1102
1143
|
remaining_mask_parts = []
|
|
1103
1144
|
if group_conditions:
|
|
@@ -1108,15 +1149,21 @@ def _gen_router_transform(lines, tx, tx_safe, input_df, source_dfs):
|
|
|
1108
1149
|
expr_py = f"pd.Series(True, index={input_df}.index)"
|
|
1109
1150
|
mask_var = f"_router_mask_{tx_safe}_{i}"
|
|
1110
1151
|
lines.append(f" {mask_var} = {expr_py} # {gname}")
|
|
1111
|
-
|
|
1112
|
-
|
|
1152
|
+
group_df_name = f"df_{tx_safe}_{_safe_name(gname)}"
|
|
1153
|
+
lines.append(f" {group_df_name} = {input_df}[{mask_var}].copy()")
|
|
1154
|
+
source_dfs[f"{tx.name}:{gname}"] = group_df_name
|
|
1113
1155
|
remaining_mask_parts.append(f"~{mask_var}")
|
|
1156
|
+
|
|
1157
|
+
default_groups = [g for g in tx.groups if "DEFAULT" in g.type.upper()]
|
|
1158
|
+
default_name = default_groups[0].name if default_groups else "DEFAULT"
|
|
1159
|
+
|
|
1114
1160
|
if remaining_mask_parts:
|
|
1115
1161
|
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")
|
|
1162
|
+
lines.append(f" df_{tx_safe} = {input_df}[_router_default_mask].copy() # Default group ({default_name})")
|
|
1117
1163
|
else:
|
|
1118
|
-
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group")
|
|
1164
|
+
lines.append(f" df_{tx_safe} = {input_df}.copy() # Default group ({default_name})")
|
|
1119
1165
|
source_dfs[tx.name] = f"df_{tx_safe}"
|
|
1166
|
+
source_dfs[f"{tx.name}:{default_name}"] = f"df_{tx_safe}"
|
|
1120
1167
|
|
|
1121
1168
|
|
|
1122
1169
|
def _gen_union_transform(lines, tx, tx_safe, input_sources, source_dfs, data_lib="pandas"):
|
|
@@ -1448,6 +1495,11 @@ def _generate_target_write(lines, tgt_name, tgt_def, connector_graph, source_dfs
|
|
|
1448
1495
|
to_conns = connector_graph.get("to", {}).get(tgt_name, [])
|
|
1449
1496
|
input_df = None
|
|
1450
1497
|
for c in to_conns:
|
|
1498
|
+
if c.from_instance_group:
|
|
1499
|
+
group_key = f"{c.from_instance}:{c.from_instance_group}"
|
|
1500
|
+
if group_key in source_dfs:
|
|
1501
|
+
input_df = source_dfs[group_key]
|
|
1502
|
+
break
|
|
1451
1503
|
if c.from_instance in source_dfs:
|
|
1452
1504
|
input_df = source_dfs[c.from_instance]
|
|
1453
1505
|
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.1
|
|
4
4
|
Summary: Convert Informatica PowerCenter workflow XML to Python/PySpark code
|
|
5
5
|
Author: Nick
|
|
6
6
|
License: MIT
|
|
@@ -414,7 +414,15 @@ The generated `helper_functions.py` provides a complete runtime library:
|
|
|
414
414
|
|
|
415
415
|
## Changelog
|
|
416
416
|
|
|
417
|
-
### v1.
|
|
417
|
+
### v1.10.1 (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
|
+
- **Joiner group-aware routing**: Joiner transformations now correctly identify master/detail sources through Router group connectors — two different Router output groups feeding a Joiner are properly resolved to their distinct DataFrames
|
|
421
|
+
- **GroupDef expression field**: `GroupDef` model now stores the `EXPRESSION` attribute from `<GROUP>` XML elements
|
|
422
|
+
- **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
|
|
423
|
+
- **223 tests** passing
|
|
424
|
+
|
|
425
|
+
### v1.9.8
|
|
418
426
|
- **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
427
|
- **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
428
|
- **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=BA703-7byzqvRy_23w-1mpWNs7v8dEaXh2c1NUwtRIk,74841
|
|
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.1.dist-info/licenses/LICENSE,sha256=77RaRDdXgey1D90YZAjXqEQdBxWfvUQqLQX3pC1qjUE,1061
|
|
19
|
+
informatica_python-1.10.1.dist-info/METADATA,sha256=FfUt2F7VzYojRHWjCMtYD-rVqJZnQR1RS3uyWC6nWXc,30668
|
|
20
|
+
informatica_python-1.10.1.dist-info/WHEEL,sha256=PovZm1ExVWmrRefZoXCfejlbKLnQI5SVIf1SWRV4QQI,97
|
|
21
|
+
informatica_python-1.10.1.dist-info/entry_points.txt,sha256=030jjTrx-1oRRQ16HZz52rdcKS8R8_llnymsTUtn_Xc,67
|
|
22
|
+
informatica_python-1.10.1.dist-info/top_level.txt,sha256=Dngg-WNteYi22XAJU2XKAQS8aZ52yM2LYC0tzxrlbVQ,19
|
|
23
|
+
informatica_python-1.10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|