b3m2a1 / mathematica-PyTools

A framework for working with python from Mathematica
https://www.wolframcloud.com/objects/b3m2a1.paclets/PacletServer/main.html#PyTools
MIT License
6 stars 2 forks source link
mathematica python

PyTools

This is an application for working with python processes. It provides a basic, extensible framework for generating python sessions, running python code, and a few utilities based on these.


Installation

The easiest way to install this package is using a paclet server installation:

 PacletInstall[
 "PyTools",
 "Site"->
  "http://www.wolframcloud.com/objects/b3m2a1.paclets/PacletServer"
 ]

If you've already installed it you can update using:

 PacletUpdate[
 "PyTools",
 "Site"->
  "http://www.wolframcloud.com/objects/b3m2a1.paclets/PacletServer"
 ]

Alternately you can download this repo as a ZIP file and put extract it in $UserBaseDirectory/Applications

The application supports a few core pieces of functionality:


Python Core

Packages

Description

This is the heart of the framework. A ProcessObject corresponding to a python interpreter or the $SystemShell can be generated and code can be passed to it to run with a convenient polling mechanism to make sure it hasn't crashed.

The core function for using the framework is PyRun which hooks up the sessions, code generation, and scripts interfaces. The Sessions package provides access to the underlying session config, the SymbolicPython package provides an interface for generating symbolic representations of python code, and the Script package provides an interface for using python-side scripts to facilitate communication between the sides.

Examples

Here's a very basic example of running some code:

 PyRun[Print["I am a llama"]]
(*Out:*)

"I am a llama"

We'll go one step further and import something on the python side to be used in a session and using a specific python version:

 PyRun[
 Import["numpy"];
 myarr=numpy.array[{1,2,3}];
 myarr,
 "Version"->"3.4", 
 "UseSession"->True
 ]
(*Out:*)

"array([1, 2, 3])"

We'll note that we can now use this myarr object elsewhere in that session. Here we'll take 10 of its properties at random

 PyRun[
 Return[dir@myarr],
 "Version"->"3.4", 
 "UseSession"->True
 ]//RandomSample[#,10]&
(*Out:*)

{"__or__","choose","__repr__","itemset","__getitem__","__isub__","transpose","__iter__","__float__","__ixor__"}

The Return keyword at the end of the code block tells the package to try to import that result as a Mathematica expression. Note that using sessions is much faster than running a bunch of stand-alone code, once the session has been initialized:

(*With session*) 
 PyRun[
 Import["numpy"],
 "Version"->"3.4", 
 "UseSession"->True
 ]//AbsoluteTiming//First
(*Out:*)

0.016188`
(*Without session*) 
 PyRun[
 Import["numpy"],
 "Version"->"3.4"
 ]//AbsoluteTiming//First
(*Out:*)

0.389223`

We can view properties of this session using the framework in the Sessions package:

 Needs["PyTools`Sessions`"]; 
 PySessions[]
(*Out:*)

title-4067019816646860284

The framework provides much more control over sessions, but we won't delve into that here.

The code PyRun is actually sending to the session is generally generated via the SymbolicPython package. (you can see the code it's building via the "EchoCode" parameter). Let's take a look at what that does:

 Needs["PyTools`Symbolic`"]; 
 ToSymbolicPython[
 Module[{x},
  Import["numpy"];
  Print[x]
  ]
 ]
(*Out:*)

PyLambda[PySymbol["x"]][{PyImport["numpy"],PyPrint[PySymbol["x"]]}]

We can see it builds up a symbolic representation of the code we sent it. Many things are supported (but obviously even more are not). This symbolic representation can then be marshalled down to a string via ToPython .

 ToSymbolicPython[
 Module[{x},
  Import["numpy"];
  Print[x]
  ]
 ]//ToPython
(*Out:*)

"( lambda x: (\n\timport numpy,\n\tprint(x),\n\t)[-1]\n )"

To effectively use this requires good knowledge of python and Mathematica, as the syntax is generally a mixture of python structures, Mathematica structures, and some judgement calls. For instance, Module is being treated as an anonymous lambda function which scopes its parameter. Other things have more direct translations:

 ToSymbolicPython[
 Map[Print, Range[10]]
 ]//ToPython
(*Out:*)

"list(map(print, range(10)))"

Other things that perhaps look like more Mathematica code are translated to their python equivalents:

 ToSymbolicPython[
 Insert[$Path, my$dir, 0]
 ]//ToPython
(*Out:*)

"import sys\nsys.path.insert(0, my_dir)\n"

The rules underlying this will be documented if there is sufficient demand for it.

The script interface makes it easier to write python-side scripts for in python code. It simply provides a loader interface for those. It also provides an export system, which is used by PyRun when the final value is wrapped in Return . Currently export always route to JSON for serialization, but for other data-types (e.g. pandas data frames) other exporters will be written. An import framework will be added in the future.

A quick example of how this works:

 PyRun[
 Import["numpy"];
 arr = numpy.array[Range[10]];
 Return[list[arr + arr]],
 "Version"->"3.4",
 "UseSession"->True
 ]
(*Out:*)

{0,2,4,6,8,10,12,14,16,18}

A python list can (generally) be serialized to JSON, so it can be imported back into Mathematica as a list. A future export rule might add support for taking a numpy.array and sending it through as a list.

Big data automatically routes through a temporary file. For example:

 PyRun[
 Import["numpy"];
 arr = numpy.random.rand[100, 100];
 Return@Map[list,arr],
 "Version"->"3.4",
 "UseSession"->True
 ]//Short
(*Out:*)

title-7501413158111774981


Python Shell

Stylesheets

Description

The PythonShell stylesheet provides a shell-like interface to the python interface. A new one can be made like so:

 PyNewShell[]
(*Out:*)

NotebookObject[![title-4753840193023331998](project/img/title-4753840193023331998.png)]

title-4982050618186759685

This interface uses PyRun naturally, allowing us to easily do things like this:

title-7010987427565799901

(Note that Return in this context simply tells PyRun to apply export scripts rather than passing the string representation through)


Python Utilities

Packages

Description

The core use of this package will be to expand the basic framework by adding a collection of utilities. For now, there is only one utility, which is an interface to the SimpleHTTPServer module. More utilities will be added as time permits.

Examples

The current sole utility is an HTTP server:

 Needs["PyTools`Utilities`"]; 
 PyServerOpen[Directory[]]

title-5604723046377168390