├── INSTALL.rst ├── LICENSE ├── README.rst ├── pythonica.py └── setup.py /INSTALL.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | Installing 3 | ********** 4 | 5 | Quick install 6 | ============= 7 | :: 8 | 9 | python setup.py install 10 | 11 | or if you need it:: 12 | 13 | sudo python setup.py install 14 | 15 | Requirements 16 | ============ 17 | 18 | Python 19 | ------ 20 | 21 | To use pythonica you must have Python version 2.6 or later. I have not tested 22 | it on earlier versions. You must also have the Mathematica MathLinks python module 23 | installed. This is distributed with Mathematica, and usually resides in 24 | /path/to/Mathematica/Version/SystemFiles/Links/Python. If you can run:: 25 | 26 | import mathlink 27 | 28 | Without any problems you should be good to go. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | pythonica is distributed with the BSD license. 4 | 5 | :: 6 | 7 | Copyright (C) 2004-2012 8 | Benjamin Edwards 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are 13 | met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above 19 | copyright notice, this list of conditions and the following 20 | disclaimer in the documentation and/or other materials provided 21 | with the distribution. 22 | 23 | * Neither the name of the NetworkX Developers nor the names of its 24 | contributors may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | pythonica 3 | ********* 4 | 5 | Introduction 6 | ============ 7 | 8 | pythonica is a Python package for the sane interface between Mathematica and 9 | python via mathlink. The mathlink api provided with Mathematica has a 10 | comprehensive and universal way of communicating with the Mathematica Kernel 11 | that doesn't make quick and dirty communication all that easy. This wraps 12 | around the mathlink module in a way to make things easier. 13 | 14 | Quick Start 15 | =========== 16 | 17 | Getting started is easy:: 18 | 19 | >>> import pythonica 20 | >>> m = pythonica.Pythonica() 21 | >>> result = m.eval('D[Log[x],x]') 22 | >>> print(result) 23 | x^(-1) 24 | >>> X = [1,2,3,4] 25 | >>> m.push(X) 26 | >>> m.eval('sX = Mean[X];') 27 | >>> sX = m.pull('sX') 28 | >>> print(sX) 29 | 2.5 30 | 31 | The Pythonica Class 32 | =================== 33 | 34 | The workhorse of the module is the Pythonica class, this makes a link to the 35 | Mathematica kernel, and takes care of all the token, packet communication that 36 | goes on between mathlink and python. It can take several arguments. 37 | 38 | * ``name`` - The name provided to mathlink to start the Kernel 39 | * ``mode`` - The mode to launch the mathlink Kernel. In combination with name 40 | you can start remote kernels, but I don't understand howthis works, so best 41 | to leave it be. 42 | * ``timeout`` - Provides the time to wait after starting the Kernel 43 | before it's usable. 1 second seems to overkill, adjust at will. 44 | * ``debug`` - For debugging use, will printout tons of information if 45 | you use it. 46 | * ``plot_dir`` - Where to store plots created by Mathematica. If set to 47 | ``None``, no plots will be produces. 48 | * ``plot_size`` - Tuple indicating the size in pixels for plots 49 | * ``plot_format`` - string indicating file extension. If Mathematica 50 | can use it, it should work. 51 | * ``input_prompt`` - boolean indicating whether to print input prompts 52 | from Mathematica 53 | * ``output_prompt`` - boolean indicating whether to print output prompts 54 | from Mathematica 55 | 56 | All of the values from ``debug`` on can be set interactively, for example:: 57 | 58 | >>> m.debug = False 59 | 60 | Pythonica.eval 61 | ================ 62 | 63 | The eval function takes string to be processes as Mathematica input and returns 64 | the result. It takes several options 65 | 66 | * ``expression`` - The expression to be evaluated. If it is malformed 67 | it will throw a ``PythonicaException``. 68 | * ``make_plots`` - A boolean indicating whether to make any plots 69 | occuring from the function call. 70 | * ``output_type`` - A string indicating the type of output to produce. 71 | If the output type is 'string' it will produce a string. If the output 72 | type is 'python' will attempt to convert the output to python for 73 | storage. For conversion to work the str_format must be 'input' See 74 | *Pythonica.pull* for more info. 75 | * ``str_format`` - A string indicating the type of string to produce if 76 | ``output_type`` is 'string'. If 'input' will produce a string which is valid 77 | Mathematica Code, and can be fed back into ``eval``, if 'tex' will produce tex 78 | code, or if 'plain' will produce whatever mathematica would have produced. If 79 | you use ``print`` this usually looks ok. 80 | 81 | 82 | 83 | Examples 84 | -------- 85 | :: 86 | 87 | >>> m.eval('Series[Exp[x],{x,0,3}]') 88 | 'SeriesData[x, 0, {1, 1, 1/2, 1/6}, 0, 4, 1]' 89 | >>> m.eval('Series[Exp[x],{x,0,3}]', str_format='tex') 90 | '1+x+\\\\frac{x^2}{2}+\\\\frac{x^3}{6}+O\\\\left(x^4\\\\right)' 91 | >>> result = m.eval('Series[Exp[x],{x,0,3}]',str_format='plain') 92 | >>> print(result) 93 | 2 3 94 | x x 4 95 | 1 + x + -- + -- + O[x] 96 | 2 6 97 | >>> m.eval('Mean[{1,2,3,4}]',output_type='python') 98 | 2.5 99 | 100 | 101 | Pythonica.push 102 | ============== 103 | 104 | This function attempts to push a python value to the Mathematica Kernel. It 105 | attempts to convert the value first then sends it. 106 | 107 | * ``name`` - The name the value will have in the Mathematica Kernel 108 | * ``value`` - The python value to be passed. 109 | 110 | This currently works by just calling the Mathematica function ``Set``. 111 | Mathematica's type system is not as extensive as Python's here are how things 112 | are set. 113 | 114 | * Python type -> Mathematica type 115 | * ``bool`` -> ``Booleans`` 116 | * ``None`` -> ``Null`` 117 | * ``float`` -> ``Real`` 118 | * ``int`` -> ``Integer`` 119 | * ``long`` -> ``Integer`` 120 | * ``complex`` -> ``Complex`` 121 | * ``iter`` -> ``List`` 122 | * ``list`` -> ``List`` 123 | * ``set`` -> ``List`` 124 | * ``xrange`` -> ``List`` 125 | * ``str``-> ``String`` 126 | * ``tuple`` -> ``List`` 127 | * ``frozenset`` -> ``List`` 128 | 129 | Note that there is currently no support for numpy arrays. This could be 130 | possible in the future given the current interface, but for large arrays would 131 | be slow. Note that since we are essentially converting everything to strings, 132 | this can be exceptionally slow and memory intensive for large amounts of data. 133 | Consider reading and writing to and from files. 134 | 135 | There is no simple type in Mathematica that corresponds to dict, or at least 136 | not that I can find, patches welcome! 137 | 138 | The conversion happens recursively so a list of lists will be appropriately 139 | converted. 140 | 141 | Examples 142 | -------- 143 | :: 144 | 145 | >>> m.push('x',5) 146 | >>> m.eval('x') 147 | '5' 148 | >>> m.push('l',4L) 149 | >>> m.eval('l') 150 | '4' 151 | >>> m.push('y',.5) 152 | >>> m.eval('y') 153 | '0.5' 154 | >>> m.push('z',complex(3,4)) 155 | >>> m.eval('z') 156 | '3. + 4.*I' 157 | >>> m.push('t',True) 158 | >>> m.eval('t') 159 | 'True' 160 | >>> m.push('f',False) 161 | >>> m.eval('f') 162 | 'False') 163 | >>> m.push('n',None) 164 | >>> m.eval('n') 165 | 'None' 166 | >>> m.push('r',range(3)) 167 | >>> m.eval('r') 168 | '{0, 1, 2}' 169 | >>> m.push('L',[1,2,3]) 170 | >>> m.eval('L') 171 | '{1, 2, 3}' 172 | >>> m.push('s',set([1,2,3]) 173 | >>> m.eval('s') 174 | '{1, 2, 3}' 175 | >>> m.push('xr',xrange(2)) 176 | >>> m.eval('xr') 177 | '{0, 1}' 178 | >>> m.push('st','spam') 179 | >>> m.eval('st') 180 | '"spam"' 181 | >>> m.push('fs',frozenset([1,2,3]) 182 | >>> m.eval('fs') 183 | '{1, 2, 3}' 184 | >>> m.push('ll', [1,2,'hello',[2,2.5,4],complex(3,4)] 185 | >>> m.eval('ll') 186 | '{1, 2, "hello", {2, 2.5, 4}, 3. + 4*I}' 187 | 188 | Pythonica.pull 189 | ============== 190 | 191 | This command pulls variables out of the Mathematica kernel into python and 192 | attempts to convert them into python types. The return value is the same as the 193 | return from ``eval`` when ``output_type`` is 'python'. Since Mathematica 194 | returns expressions which are based on function calls we take those function 195 | calls and try to convert them. First the basics. 196 | 197 | * Mathematica Type -> Python Type 198 | * ``Integer`` -> ``int`` or ``long`` depending on size 199 | * ``Rational`` or anything with '\' -> Attempts to go to ``float`` 200 | * ``Complex`` or anything with 'I' -> Attempts to go to ``complex`` 201 | * ``String`` -> str 202 | * symbols -> str 203 | * functions -> dict... 204 | 205 | Let me explain the functions -> dict. If we can't convert the part of the 206 | expression into a python type, we make a dictionary with a single key, the 207 | function name, the value of which is a list of arguments to that function. If 208 | there are nested function calls the produces dicts of lists of dicts. If all 209 | else fails it just returns the original string. Still with me? If not here 210 | are some... 211 | 212 | Examples 213 | -------- 214 | :: 215 | 216 | >>> m.eval('X = Unevaluated[D[Log[x],x]];') 217 | >>> m.pull('X') 218 | {'Hold': [{'D': [{'Log': ['q']}, 'q']}]} 219 | >>> m.eval('Y = Integrate[D[Log[q],q],{q,1.1,10.1}];' 220 | >>> m.pull('Y') 221 | 2.2172252349699813 222 | 223 | Other Types 224 | ----------- 225 | 226 | In the future we could convert different function types. IE if Mathematica 227 | returns ``Log[10]``, we could evaluate ``math.log(10)``. 228 | 229 | Plotting 230 | ======== 231 | 232 | Mathematica has a rich graphics system. If any of your output produces the 233 | words 'Graphics', 'Graphics3D', 'Image', or 'Grid', pythonica will use the 234 | ``Export`` function of Mathematica to produce the image. The images will be 235 | called 'pythonica_plot_x.ext' where 'x' is an increasing number as you produce 236 | more plots, and 'ext' is the extension provided by ``Pythonica.plot_format``. 237 | 238 | Examples 239 | -------- 240 | :: 241 | 242 | >>> m.plot_dir = '.' 243 | >>> res = m.eval('Plot[Sin[q],{q,0,10}]') 244 | 245 | Produces a plot called 'pythonica_plot_0.png' in the current directory. 246 | 247 | 248 | Copyright (C) 2012 249 | Benjamin Edwards 250 | 251 | Distributed with a BSD license; see LICENSE 252 | 253 | -------------------------------------------------------------------------------- /pythonica.py: -------------------------------------------------------------------------------- 1 | import mathlink as _ml 2 | import time as _time 3 | 4 | __author__="""\n""".join(['Benjamin Edwards (bedwards@cs.unm.edu)']) 5 | 6 | # Copyright (C) 2012 7 | # Benjamin Edwards 8 | # All rights reserved. 9 | # BSD license 10 | 11 | # Packets that signify incoming tokens 12 | _incoming_token = [_ml.RETURNPKT, 13 | _ml.RETURNEXPRPKT, 14 | _ml.DISPLAYPKT, 15 | _ml.DISPLAYENDPKT, 16 | _ml.RESUMEPKT, 17 | _ml.RETURNTEXTPKT, 18 | _ml.SUSPENDPKT, 19 | _ml.MESSAGEPKT] 20 | 21 | #identity function for anything to strings 22 | _id_to_mathematica = lambda x: str(x) 23 | 24 | #Convert a float to a string for mathematica 25 | def _float_to_mathematica(x): 26 | return ("%e"%x).replace('e','*10^') 27 | 28 | #Convert a complex to a string for mathematica 29 | def _complex_to_mathematica(z): 30 | return 'Complex' + ('[%e,%e]'%(z.real,z.imag)).replace('e','*10^') 31 | 32 | #convert some type of container to a string for matheatica 33 | def _iter_to_mathematica(xs): 34 | s = 'List[' 35 | for x in xs: 36 | s += _python_mathematica[type(x)](x) 37 | s += ',' 38 | s = s[:-1] 39 | s+= ']' 40 | return s 41 | 42 | #Convert a string to a mathematica string 43 | def _str_to_mathematica(s): 44 | return '\"%s\"'%s 45 | 46 | #Dictionary for type conversions. 47 | _python_mathematica = {bool:_id_to_mathematica, 48 | type(None):_id_to_mathematica, 49 | int:_id_to_mathematica, 50 | float:_float_to_mathematica, 51 | long:_id_to_mathematica, 52 | complex:_complex_to_mathematica, 53 | iter:_iter_to_mathematica, 54 | list:_iter_to_mathematica, 55 | set:_iter_to_mathematica, 56 | xrange:_iter_to_mathematica, 57 | str:_str_to_mathematica, 58 | tuple:_iter_to_mathematica, 59 | frozenset:_iter_to_mathematica} 60 | 61 | #Take a string from mathematica and try to make it into a python object 62 | #This could likely be written better and in the future could include 63 | #methods for other functional conversions 64 | def _mathematica_str_python(s): 65 | if s == 'Null' or s is None: 66 | return None 67 | try: 68 | val = int(s) 69 | except ValueError: 70 | try: 71 | val = float(s) 72 | except ValueError: 73 | try: 74 | val = float(s.replace('*10^','e')) 75 | except ValueError: 76 | val = None 77 | # Some sort of Number, so return it NEED TO ADD COMPLEX and Rational 78 | if val is not None: 79 | return val 80 | val = {} 81 | s = s.replace(" ","").replace('{','List[').replace('}',']') 82 | open_brack = s.find("[") 83 | #Some String not a function Call, likely rational,complex,list or symbol 84 | if open_brack == -1: 85 | div = s.find('/') 86 | if div != -1: 87 | try: 88 | num = _mathematica_str_python(s[:div]) 89 | den = _mathematica_str_python(s[div+1:]) 90 | if num/den == float(num)/den: 91 | return num/den 92 | else: 93 | return float(num)/den 94 | except TypeError: 95 | val = s 96 | im = s.find('I') 97 | if im == -1: 98 | val = s 99 | else: 100 | plus = s.find('+') 101 | times = s.find('*I') 102 | if plus != -1: 103 | if times != -1: 104 | try: 105 | return complex(_mathematica_str_python(s[:plus]), 106 | _mathematica_str_python(s[plus+1:times])) 107 | except TypeError: 108 | val = s 109 | else: 110 | try: 111 | return complex(_mathematica_str_python(s[:plus]),1) 112 | except TypeError: 113 | val = s 114 | else: 115 | if times != -1: 116 | try: 117 | return complex(0,_mathematica_str_python(s[:times])) 118 | except TypeError: 119 | val = s 120 | else: 121 | return complex(0,1) 122 | return val 123 | func = s[:open_brack] 124 | num_open_brack = 1 125 | val[func] = [] 126 | last_comma = open_brack 127 | for i in range(open_brack+1,len(s)): 128 | if s[i] == ',' and num_open_brack == 1: 129 | val[func].append(_mathematica_str_python(s[last_comma+1:i])) 130 | last_comma = i 131 | elif s[i] == '[': 132 | num_open_brack += 1 133 | elif s[i] == ']': 134 | if num_open_brack > 1: 135 | num_open_brack -= 1 136 | elif num_open_brack == 1: 137 | val[func].append(_mathematica_str_python(s[last_comma+1:len(s)-1])) 138 | else: 139 | raise Exception("Unbalanced Brackets") 140 | if func == 'List': 141 | return val['List'] 142 | elif func == 'Complex': 143 | return complex(val['Complex'][0],val['Complex'][1]) 144 | elif func == 'Rational': 145 | return float(val['Rational'][0])/val['Rational'][1] 146 | else: 147 | return val 148 | 149 | #Searches Mathematica string of type 'InputForm' for things to plot 150 | def _find_plot_strings(s): 151 | ps = [] 152 | for g_func in ['Graphics[','Graphics3D[','Image[','Grid[']: 153 | while True: 154 | graph_start = s.find(g_func) 155 | if graph_start == -1: 156 | break 157 | num_brack = 1 158 | for i in range(graph_start+len(g_func),len(s)): 159 | if s[i] == '[': 160 | num_brack += 1 161 | elif s[i] == ']': 162 | if num_brack == 1: 163 | ps.append(s[graph_start:i+1]) 164 | break 165 | else: 166 | num_brack -= 1 167 | s = s.replace(s[graph_start:i+1],'') 168 | return ps 169 | 170 | 171 | #Exception 172 | class PythonicaException(Exception): 173 | pass 174 | 175 | class Pythonica(object): 176 | """ 177 | Base class for Mathematica Communication. 178 | 179 | Creates a link to a Mathematica Kernel and stores information needed 180 | communication 181 | 182 | Parameters 183 | ---------- 184 | name : string 185 | String to launch mathlink. 186 | mode : string 187 | Sting for mode to launch mathlink 188 | timeout : int 189 | Time to give Mathematica to start the kernel 190 | debug : Bool 191 | Whether to print debug information 192 | plot_dir : string 193 | Directory to store plots 194 | plot_size : tuple of 2 ints 195 | Tuple containing plot size in pixels. If None let's Mathematica decide 196 | what size to make things 197 | plot_format : string 198 | Format for plots, default to 'png'. 'bmp', 'svg', and 'jpeg' tested and 199 | seem to work. 200 | output_prompt : string 201 | Whether to print output prompts reported from Mathematica 202 | input_prompt : string 203 | Whether to print input prompts reported from Mathematica 204 | 205 | Examples 206 | -------- 207 | >>> import pythonica 208 | >>> m = pythonica.Pythonica() 209 | >>> m.eval('Mean[{1,2,3}]') 210 | '2' 211 | """ 212 | 213 | 214 | def __init__(self, 215 | name='math -mathlink', 216 | mode='launch', 217 | timeout=1, 218 | debug=False, 219 | plot_dir=None, 220 | plot_size=None, 221 | plot_format='png', 222 | output_prompt=False, 223 | input_prompt=False): 224 | self._env = _ml.env() 225 | self.kernel = self._env.open(name,mode=mode) 226 | self.kernel.connect() 227 | self.debug=debug 228 | self.plot_dir = plot_dir 229 | self.plot_num = 0 230 | self.last_python_result=None 231 | self.last_str_result=None 232 | self.plot_size = plot_size 233 | self.plot_format = plot_format 234 | self.output_prompt = output_prompt 235 | self.input_prompt = input_prompt 236 | self.last_error = None 237 | _time.sleep(timeout) 238 | if not self.kernel.ready(): 239 | raise PythonicaException("Unable to Start Mathematica Kernel") 240 | else: 241 | packet = self.kernel.nextpacket() 242 | if self.debug: 243 | print _ml.packetdescriptiondictionary[packet] 244 | if packet == _ml.INPUTNAMEPKT: 245 | self.kernel.getstring() 246 | 247 | def eval(self,expression,make_plots=True,output_type='string',str_format='input'): 248 | """ 249 | Evaluate a string in the Mathematica Kernel 250 | 251 | Evalutes the string 'expression' in Mathematica. 252 | 253 | Parameters 254 | ---------- 255 | expression: string 256 | Expression to be evaluated 257 | make_plots: boolean 258 | Whether to produce plots, plot_dir must not be None 259 | output_type: string 260 | Whether to output a string or a python object, must be either 261 | 'string' or 'pythong' 262 | str_format: string 263 | How to format the string if output_type='string'. If 'input' will 264 | produce a string which can be used as Mathematica Input. If 'tex' 265 | will produce valid tex. If 'plain' will produce whatever plain text 266 | mathematica would produce. 267 | 268 | Returns 269 | ------- 270 | String or python object. 271 | 272 | Raises 273 | ------ 274 | PythonicaException 275 | 276 | Examples 277 | -------- 278 | 279 | >>> m.eval('D[Log[x],x]') 280 | 'x^(-1)' 281 | >>> m.eval('Mean[{1,2,3,4}]',output_type='python') 282 | 2.5 283 | >>> m.eval('D[Log[x],x]',str_output='tex') 284 | '\\\\frac{1}{x}' 285 | >>> print m.eval('D[Log[x],x]',str_output='plain') 286 | 1 287 | - 288 | x 289 | 290 | See Also 291 | -------- 292 | README.rst 293 | """ 294 | self.last_python_result=None 295 | self.last_str_result=None 296 | self.last_error=None 297 | if str_format=='tex': 298 | expression = 'ToString[' + expression+',TeXForm]' 299 | elif str_format=='input': 300 | expression = 'ToString[' + expression + ',InputForm]' 301 | elif str_format=='plain': 302 | pass 303 | else: 304 | raise PythonicaException("String Format must be 'tex', 'input', or 'plain'") 305 | self.kernel.putfunction("EnterTextPacket",1) 306 | self.kernel.putstring(expression) 307 | self.__parse_packet() 308 | str_result = self.last_str_result 309 | if self.last_error is not None: 310 | raise PythonicaException(self.last_error.decode('string_escape')) 311 | if make_plots and self.plot_dir is not None: 312 | plot_exp = _find_plot_strings(str_result) 313 | for s in plot_exp: 314 | filename='\"%s/pythonica_plot_%i.%s\"'%(self.plot_dir,self.plot_num,self.plot_format) 315 | if self.plot_size is None: 316 | self.eval('Export[%s,%s];'%(filename,s),make_plots=False,str_format='plain') 317 | else: 318 | (w,h) = self.plot_size 319 | self.eval('Export[%s,%s,ImageSize->{%i,%i}];'%(filename,s,w,h),make_plots=False,str_format='plain') 320 | self.plot_num += 1 321 | if str_format == 'plain' and str_result is not None: 322 | str_result = str_result.decode('string_escape') 323 | self.last_str_result = str_result 324 | if output_type == 'python': 325 | self.last_python_result = _mathematica_str_python(str_result) 326 | return self.last_python_result 327 | elif output_type == 'string': 328 | self.last_python_result = None 329 | if str_result == 'Null': 330 | return None 331 | else: 332 | return str_result 333 | else: 334 | raise PythonicaException("Output Type must be either 'python' or 'string'(default)") 335 | 336 | def push(self, name, value): 337 | """ 338 | Push python object to Mathematica Kernel. 339 | 340 | Can make some conversions of python objects to Mathematica. See 341 | README.rst for more information. 342 | 343 | Parameters 344 | ---------- 345 | name : string 346 | Name for value in Mathematica Kernel 347 | value : python object 348 | Object to be pushed to Mathematica Kernel 349 | 350 | Returns 351 | ------- 352 | None 353 | 354 | Raises 355 | ------ 356 | PythonicaException: If the object cannot be converted 357 | 358 | Examples 359 | -------- 360 | 361 | >>> m.push('l',[1,2,3]) 362 | >>> m.eval('l2 = 2*l;') 363 | >>> m.pull('l2') 364 | [2,4,6] 365 | """ 366 | 367 | convert_function = _python_mathematica.get(type(value),-1) 368 | if convert_function is -1: 369 | raise PythonicaException("Could not convert %s to Mathematica Object"%type(value)) 370 | s = 'Set[%s,%s];'%(name,convert_function(value)) 371 | self.eval(s,make_plots=False) 372 | 373 | def pull(self,name): 374 | """ 375 | Return a Mathematica Object to the python environment. 376 | 377 | Parameters 378 | --------- 379 | name: string 380 | Name to retrieve 381 | 382 | Returns 383 | ------- 384 | python object: 385 | Depending on type will be converted. See README.rst for more info 386 | 387 | Examples 388 | -------- 389 | 390 | >>> m.eval('X = List[1,2,3,4]') 391 | >>> m.pull('X') 392 | [1,2,3,4] 393 | """ 394 | res = self.eval(name,make_plots=False) 395 | return _mathematica_str_python(res) 396 | 397 | def __parse_packet(self): 398 | if self.debug: 399 | print("in __parse_packet") 400 | packet = self.kernel.nextpacket() 401 | if self.debug: 402 | print _ml.packetdescriptiondictionary[packet] 403 | if packet == _ml.INPUTNAMEPKT: 404 | if self.input_prompt: 405 | print(self.kernel.getstring()) 406 | else: 407 | self.kernel.getstring() 408 | return None 409 | elif packet == _ml.OUTPUTNAMEPKT: 410 | if self.output_prompt: 411 | print(self.kernel.getstring()) 412 | else: 413 | self.kernel.getstring() 414 | self.__parse_packet() 415 | elif packet == _ml.MESSAGEPKT: 416 | if self.last_error is None: 417 | self.last_error = self.kernel.getstring() 418 | else: 419 | self.last_error += "\t" + self.kernel.getstring() 420 | self.__parse_token(packet) 421 | self.__parse_packet() 422 | elif packet == _ml.TEXTPKT: 423 | self.last_error += self.kernel.getstring() 424 | self.__parse_packet() 425 | elif packet == _ml.SYNTAXPKT: 426 | self.kernel.getstring() 427 | self.__parse_packet() 428 | elif packet in _incoming_token: 429 | if self.debug: 430 | print("Going to Parse Token") 431 | self.last_str_result = self.__parse_token(packet).replace(r'\\\012','').replace(r'\012> ','') 432 | self.__parse_packet() 433 | else: 434 | raise PythonicaException("Unknown Packet %s"%_ml.packetdescriptiondictionary[packet]) 435 | 436 | 437 | def __parse_token(self,packet): 438 | if self.debug: 439 | print("In Parse Token") 440 | try: 441 | token = self.kernel.getnext() 442 | if self.debug: 443 | print _ml.tokendictionary[token] 444 | except _ml.error, e: 445 | raise PythonicaException("Got Error Token: %s"%e) 446 | if token == _ml.MLTKSTR: 447 | return self.kernel.getstring() 448 | else: 449 | raise PythonicaException("Unknown Token %i",token) 450 | 451 | def __del__(self): 452 | self.kernel.close() 453 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup(name="pythonica", 4 | version="0.1dev", 5 | py_modules=["pythonica"], 6 | license="BSD license", 7 | long_description=open("README.rst").read()) 8 | --------------------------------------------------------------------------------