Skip to main content
Solved

PythonCaller Log does not show error line number (FME(R) 2024.1.1.0 (20240729 - Build 24619 - WIN64)

  • September 30, 2024
  • 8 replies
  • 170 views

salah.elfarissi
Contributor
Forum|alt.badge.img+3

When python caller errors out.
It does not show error line number.

 

Message Type: fme::internal::_v0::py::Exception
Python Exception <GEOSException>: LocateFailureException: Could not locate vertex.
🟢06-Report (PythonFactory): PythonFactory failed to close properly

Best answer by david_r

You can try something like this to include more information in any exceptions occurring in your PythonCallers:

import fmeobjects
import traceback


class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""

def __init__(self):
"""Base constructor for class members."""
self.feature_count = 0
self.log = fmeobjects.FMELogFile()

def _log(self, message, severity=fmeobjects.FME_INFORM):
"""
Sends message to the FME log file and console window.
Severity must be one of FME_INFORM, FME_WARN, FME_ERROR,
FME_FATAL, FME_STATISTIC, or FME_STATUSREPORT.
"""
self.log.logMessageString(message, severity)

def input(self, feature):
"""This method is called for each FME Feature entering the
PythonCaller. If knowledge of all input Features is not required for
processing, then the processed Feature can be emitted from this method
through self.pyoutput(). Otherwise, the input FME Feature should be
cached to a list class member and processed in process_group() when
'Group by' attributes(s) are specified, or the close() method.

:param fmeobjects.FMEFeature feature: FME Feature entering the
transformer.
"""
try:
self.feature_count += 1

# ###########################
# Your code goes here...
# ###########################

self.pyoutput(feature)

except:
self._log('='*78, fmeobjects.FME_ERROR)
message = "{}({}) exception at feature number {}:".format(
self.factory_name, self.__class__.__name__, self.feature_count)
self._log(message, fmeobjects.FME_ERROR)
self._log('-'*78, fmeobjects.FME_ERROR)
for line in traceback.format_exc().splitlines():
self._log(line, fmeobjects.FME_ERROR)
self._log('='*78, fmeobjects.FME_ERROR)
if feature:
self.log.logFeature(feature, fmeobjects.FME_ERROR)
message = "An error occurred in {} method '{}'. See log for details.".format(
self.factory_name, self.__class__.__name__)
raise fmeobjects.FMEException(message)

As you can see, it uses the traceback module from the Python standard library to dump the entire stack trace with line numbers if an exception occurrs, then terminates your workspace.

This post is closed to further activity.
It may be an old question, an answered question, an implemented idea, or a notification-only post.
Please check post dates before relying on any information in a question or answer.
For follow-up or related questions, please post a new question or idea.
If there is a genuine update to be made, please contact us and request that the post is reopened.

8 replies

david_r
Celebrity
  • September 30, 2024

What is triggering this error? Is it a PythonCaller, or a transformer that is part of FME?

If it’s a PythonCaller, can you please show the relevant part of the code.

You can also consider using the traceback module to provide (much) more detailed error messages, see https://docs.python.org/3/library/traceback.html


salah.elfarissi
Contributor
Forum|alt.badge.img+3

Thank you for the response.
Yes, a python caller i’m using for generating custom reports.
It is this part:

        try:
            m.add_gdf(
                concave_gdf.concave_hull(float(fme.macroValues["CONCAVE_RATIO"])),
                fc=CONCAVE_HULL_COLOR,
                lw=1,
                ec="none",
            )
        except Exception as e:
            logger.logMessageString(
                f"▶▶▶▶▶▶▶ Error plotting concave hull for {feature_type_name}: {e}"
            )


david_r
Celebrity
  • September 30, 2024

Try removing the try...except block altogether. What you’re doing is effectively logging the error message but then ignoring the actual exception. It’s ofter better simply to let the exception raise itself, with all its details. That often leaves more possibilities for the Python interpreter to give a more detailed stack trace.

Alternatively, you can add “raise” on a new line under “logger.logMessageString(...” to re-raise the exception.


salah.elfarissi
Contributor
Forum|alt.badge.img+3

Sorry, forgot to mention that I added the try except after I got hold of the error line number.
Would it be possible that it is because the code is not directly enclose in the def process_group
The code is part of function that I define at the end of my python caller
outside the class definition


david_r
Celebrity
  • Best Answer
  • October 1, 2024

You can try something like this to include more information in any exceptions occurring in your PythonCallers:

import fmeobjects
import traceback


class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""

def __init__(self):
"""Base constructor for class members."""
self.feature_count = 0
self.log = fmeobjects.FMELogFile()

def _log(self, message, severity=fmeobjects.FME_INFORM):
"""
Sends message to the FME log file and console window.
Severity must be one of FME_INFORM, FME_WARN, FME_ERROR,
FME_FATAL, FME_STATISTIC, or FME_STATUSREPORT.
"""
self.log.logMessageString(message, severity)

def input(self, feature):
"""This method is called for each FME Feature entering the
PythonCaller. If knowledge of all input Features is not required for
processing, then the processed Feature can be emitted from this method
through self.pyoutput(). Otherwise, the input FME Feature should be
cached to a list class member and processed in process_group() when
'Group by' attributes(s) are specified, or the close() method.

:param fmeobjects.FMEFeature feature: FME Feature entering the
transformer.
"""
try:
self.feature_count += 1

# ###########################
# Your code goes here...
# ###########################

self.pyoutput(feature)

except:
self._log('='*78, fmeobjects.FME_ERROR)
message = "{}({}) exception at feature number {}:".format(
self.factory_name, self.__class__.__name__, self.feature_count)
self._log(message, fmeobjects.FME_ERROR)
self._log('-'*78, fmeobjects.FME_ERROR)
for line in traceback.format_exc().splitlines():
self._log(line, fmeobjects.FME_ERROR)
self._log('='*78, fmeobjects.FME_ERROR)
if feature:
self.log.logFeature(feature, fmeobjects.FME_ERROR)
message = "An error occurred in {} method '{}'. See log for details.".format(
self.factory_name, self.__class__.__name__)
raise fmeobjects.FMEException(message)

As you can see, it uses the traceback module from the Python standard library to dump the entire stack trace with line numbers if an exception occurrs, then terminates your workspace.


salah.elfarissi
Contributor
Forum|alt.badge.img+3

Thank you.
I will try that out and get back to you soon.

Here is the gist of the python caller: https://gist.github.com/salahelfarissi/ea0fc67528bfc2bfdcdbc42139ef2431


salah.elfarissi
Contributor
Forum|alt.badge.img+3

You can try something like this to include more information in any exceptions occurring in your PythonCallers:

import fmeobjects
import traceback


class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""

def __init__(self):
"""Base constructor for class members."""
self.feature_count = 0
self.log = fmeobjects.FMELogFile()

def _log(self, message, severity=fmeobjects.FME_INFORM):
"""
Sends message to the FME log file and console window.
Severity must be one of FME_INFORM, FME_WARN, FME_ERROR,
FME_FATAL, FME_STATISTIC, or FME_STATUSREPORT.
"""
self.log.logMessageString(message, severity)

def input(self, feature):
"""This method is called for each FME Feature entering the
PythonCaller. If knowledge of all input Features is not required for
processing, then the processed Feature can be emitted from this method
through self.pyoutput(). Otherwise, the input FME Feature should be
cached to a list class member and processed in process_group() when
'Group by' attributes(s) are specified, or the close() method.

:param fmeobjects.FMEFeature feature: FME Feature entering the
transformer.
"""
try:
self.feature_count += 1

# ###########################
# Your code goes here...
# ###########################

self.pyoutput(feature)

except:
self._log('='*78, fmeobjects.FME_ERROR)
message = "{}({}) exception at feature number {}:".format(
self.factory_name, self.__class__.__name__, self.feature_count)
self._log(message, fmeobjects.FME_ERROR)
self._log('-'*78, fmeobjects.FME_ERROR)
for line in traceback.format_exc().splitlines():
self._log(line, fmeobjects.FME_ERROR)
self._log('='*78, fmeobjects.FME_ERROR)
if feature:
self.log.logFeature(feature, fmeobjects.FME_ERROR)
message = "An error occurred in {} method '{}'. See log for details.".format(
self.factory_name, self.__class__.__name__)
raise fmeobjects.FMEException(message)

As you can see, it uses the traceback module from the Python standard library to dump the entire stack trace with line numbers if an exception occurrs, then terminates your workspace.


Thank you.
It worked like a breeze.

 


salah.elfarissi
Contributor
Forum|alt.badge.img+3

Also, I noticed that I had Log Debug unchecked.

Now I get the error line number without using the traceback module.