Skip to content

Commit

Permalink
Merge pull request #26 from Safe/FMEENGINE-81637
Browse files Browse the repository at this point in the history
Update FMEBaseTransformer and FMEEnhancedTransformer to support multiple input tags.
  • Loading branch information
hhtong authored and GitHub Enterprise committed Feb 3, 2024
2 parents 5fbd300 + 8cf8ae3 commit f2c45e9
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# fmetools changes

## 0.9.0

* Add support for multiple input tags. Requires FME 2024.0+.

## 0.8.0

* Add support for multiple output tags. Requires FME 2024.0+.
Expand All @@ -8,6 +12,7 @@

* Update doc for `fmetools.paramparsing` to note FME 2024 requirement
when running under FME Flow.
* Add Scripted Selection API

## 0.7.3

Expand Down
2 changes: 1 addition & 1 deletion src/fmetools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# coding: utf-8

__version__ = "0.8.0"
__version__ = "0.9.0"

import gettext
import os
Expand Down
98 changes: 90 additions & 8 deletions src/fmetools/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,10 @@ def __exit__(self, *args):

def input(self, feature: FMEFeature) -> None:
"""
Receive a feature from the transformer's input port.
Receive a feature from the transformer's single input port.
This method is used instead of :meth:`input_from` if the transformer has no input tags,
meaning that the transformer's INPUT_TAGS parameter is listed as <BLANK>.
Transformers typically receive a feature through this method, process it,
modify the feature by adding output attributes, and then output the feature using :meth:`pyoutput`.
Expand All @@ -390,6 +393,37 @@ def input(self, feature: FMEFeature) -> None:
"""
pass

def input_from(self, feature: FMEFeature, input_tag: str) -> None:
"""
Receive a feature from input_tag.
This method is used instead of :meth:`input` if the transformer has defined input tags
listed in the transformer's INPUT_TAGS parameter and the PythonFactory's
PY_INPUT_TAGS clause. Introduced in FME 2024.0.
Example of a ``PythonFactory`` definition with two input tags::
FACTORY_DEF {*} PythonFactory
FACTORY_NAME { $(XFORMER_NAME) }
PY_INPUT_TAGS INPUT0 INPUT1
$(INPUT_LINES)
SYMBOL_NAME { symbol_name }
PY_OUTPUT_TAGS Output <Rejected>
OUTPUT { Output FEATURE_TYPE $(OUTPUT_Output_FTYPE)
$(OUTPUT_Output_FUNCS) }
OUTPUT { <Rejected> FEATURE_TYPE $(OUTPUT_<Rejected>_FTYPE)
$(OUTPUT_<Rejected>_FUNCS) }
Transformers typically receive a feature through this method, process it,
modify the feature by adding output attributes, and then output the feature using :meth:`pyoutput`.
However, transformers may output any number of features for each input feature, or none at all.
Transformers may also create new :class:`FMEFeature` instances and output them.
:param feature: The feature to process.
:param input_tag: The input tag that feature came from.
"""
pass

def process_group(self) -> None:
"""
If group processing is enabled, then this is called after all the
Expand Down Expand Up @@ -518,16 +552,22 @@ class FMEEnhancedTransformer(FMEBaseTransformer):
- :meth:`receive_feature` which should contain the bulk of the main logic
- :meth:`post_input`
:meth:`input_from` is broken down to these methods for implementations to overwrite:
- :meth:`pre_input_from`
- :meth:`setup_from` which is only called for the first input feature from each input tag
- :meth:`receive_feature_from` which should contain the bulk of the main logic
- :meth:`post_input_from`
:meth:`close` is broken down to these methods for implementations to overwrite:
- :meth:`pre_close`
- :meth:`finish` which should contain any cleanup tasks
- :meth:`post_close`
**A typical transformer would implement:**
- :meth:`setup` to collect constant transformer parameters off the first input feature
- :meth:`setup` or :meth:`setup_from` to collect constant transformer parameters off the first input feature
and use them to do any initialization steps.
- :meth:`receive_feature` to process each input feature.
- :meth:`receive_feature` or :meth:`receive_feature_from` to process each input feature.
- :meth:`finish` to delete any temporary files or close any connections.
.. note::
Expand All @@ -541,6 +581,7 @@ class FMEEnhancedTransformer(FMEBaseTransformer):
def __init__(self):
super(FMEEnhancedTransformer, self).__init__()
self._initialized = False
self._initialized_tags = set()
self._log = None

@property
Expand Down Expand Up @@ -594,6 +635,50 @@ def input(self, feature: FMEFeature) -> None:
self.receive_feature(feature)
self.post_input(feature)

def setup_from(self, first_feature: FMEFeature, input_tag: str) -> None:
"""
This method is only called for the first input feature from each unique input tag.
Implement this method to perform any necessary setup operations,
such as getting constant parameters.
Constant parameters are ones that cannot change across input features.
For parameters defined using GUI Types, these are ones that do not include ``OR_ATTR``.
For parameters defined with Transformer Designer,
these are ones with Value Type Level not set to "Full Expression Support".
:param first_feature: First input feature.
:param input_tag: Input tag that first_feature came from.
"""
pass

def pre_input_from(self, feature: FMEFeature, input_tag: str) -> None:
"""
Called before each :meth:`input_from`.
"""
if input_tag not in self._initialized_tags:
self.setup_from(feature, input_tag)
self._initialized_tags.add(input_tag)

def receive_feature_from(self, feature: FMEFeature, input_tag: str) -> None:
"""
Override this method instead of :meth:`input_from`.
This method receives all input features from input_tag, including the first one that's also
passed to :meth:`setup_from`.
"""
pass

def post_input_from(self, feature: FMEFeature, input_tag: str) -> None:
"""
Called after each :meth:`input_from`.
"""
pass

def input_from(self, feature: FMEFeature, input_tag: str) -> None:
"""Do not override this method."""
self.pre_input_from(feature, input_tag)
self.receive_feature_from(feature, input_tag)
self.post_input_from(feature, input_tag)

def pre_close(self) -> None:
"""Called before :meth:`close`."""
pass
Expand Down Expand Up @@ -629,11 +714,8 @@ def reject_feature(self, feature: FMEFeature, code: str, message: str) -> None:
FACTORY_DEF {*} PythonFactory
FACTORY_NAME { $(XFORMER_NAME) }
GROUP_BY { $(GROUP_BY) }
FLUSH_WHEN_GROUPS_CHANGE { $(GROUP_BY_MODE) }
$(INPUT_LINES)
SYMBOL_NAME { $(PYTHONSYMBOL) }
SOURCE_CODE { $(PYTHONSOURCE) }
SYMBOL_NAME { symbol_name }
PY_OUTPUT_TAGS Output <Rejected>
OUTPUT { Output FEATURE_TYPE $(OUTPUT_Output_FTYPE)
$(OUTPUT_Output_FUNCS) }
Expand All @@ -659,7 +741,7 @@ def reject_feature(self, feature: FMEFeature, code: str, message: str) -> None:
FACTORY_DEF {*} PythonFactory
FACTORY_NAME { $(XFORMER_NAME) }
INPUT { FEATURE_TYPE $(XFORMER_NAME)_READY }
SYMBOL_NAME { $(PYTHONSYMBOL) }
SYMBOL_NAME { symbol_name }
OUTPUT { PYOUTPUT FEATURE_TYPE $(XFORMER_NAME)_PROCESSED }
# Removed all internal-prefixed attributes from output feature
Expand Down

0 comments on commit f2c45e9

Please sign in to comment.