php to python dynamic dispatch example

todddeluca at gmail.com todddeluca at gmail.com
Sun Dec 18 14:45:11 EST 2005


I am posting code for calling almost any python function from php,
because it seems generally useful.  Please feel free to suggest
improvements or tell me this has already been done better somewhere
else, etc.  My limited searching turned up nothing.

I work in a heterogeneous environment with php web pages and python
modules/scripts.  This code requires no no creation of an ad hoc
command line interface to the python module and/or ad hoc serialization
of inputs and outputs.

Essentially it requires python's ability to call functions with keyword
arguments, dynamic importing of modules, and a serialization protocol
shared by php and python.  In this case I use php serialization, but
perhaps YAML or JSON could be used instead.

Below is the php function.  It simply serializes an array of keyword
parameter values, invokes the python module which will call the
function (passing in the keywords) and unserializes the output.
runCommand() is a helper function that simply invokes the command,
passes it stdin, and returns stdout, stderr, and the exit code of the
command.

function python_dispatch($fullyQualifiedFuncName, $keywords) {
  $stdin = serialize($keywords);
  $cmd = PYTHON_ROOT."/php_dispatch.py
".escapeshellarg($fullyQualifiedFuncName);
  list($exitcode, $stdout, $stderr) = runCommand($cmd, $stdin);
  $retval = unserialize($stdout);
  return array($retval, $exitcode);
}


The python code is not complicated either.  It depends on
PHPSerialize.py and PHPUnserialize.py by Scott Hurring
(http://hurring.com/code/python/serialize/).  There are two functions
for dynamically importing the right module and traversing the
components of a complete function path, like
'mypackage.mymodule.myclass.myfunc'.

# this function is from http://docs.python.org/lib/built-in-funcs.html
def _dispatch_import(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod


def _dispatch_lookup_function(name, namespace, prefix=''):
    # last name in component
    if name.find('.') == -1:
        return namespace[name]
    else:
        # get next component in name, and its fully qualified component
name

        first, rest = name.split('.', 1)
        if prefix:
            modName = '.'.join([prefix, first])
        else:
            modName = first

        if first in namespace:
            # if component is in namespace, look for the function in
the namespace of the component
            return _dispatch_lookup_function(prefix=modName, name=rest,
namespace=vars(namespace[first]))
        else:
            # else import component (using fully qualified name) and
look for function in the imported component.
            module = _dispatch_import(modName)
            return _dispatch_lookup_function(prefix=modName, name=rest,
namespace=vars(module))


def main():
    import sys
    modAndFuncName = sys.argv[1]
    func = _dispatch_lookup_function(modAndFuncName, globals())

    # reconstruct function keyword arguments from serialized input


    keywords =
walkPHPArrayConvertHeuristically(PHPUnserialize.PHPUnserialize().unserialize(sys.stdin.read()))
    sys.stdin.close()

    retval = PHPSerialize.PHPSerialize().serialize(func(**keywords))

    sys.stdout.write(retval)


The last piece of the puzzle is walkPHPArrayConvertHeuristically()
which converts unserialized dicts to lists if they should be lists.
This hack gets around the fact that everything is a dict (a.k.a. array)
in php, so using php serialization to go between php and python is not
isomorphic.  This might be a good reason to use JSON or YAML for
serialization, but I do not have any experience with them.

def walkPHPArrayConvertHeuristically(data):
    if type(data) is types.DictType:
        if isPHPDataListGuess(data):
            lst = convertPHPArrayToList(data)
            return [walkPHPArrayConvertHeuristically(item) for item in
lst]
        else:
            for k in data:
                data[k] = walkPHPArrayConvertHeuristically(data[k])
            return data
    else:
        return data


def convertPHPArrayToList(array):
    keys = array.keys()
    keys.sort()
    return [array[k] for k in keys]


def isPHPDataListGuess(data):
    if type(data) is types.DictType:
        i = 0
        for k in data.keys():
            if k != i:
                return False
            i += 1
        return True
    else:
        return False


I hope you find this code useful or interesting.

Cheers,
Todd




More information about the Python-list mailing list