59
$\begingroup$

I know there is some support for running Mathematica from Python, but is there any way to do the reverse. For example, to import some Python classes and use them in Mathematica?

$\endgroup$
12
  • $\begingroup$ related: mathematica.stackexchange.com/q/1894/5 (I don't know enough to tell if it's a dupe or not) $\endgroup$
    – rm -rf
    Dec 3, 2012 at 21:59
  • $\begingroup$ @rm-rf: Different: that relies on .NET. $\endgroup$
    – orome
    Dec 3, 2012 at 22:01
  • 2
    $\begingroup$ Google Pythonika $\endgroup$ Dec 3, 2012 at 22:19
  • 3
    $\begingroup$ @Jens He says he wants "to import some Python classes and use them in Mathematica". That's exactly what Pythonika does. I've used it to run sage from Mathematica. One of the dumbest things I've ever done, but I did it: facstaff.unca.edu/mcmcclur/Mathematica/Sage $\endgroup$ Dec 3, 2012 at 22:40
  • 2
    $\begingroup$ @MarkMcClure: Pythonica seems not to be a viable solution. I can't get it to load (0S X 10.8.2, Python 2.7.2, Mathematica 9.0). Are there any alternatives? $\endgroup$
    – orome
    Dec 12, 2012 at 1:52

6 Answers 6

20
$\begingroup$

Building off of @M.R.'s idea, it is possible to set up an interactive python shell using StartProcess as opposed to RunProcess, allowing for much more flexible connectivity between Python and Wolfram without as much overhead. In it's simplest form, one can open a connection, interact with it, and close it using the following example:

path = "e:\\Programs\\python27\\python"; (*insert OS-appropriate path \
to python*)
p = StartProcess[{path, "-i"}]; (*the'-i' argument is important*)
cmd = "print 'hello'"; (*or any valid python expression*)
Pause[1]; (* important!!! *)
WriteLine[p, cmd];
out = ReadString[p, EndOfBuffer]
KillProcess@p;

Pausing after the StartProcess call is important in order to avoid the cmd being sent to the python interpreter before it has finished loading. A much shorter pause can be used on faster computers.

I've put together a short package to streamline python interactivity. The version I've posted below also reads the python shell text (which is delivered on stderr) and prints that to the Message window if the FrontEnd is active or simply Prints it if the command line is being used. It works nicely with simple commands pcmd@"print 'hello'" and I've had success with some sophisticated operations such as web scraping with Splinter and BeautifulSoup. It coughs when trying to use something like Tkinter, but it just doesn't work as opposed to throwing some form of error, making it a bit difficult to debug.

BeginPackage["wlPython`"];

$pythonprocess = Null;
    $pythonpath = "e:\\Programs\\python27\\python";
$pythonpause = 0.250; (* Pause in seconds to receive error information *)
startPython::usage = "Starts the python process";
endPython::usage = "Ends the python process";
pcmd::usage = "issue a python command";


(* Check for FrontEnd and set as appropriate, can be overridden *)
$pyfrontend = $FrontEnd=!=Null;

Begin["`Private`"];

Clear[startPython]
startPython[path_:$pythonpath]:=Module[{err},
    	$pythonprocess = StartProcess[{path,"-i"}];
    wlPython`pyerrcheck[];
    $pythonprocess 
]

Clear[endPython]
endPython[process_:Unevaluated@$pythonprocess]:=Module[{},
    KillProcess@Evaluate@process;
    NotebookWrite[MessagesNotebook[],
    Cell[RawBoxes@ToBoxes["Python process ended",TraditionalForm],"Output"]];
]

Clear[pcmd]
pcmd[cmd_String,process_:Unevaluated@$pythonprocess]:=Module[{status,err,out,p},
    p = Evaluate[process];
    status = ProcessStatus[p];
    If[status=="Running",
    WriteLine[p,cmd];
    wlPython`pyerrcheck[];
    out = ReadString[p,EndOfBuffer]
    ];
    out
]

Clear[pyerrcheck]
pyerrcheck[]:=Module[{err},
    Pause@$pythonpause;
    	err = ReadString[ProcessConnection[$pythonprocess,"StandardError"],EndOfBuffer];
    If[$pyfrontend,
        NotebookWrite[MessagesNotebook[],
        Cell[RawBoxes@ToBoxes[err,TraditionalForm],"Output"]];,
        Print[err];
    ]

]

End[];

EndPackage[];
$\endgroup$
1
  • 1
    $\begingroup$ This (excellent and previously accepted) answer is outdated as of 11.2. $\endgroup$
    – orome
    Mar 14, 2018 at 17:52
17
$\begingroup$

Here is a more robust solution using Process:

Clear[runPython];
runPython::badCommand  ="Python code failed to run with message `StandardError`";
$pyimports="from random import randint
";
runPython[str_String, imports_:$pyimports] := Module[
    {pyscrpt = ToString[$pyimports<>str, CharacterEncoding->"ASCII"], file=CreateTemporary[], res},
    Export[file,pyscrpt,"Text"];
    res = RunProcess[{"/anaconda/bin/python",file}];
    DeleteFile[file];
    If[res["ExitCode"]!=0, 
        Return @ Failure["badCommand",<|"MessageTemplate" :> runPython::badCommand,"MessageParameters"-> <|"Message" -> res["StandardError"]|>|>],
        Return @ ImportString @ res["StandardOutput"]
    ]
]

enter image description here

I had to use anaconda's python executable - Mathematica was crashing the system's python runtime.

$\endgroup$
4
  • $\begingroup$ " Here is a more robust solution using Process:" More robust than what? $\endgroup$ Nov 30, 2016 at 19:33
  • 1
    $\begingroup$ Than the original answer from @raxa... $\endgroup$
    – M.R.
    Dec 2, 2016 at 19:11
  • $\begingroup$ did you mean to write (...) ToString[imports<>str (...) in the second line of the definition of the function? The way it is now the second argument of runPython is useless $\endgroup$
    – glS
    Jan 14, 2017 at 21:06
  • $\begingroup$ This (excellent and previously accepted) answer is outdated as of 11.2. $\endgroup$
    – orome
    Mar 14, 2018 at 17:52
15
$\begingroup$

Since 11.2 Mathematica has supported ExternalEvaluate and since 11.3 this functionality has been conveniently available simply by beginning an input cell with > which produces an external code cell:

enter image description here

The output of these cells is a Wolfram Language expression that you can then compute with.

$\endgroup$
5
  • 3
    $\begingroup$ Please clarify with an example, say, for Python, including when Python is not the default OS system installation but another, e.g., anaconda's. $\endgroup$
    – murray
    Mar 14, 2018 at 19:06
  • 1
    $\begingroup$ @murray: Was that the OP's question? $\endgroup$
    – orome
    Mar 14, 2018 at 19:54
  • 1
    $\begingroup$ No, but it certainly would be helpful. $\endgroup$ Feb 5, 2019 at 10:52
  • $\begingroup$ But it seems that Mathematica complains when you try to import some python packages? $\endgroup$
    – MKF
    Mar 2, 2019 at 12:43
  • $\begingroup$ You may have to configure python by calling the RegisterExternalEvaluators[] function and supplying a path to your executable $\endgroup$ Apr 22, 2019 at 23:42
9
$\begingroup$

If all you want is to invoke Python scripts and use the output they generate in Mathematica, then simply

pythonOutput = Import["!python fullpathtoscript/your_scipt.py --some_opt arg arg ...", "String"];

is sufficient.

$\endgroup$
4
  • $\begingroup$ Commands like this don't work at all for me in Mathematica 10.0.1. $\endgroup$
    – Mike
    Nov 21, 2014 at 3:44
  • $\begingroup$ It works for me in Mathematica 10.0.2.0. $\endgroup$
    – ThomasH
    Apr 6, 2015 at 18:55
  • $\begingroup$ @ThomasH: Can you give me a specific example of something that works that I can try. It doesn't even work for me anymore (10.0.2.0). $\endgroup$
    – orome
    Apr 6, 2015 at 20:24
  • $\begingroup$ For a MWE, I made a file in my home directory called test.py containing "print('hello world')", then in Mathematica (also pointed at my home directory by default), 'Import["!python test.py","String"]' produces the string "hello world". I have a simple but slightly more complicated example I can point you to on github if you'd like. $\endgroup$
    – ThomasH
    Apr 9, 2015 at 17:14
6
$\begingroup$

Here is an approach that works on Mac OS X or unix-like systems without the need to explicitly create any temp directory:

code = "print \\\"hello, starting\\\"
for i in [1,2,3]:
\t print i*i
 "

(* ==> 
"print \\\"hello, starting\\\" 
for i in [1,2,3]:
     print i*i
"
*)


RunProcess[$SystemShell, "StandardOutput", 
 "printf \"" <> code <> "\" | python
  exit
  "]

(* ==>
"hello, starting
1
4
9
"
*)

First, I define the string containing the Python code. I included a test message to illustrate how you have to escape the quotation marks using \\\" inside the string, because it's going to be passed into a printf command on the shell command line. THat printf is subsequently piped into python. Note that I didn't have to specify the path because it's found from the SystemShell environment.

The interaction with Mathematica takes place via RunProcess, and the standard output of the Python script is directly captured into Mathematica. Again, it's not necessary to bother with any temporary files in this approach.

$\endgroup$
6
  • $\begingroup$ Can you contrast this with the Import approach? Is it just that it avoids using an existing script? FWIW, I'd never write the Python code in the Mathematica environment: the code editing tools are just too poor. $\endgroup$
    – orome
    Apr 5, 2015 at 14:14
  • $\begingroup$ Also, can you show how it would work with an existing script? $\endgroup$
    – orome
    Apr 5, 2015 at 14:21
  • $\begingroup$ @raxacoricofallapatorius Yes, my main point is to have the script and everything it needs completely inside Mathematica. If you already have an existing script outside Mathematica, then it's not a real two-way interaction, I'd say. But what to do with an existing script specifically would depend on the application. I can't think of anything general to say about that, except that it would be possible, e.g., to pass arguments to it using my approach. An important advantage of my approach is, however, that it makes the entire process portable: you can email the notebook without attachments. $\endgroup$
    – Jens
    Apr 5, 2015 at 16:13
  • $\begingroup$ Yes; also, it looks like the approach I had no longer works. $\endgroup$
    – orome
    Apr 5, 2015 at 18:19
  • 2
    $\begingroup$ Oh well, that's just a formatting error when I manually tried to put the python string into a form that looks right in the code cell above. One extra space crept in, and that kills python. I fixed it. If it still doesn't work, it could be dependent on the OS and version of Mathematica. This RunProcess command doesn't work in earlier versions... by the way, another way of interacting with python is through the clipboard on Mac. But I don't know what your actual goal is, so I'll skip that for now. $\endgroup$
    – Jens
    Apr 5, 2015 at 21:19
4
$\begingroup$

There's a "Python link" called Pythonika. I have used it briefly and it works.

What it does:

  • translates basic data types, including arbitrary size integers, to/from Python
  • makes it possible to implement functions in Python and call them from Mathematica

It does not provide any means to work with classes directly. Also, the implementation is rather hackish using unsafe practices (e.g. symbols are not localized to a context, and this has bitten me). To implement a link properly it would take lot more work.

I made a few basic fixes (e.g. using contexts), but I haven't used the thing in a long time. Contact me if you want the fixes.

$\endgroup$
11
  • $\begingroup$ Does nt compile and does nt work. $\endgroup$
    – User12309
    Aug 12, 2014 at 20:01
  • 1
    $\begingroup$ Correct, it is deprecated and seems no longer maintained... $\endgroup$
    – M.R.
    Jan 26, 2015 at 2:36
  • $\begingroup$ Hi, @Szabolcs, is there librarylink way use of python in Mathematica? $\endgroup$
    – matheorem
    Sep 5, 2016 at 14:19
  • $\begingroup$ @matheorem There's no simple way. LibraryLink or MathLink won't solve the problem of transferring and converting data structures between Python and Mathematica. Someone would have to think about how that conversion should work and implement it on top of LibraryLink or MathLink. (Actually the right choice is MathLink here in my opinion.) That is a job that would take several weeks of full time work, even for a simple version. I don't know who has time for that. $\endgroup$
    – Szabolcs
    Sep 5, 2016 at 14:22
  • 1
    $\begingroup$ @Szabolcs I'm developing a brand new PyWSTP. Currently I'm having some issues dealing with packed arrays. $\endgroup$
    – kh40tika
    Aug 27, 2018 at 5:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.