Introduction to the Wolfram Client Library

The Wolfram Client Library is structured in submodules all located in wolframclient:

  • evaluation provides convenient methods to evaluate Wolfram Language expressions directly from Python. There are many ways to evaluate code including: evaluation by a local kernel, direct evaluation by a public or private Wolfram Cloud, calling a deployed API.
  • language provides a Python representation of Wolfram Language symbols and functions.
  • serializers provides serialization methods to various formats such as string InputForm and binary WXF format.
  • deserializers contains a parser for WXF.
  • exception regroups the exceptions and errors that the library may raise.

Wolfram Language Expression Representation

The library exposes many kinds of interactions with the Wolfram Language, many of which require representation of Wolfram Language expressions as Python objects. A straightforward way to construct Python objects representing expressions is to call attributes of wl.

Import the factory:

>>> from wolframclient.language import wl

Represent the Wolfram Language symbol Now:

>>> wl.Now
Now

Functions are represented in a similar fashion:

>>> wl.Select(wl.PrimeQ, wl.Range(5))
Select[PrimeQ, Range[5]]

Option are defined using named parameters. ArrayPad accepts option Padding:

>>> wl.ArrayPad([[0]], 1, Padding=1)
ArrayPad[[[0]], 1, Rule[Padding, 1]]

The function wlexpr() conveniently represents expressions with input form strings. Import the function:

>>> from wolframclient.language import wlexpr

Represent a Wolfram Language pure function:

>>> wlexpr('#^2 &')
(#^2 &)

Combine both expression representations, wlexpr() and wl and represent a complex expression:

>>> wl.Map(wlexpr('#^2&'), [1,2,3])
Map[(#^2&), [1, 2, 3]]
Map[#^2 &, {1,2,3}]

Note

for more details about the Python representation of Wolfram Language expressions, refer to the advanced usage section.

Wolfram Language Evaluation

Local Kernel

The Wolfram Language session WolframLanguageSession is initialized with a WolframKernel executable specified by its path. A session enables local evaluation of Wolfram Language code directly from Python.

Note

the typical location of the WolframKernel executable depends on the operating system. The relative path from your installation directory should be:

  • On MacOS: Contents/MacOS/WolframKernel
  • On Windows: WolframKernel.exe
  • On Linux: Files/Executables/WolframKernel

It is advised to first try to run the WolframKernel executable once from your terminal.

Initialization

Import WolframLanguageSession:

>>> from wolframclient.evaluation import WolframLanguageSession

Create a new session targeting a local WolframKernel specified by its path:

>>> session = WolframLanguageSession('/path/to/kernel-executable')

Note that sessions are also automatically started when the first evaluation occurs.

Expression Evaluation

Functions are conveniently represented using wl. First import it:

>>> from wolframclient.language import wl

Evaluate a Wolfram Language function from Python using evaluate():

>>> session.evaluate(wl.StringReverse('abc'))
'cba'
StringReverse["abc"]

Call the Wolfram Language function MinMax on a Python list:

>>> session.evaluate(wl.MinMax([1, 5, -3, 9]))
[-3, 9]
MinMax[{1, 5, -3, 9}]

Query WolframAlpha for the distance between the Earth and the Sun using the function WolframAlpha:

>>> distance = session.evaluate(wl.WolframAlpha("Earth distance from Sun", "Result"))
Quantity[1.008045994315923, AstronomicalUnit]
WolframAlpha["Earth distance from Sun", "Result"]

The Python object stored in the distance variable is a Wolfram Language Quantity. Convert the unit to kilometers, looping back the previous result in a new expression evaluation:

>>> d_km = session.evaluate(wl.UnitConvert(distance, "Kilometers"))
Quantity[150801534.3173264, Kilometers]
dkm = UnitConvert[distance, "Kilometers"]

Finally, retrieve the result as a Python number:

>>> session.evaluate(wl.QuantityMagnitude(d_km))
150801534.3173264
QuantityMagnitude[dkm]

Associations are represented as Python dictionaries and vice versa:

>>> session.evaluate(wl.AssociationMap(wl.Prime, [1, 3, 5]))
{1: 2, 3: 5, 5: 11}
AssociationMap[Prime, {1, 3, 5}]

Options

Wolfram Language options are passed as Python named arguments (a.k.a. **kwargs). As seen previously, ArrayPad accepts an option Padding to specify what padding to use. Pad an array with ones:

>>> session.evaluate(wl.ArrayPad([[0]], 1, Padding=1))
[[1, 1, 1], [1, 0, 1], [1, 1, 1]]
ArrayPad[{{0}}, 1, Padding->1]

InputForm String Evaluate

It is sometimes simpler to input Wolfram Language code as InputForm strings.

First import wlexpr():

>>> from wolframclient.language import wlexpr

Compute the squares of an array of integers:

>>> session.evaluate(wlexpr('Map[#^2 &, Range[5]]'))
[1, 4, 9, 16, 25]

The function wlexpr() is particularly useful for defining pure functions, which can be combined with wl.

Evaluate an alternative representation of the previous expression:

>>> session.evaluate(wl.Map(wlexpr('#^2&'), wl.Range(5)))

Persistence

Expressions evaluated in a given session are persistent. Define a function, and call it:

>>> session.evaluate('f[x_] := x ^ 2')
Null
>>> session.evaluate('f[4]')
16

Create Python Function

From a Wolfram Language expression, it is possible to create a Python function that directly evaluates when called using function():

>>> str_reverse = session.function('StringReverse')
>>> str_reverse('abc')
'cba'

Define a Wolfram Language function that takes a list or a sequence of integers and only returns the primes:

>>> session.evaluate('selectPrimes[integers : List[__Integer]] := Select[integers, PrimeQ]')
>>> session.evaluate('selectPrimes[integers___Integer] := selectPrimes[{integers}]')

Create a Python function from it:

>>> selectPrimes = session.function('selectPrimes')

Apply the function to a list:

>>> selectPrimes([1,2,3,4])
[2, 3]

Apply the function to a sequence of values:

>>> selectPrimes(2, 3, 4, 5)
[2, 3, 5]

It also works with an iterator of integers:

>>> selectPrimes(range(100, 120))
[101, 103, 107, 109, 113]

The session is no more useful, so terminate it:

>>> session.terminate()

Session management

WolframLanguageSession must be terminated, either by explicitly calling terminate() or, preferably, using it in a with block that achieves the same result automatically.

It is highly recommended to initialize a session once and for all to mitigate the initialization cost.

Delegate the handling of the life-cycle of a session using a with block:

>>> with WolframLanguageSession('/path/to/kernel-executable') as wl_session:
...     wl_session.StringReverse('abc')
...
'cba'

Alternatively, start a session manually:

>>> session = WolframLanguageSession('/path/to/kernel-executable')
>>> session.start()

This is not required, since this operation is automatically performed during the first evaluation. Ensure the session started successfully:

>>> session.started
True

Manually terminate the session:

>>> session.terminate()

Note

non-terminated sessions usually result in orphan kernel processes, which ultimately lead to the inability to spawn any usable instance at all. Typically, this ends up with a WolframKernelException raised after a failure to communicate with the kernel.

Note

for in-depth explanations and use cases of local evaluation, refer to the advanced usage section.

Wolfram Cloud Interactions

Most Cloud interactions require proper authentication to the Wolfram Cloud. Authentication from the library is done with a secured authentication key. Secured authentication keys are attached to a Wolfram account. To create one, visit https://account.wolfram.com/auth/create.

Authenticate

Generate a Secured Authentication Key

Using a Wolfram Desktop, or a the Wolfram Public Cloud, generate a new authentication key called pythonclientlibrary:

sak = GenerateSecuredAuthenticationKey["pythonclientlibrary"]

Get the key and secret as strings:

sak["ConsumerKey"]
sak["ConsumerSecret"]

Start authenticated cloud session

Begin by importing the classes SecuredAuthenticationKey and WolframCloudSession from the evaluation module.

>>> from wolframclient.evaluation import SecuredAuthenticationKey, WolframCloudSession

Create a new instance of SecuredAuthenticationKey with the consumer key and secret strings:

>>> sak = SecuredAuthenticationKey('my consumer key', 'my consumer secret')

Using sak, start a new authenticated cloud session:

>>> session = WolframCloudSession(credentials=sak)
>>> session.start()
>>> session.authorized()
True

In the following sections, the authenticated session initialized here is simply referred to by its variable name session.

Cloud Evaluation

One-Shot Evaluation

A one-shot evaluation on the Wolfram Public Cloud requires the initiation of an authenticated session.

First import the expression factory:

>>> from wolframclient.language import wl

Using an authenticated session, evaluate Wolfram Language expressions:

>>> session.evaluate(wl.Range(3))
'{1, 2, 3}'

>>> session.evaluate(wl.StringReverse('abc'))
'"cba"'

Even if the authenticated session is a persistent object, each evaluation occurs independently, similarly to CloudEvaluate. This means that this is not the appropriate tool to work with variables and functions.

Define a function f:

>>> result = session.evaluate(wlexpr('f[x_]:=x+1'))
'Null'

Apply f to 1. However, f is no longer defined, thus returning an unevaluated result:

>>> result = session.evaluate(wlexpr('f[1]'))
'f[1]'

Cloud Functions

From an authenticated session, it is possible to build a cloud function to later use it with various parameters. Create a cloud function:

>>> wl_str_reverse = session.function(wl.StringReverse)

Apply it to a first string:

>>> wl_str_reverse("hello")
'"olleh"'

Use the function again with a new argument:

>>> wl_str_reverse("world.")
'".dlrow"'

Functions may accept more than one input parameter. Define a cloud function that applies Join on all arguments it is given. Join multiple Python arrays:

>>> wl_join = session.function(wlexpr('Join[##] &'))
>>> wl_join([0,1], ["a", "b"], [2, "c"])
'{0, 1, "a", "b", 2, "c"}'

API

Deploy a Wolfram Language API

From the Wolfram Language, it is possible to deploy arbitrary code and expose it through an API.

Using the Wolfram Language, connect to the Wolfram Cloud using your Wolfram ID and password:

CloudConnect["MyWolframID", "myPassword"]

Create an APIFunction that takes an integer and returns its squared value:

api = APIFunction[{"x" -> "Integer"},
    #x^2 &
]

Note

By default, APIFunction formats output as string InputForm, which is not always suited for interoperability with Python. For better interoperability, JSON is preferable. WXF is another versatile option.

Deploy the API as a cloud object named api/private/xsquared:

CloudDeploy[api, CloudObject["api/private/xsquared"]]

The API was deployed with default permissions, and as such is a private CloudObject only usable by its owner. Provide read access to the newly created secured authentication key called pythonclientlibrary:

SetPermissions[CloudObject["api/private/xsquared"], SecuredAuthenticationKeys["pythonclientlibrary"] -> {"Read", "Execute"}]

API Call from Python

Once again, an authenticated session is needed to call the API from Python. APIs are specified with a two-value tuple made up of the owner ID (your Wolfram ID) and the name of the CloudObject used to deploy:

>>> api = ('MyWolframID', 'api/private/xsquared')

The API’s sole input is “list”. In general, an API value is specified as a dict, in which keys are parameters names. A list in Python is automatically converted to a Wolfram Language List and is thus a valid API input values.

Call the API:

>>> result = session.call(api, {'x' : 4})

Check that the API call succeeded:

>>> result.success
True

Get the result as a string:

>>> result.get()
b'16'

Parse it as an int:

>>> int(result.get())
16

Use WolframAPICall

WolframAPICall provides a convenient interface to call an API. Using the previously deployed API and the authenticated session, initiate a new WolframAPICall:

>>> from wolframclient.evaluation import WolframAPICall
>>> call = WolframAPICall(session, ('MyWolframID', 'api/private/xsquared'))

Add an input parameter:

>>> call.add_parameter('x', 4)
WolframAPICall<api=('MyWolframID', 'api/private/xsquared')>

Perform the call:

>>> result = call.perform()

Get the result:

>>> result.get()
b'16'

The class WolframAPICall exposes some helper functions to deal with specific content types and files. It is particularly useful when using image inputs.

Deploy an API that takes an image and returns its ImageDimensions as a JSON array:

CloudDeploy[
    APIFunction[{"image" -> "Image"},
        ImageDimensions[#image] &,
        "JSON"
    ],
    CloudObject["api/private/imagedimensions"]
]

Specify the API as a tuple:

>>> api = ('MyWolframID', 'api/private/imagedimensions')

Create a WolframAPICall targeting the API:

>>> api_call = WolframAPICall(session, api)

Alternatively, it is possible to create a WolframAPICall directly from a session:

>>> api_call =  session.wolfram_api_call(api)

Add a new file parameter. File parameters have a name, and their values must be an opened file object as returned by open(). Call the API using an image stored in /path/to/example/image.png:

>>> with open('/path/to/example/image.png', 'rb') as fp:
...     api_call.add_file_parameter('image', fp)
...     result = api_call.perform()
...
WolframAPICall<api=('dorianb', 'api/private/imagedimensions')>

Note

the API call must be performed while the file object is opened, i.e. inside the with statement.

Parse the JSON API response:

>>> import json
>>> json.loads(result.get())
[320, 240]

Serialization

This library is intended to provide a way to serialize Python expressions to Wolfram Language InputForm strings and WXF strings of bytes. The functionality was designed to be extensible, so that any arbitrary Python object can be serialized with the addition of custom encoders.

Serialize

The modules serializers and language provide tools to represent and serialize arbitrary Wolfram Language expressions. The function export() can serialize a variety of standard Python objects, such as list or dict.

Import the function:

>>> from wolframclient.serializers import export

Serialize a Python list of integers into a Wolfram Language InputForm string representation:

>>> export([1,2,3])
b'{1, 2, 3}'

Wolfram Language expressions are conveniently represented using wl.

Import the function:

>>> from wolframclient.language import wl

Build a Python object representing a Quantity:

>>> wl.Quantity(12, "Hours")
Quantity[<< 2 >>]

The export() function can serialize Python objects built with wl:

>>> export(wl.Quantity(12, "Hours"))
b'Quantity[12, "Hours"]'

Expressions can be nested and mixed with serializable Python types:

>>> export(wl.Select(wl.PrimeQ, [1,2,3]))
b'Select[PrimeQ, {1, 2, 3}]'

The WXF format is also supported. It is a binary format, thus not always human readable, but it is the most efficient way to exchange Wolfram Language expressions. Specify a target_format argument to serialize the previous expression to WXF:

>>> export(wl.Select(wl.PrimeQ, [1,2,3]), target_format='wxf')
b'8:f\x02s\x06Selects\x06PrimeQf\x03s\x04ListC\x01C\x02C\x03'

If the stream parameter is set to a string path, the serialized output is directly written to the file.

First, represent a Wolfram Language ListPlot of the first 25 Prime numbers:

wl_expr = wl.ListPlot(wl.Prime(wl.Range(25)))

Serialize it to WXF and print the resulting bytes to the file /path/to/file.wxf:

>>> export(wl_expr, stream='/path/to/file.wxf', target_format='wxf')
'/path/to/file.wxf'

Using the Wolfram Desktop, import the file:

Import["/path/to/file.wxf"]
ListPlot graphic. If this image does not display, it might be that your browser does not support the SVG image format.

The library also provides extensible serialization mechanisms for custom Python classes. Refer to the API guide page for detailed explanations and to the examples page for some use cases.

Deserialize

The library can parse WXF binary inputs and return Python objects from it. The function binary_deserialize() can deserialize any WXF input into standard Python objects and, eventually, NumPy arrays.

Export a Python list of integers to WXF:

>>> from wolframclient.serializers import export
>>> my_list = [1,2,3]
>>> wxf = export(my_list, target_format='wxf')

Import the binary_deserialize() function:

>>> from wolframclient.deserializers import binary_deserialize

Deserialize the bytes:

>>> binary_deserialize(wxf)
[1, 2, 3]

Export a dict using the WXF format:

>>> export({'name' : 'Alice', 'age' : 37}, target_format='wxf', stream='/path/to/file.wxf')
'/path/to/file.wxf'

Import it as a Python object:

>>> with open('/path/to/file.wxf', 'rb') as fp:
...     binary_deserialize(fp)
...
{'name': 'Alice', 'age': 37}

Note

make sure to open() WXF files in binary mode ‘b’ to avoid encoding issues.