Skip to main content

Hello,

I'm new to FME and I'm trying to write a PythonCaller in a Custom Transformer. I have a working standalone python program that uses the following code to load a DLL:

 

 

SourceFolder = os.path.dirname(__file__)

 

giqdll = WinDLL(os.path.join(SourceFolder, "LibGIQ.dll"))

 

 

When I try to use this in my PythonCaller I get the following error:

 

 

Python Exception <NameError>: global name '__file__' is not defined

 

 

Is there an FME way of finding the full path of the fmx file?

 

 

Thanks,

 

 

Paul Michell

Hi

Have a look at the FME_MF_DIR parameter, e.g.

import os.path
workspace_filename = FME_MacroValuesa'FME_MF_DIR']
workspace_dir = os.path.dirname(workspace_filename)

David


Thanks for the reply,

Yes I have seen that macro value, but it returns the file path of the workspace fmw that is using the transformer fmx. As I understand it, they are unlikely to be in the same place?


The "fmesuite.fmx" (regular transformers definition) is saved in "<FME_HOME>/transformers", and you can retrieve the <FME_HOME> path through a system parameter called "FME_HOME" or "FME_HOME_UNIX".

The fmx files for custom transformers are usually saved in these folders (Windows).

  • C:/Users/<user name>/AppData/Roaming/Safe Software/FME/FME Store/Transformers
  • C:/Users/<user name>/Documents/FME/Transformers

However, I don't think there is a general way to retrieve the fmx file path of a specific custom transformer.

btw, why do you need to get an fmx file path?


Thanks for the reply,

Yes I have seen that macro value, but it returns the file path of the workspace fmw that is using the transformer fmx. As I understand it, they are unlikely to be in the same place?

Without experimenting, my recollection is that if you "Link" and not "Embed" a custom transformer, then the FME_MF_DIR macro *should* be the location of the .fmx file. But you'd have to get it as a parameter to your function $(FME_MF_DIR) and not from the FME_MacroValues array - because the latter refers to the value of the macro values after all the "mapping file" parts are parsed...hmm...seems best to await the answer to Takashi's "why do you need the .fmx file path" question...


The "fmesuite.fmx" (regular transformers definition) is saved in "<FME_HOME>/transformers", and you can retrieve the <FME_HOME> path through a system parameter called "FME_HOME" or "FME_HOME_UNIX".

The fmx files for custom transformers are usually saved in these folders (Windows).

  • C:/Users/<user name>/AppData/Roaming/Safe Software/FME/FME Store/Transformers
  • C:/Users/<user name>/Documents/FME/Transformers

However, I don't think there is a general way to retrieve the fmx file path of a specific custom transformer.

btw, why do you need to get an fmx file path?

OK, thanks for the information.

I want to know the fmx path because the PythonCaller it contains is an interface to a DLL. My intention is to distribute the fmx with the DLL and its data files. The path will be required so that the python code knows where to load the DLL from.


OK, thanks for the information.

I want to know the fmx path because the PythonCaller it contains is an interface to a DLL. My intention is to distribute the fmx with the DLL and its data files. The path will be required so that the python code knows where to load the DLL from.

I understand your intention but I don't think it's a good idea to specify the DLL path based on the fmx path unfortunately. Because:

The fmx file (custom transformer) will be copied to a specific folder once the user installed it into his/her machine, and then FME will refer to the copied fmx. However the DLL distributed with the fmx will not be copied to the same folder, so the fmx path may not be used to specify the DLL path, even if it could be retrieved with the script. Further, if the user has embedded the custom transformer into a workspace, the fmx itself will not be referred from FME at run-time.

Maybe you will have to consider other approach. For example, add a published parameter (existing folder) to the custom transformer, so that the user can specify the folder in which the DLL is saved, etc...


OK, thanks for the information.

I want to know the fmx path because the PythonCaller it contains is an interface to a DLL.  My intention is to distribute the fmx with the DLL and its data files.  The path will be required so that the python code knows where to load the DLL from.

Try inserting a PythonCaller in your custom transformer with the following code:

from fmeobjects import *
def FeatureProcessor(feature):
    for k, v in FME_MacroValues.iteritems():
        if v.find('<') > -1:
            v = FMESession().decodeFromFMEParsableText(v)
        FMELogFile().logMessageString('  %s = %s' % (k, v), FME_WARN)

It will dump out all the available values of the FME_MacroValues dictionary to the FME log (using the blue "warning" color, to make them easier to spot), you could see if there is something in there that could help you along.

David


I understand your intention but I don't think it's a good idea to specify the DLL path based on the fmx path unfortunately. Because:

The fmx file (custom transformer) will be copied to a specific folder once the user installed it into his/her machine, and then FME will refer to the copied fmx. However the DLL distributed with the fmx will not be copied to the same folder, so the fmx path may not be used to specify the DLL path, even if it could be retrieved with the script. Further, if the user has embedded the custom transformer into a workspace, the fmx itself will not be referred from FME at run-time.

Maybe you will have to consider other approach. For example, add a published parameter (existing folder) to the custom transformer, so that the user can specify the folder in which the DLL is saved, etc...

I agree. But if you want to follow best practices, I would recommend for the installer to register the DLL with Windows using the KnownDLLs registry entry, see this article from Microsoft.


Try inserting a PythonCaller in your custom transformer with the following code:

from fmeobjects import *
def FeatureProcessor(feature):
    for k, v in FME_MacroValues.iteritems():
        if v.find('<') > -1:
            v = FMESession().decodeFromFMEParsableText(v)
        FMELogFile().logMessageString('  %s = %s' % (k, v), FME_WARN)

It will dump out all the available values of the FME_MacroValues dictionary to the FME log (using the blue "warning" color, to make them easier to spot), you could see if there is something in there that could help you along.

David

@david_r, @takashi,

Thank you both for the detailed responses.  I have written a function to perform WGS84 to BNG using the new TN15/GM15 parameter grids.  It is written in cross-platform Object Pascal that can be compiled to a DLL on Windows, or SO on Linux/OSX in either 32 or 64 bit variants. At the moment I have only been asked to produce a 32bit Windows version and I am looking for the minimum "glue" to enable it's use from within FME on that platform.

I would rather not get into writing complex platform specific installers, I prefer a zero install approach of a downloadable archive that can be decompressed and run from anywhere the end user chooses.  I understand I can make the fmx link rather than embed, which should reduce the chances of the python code being separated from the DLL, but I still need it to locate at least the DLL file first.  I will have a play with David's code to see what I can dig up!

 


Try inserting a PythonCaller in your custom transformer with the following code:

from fmeobjects import *
def FeatureProcessor(feature):
    for k, v in FME_MacroValues.iteritems():
        if v.find('<') > -1:
            v = FMESession().decodeFromFMEParsableText(v)
        FMELogFile().logMessageString('  %s = %s' % (k, v), FME_WARN)

It will dump out all the available values of the FME_MacroValues dictionary to the FME log (using the blue "warning" color, to make them easier to spot), you could see if there is something in there that could help you along.

David

Unfortunately all of the macro values either point to:

 

C:\Users\paul\Documents\My FME Workspaces

 

the location of my testing fmw, or to:

 

C:\apps\FME

 

The root of my FME installation. Neither directly relate to:

 

C:\Users\paul\Documents\FME\Transformers

 

The actual location of my transformer files.  I guess I will have to instruct my users to install the archive folder to a specific location probably "<FME_HOME>/transformers" as Takashi suggested and then test that the file exists before attempting to load the dll.

 

 

Thanks again to all.

Hi @paulfmichell.

You can win.

Make your custom transformer LINKed only.

Use a ParameterFetcher in said custom transformer to retrieve FME_MF_DIR

If the custom xformer is LINKed (not Embedded), then the FME_MF_DIR retrieved by the ParameterFetcher will hold the directory that the custom xformer lives in.

I'll attach both my .fmx and my .fmw I used to prove this. The Log they generate also is enlightening:

Feature Type: `ParameterFetcher_2_Output_LOGGED'

Attribute(32 bit unsigned integer): `_creation_instance' has value `0'

Attribute(encoded: utf-8) : `_mfdir' has value `/Users/dal/Documents/workspaces/'

Attribute(encoded: utf-8) : `_mfdirinside' has value `/Users/dal/Library/Application Support/FME/Transformers/'

mfdirtester.fmw mfdirtester.zip


Unfortunately all of the macro values either point to:

 

C:\\Users\\paul\\Documents\\My FME Workspaces

 

the location of my testing fmw, or to:

 

C:\\apps\\FME

 

The root of my FME installation. Neither directly relate to:

 

C:\\Users\\paul\\Documents\\FME\\Transformers

 

The actual location of my transformer files. I guess I will have to instruct my users to install the archive folder to a specific location probably "<FME_HOME>/transformers" as Takashi suggested and then test that the file exists before attempting to load the dll.

 

 

Thanks again to all.

Hi @paulfmichell.

You can win.

Make your custom transformer LINKed only.

Use a ParameterFetcher in said custom transformer to retrieve FME_MF_DIR

If the custom xformer is LINKed (not Embedded), then the FME_MF_DIR retrieved by the ParameterFetcher will hold the directory that the custom xformer lives in.

I'll attach both my .fmx and my .fmw I used to prove this. The Log they generate also is enlightening:

Feature Type: `ParameterFetcher_2_Output_LOGGED'

Attribute(32 bit unsigned integer): `_creation_instance' has value `0'

Attribute(encoded: utf-8) : `_mfdir' has value `/Users/dal/Documents/workspaces/'

Attribute(encoded: utf-8) : `_mfdirinside' has value `/Users/dal/Library/Application Support/FME/Transformers/'

mfdirtester.fmw mfdirtester.zip


Hi @paulfmichell.

You can win.

Make your custom transformer LINKed only.

Use a ParameterFetcher in said custom transformer to retrieve FME_MF_DIR

If the custom xformer is LINKed (not Embedded), then the FME_MF_DIR retrieved by the ParameterFetcher will hold the directory that the custom xformer lives in.

I'll attach both my .fmx and my .fmw I used to prove this. The Log they generate also is enlightening:

Feature Type: `ParameterFetcher_2_Output_LOGGED'

Attribute(32 bit unsigned integer): `_creation_instance' has value `0'

Attribute(encoded: utf-8) : `_mfdir' has value `/Users/dal/Documents/workspaces/'

Attribute(encoded: utf-8) : `_mfdirinside' has value `/Users/dal/Library/Application Support/FME/Transformers/'

mfdirtester.fmw mfdirtester.zip

Addendum: Why does this work? ( @david_r @takashi )

To fully appreciate why this is, one has to deeply understand the FME mapping file parser (sorry). In general this is not worth knowing. But as one of the original authors, I can shed some light.

As we "INCLUDE" linked custom xformers, several of the system parameters (macros) get redefined, and then once that INCLUDE is done and we go back to the main workspace, they get redefined again. But if you use a ParameterFetcher, you get the value at the moment the file is being parsed. If you use the FME_MacroValues array in python, you get the value at the END of all the parsing.

I hope this helps.


Hi

Have a look at the FME_MF_DIR parameter, e.g.

import os.path
workspace_filename = FME_MacroValuesa'FME_MF_DIR']
workspace_dir = os.path.dirname(workspace_filename)

David

I was looking for a way to find out the path of the FMW file as part of the Workspace and this is it :-)

 


Reply