Products Resources Support About Us

Rocket Software

Python Wont Run in Phantoms

I am able to use PyCallFunction in PICK code just fine to call into python modules. If I run the exact same code in a phantom routine instead it silently fails on the PyCallFunction line. I am looking at apart of the u2py docs that talks about python licensing (page 8), and briefly mentions phantom scenarios:

Whenever Python is used in a U2 process, the Python library is loaded into memory and UniVerse or UniData checks whether a license is already consumed for that U2 process. A non-phantom process must have already acquired a license for the check to succeed, and the loading of the Python library is allowed to continue. If a phantom process is already charged for an I-phantom license, then the check succeeds as well and the loading of the Python library is allowed to proceed. For a phantom process that has taken no license, UniVerse or UniData tries to acquire an I-phantom license for it and if successful, then the process is allowed to continue; if unsuccessful, an error occurs and prints to the associated PH file and the process terminates.

Can someone translate this for me? I have UniVerse 11.3.1, and have the python licensing loaded, since it works in a normal PICK SUBROUTINE. Is there any additional step I need to do to make python calls work in a phantom?

Some more info: my code is using SQLAlchemy and pyodbc packages. When in the phantom it cannot create a connection to the SQL server. I am wondering if there is something missing from Linux environment variables/settings/resources available to a regular process vs a phantom process.

Using the XDEMO account and one of the PBP programs, PHANTOMs seem to run OK:

PHANTOM RUN PBP LARGENUM_TEST
and the resulting &PH& log file:

001:
002: Welcome to the XDEMO Account
003: Version: 3.1.5
004:
005: NMBR=12345678901234567894.1111111
006: U2 RESULT:
007: SQRT(NMBR)= 3513641828.8201
008: Python RESULT:
009: SQRT(NMBR)= 3513641828.8201442536786755407383897272074033506064971502928016
83

Could you gather the &PH& (UniVerse) or PH (UniData) log file and share it please?

The licensing section to which you referred is with regard to a PHANTOM becoming elevated to an iPHANTOM when it calls Python. This requires a license slot, so just make sure there is at least one license slot free when you launch the PHANTOM. If there are not any licenses available then the process cannot elevate to an iPHANTOM and will fail.

If all the above look OK, please check the platform environment variables. You don’t mention the platform, but the usual suspects are PATH and LD_LIBRARY_PATH.

Some more info after debugging:

It seems that the issue is running pyodbc in a phantom. Same result whether using pyodbc directly or in sqlalchemy. Whenever it tries to create a connection it throws an Exception. The exact same code will work in a non-phantom setting. I can confirm that it cannot connect to the database, and it doesn’t appear that it can even open up a port to create the connection. Is there something in the Linux PID that underlies the Phantom process that has permissions issues?

Error:

DBAPIError("(pyodbc.Error) ('HY000', 'The driver did not supply an error!')",)

And a stack trace

' File "/u2/...my file..., line ##, in execSql\n connection = engine.raw_connection()\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/base.py", line 2307, in raw_connection\n self.pool.unique_connection, _connection\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/base.py", line 2280, in _wrap_pool_connect\n e, dialect, self\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/base.py", line 1547, in _handle_dbapi_exception_noconnection\n util.raise_from_cause(sqlalchemy_exception, exc_info)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/util/compat.py", line 398, in raise_from_cause\n reraise(type(exception), exception, tb=exc_tb, cause=cause)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/util/compat.py", line 152, in reraise\n raise value.with_traceback(tb)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/base.py", line 2276, in _wrap_pool_connect\n return fn()\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 303, in unique_connection\n return _ConnectionFairy._checkout(self)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 773, in _checkout\n fairy = _ConnectionRecord.checkout(pool)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 492, in checkout\n rec = pool._do_get()\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/impl.py", line 139, in _do_get\n self._dec_overflow()\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/util/langhelpers.py", line 68, in __exit__\n compat.reraise(exc_type, exc_value, exc_tb)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/util/compat.py", line 153, in reraise\n raise value\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/impl.py", line 136, in _do_get\n return self._create_connection()\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 308, in _create_connection\n return _ConnectionR ecord(self)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 437, in __init__\n self.__connect(first_connect_check=True)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/pool/base.py", line 662, in __connect\n ).exec_once_unless_exception(self.connection, self)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/event/attr.py", line 314, in exec_once_unless_exception\n self._exec_once_impl(True, *args, **kw)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/event/attr.py", line 285, in _exec_once_impl\n self(*args, **kw)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/event/attr.py", line 322, in __call__\n fn(*args, **kw)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/util/langhelpers.py", line 1485, in go\n return once_fn(*arg, **kw)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/strategies.py", line 199, in first_connect\n dialect.initialize(c)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/dialects/mssql/ base.py", line 2378, in initialize\n super(MSDialect, self).initialize(connection)\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/engine/default.py", line 300, in initialize\n connection\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/dialects/mssql/pyodbc.py", line 353, in _get_server_version_info\n connection, allow_chars=False\n', ' File "/u2/uv/python/lib/python3.4/sqlalchemy/connectors/pyodbc.py", line 145, in _get_server_version_info\n for n in r.split(dbapi_con.getinfo(self.dbapi.SQL_DBMS_VER)):\n'

Most likely it is looking for the current TTY device or trying to perform an input - which with no real TTY device would fail. There is no other difference between a PHANTOM and non-PHANTOM process.

As a thought, try stacking some input data to the program concerned - e.g. in a PROC
PQN
HVERB_TO_RUN_PROGRAM
STON
H<
H<
H<
P

If you invoke the program using a PROC wrapper for a data statement it might run differently, though beyond that the problem and solution would lie in the pyodbc source code. You could try investigating further with truss/strace/tusc (depending upon the UNIX platform), though this might only highlight a cause and not a solution after analysis.