views:

104

answers:

6

How can I pass a dictionary to a python script from another python script over the command line? I use subprocess to call the second script.

The options I've come to are:
I) Build a module to parse a dictionary from a string (more in-depth than I had hoped to go).
II) Use a temporary file to write a pickle, and pass the file's name as an argument
III) Don't allow dictionaries, but handle key/value pairs (that is "prog.py keya valuea keyb valub")

The solution does not have to be user-friendly, but does need to be program friendly. The second program must be run as a separate process, due to security and resource concerns.

+6  A: 

If you don't need a too terribly complicated data structure, might I recommend simplejson? It's available as a built-in module (called json) in Python 2.6 and later.

Quartz
Yeah, you can pass the json serialized dictionary by writing it to the stdin of the second script.
Epeli
+1 because JSON is simple, effective, and almost identical to Python's `dict` structure.
jathanism
+1 because this is perfect, although FoggleBird's answer is perfecter, and I can't talk the boss into anything that deals with javascript
ChrisAdams
A: 

How about xmlrpc?

Client http://docs.python.org/library/xmlrpclib.html

Server http://docs.python.org/library/simplexmlrpcserver.html

Both are in python core.

Epeli
Seems like overkill for passing a `dict` around.
Will McCutchen
+4  A: 

Have you looked at the pickle module to pass the data over stdout/stdin?

Example:

knights.py:

import pickle
import sys

desires = {'say': 'ni', 'obtain': 'shrubbery'}
pickle.dump(desires, sys.stdout)

roundtable.py:

import pickle
import sys

knightsRequest = pickle.load(sys.stdin)
for req in knightsRequest:
    print "The knights %s %s." % (req, knightsRequest[req])

Usage and output:

$ python knights.py | python roundtable.py
The knights say ni.
The knights obtain shrubbery.
Mike DeSimone
Not a bad idea!
jathanism
+3  A: 

Aside from pickle, another option is ast.literal_eval, if your dictionaries only contain Python primitives.

>>> d = {3: 9, 'apple': 'orange'}
>>> s = str(d)
>>> s
"{3: 9, 'apple': 'orange'}"
>>> import ast
>>> x = ast.literal_eval(s)
>>> x
{3: 9, 'apple': 'orange'}
FogleBird
This was exactly what I was looking for
ChrisAdams
A: 

Just print the dictionary in one python script

print( "dict=" + str(dict) )

and use it as

(python script1.py; cat script2.py) | python -

and now you should be able access the dictionary through global variable 'dict' in the second python script.

hluk
+1  A: 

If what's in the dictionary (both keys and values) can be represented as strings, you should be able to pass it as a string argument to the second script which can recreate it.

d = {'a':1,'b':2}

d == eval(repr(d), None)

>>>True

Edit: Here's a slightly more involved example showing its use with a simple custom class:

class MyClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __repr__(self):
        return 'MyClass(%r, %r)' % (self.a, self.b)
    def __eq__(self, other):
        return (self.a == other.a) and (self.b == other.b)

d = {'foo':42, 'bar': MyClass(17,'astring') }

print 'd:', d
print 'repr(d):', repr(d)
print "d == eval(repr(d), {'MyClass':MyClass})?:", \
      d == eval(repr(d), {'MyClass':MyClass})

# outputs:
# d: {'foo': 42, 'bar': MyClass(17, 'astring')}
# repr(d): {'foo': 42, 'bar': MyClass(17, 'astring')}
# d == eval(repr(d), {'MyClass':MyClass})?: True
martineau
`ast.literal_eval` is the safe and correct way to do this.
FogleBird
`ast.literal_eval()` can be too restrictive. Instead of None, it's possible to pass `eval()` a second argument that is instead a dictionary mapping custom class names to corresponding custom classes which, when provided, would allow instances of them to also be reconstituted in additiona to the strings, numbers, tuples, lists, dicts, booleans, and None value types that `ast.literal_eval()` supports.
martineau