You are a large language model with a special focus on Feature Manipulation Engine (FME) and adept and helping users generate code for use in it's Regex, SQL, and Python editors.
Below is the default code for PythonCaller. Add a function that outputs a list of the currently active python environment, location on disk, and version numbers of primary modules and libraries important for use in FME. import fme import fmeobjects 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.""" pass def has_support_for(self, support_type: int): """This method is called by FME to determine if the PythonCaller supports Bulk mode, which allows for significant performance gains when processing large numbers of features. Bulk mode cannot always be supported. More information available in transformer help. """ return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM def input(self, feature: fmeobjects.FMEFeature): """This method is called for each feature which enters the PythonCaller. Processed input features can be emitted from this method using self.pyoutput(). If knowledge of all input features is required for processing, then input features should be cached to a list instance variable and processed using group processing or in the close() method. """ self.pyoutput(feature) def close(self): """This method is called once all the FME Features have been processed from input().""" pass def process_group(self): """This method is called by FME for each group when group processing mode is enabled. This implementation should reset any instance variables used for the next group. Bulk mode should not be enabled when using group processing. More information available in transformer help. """ pass
Sure, here's an example of that Python code function within your class. I've used `sys`, `os`, `platform`, `pip` and `importlib` modules to get the required information.
import fme
import fmeobjects
import sys
import os
import platform
import pip
import importlib
# Necessary to get the local site-packages path
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
self.pyoutput(feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pip.get_installed_distributions()
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
The code runs successfully but FME reports empty output. Here is the relevant log secton: PythonCreator_Creator (CreationFactory): Created 1 features Optional `process_group' method not present; not called PythonCreator_OUTPUT Brancher -1 4 (BranchingFactory): Branched 1 input feature -- 1 feature routed to the target factory, and 0 features routed to the fallback factory. _CREATOR_BRANCH_TARGET (TeeFactory): Cloned 1 input feature(s) into 1 output feature(s) Destination Feature Type Routing Correlator (RoutingFactory): Tested 0 input feature(s), wrote 0 output feature(s): 0 matched merge filters, 0 were routed to output, 0 could not be routed. Final Output Nuker (TeeFactory): Cloned 0 input feature(s) into 0 output feature(s)
It seems like there's been some confusion. The function `get_environment_info` is merely returning the dictionary of the information about the environment, but it isn't outputting this data as a feature nor writing it into the logs. To output this data as a feature, you would need to call `get_environment_info` within `input` or `close` methods and utilize `fmeobjects.FMEFeature` to transform this data into a format FME can handle. Here's a possible way to do this with making use of your `input` method:
import fme
import fmeobjects
import sys
import os
import platform
import pip
import importlib
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
# Call get_environment_info instead of self.pyoutput(feature)
env_info = self.get_environment_info()
# Use the dictionary to create a new feature
for key, value in env_info.items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute(key, str(value))
self.pyoutput(new_feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
env_info = {}
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
env_info["site_packages_path"] = get_python_lib()
installed_libraries = pip.get_installed_distributions()
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
Python Exception
The method `pip.get_installed_distributions()` was removed starting with pip version 10.0. Instead, you could use `pkg_resources` to get a list of installed distributions. Here's your updated `get_environment_info` method:
import pkg_resources
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pkg_resources.working_set
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
what needs to be entered in to the "Attributes to Expose" field of the "Output Attributes" section of the PythonCaller dialog to use the values returned by the code?
The "Attributes to Expose" field specifies which Python variables you want to expose as attributes in the FME feature schema. These exposed attributes will be viewable and usable within your FME workspace. Since you are outputting attributes dynamically by setting them directly on the FMEFeature object within the `input` method, you do not need to specify anything in the 'Attributes to Expose' field. The created attributes ("python_version", "python_location", "site_packages_path" and "library_versions") will be added to the feature regardless of what is specified in the 'Attributes to Expose' field. If you were returning a dictionary from the `input` method, you would need to specify the keys of the dictionary in the 'Attributes to Expose' field. But since you are creating attributes directly on the features by using `setAttribute`, this is not necessary. However, if you still want to explicity expose attribute via "Attributes to Expose" field, you could enter a comma-separated list like so: ``` python_version, python_location, site_packages_path, library_versions ``` Ensure these align exactly with the keys in the dictionary returned by `get_environment_info`, including case sensitivity.
When I leave Attributes to Expose empty I get output of 4 rows and 1 column, all values empty. When I enter `python_version, python_location, site_packages_path, library_versions` I get the output in Visual Preview. (A 4 by 4 table matrix, white space delimited, all cells empty except the bottom right, corresponding to col `library_versions` and row 3): ``` python_version, python_location, site_packages_path, library_versions {'fme-emailer': '3.3.3', 'certifi': '2022.9.24', 'regex': '2022.7.9', 'fsspec': '2022.7.1', 'dask': '2022.7.0', 'distributed': '2022.7.0', 'pefile': '2022.5.30', 'setuptools': '65.5.1', 'cryptography': '38.0.4', 'pyzmq': '24.0.1', 'keyring': '23.4.0', 'pip': '22.2.2', 'pyopenssl': '22.0.0', 'attrs': '21.4.0', 'argon2-cffi': '21.3.0', 'packaging': '21.3', 'argon2-cffi-bindings': '21.2.0', 'azure-storage-blob': '12.8.0', 'pillow': '9.5.0', 'click': '8.0.4', 'ipython': '7.34.0', 'ipywidgets': '7.7.2', 'nbconvert': '7.2.1', 'pytest': '7.2.0', 'ipykernel': '6.16.0', 'notebook': '6.4.12', 'jupyter-console': '6.4.2', 'jupyter-client': '6.1.12', 'tornado': '6.1', 'pyyaml': '6.0', 'psutil': '5.9.0', 'nbformat': '5.6.1', 'traitlets': '5.5.0', 'ujson': '5.4.0', 'decorator': '5.1.1', 'tqdm': '4.64.1', 'fonttools': '4.25.0', 'jsonschema': '4.16.0', 'importlib-metadata': '4.11.3', 'jupyter-core': '4.11.2', 'beautifulsoup4': '4.11.1', 'lxml': '4.9.1', 'saspy': '4.3.2', 'typing-extensions': '4.3.0', 'cachetools': '4.2.2', 'bleach': '4.1.0', 'pyodbc': '4.0.34', 'flake8': '4.0.1', 'chardet': '4.0.0', 'lerc': '4.0', 'protobuf': '3.19.4', 'zipp': '3.8.0', 'h5py': '3.7.0', 'widgetsnbextension': '3.6.1', 'matplotlib': '3.6.0', 'anyio': '3.5.0', 'jupyterlab': '3.4.8', 'idna': '3.4', 'oauthlib': '3.2.0', 'lz4': '3.1.3', 'pro-notebook-integration': '3.1', 'openpyxl': '3.0.10', 'pyparsing': '3.0.9', 'prompt-toolkit': '3.0.5', 'jinja2': '3.0.3', 'jupyterlab-widgets': '3.0.3', 'requests': '2.28.1', 'pycparser': '2.21', 'fastjsonschema': '2.16.2', 'jupyterlab-server': '2.16.0', 'pygments': '2.9.0', 'networkx': '2.8.4', 'python-dateutil': '2.8.2', 'numexpr': '2.8.1', 'pycodestyle': '2.8.0', 'platformdirs': '2.5.2', 'affine': '2.4.0', 'pyflakes': '2.4.0', 'pyjwt': '2.4.0', 'sortedcontainers': '2.4.0', 'soupsieve': '2.3.2.post1', 'mkl-service': '2.3.0', 'pyshp': '2.1.3', 'markupsafe': '2.1.1', 'arcgis': '2.1.0.2', 'zict': '2.1.0', 'asttokens': '2.0.5', 'charset-normalizer': '2.0.4', 'mistune': '2.0.4', 'pywinpty': '2.0.1', 'shapely': '2.0.1', 'tomli': '2.0.1', 'xlrd': '2.0.1', 'cloudpickle': '2.0.0', 'botocore': '1.29.76', 'boto3': '1.26.76', 'urllib3': '1.26.12', 'pypdf2': '1.26.0', 'jupyter-server': '1.21.0', 'numpy': '1.20.1', 'six': '1.16.0', 'cffi': '1.15.1', 'wrapt': '1.14.1', 'azure-core': '1.12.0', 'swat': '1.12.0', 'gssapi': '1.8.1', 'send2trash': '1.8.0', 'pysocks': '1.7.1', 'tblib': '1.7.0', 'python-certifi-win32': '1.7.dev1+g24a5d1b', 'cftime': '1.6.2', 'scipy': '1.6.2', 'netcdf4': '1.6.1', 'pystac': '1.6.1', 'nest-asyncio': '1.5.5', 'debugpy': '1.5.1', 'pandocfilters': '1.5.0', 'sqlalchemy': '1.4.39', 'snuggs': '1.4.7', 'jupyter-latex-envs': '1.4.6', 'appdirs': '1.4.4', 'pandas': '1.4.4', 'typed-ast': '1.4.3', 'kiwisolver': '1.4.2', 'jdcal': '1.4.1', 'blinker': '1.4', 'ntlm-auth': '1.4.0', 'nose': '1.3.7', 'rasterio': '1.3.6', 'bottleneck': '1.3.5', 'mkl-fft': '1.3.0', 'xlwt': '1.3.0', 'requests-oauthlib': '1.3.0', 'requests-gssapi': '1.2.3', 'mkl-random': '1.2.2', 'mpmath': '1.2.1', 'partd': '1.2.0', 'sniffio': '1.2.0', 'munkres': '1.1.4', 'click-plugins': '1.1.1', 'greenlet': '1.1.1', 'iniconfig': '1.1.1', 'tinycss2': '1.1.1', 'et-xmlfile': '1.1.0', 'win-inet-pton': '1.1.0', 'contourpy': '1.0.5', 'msgpack': '1.0.3', 'heapdict': '1.0.1', 'jmespath': '1.0.1', 'geomet': '1.0.0', 'locket': '1.0.0', 'exceptiongroup': '1.0.0rc9', 'websocket-client': '0.58.0', 'olefile': '0.46', 'wheel': '0.37.1', 'xarray': '0.20.1', 'pyrsistent': '0.18.0', 'pluggy': '0.13.1', 'terminado': '0.13.1', 'seaborn': '0.12.1', 'requests-kerberos': '0.12.0', 'toolz': '0.11.2', 'cytoolz': '0.11.0', 'cycler': '0.11.0', 'toml': '0.10.2', 'json5': '0.9.5', 'requests-toolbelt': '0.9.1', 'pathspec': '0.9.0', 'executing': '0.8.3', 'parso': '0.8.3', 'prometheus-client': '0.8.0', 'winkerberos': '0.8.0', 'pickleshare': '0.7.5', 'cligj': '0.7.2', 'defusedxml': '0.7.1', 'brotlipy': '0.7.0', 'mccabe': '0.7.0', 'nbclient': '0.7.0', 'msrest': '0.6.21', 'isodate': '0.6.0', 'jupyter-contrib-nbextensions': '0.6.0', 's3transfer': '0.6.0', 'webencodings': '0.5.1', 'stack-data': '0.5.1', 'testpath': '0.5.0', 'nbclassic': '0.4.8', 'colorama': '0.4.5', 'mypy-extensions': '0.4.3', 'jupyter-nbextensions-configurator': '0.4.1', 'x86cpu': '0.4', 'entrypoints': '0.4', 'jupyter-contrib-core': '0.4.0', 'wcwidth': '0.2.5', 'jupyterlab-pygments': '0.2.2', 'pure-eval': '0.2.2', 'pywin32-ctypes': '0.2.0', 'wincertstore': '0.2', 'backcall': '0.2.0', 'despatch': '0.2.0', 'ipython-genutils': '0.2.0', 'jupyter-highlight-selected-word': '0.2.0', 'matplotlib-inline': '0.1.6', 'notebook-shim': '0.1.0', 'black': '0.0.0', 'requests-negotiate-sspi': '0.0.0', 'babel': '2.11.0', 'future': '0.18.2', 'jedi': '0.18.2', 'pytz': '2022.6', 'sympy': '1.9', 'filelock': '3.10.6', 'tldextract': '3.4.0', 'markdown': '3.1.1', 'xlsxwriter': '3.0.9', 'tzlocal': '2.1', 'hdfs': '2.0.16', 'diskcache': '2.0.2', 'requests-file': '1.5.1', 'requests-aws4auth': '1.2.2', 'requests-ntlm': '1.2.0', 'pypac': '0.16.1', 'xmltodict': '0.13.0', 'python-docx': '0.8.11', 'pyspnego': '0.8.0', 'python-pptx': '0.6.21', 'docopt': '0.6.2', 'dukpy': '0.3.0'} ```
This error suggests that the loop handling creating new features and setting attributes is not working correctly. The result "`library_versions`" should have been written into a series of features, one for each library. However, the visual output suggests that it's not happening right so. Perhaps it's because the loop is not iterating through the `library_versions` dictionary separately. Let's adjust the function to ensure each library and its corresponding version results in a single feature:
import fme
import fmeobjects
import sys
import os
import platform
import pkg_resources
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
# Call get_environment_info instead of self.pyoutput(feature)
env_info = self.get_environment_info()
# Use the dictionary to create a new feature
# Deal with dictionary within the dictionary separately (i.e., for library_versions)
for key, value in env_info.items():
if key == 'library_versions':
for lib, version in value.items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Library", lib)
new_feature.setAttribute("Version", version)
self.pyoutput(new_feature)
else:
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute(key, str(value))
self.pyoutput(new_feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pkg_resources.working_set
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
now it outputs 230 rows and 5 columns. All cells are empty except for col 'site_packages_path' row 3, which holds "C:\ArcGIS\bin\Python\envs\arcgispro-py3\Lib\site-packages". The headers are "Library, Version, python_version, python_location, site_packages_path"
The issue refers that the attributes of a specific feature are only written when it corresponds to it. This results in a sparse data with lot of None or empty cells. To make it easier to interpret the result in Visual Preview or any consequent transformations, we should create an individual feature for each piece of information. We can do this by modifying the loop in the `input` method like follows:
def input(self, feature: fmeobjects.FMEFeature):
# Get the environment information
env_info = self.get_environment_info()
# Handling general python attributes
for key, value in env_info.items():
if key != 'library_versions':
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Attribute", key)
new_feature.setAttribute("Value", str(value))
self.pyoutput(new_feature)
# Handling library versions separately
for library, version in env_info['library_versions'].items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Attribute", library)
new_feature.setAttribute("Value", version)
self.pyoutput(new_feature)
Ah, success. Now it works.
Great to hear that it's working now! Just for the sake of summarization, what was done here is we gathered various pieces of information about your Python environment – including the Python version, the location of Python executable, the site-packages path, and the version numbers of installed libraries. This information was then written into individual features on an attribute level. If you have any more questions about FME, PythonCaller or anything else, feel free to ask. Happy data processing with FME!
Below is the default code for PythonCaller. Add a function that outputs a list of the currently active python environment, location on disk, and version numbers of primary modules and libraries important for use in FME. import fme import fmeobjects 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.""" pass def has_support_for(self, support_type: int): """This method is called by FME to determine if the PythonCaller supports Bulk mode, which allows for significant performance gains when processing large numbers of features. Bulk mode cannot always be supported. More information available in transformer help. """ return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM def input(self, feature: fmeobjects.FMEFeature): """This method is called for each feature which enters the PythonCaller. Processed input features can be emitted from this method using self.pyoutput(). If knowledge of all input features is required for processing, then input features should be cached to a list instance variable and processed using group processing or in the close() method. """ self.pyoutput(feature) def close(self): """This method is called once all the FME Features have been processed from input().""" pass def process_group(self): """This method is called by FME for each group when group processing mode is enabled. This implementation should reset any instance variables used for the next group. Bulk mode should not be enabled when using group processing. More information available in transformer help. """ pass
Sure, here's an example of that Python code function within your class. I've used `sys`, `os`, `platform`, `pip` and `importlib` modules to get the required information.
import fme
import fmeobjects
import sys
import os
import platform
import pip
import importlib
# Necessary to get the local site-packages path
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
self.pyoutput(feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pip.get_installed_distributions()
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
The code runs successfully but FME reports empty output. Here is the relevant log secton: PythonCreator_Creator (CreationFactory): Created 1 features Optional `process_group' method not present; not called PythonCreator_OUTPUT Brancher -1 4 (BranchingFactory): Branched 1 input feature -- 1 feature routed to the target factory, and 0 features routed to the fallback factory. _CREATOR_BRANCH_TARGET (TeeFactory): Cloned 1 input feature(s) into 1 output feature(s) Destination Feature Type Routing Correlator (RoutingFactory): Tested 0 input feature(s), wrote 0 output feature(s): 0 matched merge filters, 0 were routed to output, 0 could not be routed. Final Output Nuker (TeeFactory): Cloned 0 input feature(s) into 0 output feature(s)
It seems like there's been some confusion. The function `get_environment_info` is merely returning the dictionary of the information about the environment, but it isn't outputting this data as a feature nor writing it into the logs. To output this data as a feature, you would need to call `get_environment_info` within `input` or `close` methods and utilize `fmeobjects.FMEFeature` to transform this data into a format FME can handle. Here's a possible way to do this with making use of your `input` method:
import fme
import fmeobjects
import sys
import os
import platform
import pip
import importlib
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
# Call get_environment_info instead of self.pyoutput(feature)
env_info = self.get_environment_info()
# Use the dictionary to create a new feature
for key, value in env_info.items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute(key, str(value))
self.pyoutput(new_feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
env_info = {}
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
env_info["site_packages_path"] = get_python_lib()
installed_libraries = pip.get_installed_distributions()
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
Python Exception
The method `pip.get_installed_distributions()` was removed starting with pip version 10.0. Instead, you could use `pkg_resources` to get a list of installed distributions. Here's your updated `get_environment_info` method:
import pkg_resources
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pkg_resources.working_set
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
what needs to be entered in to the "Attributes to Expose" field of the "Output Attributes" section of the PythonCaller dialog to use the values returned by the code?
The "Attributes to Expose" field specifies which Python variables you want to expose as attributes in the FME feature schema. These exposed attributes will be viewable and usable within your FME workspace. Since you are outputting attributes dynamically by setting them directly on the FMEFeature object within the `input` method, you do not need to specify anything in the 'Attributes to Expose' field. The created attributes ("python_version", "python_location", "site_packages_path" and "library_versions") will be added to the feature regardless of what is specified in the 'Attributes to Expose' field. If you were returning a dictionary from the `input` method, you would need to specify the keys of the dictionary in the 'Attributes to Expose' field. But since you are creating attributes directly on the features by using `setAttribute`, this is not necessary. However, if you still want to explicity expose attribute via "Attributes to Expose" field, you could enter a comma-separated list like so: ``` python_version, python_location, site_packages_path, library_versions ``` Ensure these align exactly with the keys in the dictionary returned by `get_environment_info`, including case sensitivity.
When I leave Attributes to Expose empty I get output of 4 rows and 1 column, all values empty. When I enter `python_version, python_location, site_packages_path, library_versions` I get the output in Visual Preview. (A 4 by 4 table matrix, white space delimited, all cells empty except the bottom right, corresponding to col `library_versions` and row 3): ``` python_version, python_location, site_packages_path, library_versions {'fme-emailer': '3.3.3', 'certifi': '2022.9.24', 'regex': '2022.7.9', 'fsspec': '2022.7.1', 'dask': '2022.7.0', 'distributed': '2022.7.0', 'pefile': '2022.5.30', 'setuptools': '65.5.1', 'cryptography': '38.0.4', 'pyzmq': '24.0.1', 'keyring': '23.4.0', 'pip': '22.2.2', 'pyopenssl': '22.0.0', 'attrs': '21.4.0', 'argon2-cffi': '21.3.0', 'packaging': '21.3', 'argon2-cffi-bindings': '21.2.0', 'azure-storage-blob': '12.8.0', 'pillow': '9.5.0', 'click': '8.0.4', 'ipython': '7.34.0', 'ipywidgets': '7.7.2', 'nbconvert': '7.2.1', 'pytest': '7.2.0', 'ipykernel': '6.16.0', 'notebook': '6.4.12', 'jupyter-console': '6.4.2', 'jupyter-client': '6.1.12', 'tornado': '6.1', 'pyyaml': '6.0', 'psutil': '5.9.0', 'nbformat': '5.6.1', 'traitlets': '5.5.0', 'ujson': '5.4.0', 'decorator': '5.1.1', 'tqdm': '4.64.1', 'fonttools': '4.25.0', 'jsonschema': '4.16.0', 'importlib-metadata': '4.11.3', 'jupyter-core': '4.11.2', 'beautifulsoup4': '4.11.1', 'lxml': '4.9.1', 'saspy': '4.3.2', 'typing-extensions': '4.3.0', 'cachetools': '4.2.2', 'bleach': '4.1.0', 'pyodbc': '4.0.34', 'flake8': '4.0.1', 'chardet': '4.0.0', 'lerc': '4.0', 'protobuf': '3.19.4', 'zipp': '3.8.0', 'h5py': '3.7.0', 'widgetsnbextension': '3.6.1', 'matplotlib': '3.6.0', 'anyio': '3.5.0', 'jupyterlab': '3.4.8', 'idna': '3.4', 'oauthlib': '3.2.0', 'lz4': '3.1.3', 'pro-notebook-integration': '3.1', 'openpyxl': '3.0.10', 'pyparsing': '3.0.9', 'prompt-toolkit': '3.0.5', 'jinja2': '3.0.3', 'jupyterlab-widgets': '3.0.3', 'requests': '2.28.1', 'pycparser': '2.21', 'fastjsonschema': '2.16.2', 'jupyterlab-server': '2.16.0', 'pygments': '2.9.0', 'networkx': '2.8.4', 'python-dateutil': '2.8.2', 'numexpr': '2.8.1', 'pycodestyle': '2.8.0', 'platformdirs': '2.5.2', 'affine': '2.4.0', 'pyflakes': '2.4.0', 'pyjwt': '2.4.0', 'sortedcontainers': '2.4.0', 'soupsieve': '2.3.2.post1', 'mkl-service': '2.3.0', 'pyshp': '2.1.3', 'markupsafe': '2.1.1', 'arcgis': '2.1.0.2', 'zict': '2.1.0', 'asttokens': '2.0.5', 'charset-normalizer': '2.0.4', 'mistune': '2.0.4', 'pywinpty': '2.0.1', 'shapely': '2.0.1', 'tomli': '2.0.1', 'xlrd': '2.0.1', 'cloudpickle': '2.0.0', 'botocore': '1.29.76', 'boto3': '1.26.76', 'urllib3': '1.26.12', 'pypdf2': '1.26.0', 'jupyter-server': '1.21.0', 'numpy': '1.20.1', 'six': '1.16.0', 'cffi': '1.15.1', 'wrapt': '1.14.1', 'azure-core': '1.12.0', 'swat': '1.12.0', 'gssapi': '1.8.1', 'send2trash': '1.8.0', 'pysocks': '1.7.1', 'tblib': '1.7.0', 'python-certifi-win32': '1.7.dev1+g24a5d1b', 'cftime': '1.6.2', 'scipy': '1.6.2', 'netcdf4': '1.6.1', 'pystac': '1.6.1', 'nest-asyncio': '1.5.5', 'debugpy': '1.5.1', 'pandocfilters': '1.5.0', 'sqlalchemy': '1.4.39', 'snuggs': '1.4.7', 'jupyter-latex-envs': '1.4.6', 'appdirs': '1.4.4', 'pandas': '1.4.4', 'typed-ast': '1.4.3', 'kiwisolver': '1.4.2', 'jdcal': '1.4.1', 'blinker': '1.4', 'ntlm-auth': '1.4.0', 'nose': '1.3.7', 'rasterio': '1.3.6', 'bottleneck': '1.3.5', 'mkl-fft': '1.3.0', 'xlwt': '1.3.0', 'requests-oauthlib': '1.3.0', 'requests-gssapi': '1.2.3', 'mkl-random': '1.2.2', 'mpmath': '1.2.1', 'partd': '1.2.0', 'sniffio': '1.2.0', 'munkres': '1.1.4', 'click-plugins': '1.1.1', 'greenlet': '1.1.1', 'iniconfig': '1.1.1', 'tinycss2': '1.1.1', 'et-xmlfile': '1.1.0', 'win-inet-pton': '1.1.0', 'contourpy': '1.0.5', 'msgpack': '1.0.3', 'heapdict': '1.0.1', 'jmespath': '1.0.1', 'geomet': '1.0.0', 'locket': '1.0.0', 'exceptiongroup': '1.0.0rc9', 'websocket-client': '0.58.0', 'olefile': '0.46', 'wheel': '0.37.1', 'xarray': '0.20.1', 'pyrsistent': '0.18.0', 'pluggy': '0.13.1', 'terminado': '0.13.1', 'seaborn': '0.12.1', 'requests-kerberos': '0.12.0', 'toolz': '0.11.2', 'cytoolz': '0.11.0', 'cycler': '0.11.0', 'toml': '0.10.2', 'json5': '0.9.5', 'requests-toolbelt': '0.9.1', 'pathspec': '0.9.0', 'executing': '0.8.3', 'parso': '0.8.3', 'prometheus-client': '0.8.0', 'winkerberos': '0.8.0', 'pickleshare': '0.7.5', 'cligj': '0.7.2', 'defusedxml': '0.7.1', 'brotlipy': '0.7.0', 'mccabe': '0.7.0', 'nbclient': '0.7.0', 'msrest': '0.6.21', 'isodate': '0.6.0', 'jupyter-contrib-nbextensions': '0.6.0', 's3transfer': '0.6.0', 'webencodings': '0.5.1', 'stack-data': '0.5.1', 'testpath': '0.5.0', 'nbclassic': '0.4.8', 'colorama': '0.4.5', 'mypy-extensions': '0.4.3', 'jupyter-nbextensions-configurator': '0.4.1', 'x86cpu': '0.4', 'entrypoints': '0.4', 'jupyter-contrib-core': '0.4.0', 'wcwidth': '0.2.5', 'jupyterlab-pygments': '0.2.2', 'pure-eval': '0.2.2', 'pywin32-ctypes': '0.2.0', 'wincertstore': '0.2', 'backcall': '0.2.0', 'despatch': '0.2.0', 'ipython-genutils': '0.2.0', 'jupyter-highlight-selected-word': '0.2.0', 'matplotlib-inline': '0.1.6', 'notebook-shim': '0.1.0', 'black': '0.0.0', 'requests-negotiate-sspi': '0.0.0', 'babel': '2.11.0', 'future': '0.18.2', 'jedi': '0.18.2', 'pytz': '2022.6', 'sympy': '1.9', 'filelock': '3.10.6', 'tldextract': '3.4.0', 'markdown': '3.1.1', 'xlsxwriter': '3.0.9', 'tzlocal': '2.1', 'hdfs': '2.0.16', 'diskcache': '2.0.2', 'requests-file': '1.5.1', 'requests-aws4auth': '1.2.2', 'requests-ntlm': '1.2.0', 'pypac': '0.16.1', 'xmltodict': '0.13.0', 'python-docx': '0.8.11', 'pyspnego': '0.8.0', 'python-pptx': '0.6.21', 'docopt': '0.6.2', 'dukpy': '0.3.0'} ```
This error suggests that the loop handling creating new features and setting attributes is not working correctly. The result "`library_versions`" should have been written into a series of features, one for each library. However, the visual output suggests that it's not happening right so. Perhaps it's because the loop is not iterating through the `library_versions` dictionary separately. Let's adjust the function to ensure each library and its corresponding version results in a single feature:
import fme
import fmeobjects
import sys
import os
import platform
import pkg_resources
from distutils.sysconfig import get_python_lib
class FeatureProcessor(object):
def __init__(self):
pass
def has_support_for(self, support_type: int):
return support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM
def input(self, feature: fmeobjects.FMEFeature):
# Call get_environment_info instead of self.pyoutput(feature)
env_info = self.get_environment_info()
# Use the dictionary to create a new feature
# Deal with dictionary within the dictionary separately (i.e., for library_versions)
for key, value in env_info.items():
if key == 'library_versions':
for lib, version in value.items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Library", lib)
new_feature.setAttribute("Version", version)
self.pyoutput(new_feature)
else:
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute(key, str(value))
self.pyoutput(new_feature)
def close(self):
pass
def process_group(self):
pass
def get_environment_info(self):
"""Outputs a list of the currently active python environment, location on disk, and version numbers."""
env_info = {}
# Getting Python Version and location
env_info["python_version"] = platform.python_version()
env_info["python_location"] = sys.executable
# Getting the local site-packages path
env_info["site_packages_path"] = get_python_lib()
# Getting installed libraries
installed_libraries = pkg_resources.working_set
library_versions = {library.key: library.version for library in installed_libraries}
env_info["library_versions"] = library_versions
return env_info
now it outputs 230 rows and 5 columns. All cells are empty except for col 'site_packages_path' row 3, which holds "C:\ArcGIS\bin\Python\envs\arcgispro-py3\Lib\site-packages". The headers are "Library, Version, python_version, python_location, site_packages_path"
The issue refers that the attributes of a specific feature are only written when it corresponds to it. This results in a sparse data with lot of None or empty cells. To make it easier to interpret the result in Visual Preview or any consequent transformations, we should create an individual feature for each piece of information. We can do this by modifying the loop in the `input` method like follows:
def input(self, feature: fmeobjects.FMEFeature):
# Get the environment information
env_info = self.get_environment_info()
# Handling general python attributes
for key, value in env_info.items():
if key != 'library_versions':
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Attribute", key)
new_feature.setAttribute("Value", str(value))
self.pyoutput(new_feature)
# Handling library versions separately
for library, version in env_info['library_versions'].items():
new_feature = fmeobjects.FMEFeature()
new_feature.setAttribute("Attribute", library)
new_feature.setAttribute("Value", version)
self.pyoutput(new_feature)
Ah, success. Now it works.
Great to hear that it's working now! Just for the sake of summarization, what was done here is we gathered various pieces of information about your Python environment – including the Python version, the location of Python executable, the site-packages path, and the version numbers of installed libraries. This information was then written into individual features on an attribute level. If you have any more questions about FME, PythonCaller or anything else, feel free to ask. Happy data processing with FME!