Function Repository Resource:

PythonObject

Source Notebook

Create a Wolfram Language representation of a Python object

Contributed by: Igor Bakshee

ResourceFunction["PythonObject"][<||>]

represents a Python object defined in an ExternalSessionObject.

ResourceFunction["PythonObject"][session,"cmd"]

evaluates cmd in the specified running ExternalSessionObject and creates a reference to the resulting external object.

ResourceFunction["PythonObject"]["cmd"]

starts a new external session and evaluates cmd.

ResourceFunction["PythonObject"][][prop]

gives the specified property of the ResourceFunction["PythonObject"][].

ResourceFunction["PythonObject"][][func[args,opts]]

calls the function func defined in the Python object with the specified arguments and options.

ResourceFunction["PythonObject"][][All][args,opts]]

invokes the callable Python object with the specified arguments and options.

Details and Options

ResourceFunction["PythonObject"][] is a Wolfram Language representation of a Python-side object created with ExternalEvaluate.
ResourceFunction["PythonObject"][session,"cmd"] effectively executes ExternalEvaluate[session,"cmd"] and returns either its output, ResourceFunction["PythonObject"][], or an expression containing one or more Python objects. The ResourceFunction["PythonObject"] is returned when the output contains ExternalObject[], ExternalFunction[] or certain Python-side objects.
ResourceFunction["PythonObject"][session,assoc], with a Python command specified by assoc, is also supported.
ResourceFunction["PythonObject"][] is also returned for some specific types of ExternalEvaluate failures, such as the ones resulting from an error in passing a valid object to Wolfram Language.
ResourceFunction["PythonObject"][session,"cmd",True] returns ResourceFunction["PythonObject"][] even for the simplest objects, such as integers or strings, or a Failure object if the result of executing "cmd” does not represent a Python object.
ResourceFunction["PythonObject"][session,"cmd",False] returns the raw output of ExternalEvaluate.
The following options can be given:
InitializationAutomaticPython comands to execute prior to creating the object
"Configuration"Automaticcustom package-level setup
"LocalCache"Automaticwhether cached values should be persistent
ResourceFunction["PythonObject"][session,"cmd",,InitializationAutomatic] attempts to detect, install, and import packages required for successful execution of"cmd". Other possible values of the Initialization option are:
"cmd1;cmd2;…"execute the specified commands
{"cmd1","cmd2",,Automatic}execute cmdi prior to the automatic initialization
Noneno initialization
The commands "cmdi" typically include one or more import statements. If needed, the necessary packages are installed automatically using the resource function PythonEvaluateWithDependencies.
The "Configuration" option makes using Python objects more convenient. Configurations are constructed using the ResourceFunction["PythonObject"] API. See the Author Notes below for details. Possible option values include:
ResourceFunction["f"]resource function
"Custom"interactively defined
Automaticautomatic
Noneno configuration
Examples of configurations include resource functions PandasObject, NetworkXObject and SciPyObject for accessing Python packages pandas, NetworkX, and SciPy, respectively.
Non-default configurations are identified by an icon specific to the configuration or the generic gear icon .
The option "LocalCache" specifies whether certain cached information about Python objects should be stored in a LocalObject. Possible option values include:
Automaticstore only large Python packages
Truealways store packages
Falsenever store packages
Available properties of a Python object p can be queried with p["Properties"]. With a few exceptions, the list does not include "non-public" names that begin with an underscore "_".
Properties of a ResourceFunction["PythonObject"] include object-specific properties as well as the following properties:
"Aliases"aliases of a function or class
"CacheLocation"persistent local cache
"Configuration"configuration
"FullInformation"recursive information for modules
"Information"Python-side information
"PythonReference"a reference to the Python-side object
"RawInformation"Python-side help
"ReferenceValid"whether the reference corresponds to an active Python session
"Session"ExternalSessionObject
Object-specific properties include:
"IsCallable"callable object
"IsModule"module
"IsClass"class
"IsFunction"function
"IsBuiltin"built-in function
The default property is "PythonReference".
ResourceFunction["PythonObject"][]["Information","obj"] gives information on function or class objects defined in the parent module or methods of the parent class.
For callable objects, “Information” also contains the expected call signature, including:
"Arguments"arguments
"Options"options
Optional signature elements include:
"VarArgs"whether the function accepts a variable number of positional arguments
"VarKeywords"whether the function accepts arbitrary named arguments
"KeywordOnlyArguments"arguments that must be specified by name
"KeywordOnlyDefaults"default values of named arguments
"Information" also gives signatures of decorated functions.
In ResourceFunction["PythonObject"], both optional positional arguments and named arguments of functions and callable objects are given as Wolfram Language options.
For a ResourceFunction["PythonObject"] p that represents a Python module, p["FullInformation",type] gives a list of attributes of the specified type defined in the module itself and its submodules, recursively. The type can be one of the following:
"Functions"functions
"Classes"classes
"Modules"modules
If one or more functiona or classes appear in multiple contexts, "FullInformation" returns a list in the form {"name1","name2",,{"contexti1..namei","contexti2.name2",},}, where contextij is the shortest context sufficient to uniquely identify "namei". p["FullInformation",type,"QualifiedName"True] gives fully qualified contexts for all objects.
p["Aliases","name"] gives a list of aliases defined for a function or class name.
ResourceFunction["PythonObject"][session,{"cmd1","cmd2",}] returns a list of objects.
ResourceFunction["PythonObject"][session,{"cmd1","cmd2",},{policy1,policy2,}] applies policyi when creating an object from cmdi. The policyi values can be True, False or Automatic.
ResourceFunction["PythonObject"][{"cmd1","cmd2",},] starts a new ExternalSession and creates all obji in that session.
In ResourceFunction["PythonObject"][][prop] and ResourceFunction["PythonObject"][][func[args,opts]], prop, func and option names can be specified as a string or, as long as a name does not include underscores "_", a symbol.
When there is more than one function with the specified name in the Python object, the function name must be given as a string, in the fully qualified dot notation "module1.module2….name", or as a symbol in the corresponding nested context module1`module2`…`name.
Option names in opts can be given as strings or symbols in any context.
NumericArray objects in args are passed as NumPy ndarray objects.
ResourceFunction["PythonObject"][][prop,policy] and ResourceFunction["PythonObject"][][func[args,opts],policy] apply the specified policy when returning the property prop or the result of the function func.
Functions func include both native functions provided in the Python code and curated functions added in Wolfram Language.
Curated functions include:
"Part"part extraction
"Assign"part or variable assignment
"Evaluate"Python code evaluation
ResourceFunction["PythonObject"][]["Part"[part]] gives the specified part of a list-like (iterable) object without fetching the object from Python.
The part specification follows the Wolfram Language conventions, including one-based indexes, and can contain the ;; (Span) specifications.
ResourceFunction["PythonObject"][]["Assign"[objval]] sets the value of the object obj to the specified value. Possible obj specifications include:
"var"ResourceFunction["PythonObject"] variable
"var"["var1"]["var2"]vari components of of the variable var
"Part"[…]part specification for list-like objects
For a ResourceFunction["PythonObject"] p, p["Evaluate"["code"]], evaluates the specified Python code in p["Session"], substituting the Python reference p[] for all not explicitly numbered TemplateSlot instances `` in "code".
p["Evaluate"["code",args]] is equivalent to p["Evaluate"[StringTemplate["code"][args]]], with args that are not explicitly strings converted to strings by applying ExportString[#,"PythonExpression"]&.
Strings that are to be spliced into "code" retaining their quotation marks must have the escaped quotation marks "'" or "\"".
PythonObject directly supports object comparisons, some logical, sequence, mathematical and bitwise operations op in the form op[p,arg].
The following comparison operators are supported:
Supported mathematical operations include:
Absabs
Modmod
Plusadd
Timesmul
Supported bitwise operations include:
Currently supported logical and sequence operators include:
Notnot_
Joinconcat
MemberQin
Logical And and Or operations are done via bitwise operators and_ and or_.
Operators and Python keywords that are not directly supported can be done in Python with p["Evaluate"[]].
For a ResourceFunction["PythonObject"] p representing a callable object, such as classes, callable instances or functions, the Python-side object can be invoked with p[All][args,opts]. p[All,policy][args,opts] applies the specified policy when returning the result.
Arguments args and options opts must be given as exportable Python expressions, an ExternalFunction object or a ResourceFunction["PythonObject"].
For a ResourceFunction["PythonObject"] p, Normal[p] gives an expression containing components of p.
ResourceFunction["PythonObject"][session,"TemporaryPackage"[assoc]] creates a Python object from a package specified by the association assoc deployed in a temporary directory.
DeleteObject[p] deletes the Python-side reference to the ResourceFunction["PythonObject"] p.
Deleting ResourceFunction["PythonObject"][] is typically not necessary in short sessions or if the session is about to be closed anyway.
A ResourceFunction["PythonObject"][] is specific to a particular ExternalSessionObject, which may not be persistent.
DeleteObject[p["Session"]] kills the Python session in which the ResourceFunction["PythonObject"] p is defined.

Examples

Basic Examples (2) 

Create a Python object for a package:

In[1]:=
p = ResourceFunction["PythonObject"]["camelcase"]
Out[1]=

Explore its contents:

In[2]:=
p["Information"]
Out[2]=

Create an instance of a class in the package:

In[3]:=
instance = p["CamelCase()"]
Out[3]=

Examine its contents:

In[4]:=
instance["Information"]
Out[4]=

Get a the value of an instance variable:

In[5]:=
instance["stop_words"]
Out[5]=

Find the signature of a method:

In[6]:=
instance["Information", "hump"]
Out[6]=

Call the method:

In[7]:=
instance["hump"["hello world"]]
Out[7]=

Uninstall the package and close the session to clean up:

In[8]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], p["Module"]]
Out[8]=
In[9]:=
DeleteObject[p["Session"]]

Define a simple class in Python:

In[10]:=
session = StartExternalSession["Python"]
Out[10]=
In[11]:=
ExternalEvaluate[session, "class MyClass:
	cvar = 'myClassVariable'
	def __init__(self, x):
		self.x = x
		print('instance initialized with '+str(x))
	def __call__(self, x):
		self.x = x
		return 'instance called with '+str(x)
	def f(self,x,y):
		self.x = x
		self.y = y
		return 'f() fired with '+str(x)+', '+str(y)
"]
Out[11]=

Create a Python object referring to the class:

In[12]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[12]=

Get the value of a class variable from Python:

In[13]:=
p["cvar"]
Out[13]=

Create an instance of the class:

In[14]:=
instance = p[All][1]
Out[14]=

Check the instance variable:

In[15]:=
instance["x"]
Out[15]=

Set the value of the variable and check the new value:

In[16]:=
instance["Assign"["x" -> 2]]
Out[16]=
In[17]:=
instance["x"]
Out[17]=

Call a method function of the instance:

In[18]:=
instance["f"[2, 3]]
Out[18]=

Check the new instance variables:

In[19]:=
{instance["x"], instance["y"]}
Out[19]=

Close the session:

In[20]:=
DeleteObject[session]

Scope (48) 

Basic Usage (13) 

A Python command specified as a String:

In[21]:=
ResourceFunction["PythonObject"]["object()"]
Out[21]=
In[22]:=
DeleteObject[%["Session"]]

The command specified by an Association:

In[23]:=
ResourceFunction[
 "PythonObject"][<|"Command" -> "``", "TemplateArguments" -> {NumericArray[{1, 2, 3}]}|>]
Out[23]=

Multiple commands:

In[24]:=
ResourceFunction["PythonObject"][{"object()", "object()"}]
Out[24]=
In[25]:=
DeleteObject[First[%]["Session"]]

Components of a Python list:

In[26]:=
ResourceFunction["PythonObject"]["[object(),object()]"]
Out[26]=
In[27]:=
DeleteObject[First[%]["Session"]]

Python dictionaries containing objects:

In[28]:=
session = StartExternalSession["Python"];
In[29]:=
ResourceFunction[
 "PythonObject"][session, "{'a': 1, 'b': object(), 'c': {1: 2, 3: object()}}"]
Out[29]=
In[30]:=
DeleteObject[session]

Create a PythonObject[] only for those objects that do not have a Wolfram Language representation:

In[31]:=
session = StartExternalSession["Python"];
In[32]:=
commands = {"1", "'string'", "datetime.datetime.now()", "object()"};
In[33]:=
ResourceFunction["PythonObject"][session, commands]
Out[33]=

Create Python objects for all objects, even the simplest:

In[34]:=
ResourceFunction["PythonObject"][session, commands, True]
Out[34]=
In[35]:=
DeleteObject[session]

Get a list of items in a Python tuple:

In[36]:=
session = StartExternalSession["Python"];
In[37]:=
tuple = "(object(),object())"
Out[37]=
In[38]:=
ResourceFunction["PythonObject"][session, tuple]
Out[38]=

Get the tuple as a PythonObject:

In[39]:=
ResourceFunction["PythonObject"][session, tuple, True]
Out[39]=

Get the tuple components:

In[40]:=
Normal[%]
Out[40]=
In[41]:=
DeleteObject[session]

Define an instance object in Python and access its Python-side property:

In[42]:=
session = StartExternalSession["Python"];
In[43]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self, x):
		self.x = x*x
	def f(self,y):
		return y+100
"]
Out[43]=
In[44]:=
p = ResourceFunction["PythonObject"][session, "MyClass(2)"]
Out[44]=
In[45]:=
p["x"]
Out[45]=

Assign a new value to the property:

In[46]:=
p["Assign"["x" -> 3]]
Out[46]=
In[47]:=
p["x"]
Out[47]=

Call a method:

In[48]:=
p["f"[1]]
Out[48]=

Alternatively, define a Python object referring to the class and construct an instance on the Wolfram Language side:

In[49]:=
class = ResourceFunction["PythonObject"][session, "MyClass"]
Out[49]=
In[50]:=
instance = class[All][3]
Out[50]=

The new value of the instance variable:

In[51]:=
instance["x"]
Out[51]=

Call a member function:

In[52]:=
p["f"[2]]
Out[52]=
In[53]:=
DeleteObject[session]

Get a part of a list-like object without bringing the object from Python:

In[54]:=
range = ResourceFunction["PythonObject"]["[1,2,3,4,5]", True]
Out[54]=
In[55]:=
% // Normal
Out[55]=
In[56]:=
range["Part"[2]]
Out[56]=

Get spans of elements:

In[57]:=
range["Part"[2 ;; 4]]
Out[57]=
In[58]:=
range["Part"[2 ;; -2]]
Out[58]=
In[59]:=
range["Part"[2 ;; UpTo[100]]]
Out[59]=
In[60]:=
range["Part"[1 ;; -1 ;; 2]]
Out[60]=

Get all parts:

In[61]:=
range["Part"[ ;; ]]
Out[61]=
In[62]:=
DeleteObject[range["Session"]]

Get a part of a multidimensional Python array:

In[63]:=
Partition[Range [15], 5] // MatrixForm
Out[63]=
In[64]:=
arr = ResourceFunction["PythonObject"][
  ExportString[%, "PythonExpression"], True]
Out[64]=
In[65]:=
arr["Part"[2, 3]]
Out[65]=

Get a span of elements:

In[66]:=
arr["Part"[2, 3 ;;]]
Out[66]=
In[67]:=
DeleteObject[arr["Session"]]

Define a Python list object:

In[68]:=
l = ResourceFunction["PythonObject"]["[1,2,3,4,5]", True]
Out[68]=
In[69]:=
Normal[%]
Out[69]=

Assign a new value to a part of the list:

In[70]:=
l["Assign"["Part"[2] -> 20]]
Out[70]=
In[71]:=
Normal[l]
Out[71]=

Assign a new value to a span of elements:

In[72]:=
l["Assign"["Part"[3 ;;] -> {100, 200, 300}]]
Out[72]=
In[73]:=
l // Normal
Out[73]=
In[74]:=
DeleteObject[l["Session"]]

Define a Python class whose variable is an instance of another class:

In[75]:=
session = StartExternalSession["Python"]
Out[75]=
In[76]:=
ExternalEvaluate[session, "class MyClass2:
	c = 1
class MyClass1:
	b = MyClass2()
class MyClass:
	a = MyClass1()
"]
Out[76]=
In[77]:=
p = ResourceFunction["PythonObject"][session, "MyClass()", True]
Out[77]=

The value of the variable c, "a.b.c" in the Python notation:

In[78]:=
p["a"]["b"]["c"]
Out[78]=

Assign the value to c:

In[79]:=
p["Assign"["a"["b"]["c"] -> 100]]
Out[79]=

Check the value on the Python side:

In[80]:=
p["a"]["b"]["c", True]
Out[80]=
In[81]:=
Normal[%]
Out[81]=

Alternatively:

In[82]:=
p["a"]["b"]["c"]
Out[82]=
In[83]:=
DeleteObject[session]

Define a Python object:

In[84]:=
p = ResourceFunction["PythonObject"]["object()"]
Out[84]=

Evaluate Python code referencing the object:

In[85]:=
p["Evaluate"["print(``)"]]

Equivalently:

In[86]:=
p["Evaluate"["print(``)", p]]
In[87]:=
ExternalEvaluate[p["Session"], "print(" <> p[] <> ")"]

Supply arguments to be converted to Python expressions:

In[88]:=
p["Evaluate"["print([``,``])", {1, 2}, 3]]

String arguments are passed verbatim, without extra quotation marks:

In[89]:=
p["Evaluate"["``", "print(123)"]]

Escape quotation marks to pass an expression as a string:

In[90]:=
p["Evaluate"["``", "\"print(123)\""]]
Out[90]=

Or use single quotes inside the string:

In[91]:=
p["Evaluate"["``", "'print(123)'"]]
Out[91]=
In[92]:=
DeleteObject[p["Session"]]

Object Types (7) 

Interactively defined object:

In[93]:=
ResourceFunction["PythonObject"]["object()"]
Out[93]=
In[94]:=
DeleteObject[%["Session"]]

A module in Python's standard library:

In[95]:=
ResourceFunction["PythonObject"]["uuid"]
Out[95]=
In[96]:=
DeleteObject[%["Session"]]

A class in the standard library:

In[97]:=
ResourceFunction["PythonObject"]["uuid.UUID"]
Out[97]=
In[98]:=
DeleteObject[%["Session"]]

An instance of a class in the standard library:

In[99]:=
ResourceFunction["PythonObject"]["uuid.uuid4()"]
Out[99]=
In[100]:=
DeleteObject[%["Session"]]

A function in the standard library:

In[101]:=
ResourceFunction["PythonObject"]["uuid.uuid4"]
Out[101]=
In[102]:=
DeleteObject[%["Session"]]

A built-in function:

In[103]:=
ResourceFunction["PythonObject"]["math.cos"]
Out[103]=
In[104]:=
DeleteObject[%["Session"]]

An installable package, with its classes and functions:

In[105]:=
session = StartExternalSession["Python"];
In[106]:=
ResourceFunction["PythonObject"][session, "requests"]
Out[106]=
In[107]:=
ResourceFunction["PythonObject"][session, "requests.Request"]
Out[107]=
In[108]:=
ResourceFunction["PythonObject"][session, "requests.get"]
Out[108]=
In[109]:=
DeleteObject[session]

Argument Types  (4) 

Python's positional arguments are supported. Define a function that takes positional arguments:

In[110]:=
session = StartExternalSession["Python"]
Out[110]=
In[111]:=
ExternalEvaluate[session, "def positional(x,y=10):
	return {'x':x,'y':y}	
"]
Out[111]=

The Python object representing the function:

In[112]:=
p = ResourceFunction["PythonObject"][session, "positional"]
Out[112]=

Call the function with the mandatory positional argument:

In[113]:=
p["positional"[1]]
Out[113]=

Supply a value of the optional argument:

In[114]:=
p["positional"[1, "y" -> 2]]
Out[114]=
In[115]:=
DeleteObject[session]

Positional arguments can have variable lengths. Define a function that takes an arbitrary number of positional arguments in a Python session:

In[116]:=
session = StartExternalSession["Python"]
Out[116]=
In[117]:=
ExternalEvaluate[session, "def vararg(x,*varargs):
	return {'x':x,'varargs':varargs}	
"]
Out[117]=

The Python object representing the function:

In[118]:=
p = ResourceFunction["PythonObject"][session, "vararg"]
Out[118]=

The parameter "VarArgs" is True for information on a function that takes variable-length positional arguments:

In[119]:=
p["Information"]
Out[119]=

Call the function with one or more arguments:

In[120]:=
p["vararg"[1]]
Out[120]=
In[121]:=
p["vararg"[1, 2, 3, 4]]
Out[121]=
In[122]:=
DeleteObject[session]

Named arguments are supported. Define a Python function that takes an arbitrary number of positional arguments as well as named arguments, with or without default values:

In[123]:=
session = StartExternalSession["Python"]
Out[123]=
In[124]:=
ExternalEvaluate[session, "def kwarg(x,*varargs,y=10,z):
	return {'x':x,'varargs':varargs,'y':y,'z':z}
None
"]

The Python object representing the function:

In[125]:=
p = ResourceFunction["PythonObject"][session, "kwarg"]
Out[125]=

Call the function with one positional and one named argument:

In[126]:=
p["kwarg"[1, "z" -> 2]]
Out[126]=

Call the function with positional and named arguments:

In[127]:=
p["kwarg"[1, 2, 3, "y" -> 4, "z" -> 5]]
Out[127]=
In[128]:=
DeleteObject[session]

Arbitrary names can be used. Define a Python function that takes arbitrary named arguments:

In[129]:=
session = StartExternalSession["Python"]
Out[129]=
In[130]:=
ExternalEvaluate[session, "def vrkw(**varkw):
	return {**varkw}
None
"]

The Python object representing the function:

In[131]:=
p = ResourceFunction["PythonObject"][session, "vrkw"]
Out[131]=

Call the function supplying a few arbitrary named arguments:

In[132]:=
p["vrkw"["a" -> 1, "b" -> 2]]
Out[132]=
In[133]:=
DeleteObject[session]

Operators (5) 

Perform basic arithmetic operations on objects on the Python side:

In[134]:=
session = StartExternalSession["Python"];
In[135]:=
mone = ResourceFunction["PythonObject"][session, "-1", True]
Out[135]=
In[136]:=
five = ResourceFunction["PythonObject"][session, "5", True]
Out[136]=
In[137]:=
mone + 3 five
Out[137]=
In[138]:=
Normal[%]
Out[138]=

The absolute value:

In[139]:=
Abs[mone] // Normal
Out[139]=

The modulo operation:

In[140]:=
Mod[five, 3] // Normal
Out[140]=
In[141]:=
DeleteObject[session]

Compare objects in Python, without bringing them to Wolfram Language:

In[142]:=
session = StartExternalSession["Python"];
In[143]:=
two = ResourceFunction["PythonObject"][session, "2", True]
Out[143]=
In[144]:=
three = ResourceFunction["PythonObject"][session, "3", True]
Out[144]=
In[145]:=
Normal /@ {two == three, two != three, two < three, two <= three, three > two, three <= three}
Out[145]=
In[146]:=
DeleteObject[session]

Bitwise operations in Python:

In[147]:=
session = StartExternalSession["Python"];
In[148]:=
two = ResourceFunction["PythonObject"][session, "2", True]
Out[148]=
In[149]:=
three = ResourceFunction["PythonObject"][session, "3", True]
Out[149]=
In[150]:=
funcs = {BitAnd[##], BitOr[##], BitXor[##], BitShiftLeft[##], BitShiftRight[##]} &;
In[151]:=
Normal /@ funcs @@ {two, three}
Out[151]=

Compare with the same operations in Wolfram Language:

In[152]:=
funcs @@ {2, 3}
Out[152]=
In[153]:=
% === %%
Out[153]=

Bit inverse:

In[154]:=
BitNot[two] // Normal
Out[154]=
In[155]:=
BitNot[2]
Out[155]=
In[156]:=
DeleteObject[session]

Logical operations:

In[157]:=
session = StartExternalSession["Python"];
In[158]:=
true = ResourceFunction["PythonObject"][session, "True", True]
Out[158]=
In[159]:=
false = ResourceFunction["PythonObject"][session, "False", True]
Out[159]=
In[160]:=
true && false // Normal
Out[160]=
In[161]:=
true || false // Normal
Out[161]=
In[162]:=
! true // Normal
Out[162]=

Logical operations done via bitwise operations:

In[163]:=
ResourceFunction["PythonObject"][session, "11", True] && ResourceFunction["PythonObject"][session, "12", True]
Out[163]=
In[164]:=
Normal[%]
Out[164]=
In[165]:=
BitAnd[11, 12]
Out[165]=
In[166]:=
DeleteObject[session]

Test membership:

In[167]:=
list = ResourceFunction["PythonObject"]["[1,2,3]", True]
Out[167]=
In[168]:=
MemberQ[list, 1]
Out[168]=
In[169]:=
Normal[%]
Out[169]=
In[170]:=
DeleteObject[list["Session"]]

Callable Objects (3) 

A class:

In[171]:=
session = StartExternalSession["Python"];
In[172]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self):
		print('instance created')
"]
Out[172]=
In[173]:=
class = ResourceFunction["PythonObject"][session, "MyClass"]
Out[173]=

Create an instance of the class:

In[174]:=
class[All][]
Out[174]=
In[175]:=
DeleteObject[session]

A callable instance of the class:

In[176]:=
session = StartExternalSession["Python"];
In[177]:=
ExternalEvaluate[session, "class MyClass:
	def __call__(self):
		return 'instance called'
"]
Out[177]=
In[178]:=
instance = ResourceFunction["PythonObject"][session, "MyClass()"]
Out[178]=

Call the instance:

In[179]:=
instance[All][]
Out[179]=
In[180]:=
DeleteObject[session]

A function:

In[181]:=
session = StartExternalSession["Python"];
In[182]:=
ExternalEvaluate[session, "def func():
	return 'func() called'
"]
Out[182]=
In[183]:=
func = ResourceFunction["PythonObject"][session, "func"]
Out[183]=

Call the function:

In[184]:=
func[All][]
Out[184]=
In[185]:=
DeleteObject[session]

Calling Conventions (6) 

Call a function defined in a module via the parent module object:

In[186]:=
session = StartExternalSession["Python"];
In[187]:=
p = ResourceFunction["PythonObject"][session, "requests"]
Out[187]=
In[188]:=
url = "https://wolfram.com";
In[189]:=
p["get"[url]]
Out[189]=
In[190]:=
%["status_code"]
Out[190]=

Alternatively, define a callable function object and call it directly:

In[191]:=
get = ResourceFunction["PythonObject"][session, "requests.get"]
Out[191]=
In[192]:=
get[All][url]
Out[192]=
In[193]:=
%["status_code"]
Out[193]=
In[194]:=
DeleteObject[session]

Define a Python class:

In[195]:=
session = StartExternalSession["Python"];
In[196]:=
ExternalEvaluate[session, "class MyClass:
	def __init__(self, x, y = 0, z=0):
		self.x = x
		self.y = y
		self.z = z
"]
Out[196]=

Create a Python object referring to the class:

In[197]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[197]=

In PythonObject, optional arguments of a callable Python object become standard Wolfram Language options:

In[198]:=
KeyTake[p["Information"], {"Arguments", "Options"}]
Out[198]=

Create an instance and check its variables:

In[199]:=
instance = p[All][1, "z" -> 2]
Out[199]=
In[200]:=
instance /@ {"x", "y", "z"}
Out[200]=

For comparison, specify the optional argument in the Python code and check the new instance variables:

In[201]:=
ExternalEvaluate[session, "o=MyClass(10,z=20);[o.x, o.y, o.z]"]
Out[201]=
In[202]:=
DeleteObject[session]

Define a Python class in which the constructor takes a function as an argument:

In[203]:=
session = StartExternalSession["Python"];
In[204]:=
ExternalEvaluate[session, "class CallF:
	cvar = 'myClassVariable'
	def __init__(self, f):
		self.f = f
	def call(self):
		return self.f()
"]
Out[204]=

Create a Python object referring to the class:

In[205]:=
p = ResourceFunction["PythonObject"][session, "CallF"]
Out[205]=

To create an instance of the class, define a function on the Python side:

In[206]:=
ExternalEvaluate[session, "def my_func():
	print('my_func() called')
"]
Out[206]=

Supply the ExternalFunction object to the class constructor::

In[207]:=
p[All][%]
Out[207]=

Invoke a method of the class:

In[208]:=
%["call"[]]

Alternatively, initialize an instance with a PythonObject:

In[209]:=
ResourceFunction["PythonObject"][session, "my_func"]
Out[209]=
In[210]:=
p[All][%]
Out[210]=

Invoke the method:

In[211]:=
%["call"[]]
In[212]:=
DeleteObject[session]

Define a Python function:

In[213]:=
ExternalEvaluate[session, "def func(x): return x"]
Out[213]=
In[214]:=
func = ResourceFunction["PythonObject"][session, "func"]
Out[214]=

Pass a NumericArray to the function:

In[215]:=
func[All][NumericArray[{1, 2, 3}]]
Out[215]=

Define a Python function which expects a specific type of a Python object, for instance, the enum Color, as an option:

In[216]:=
session = StartExternalSession["Python"];
In[217]:=
package = "TemporaryPackage"[<|"Name" -> "enumarg",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "from enum import Enum
class Color(Enum):
	RED = 1
	GREEN = 2
	BLUE = 3

def do_color(color=Color.BLUE):
    if not isinstance(color, Color):
        raise TypeError('color is not an instance of the Color Enum')
    return color.value
"|>];
In[218]:=
p = ResourceFunction["PythonObject"][session, package]
Out[218]=

A Python object for the enum:

In[219]:=
clr = p["Color"]
Out[219]=

Supply an enum value to the function:

In[220]:=
p["do_color"["color" -> clr["RED"]]]
Out[220]=
In[221]:=
DeleteObject[session]

Define a class in Python:

In[222]:=
session = StartExternalSession["Python"];
In[223]:=
ExternalEvaluate[session, "class MyClass:
	def f(self, x, y=0):
		self.z = x + y
"];

Create a Python object referring to the class:

In[224]:=
p = ResourceFunction["PythonObject"][session, "MyClass()"]
Out[224]=

Call a method function and get the instance variable using strings as names:

In[225]:=
p["f"[1, "y" -> 2]]
p["z"]
Out[207]=

Alternatively, use symbols as names:

In[226]:=
p[f[3, y -> 4]]
p[z]
Out[209]=
In[227]:=
DeleteObject[session]

Returned Objects (2) 

Define a Python package with a variable and a function that both return lists:

In[228]:=
session = StartExternalSession["Python"];
In[229]:=
package = "TemporaryPackage"[<|"Name" -> "myrange",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "var = [1,2]
def my_range(n):
	return range(n)
"|>];
In[230]:=
p = ResourceFunction["PythonObject"][session, package]
Out[230]=

Variable as a list:

In[231]:=
p["var"]
Out[231]=

Use the policy argument to keep the list on the Python side:

In[232]:=
p["var", True]
Out[232]=

Function result as a list:

In[233]:=
p["my_range"[3]]
Out[233]=

Create a Python-side object using the policy argument:

In[234]:=
p["my_range"[3], True]
Out[234]=

Callable:

In[235]:=
p1 = p["my_range"]
Out[235]=
In[236]:=
p1[All][3]
Out[236]=

Invoke the callable with the policy argument:

In[237]:=
p1[All, True][3]
Out[237]=
In[238]:=
DeleteObject[session]

Create a Python object for a list:

In[239]:=
p = ResourceFunction["PythonObject"]["[1,2,3]", True]
Out[239]=

Bring the result to the Wolfram Language:

In[240]:=
Normal[%]
Out[240]=
In[241]:=
DeleteObject[p["Session"]]

Class Methods (4) 

Deploy a temporary package that defines a class with a class method:

In[242]:=
session = StartExternalSession["Python"];
In[243]:=
package = "TemporaryPackage"[<|"Name" -> "clmethod",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "class MyClass:
    @classmethod
    def classmethod(cls):
        return 'class method called', cls
"|>];
In[244]:=
p = ResourceFunction["PythonObject"][session, package]
Out[244]=

Create a Python object referring to the class:

In[245]:=
class = p["MyClass"]
Out[245]=

Create a Python object referring to the class method:

In[246]:=
clm = class["classmethod"]
Out[246]=

Call the method and notice that the class object is passed to the class method as the first argument:

In[247]:=
clm[All][]
Out[247]=
In[248]:=
DeleteObject[session]

Decorated Functions (4) 

Create a decorated function in a Python session:

In[249]:=
session = StartExternalSession["Python"]
Out[249]=
In[250]:=
ExternalEvaluate[session, "import functools

def decorator(func):
	@functools.wraps(func)
	def wrapper(*args, **kwargs):
		print('hello')
		return func(*args, **kwargs)
	return wrapper

@decorator
def f(arg, opt=None):
	return f'arg: {arg}; opts: {opt}'
"]
Out[250]=

The Python object representing the function:

In[251]:=
p = ResourceFunction["PythonObject"][session, "f"]
Out[251]=

The signature of the decorated function is inferred correctly, despite it being masked by the decoration:

In[252]:=
p["Information"]
Out[252]=

Call the function with the mandatory and optional arguments:

In[253]:=
p[All][1]
Out[253]=
In[254]:=
p[All][1, "opt" -> 2]
Out[254]=
In[255]:=
DeleteObject[session]

Options (8) 

Initialization (3) 

Without initialization, PythonObject fails for this command because the required package is not available in the current session:

In[256]:=
session = StartExternalSession["Python"];
In[257]:=
ResourceFunction["PythonObject"][session, "camelcase", Initialization -> None]
Out[257]=

In fact, the package is not even installed:

In[258]:=
KeyExistsQ[
 ResourceFunction["PythonPackageList"][session], "camelcase"]
Out[258]=

With the default setting InitializationAutomatic, the package is automatically installed and imported:

In[259]:=
p = ResourceFunction["PythonObject"]["camelcase", Initialization -> Automatic]
Out[259]=

The package is now installed:

In[260]:=
KeyExistsQ[
 ResourceFunction["PythonPackageList"][p["Session"]], "camelcase"]
Out[260]=

Uninstall to clean up:

In[261]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "camelcase"]
Out[261]=
In[262]:=
DeleteObject[p["Session"]]

Specify a custom initialization:

In[263]:=
p = ResourceFunction["PythonObject"]["c()", Initialization -> "from camelcase import CamelCase as c"]
Out[263]=

Use the new object:

In[264]:=
p["hump"["hello world"]]
Out[264]=

Uninstall the package and close the session to clean up:

In[265]:=
ResourceFunction["PythonPackageUninstall"][p["Session"], "camelcase"]
Out[265]=
In[266]:=
DeleteObject[p["Session"]]

Configuration (2) 

Without configuration, the raw Python's uuid4() function returns an instance of the UUID class:

In[267]:=
p = ResourceFunction["PythonObject"]["uuid.uuid4", "Configuration" -> None]
Out[267]=
In[268]:=
p[All][]
Out[268]=
In[269]:=
p["Configuration"]
Out[269]=
In[270]:=
DeleteObject[p["Session"]]

Specify a configuration defined in a resource function:

In[271]:=
p = ResourceFunction["PythonObject"]["uuid.uuid4", "Configuration" -> ResourceFunction["UUIDPythonObjectConfiguration"]]
Out[271]=

The custom configuration icon alerts you to the fact that the object has a non-default behavior. In this case, the return value of the Python object changes to a string:

In[272]:=
p[All][]
Out[272]=
In[273]:=
p["Configuration"]
Out[273]=
In[274]:=
DeleteObject[p["Session"]]

LocalCache (3) 

By default, PythonObject does not cache information for smaller packages:

In[275]:=
p = ResourceFunction["PythonObject"]["camelcase"]
Out[275]=
In[276]:=
p["CacheLocation"]
Out[276]=

Force caching the information:

In[277]:=
p = ResourceFunction["PythonObject"]["camelcase", "LocalCache" -> True]
Out[277]=
In[278]:=
p["CacheLocation"]
Out[278]=

Delete the cache:

In[279]:=
DeleteObject[%]
In[280]:=
p["CacheLocation"]
Out[280]=
In[281]:=
DeleteObject[p["Session"]]

Applications (4) 

Create a PythonObject to efficiently reuse data on the Python side, even when you do not have access to the Python code:

In[282]:=
session = StartExternalSession["Python"];

Execute some code that defines a big object and keep a reference to that object:

In[283]:=
blackBoxCode = "range(1,100)";
In[284]:=
p = ResourceFunction["PythonObject"][session, blackBoxCode, True]
Out[284]=

Use the object:

In[285]:=
ExternalEvaluate[session, "sum(" <> p[] <> ")"]
Out[285]=
In[286]:=
DeleteObject[p["Session"]]

Make a function defined in an external module available in your Wolfram Language session:

In[287]:=
get = ResourceFunction["PythonObject"]["requests.get"]
Out[287]=

The function signature:

In[288]:=
get["Information"]
Out[288]=

Call the function:

In[289]:=
response = get[All]["https://wolfram.com"]
Out[289]=

Information about the returned object:

In[290]:=
response["Information"]
Out[290]=

The value of one of the available variables:

In[291]:=
response["status_code"]
Out[291]=

Another variable refers to a more complex object:

In[292]:=
headers = response["headers"]
Out[292]=
In[293]:=
headers["Information"]
Out[293]=

Get the signature of one of the methods of the new object:

In[294]:=
headers["Information", "popitem"]
Out[294]=

Call the method several times:

In[295]:=
Table[headers["popitem"[]], {3}]
Out[295]=
In[296]:=
DeleteObject[get["Session"]]

Create a Python object for an external package, for instance, Topoly:

In[297]:=
topoly = ResourceFunction["PythonObject"]["topoly"]
Out[297]=

Explore its contents:

In[298]:=
%["Information"]
Out[298]=

Find the signature of a function from the package:

In[299]:=
topoly["Information", "conway"]
Out[299]=

Prepare and plot a list of coordinates that represent a 51knot:

In[300]:=
coordinates51 = CompressedData["
1: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
dw5t
"];
In[301]:=
Graphics3D[Tube[coordinates51, .1]]
Out[301]=

Call the Topoly function and display its result; in this case, a plot of the fingerprint matrix for the coordinates of the knot:

In[302]:=
filename = "map_knot_5_1";
In[303]:=
PrintTemporary["This may take a while..."];
topoly["conway"[coordinates51, "tries" -> 10, "matrix" -> True, "matrix_map" -> True, "map_filename" -> filename]];
In[304]:=
Import[FileNameJoin[{$HomeDirectory, filename <> ".png"}]]
Out[304]=
In[305]:=
DeleteObject[topoly["Session"]]

Create a Python object for the igraph package:

In[306]:=
ig = ResourceFunction["PythonObject"]["igraph"]
Out[306]=

Create an igraph "Graph" object:

In[307]:=
g = ig["Graph"[]]
Out[307]=

Read a GraphML file from ExampleData:

In[308]:=
file = file = FileNameJoin[{$InstallationDirectory, "Documentation", "English", "System", "ExampleData", "simple.graphml"}];
In[309]:=
graph = g["Read_GraphML"[file]]
Out[309]=

An igraph object for the vertex list:

In[310]:=
vs = graph["vs"]
Out[310]=

Find the shortest path between vertices number 1 and 7:

In[311]:=
sp = graph[
  "get_shortest_paths"[vs["Part"[1]], "to" -> vs["Part"[7]], "output" -> "vpath"]]
Out[311]=

To compare with the Wolfram Language result, import the graph:

In[312]:=
simple = Import[file , "Graph", VertexLabels -> Automatic]
Out[312]=

Find the shortest path between the vertices:

In[313]:=
sp1 = FindShortestPath[simple, "n0", "n6"]
Out[313]=

Compare the paths:

In[314]:=
sp /. Thread[Range[0, 10] -> VertexList[simple]]
Out[314]=
In[315]:=
First[%] === sp1
Out[315]=
In[316]:=
DeleteObject[ig["Session"]]

Properties and Relations (22) 

Properties and Contents (3) 

Some common properties:

In[317]:=
p = ResourceFunction["PythonObject"]["object()"]
Out[317]=
In[318]:=
p["Properties"]
Out[318]=

Curated properties:

In[319]:=
Select[%, StringStartsQ[#, RegularExpression["^[A-Z]"]] &]
Out[319]=
In[320]:=
DeleteObject[p["Session"]]

Contents of a package:

In[321]:=
p = ResourceFunction["PythonObject"]["requests"]
Out[321]=
In[322]:=
p["Information"]
Out[322]=

Recurse into submodules:

In[323]:=
p["FullInformation"]
Out[323]=

All modules in the package:

In[324]:=
p["FullInformation", "Modules"]
Out[324]=

Contents of a module:

In[325]:=
module = p["utils"]
Out[325]=
In[326]:=
module["Information"]
Out[326]=
In[327]:=
DeleteObject[p["Session"]]

Get a list of all functions in the SciPy package:

In[328]:=
p = ResourceFunction["PythonObject"]["scipy"]
Out[328]=
In[329]:=
p["FullInformation", "Functions"] // Short
Out[329]=

Functions of the same name that appear in multiple contexts:

In[330]:=
Cases[%, _List]
Out[330]=

Access the matrix exponentials for dense and sparse matrices:

In[331]:=
p["linalg.expm"]
Out[331]=
In[332]:=
%["Information"]
Out[332]=
In[333]:=
p["sparse.linalg.expm"]
Out[333]=
In[334]:=
%["Information"]
Out[334]=

The function with a shorter context path can be also accessed by its shorter name:

In[335]:=
p["expm"]
Out[335]=
In[336]:=
DeleteObject[p["Session"]]

Information and Signatures (6) 

Information on a class, including the class variable and the methods:

In[337]:=
session = StartExternalSession["Python"];
In[338]:=
ExternalEvaluate[session, "class MyClass:
	cvar = 'myClassVariable'
	def __init__(self, x):
		self.x = x
		print('instance initialized with '+str(x))
	def add_two(self,x,y):
		return x+y
"]
Out[338]=
In[339]:=
p = ResourceFunction["PythonObject"][session, "MyClass"]
Out[339]=
In[340]:=
p["Information"]
Out[340]=

Information on a class instance, including the class and instance variables and the methods:

In[341]:=
instance = p[All][1]
Out[341]=
In[342]:=
instance["Information"]
Out[342]=
In[343]:=
DeleteObject[p["Session"]]

Obtain the signature of a callable object:

In[344]:=
p = ResourceFunction["PythonObject"]["requests"]
Out[344]=
In[345]:=
p["Information", "Request"]
Out[345]=
In[346]:=
DeleteObject[p["Session"]]

Access Python's "built-ins", mathematical functions defined by the C standard:

In[347]:=
p = ResourceFunction["PythonObject"]["math"]
Out[347]=
In[348]:=
p["FullInformation", "Functions"]
Out[348]=
In[349]:=
p["Information", "nextafter"]
Out[349]=
In[350]:=
p["nextafter"[0, 1]]
Out[350]=
In[351]:=
DeleteObject[p["Session"]]

Even if not listed in "Properties", native Python attributes are also accessible:

In[352]:=
p = ResourceFunction["PythonObject"]["object()"];
In[353]:=
p["__dir__()"]
Out[353]=
In[354]:=
p["__doc__"]
Out[354]=
In[355]:=
DeleteObject[p["Session"]]

Similarly, you can use double underscore methods ("dunders"):

In[356]:=
float = ResourceFunction["PythonObject"]["5.6", True]
Out[356]=
In[357]:=
Normal[%]
Out[357]=
In[358]:=
float["__int__"[]]
Out[358]=
In[359]:=
DeleteObject[float["Session"]]

Get the Python-side help:

In[360]:=
p = ResourceFunction["PythonObject"]["object()"];
In[361]:=
p["RawInformation", "__init__"]
In[362]:=
DeleteObject[p["Session"]]

Python Reference (2) 

Use the "PythonReference" property to access the object on the Python side with ExternalEvaluate:

In[363]:=
p = ResourceFunction["PythonObject"]["object()"]
Out[363]=
In[364]:=
ExternalEvaluate[p["Session"], "dir(" <> p["PythonReference"] <> ")"]
Out[364]=

The explicit "PythonReference" specification can be omitted since it is the default property:

In[365]:=
p["PythonReference"]
Out[365]=
In[366]:=
p[]
Out[366]=
In[367]:=
DeleteObject[p["Session"]]

"PythonReference" becomes stale if you delete the Python object:

In[368]:=
p = ResourceFunction["PythonObject"]["object()"];
p["ReferenceValid"]
Out[364]=
In[369]:=
DeleteObject[p]
p["ReferenceValid"]
Out[366]=

"PythonReference" also stales if you close the session in which it is defined:

In[370]:=
p = ResourceFunction["PythonObject"]["object()"];
DeleteObject[p["Session"]]
p["ReferenceValid"]
Out[343]=

PythonObject vs. ExternalObject (4) 

PythonObject is similar to ExternalObject returned by ExternalEvaluate:

In[371]:=
session = StartExternalSession["Python"];
In[372]:=
ResourceFunction["PythonObject"][session, "requests"]
Out[372]=
In[373]:=
ExternalEvaluate[session, "requests"]
Out[373]=

PythonObject is also similar to an ExternalFunction object that can be defined to call a Python function:

In[374]:=
efget = ExternalEvaluate[session, "from requests import get; get"]
Out[374]=
In[375]:=
get = ResourceFunction["PythonObject"][session, "requests.get"]
Out[375]=
In[376]:=
get["IsFunction"]
Out[376]=

Call the two functions:

In[377]:=
url = "https://wolfram.com";
In[378]:=
{o, p} = {efget[url], get[All][url]}
Out[378]=

The major difference between the produced objects is that the contents of the latter are accessible directly in Wolfram Language without any Python programming:

In[379]:=
p["Information"]
Out[379]=
In[380]:=
Snippet[p["text"], 5]
Out[380]=
In[381]:=
DeleteObject[p["Session"]]

Invoking Object Components (2) 

For callable objects, PythonObject[][All] is used only for calling the object and otherwise returns unevaluated:

In[382]:=
p = ResourceFunction["PythonObject"]["object"]
Out[382]=
In[383]:=
p[All][]
Out[383]=
In[384]:=
p[All]
Out[384]=
In[385]:=
DeleteObject[p["Session"]]

Create and deploy a Python package with one unique function name, two identically named functions at different depth, and another two identically named functions at the same depth in two modules:

In[386]:=
session = StartExternalSession["Python"];
In[387]:=
p = ResourceFunction["PythonObject"][session, "TemporaryPackage"[<|"Name" -> "fname", "__init__.py" -> "from . import mod1, mod2\ndef function(): print('in function()')", "mod1.py" -> "def unique(): print('in unique()')\ndef function(): print('in mod1.function()')\ndef function2(): print('in mod1.function2()')", "mod2.py" -> "def function(): print('in mod2.function()')\ndef function2(): print('in mod2.function2()')"|>]]
Out[387]=

Call the unique function by its name:

In[388]:=
p["unique"[]]

Call the top-level function by its name:

In[389]:=
p["function"[]]

Use the qualified name to call the function at a deeper level:

In[390]:=
p["mod1.function"[]]

Also use the qualified name to call the two functions at the same level in two modules:

In[391]:=
p["mod1.function2"[]]
In[392]:=
p["mod2.function2"[]]

Alternatively, create another PythonObject for one of the modules:

In[393]:=
p1 = ResourceFunction["PythonObject"][session, "fname.mod1"]
Out[393]=

In that context, the function name is unique and the function can be called just by its name:

In[394]:=
p1["function"[]]
In[395]:=
DeleteObject[session]

Span vs. Python's slice (5) 

The Span notation is internally converted to Python's slice to extract parts of list-like objects:

In[396]:=
session = StartExternalSession["Python"]
Out[396]=
In[397]:=
range = ResourceFunction["PythonObject"][session, "[1,2,3,4,5]", True]
Out[397]=
In[398]:=
range["Part"[3 ;;]]
Out[398]=

When using the slice notation directly, keep in mind that Python's list indexes are zero-based:

In[399]:=
ExternalEvaluate[session, range[] <> "[2:]"]
Out[399]=

Also, the start:end notation in Python does not include the right-end boundary:

In[400]:=
range["Part"[2 ;; 4]]
Out[400]=

Get the same elements with slice, using ExternalEvaluate or PythonObject:

In[401]:=
ExternalEvaluate[session, range[] <> "[1:4]"]
Out[401]=
In[402]:=
slice = ResourceFunction["PythonObject"][session, "slice(1,4)"]
Out[402]=
In[403]:=
range["Part"[slice]]
Out[403]=

Although typically using "Part"[] with Span in PythonObject gives the result equivalent to using Part[] on the fetched expression, there are no out-of-range error messages for slicing in Python:

In[404]:=
range["Part"[100 ;;]]
Out[404]=
In[405]:=
Normal[range][[100 ;;]]
Out[405]=
In[406]:=
DeleteObject[session]

Possible Issues (9) 

PythonObject cannot be created by Python statements that do not return an object, such as statements defining a function:

In[407]:=
session = StartExternalSession["Python"];
In[408]:=
pfun = "def func(): print(123)";
In[409]:=
ResourceFunction["PythonObject"][session, pfun]
Out[409]=

Define the function using ExternalEvaluate and give the function name to PythonObject:

In[410]:=
ExternalEvaluate[session, pfun];
In[411]:=
p = ResourceFunction["PythonObject"][session, "func"]
Out[411]=

Use the object:

In[412]:=
p[All][]
In[413]:=
DeleteObject[p["Session"]]

If "cmd" in PythonObject[session,"cmd"] contains multiple statements, all the statements are executed, but the PythonObject is currently created from the first statement:

In[414]:=
session = StartExternalSession["Python"];
In[415]:=
p = ResourceFunction["PythonObject"][session, "1+1; print('proceeding...'); object()", True]
Out[415]=
In[416]:=
ExternalEvaluate[session, p[]]
Out[416]=

This will be improved on in a future update; in the meantime, use separate statements for each object you want to create:

In[417]:=
ResourceFunction[
 "PythonObject"][session, {"1+1; print('proceeding...')", "object()"}]
Out[417]=
In[418]:=
DeleteObject[session]

If a Python object cannot be created, PythonObject might return a syntax error:

In[419]:=
session = StartExternalSession["Python"];
In[420]:=
ResourceFunction["PythonObject"][session, "import uuid"]
Out[420]=

Use ExternalEvaluate if you want to import the package manually:

In[421]:=
ExternalEvaluate[session, "import uuid"]
In[422]:=
DeleteObject[session]

The arithmetic operation Times on Python objects follows the Python conventions for the mul method, which is object dependent:

In[423]:=
session = StartExternalSession["Python"];
In[424]:=
three = ResourceFunction["PythonObject"][session, "3", True]
Out[424]=
In[425]:=
%*2 // Normal
Out[425]=
In[426]:=
arr = ResourceFunction["PythonObject"][session, "[1,2,3]", True]
Out[426]=
In[427]:=
%*2 // Normal
Out[427]=

Use nympy arrays to make Times behave as expected in Wolfram Language:

In[428]:=
np = ResourceFunction["PythonObject"][session, "numpy.array([1,2,3])",
   True]
Out[428]=
In[429]:=
np*2 // Normal // Normal
Out[429]=

Similarly, for Plus:

In[430]:=
arr + arr // Normal
Out[430]=
In[431]:=
np + np // Normal // Normal
Out[431]=
In[432]:=
DeleteObject[session]

Orderless operations, like Plus and Times, send operands to Python sorted in canonical order:

In[433]:=
session = StartExternalSession["Python"];
In[434]:=
p1 = ResourceFunction["PythonObject"][session, "[1,2,3]", True]
Out[434]=
In[435]:=
p2 = ResourceFunction["PythonObject"][session, "[2]", True]
Out[435]=
In[436]:=
p1 + p2 // Normal
Out[436]=

Use "Evaluate" to send the operands in the desired order:

In[437]:=
p1["Evaluate"["``+``", p1, p2]]
Out[437]=
In[438]:=
DeleteObject[session]

For comparison operators, which do not have the Orderless attribute, the first operand must be a Python object:

In[439]:=
session = StartExternalSession["Python"];
In[440]:=
three = ResourceFunction["PythonObject"][session, "3", True]
Out[440]=
In[441]:=
three > 2 // Normal
Out[441]=

The mathematically equivalent operation in the reverse order does not evaluate:

In[442]:=
2 <= three
Out[442]=

Define the first object in Python to have the comparison evaluated:

In[443]:=
ResourceFunction["PythonObject"][session, "2", True] <= three // Normal
Out[443]=
In[444]:=
DeleteObject[session]

Bitwise operators in Python evaluate the operands:

In[445]:=
session = StartExternalSession["Python"];
In[446]:=
true = ResourceFunction["PythonObject"][session, "True", True]
Out[446]=
In[447]:=
true || ResourceFunction["PythonObject"][session, "print(123)", True]
Out[447]=

Use the keyword or with "Evaluate" to get the Boolean result:

In[448]:=
true["Evaluate"["`` or ``", true, "print(123)"]]
Out[448]=
In[449]:=
DeleteObject[session]

If a Python function does not specify default values for one or more named arguments, options that represent such arguments become mandatory:

In[450]:=
session = StartExternalSession["Python"]
Out[450]=
In[451]:=
ExternalEvaluate[session, "def kwonly(*varargs,x):
	return {'varargs':varargs,'x':x}
None
"]
In[452]:=
p = ResourceFunction["PythonObject"][session, "kwonly"]
Out[452]=

The function fails if the mandatory argument is not supplied:

In[453]:=
p["kwonly"[1, 2, 3]]
Out[453]=

Specify the mandatory argument:

In[454]:=
p["kwonly"[1, 2, 3, "x" -> 4]]
Out[454]=
In[455]:=
DeleteObject[session]

Signatures of some callable objects can contain Missing elements:

In[456]:=
session = StartExternalSession["Python"];
In[457]:=
p = ResourceFunction["PythonObject"][session, "math"]
Out[457]=
In[458]:=
p["Information", "log"]
Out[458]=

You can still use such objects, but need to obtain their signatures manually:

In[459]:=
p["RawInformation", "log"]
In[460]:=
p["log"[10.]]
Out[460]=

Also, for such objects, you cannot use standard Wolfram Language options and should instead supply optional arguments:

In[461]:=
p["log"[10., "base" -> 10.]]
Out[461]=
In[462]:=
p["log"[10., 10.]]
Out[462]=
In[463]:=
DeleteObject[session]

Requirements

Wolfram Language 13.0 (December 2021) or above

Version History

  • 1.4.0 – 07 February 2024
  • 1.3.0 – 29 November 2022
  • 1.2.0 – 23 June 2022
  • 1.1.0 – 08 March 2022
  • 1.0.0 – 23 November 2021

Related Resources

Author Notes

The current treatment of multiple statements in "cmd" is suboptimal. PythonObject should really be created from the last statement, not the first. This is a TODO item, see "Possible Issues".
PythonObject[session,"TemporaryPackage"[assoc]] created specifically to simplify examples in this documentation and is not recommended for any other use.

Developer Notes

Besides giving access to "raw" Python code, PythonObject is also the first step towards a full-fledged framework for creating Python package wrappers and developing Wolfram Language applications backed by Python code. To this end, it is possible to configure the wrappers to adhere closely to the Wolfram Language conventions. The framework also provides utility functions that make developing Python package wrappers simpler.

More examples of using the PythonObject API can be found in the resource functions MatplotlibObject, PandasObject, NetworkXObject, SciPyObject and others.

Configuration

Python package configurations can be defined interactively in the current Wolfram Language session or saved in a repository function and loaded on demand. In the future, saved configurations can be made discoverable automatically.

Configurations are defined through one or more of the PythonObject API functions including:

"WebInformation"documentation and other links
"NumberOfOptionalArguments"promote options to optional arguments
"Signatures"set signatures of callable objects
"PostProcess"apply post-processing to Python results
"PreProcess"preprocess arguments before sending to Python
"Rename"use Wolfram Language naming conventions in Python objects
"PreserveObjects"keep specified Python-side objects as a single PythonObject
"Normal"define Normal for objects
"Icon"specify a custom icon
"Dependencies"load configurations for specified modules

Currently, "Configuration"Automatic uses the custom configuration, if defined, or the first non-custom configuration applied in the current Wolfram Language session.

Note: Although configurations can be loaded before or after a PythonObject is created, they do not apply retroactively.In most cases, to see the expected behavior, you have to create a new PythonObject instance after loading your configuration.

WebInformation (Experimental)

PythonObject["API","WebInformation"["package",<|key1val1,|>]]

defines URIs of the documentation and other information for the package.

Possible keys include:

"DocumentationLink"documentation URI
"DocumentationSearchTemplate"URI template for documentation search

"DocumentationSearchTemplate" must give a StringTemplate expression to be applied to a search string.


Define the documentation link and the documentation search links for a package:

In[1]:=
PythonObject["API", "WebInformation"[
  "uuid", <|
   "DocumentationLink" -> "https://docs.python.org/3/library/uuid.html", "DocumentationSearchTemplate" -> StringTemplate[
     "https://docs.python.org/3/library/uuid.html#uuid.``"]|>]]
In[2]:=
p = PythonObject["uuid"]

Open the web documentation link for a package in your default browser:

In[3]:=
p["WebInformation"]
In[4]:=
% // SystemOpen

In[5]:=
DeleteObject[p["Session"]]

NumberOfOptionalArguments

In general, the convention to represent Python's optional arguments as Wolfram Language options works well, but occasionally the design can be improved by keeping optional arguments at the same level with required positional arguments.

PythonObject["API","NumberOfOptionalArguments"["package",<|target1n1,|>]]

promotes the first n Python's optional arguments in targeti in the specified package to Wolfram Language arguments.

"package" must be the package name or the name of the top-level module. targeti can be:

"func"function
{"class","method"}method of the class

A sample package that implements an "add_two" function":

In[6]:=
package = "TemporaryPackage"[<|"Name" -> "addtwo",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def add_two(x,y=0):
	return x+y
"|>];

Define a custom configuration in which the add_two function takes an optional argument, rather than an option:

In[7]:=
PythonObject["API", "NumberOfOptionalArguments"["addtwo", <|"add_two" -> 1|>]]

Create a Python object for the package using the custom configuration:

In[8]:=
session = StartExternalSession["Python"];
In[9]:=
p = PythonObject[session, package, "Configuration" -> "Custom"]

The signature of the add_two function:

In[10]:=
p["Information", "add_two"]

Call the function with one and two arguments:

In[11]:=
p["add_two"[1]]
In[12]:=
p["add_two"[2, 3]]

For comparison, with no configuration, the second summand needs to be supplied as an option:

In[13]:=
p1 = PythonObject[session, package, "Configuration" -> None]
In[14]:=
p1["add_two"[2, "y" -> 3]]
In[15]:=
DeleteObject[session]

A package that defines a class and a function:

In[16]:=
package = "TemporaryPackage"[<|"Name" -> "opargs",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "class MyClass:
	def __init__(self,x,y=0):
		print('init done')
	def __call__(self,t,u=0,v=1):
		return 'call done'
	def add_three(self,x,y,z=0):
		return 'MyClass.add_three called'
def add_many(x,y=0,t=0,u=0):
	return 'mod.add_many called'
"|>];

Set the number of optional arguments for the package function and all the methods in the class, using the "__init__" method to create an instance of the class and the "__call__" method to call it:

In[17]:=
PythonObject["API", "NumberOfOptionalArguments"[
  "opargs", <|
   "add_many" -> 2, {"MyClass", "__init__"} -> 1, {"MyClass", "__call__"} -> 1, {"MyClass", "add_three"} -> 1|>]]
In[18]:=
p = PythonObject[package, "Configuration" -> "Custom"]

Create a class instance with one argument:

In[19]:=
p1 = p["MyClass"[1]]

Create a second instance with an optional argument:

In[20]:=
p2 = p["MyClass"[1, 2]]

Call the second instance with one or two arguments:

In[21]:=
p2[All][1]
In[22]:=
p2[All][1, 2]

Call a method:

In[23]:=
p2["add_three"[1, 2]]
In[24]:=
p2["add_three"[1, 2, 3]]

Call the add_many function in the module:

In[25]:=
p["add_many"[1, 2]]
In[26]:=
p["add_many"[1, 2, 3]]

For this function, one optional argument is left as an option:

In[27]:=
p["Information", "add_many"]
In[28]:=
p["add_many"[1, 2, 3, "u" -> 4]]
In[29]:=
DeleteObject[p["Session"]]

Signatures

PythonObject["API","Signatures"["package",<|target1assoc1,|>]]

adds missing elements to, or otherwise alters, signatures of the callable objects in the specified package.

targeti can be specified using the same conventions as for "NumberOfOptionalArguments".

Possible elements of associ include:

"Arguments"list of arguements
"Options"list of options
"IsMethod"whether the callable is a method

Signatures of some callable objects in this package contain missing elements:

In[30]:=
PythonObject["API", "Reset"["math"]]
In[31]:=
session = StartExternalSession["Python"];
In[32]:=
p = PythonObject[session, "math"]
In[33]:=
PythonObject["API", "MissingSignatures"[p]]
In[34]:=
p["Information", "log"]
In[35]:=
DeleteObject[session]

PostProcess

PythonObject["API","PostProcess"["package",func]]

applies the specified function to results received from the package.

The post-processor function func[e,{p,f},{farg1,farg2,}]receives the result of the computation e; the caller object p, the name of the function or method f, and the list of arguments sent to f, including some arguments with default values.


For the module "uuid", define a post-processor function that converts the instances of the UUID class to their string values:

In[36]:=
post[p_PythonObject, __] := ExternalEvaluate[p["Session"], StringTemplate["str(``)"][p[]]] /; p["FullName"] === "uuid.UUID"
post[res_, __] := res
In[37]:=
PythonObject["API", "PostProcess"["uuid", post]]

The Python object for the uuid.uuid4() function from the package:

In[38]:=
p = PythonObject["uuid.uuid4", "Configuration" -> "Custom"]

Call the function:

In[39]:=
p[All][]

For comparison, with no configuration, the function returns an instance of the UUID class:

In[40]:=
p1 = PythonObject["uuid.uuid4", "Configuration" -> None]
In[41]:=
p1[All][]

PreProcess

PythonObject["API","PreProcess"["package",func]]

applies the specified function to Wolfram Language values before sending them to the package.

The preprocessor function func[{p,f},{arg1,arg2,}]receives the caller object p, the name of the function or method f, and the list of arguments argi to be sent to f.


A sample package with a function that expects an immutable Python object, a tuple:

In[42]:=
package = "TemporaryPackage"[<|"Name" -> "preprocess",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def do_tuple(t):
    if not isinstance(t, tuple):
        raise TypeError(str(t)+' is not a tuple')
    print('do_tuple called')
"|>];

Define a custom configuration in which a Wolfram Language list is automatically converted to a tuple before sending it to the do_tuple function:

In[43]:=
tuple =.
In[44]:=
tuple[p_, list_List] := PythonObject[p["Session"], StringTemplate["tuple(``)"][list], True]
In[45]:=
pre[{p_, "preprocess.do_tuple"}, {list_List}] := {tuple[p, list]}
pre[_, args_] := args
In[46]:=
PythonObject["API", "PreProcess"["preprocess", pre]]

The Python object for the package:

In[47]:=
session = StartExternalSession["Python"];
In[48]:=
p = PythonObject[session, package, "Configuration" -> "Custom"]

Call the function:

In[49]:=
p["do_tuple"[{1, 2, 3}]]

For comparison, with no configuration, the function does not accept a mutable array object:

In[50]:=
p1 = PythonObject[session, package, "Configuration" -> None]
In[51]:=
p1["do_tuple"[{1, 2, 3}]]
In[52]:=
DeleteObject[session]

Rename

PythonObject["API","Rename"["package"]]

uses heuristic to rename function names, class names, methods and options in the package.

PythonObject["API","Rename"["package",{rules1,}]]

sets up the specified name replacement rules.

Name replacements help to use Wolfram Language naming conventions.

For option names, replacement rules apply to both optional positional arguments, specified as "Options", and named arguments, specified as "KeywordOnlyArguments".

Support for arbitrary named arguments in "VarKeywords" is planned for a future version.

rulesi are applied in the specified order and can be either in the form suitable for the use in StringReplace or Automatic.

Automatic removes underscores and converts names to CamelCase.

The replacement rules allow the use of curated names, but do not mandate them. The original Python names can be used as well.


A sample package with one function:

In[53]:=
package = "TemporaryPackage"[<|"Name" -> "onefunc",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def my_function(x):
	return 'my_function called'
"|>];

Allow calling the function by its CamelCase name:

In[54]:=
PythonObject["API", "Rename"["onefunc"]]

Create a Python object and call the function by its new name:

In[55]:=
p = PythonObject[package]
In[56]:=
p["Information"]
In[57]:=
p["MyFunction"[1]]
In[58]:=
func = p["MyFunction"]
In[59]:=
func[All][1]

The native Python function name is still accessible:

In[60]:=
p["my_function"[1]]
In[61]:=
DeleteObject[p["Session"]]

Set up custom naming rules for the package:

In[62]:=
package = "TemporaryPackage"[<|"Name" -> "cnames",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "mvar = 'module variable'
class MyClass:
	clvar = 'class variable'
	def __init__(self,x,asp_rat=0):
		self.instvar = x
		self.asp_rat = asp_rat
		print('init done')
	def my_plot(self,method='m'):
		return 'MyClass.my_plot called with method = '+str(method)
def get_approx(iter=1):
	return 'mod.get_approx called with iter = '+str(iter)
def positional(*varargs,my_argument=10):
	return 'mod.get_approx called with varargs = '+str(varargs)+'positional = '+str(my_argument)
"|>];
In[63]:=
PythonObject["API", "Rename"[
  "cnames", {{"asp_rat" -> "AspectRatio", "get_approx" -> "Approximation", "iter" -> "Iterations"}, Automatic}]]
In[64]:=
p = PythonObject[package]
In[65]:=
p["Information"]

Call the "get_approx" function using the new names:

In[66]:=
p["Information", "Approximation"]
In[67]:=
p["Approximation"[Iterations -> 1]]
In[68]:=
p[Approximation[Iterations -> 1]]
In[69]:=
p["Approximation"["Iterations" -> 1]]

Call the variable-arguments function with the new names:

In[70]:=
p["Information", "Positional"]
In[71]:=
p["Positional"[1, 2, 3, "MyArgument" -> 10]]

Renaming rules for the package:

In[72]:=
p["RenamingRules"]

The rule for a particular name:

In[73]:=
p["RenamingRules", "Approximation"]

Convert to and from Python name:

In[74]:=
p["ToPythonName", "Approximation"]
In[75]:=
p["FromPythonName", "get_approx"]
In[76]:=
DeleteObject[p["Session"]]

PreserveObjects

PythonObject["API","PreserveObjects"["package",{obj1,obj2,}]]

prevents obji from automatically expanding into components.


Certain Python-side objects are normally expanded into components by default:

In[77]:=
session = StartExternalSession["Python"];
In[78]:=
ExternalEvaluate[session, "tuple(range(3))"]

Create a package with two functions, one that returns a tuple and another that expects a tuple argument:

In[79]:=
package = "TemporaryPackage"[<|"Name" -> "tuples",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def return_tuple(n):
	return tuple(range(n))
def use_tuple(t):
	if not isinstance(t,tuple):
		raise TypeError('expecting tuple')
	return 'got tuple'
"|>];

Prevent tuples returned by the package from being automatically expanded:

In[80]:=
PythonObject["API", "PreserveObjects"["tuples", {"tuple"}]]
In[81]:=
p = PythonObject[session, package]
In[82]:=
p["return_tuple"[3]]

Supply the tuple as argument:

In[83]:=
p["use_tuple"[%]]
In[84]:=
DeleteObject[session]

Normal

PythonObject["API","Normal"["package",f]]

specifies that the function f should be applied to package objects to convert them to normal expressions.

If the function f is intended to be applied only to a subset of objects in the package, it must return Failure["ConditionFailure",] for other objects.

Set up a rule to convert sparse SciPy matrices to SparseArray objects:

In[85]:=
myNormal[p_] := SparseArray[Keys[#] + 1 -> Values[#], p["shape"]] &@p["todok"[]] /; StringQ[p["InstanceOf"]] && MemberQ[p["Information"]["Methods"], "todok"]
In[86]:=
myNormal[_] := Failure["ConditionFailure", <||>]
In[87]:=
PythonObject["API", "Normal"["scipy", myNormal]]

Create a sparse matrix in SciPy:

In[88]:=
csr = PythonObject["csr_matrix", Initialization -> {"import scipy", "from scipy.sparse import csr_matrix"}]
In[89]:=
csr[All][IdentityMatrix[{5, 10}]]

Convert the SciPy matrix to SparseArray:

In[90]:=
% // Normal

Display the dense form:

In[91]:=
Normal[%] // TraditionalForm
In[92]:=
DeleteObject[csr["Session"]]

Icon

PythonObject["API","Icon"["package",icon]]

specifies a custom icon for the package objects.

Specify an icon for objects in the SciPy package:

In[93]:=
PythonObject["API", "Icon"["scipy", \!\(\*
GraphicsBox[RasterBox[CompressedData["
1:eJztnQd8FNUTx99dKqGF3kKAkADSIQVCEgg9QKghJIQWWui9KR3Se6EjSFVB
BURUFFDBjgUsYMGuqH8RBZHe5v9my93bvd27vUtCEtz5fL4WSO52573fzsxr
22jcrCEJRkLIPHf6jyFjF3WdO3fsY1Ge9H+iZ86bMmnmxAl9Zs6fOGni3I7j
nOgfHqW8T3Emuummm2666aabbrrppptuuummm2666aabbrrppptuuummm266
6aabbg/UDAxGK7A/p5tuD4uJ/R6X7bhQ3CjlKOUplSi43qcqpRqlBqWmAjWE
v68q/Dz+XgWKh/B5LsLnizrSTbfSaNg3cSkb9lfUQEXC9+laFC9KI0pTSktK
O0ogpROlM6UrpTulJ6UXpbfw757Cn3cVfg5/PojSXvgc/Dwf4fNrCd9XUfh+
F+F69LijW0mYqAdXwseDKpTalIaURyj+lFDC9/EBlGGU0ZQEynTKXMoiyhLK
csoqympKIiVJYLXw58uFn1sk/N4M4XPGUGKEz+8pfJ+/8P0NheupIlyfGzHr
RTfditrEXEmMD5jrYP/D53drwj/b8bk/lDKW8H34McL38QxKPmUDZQtlO2U3
5WnKXsqzlOco+yj7GfYJf/6s8HNPC7+3Q/icDcLnZgrfg983kzKOEi1cTyfh
+nyE661MzPFFz8l0K6yJmsBnMNYB9SnNKR0pEZRYymTCP9vxmZ9H2Uz4PryH
8H38IOUlymHKq8TJ9TXiVvG4oXz1t0iFWu+Qqj4nSXW/D0jNFh+Ruv6nOWq3
PsX9GUJ/xqlizbedyld5kzi7v067NG5HOCJ8Hn7uC8L34PftFL4fryNZuK7J
wnVGCNfdXLgPzMc8iFkruummxcTcCZ+zmJ9gX2pB+PwFcxmMD/MInwvh83sr
5SnC99FDlFeI0eWYwdP7XeIV9Imx+YCvScD4X0jYgj8NvZKukD6Z10lk/m3j
oI33yODHQQtOQzbfdx60/g7pk3XD2Dvl33K9lv9tCEr41a398O9d/cLPuNVp
9oFzuUrHCb+V5xXhOvYL17VVuM5E4brHCvcRQnitYP2C8dCd6DmYbuomxgoc
H8J6twnhn7f9CZ+z4LM4nfB5DeY5qIcXicH4KqlQ+y3iE/45CRj3M+m27BLp
v+Y2GbzpHhmyBSRo1INmhM81Rm25b6T6KT8w+5pr2Mzf3VsN/Lq8V8sPDEbn
1wjGLLxO/np3UTZS0igLCa+VfpQOFD/Cj5lVEPyg60Q3sa7A+hWfoRgr2lB6
UEYS/nmbSvg+hc/i56keDpMKNd8kjbueIR2n/Uaf6dfJkK1gZosyRa0NRh9m
tkooF735bvneyy56tB/2jXvtJiedXNxRL4e5++DvB+8rhTKHEkfpRmlF+JiC
tYor0XOv/6KJORTmFJiH4/grjrlGEn5sCMeMCgifz2Oe8jKp2uhd0nLot6T7
istk4Ia7qjp4UNrQoA8zT4BT1BPgMSjvumfXmb9WbhJy2tXd4zXuvvi4gvUS
5mBLKeMpfQk/juxN+OeGrpP/jmE7i7rAcR3MoYYQftwVa1qsb/dSCb1Iytc4
QVoMPkd60rpBKV8qQ/rgiOIxDt0GFaM33qraffZ5T5/2HxqMRqz3sWbBGh9j
Co6HTSF8bhlAaUD0ePKwG7Yrti8+DzFeoC5wPHY24cdgnyD4LHV2f5V4B58m
nRf8STVx32FNlDZtMPogUdtMGKK3Q+Wo/Ks1Oo38pnzV2m8SPqbguDKOIWPu
NY0ykPA6wXiCc/h6ffLwGLYjtifOLWN9ge08iDKL8LrYRjlA3D1fIy2GnCMR
6dcKrYni1kYhYgerDTJ0u8AODo+YJ+5U773wtyoNW50kfJ0i6gTjKsYTrOWx
PqtD+DFvfbyr7Bo7TovjUVh3Yl6N7YzPRYwXB2gO9QbxH/sjN+ZUVLooU/rY
YSZ6J2UXOMfsvF9zcOpftR7p9JEBxyN4nWDeifP5OJaH615wfQvOCeHYhp5z
lS1DbWAuhfMXOG4ZTvixTGzfxwnmURVqv0ECxv1kWWtbGYP6j+iDDNvN4Rz7
JNSMyvi7xiMhHzk5OaFOnqGsI/xc/XDC56iYc2FsdiZ6LCkLhjED54XrEn49
EtYYCyhrCNbdbpWOkLYjvyMD1t623q/k/Pf0QYY9yeES++T92kNSL1b1aYvH
CGIdj/M/WYRfR4MxGedPqxM9lpRmE+sMrL8xZmAOgGO1OBe2gxidD5Emfc6Q
frk3tGuiCPRSGrRRSH1wxDwFbnFP3qvT79HzlarXfYNgbsrHYhwLx7miYMLX
dzi/qNclpcvEMVusM3D9eBTh57s30GbaR2q1fI/0WHW5aHRhp14eIn2IVB65
83adkLgvXVzdMefCdZM5hB8fxzWRWJdgXquPcZUOw2cVPrNwjB7XSGH9iPX3
TuJW4TAJGP8jGYRzFyr9RpUi1EmxauPB6wMxxj4NNYbmXKru0+ZdwscSrN8X
E37tcFvCr1XR862SMzafwrVSuD4V13cX0L96ltRt9z7pk3HNel9RobTqRDWP
e/D6ECkXt/tuvW4J3zo5u+K8Ca7vwmdTPOHX1uM6FawFdY08WBPHp7AmxP0N
mE/hHqLHiYvHIYN//Pe0P923WZ8qUpQ6KUKN2K2NB6MPDvr3GEs8vZrg/CKO
ceH6epxXxBoQ1yiINYluxW/iekLc74NrprA2xL0XO4mn9+uGHqsvWerClibs
0Ym9Wilk7W5zDKB06AOpMHz77TqBgz4zGAyYb+E65/mEn1PE+I7rU/RXaBSv
oTZwvq8e4fcvTCDc/jnDHtKoy8ek/9pbpn6hpAvTfLEKduvEHq0U0ZyKZm1o
1YeCRkz60KgR5mecYnbfrx0x7ycXN3dcU497TzCuDyb8GDDW7fo8SfEYagNz
WZyP6kKZSnDNqZPLPkO70d/S/nJfURdKfUAVNZ1Yq0/s0cmD1EYh9GFPDGH/
noP/vZpDsy9VqFEf1wjjGuiVhN+Dj7kwrgvVx7aK1kRt4BgV5rRYh68jbpUO
GcIW/K4YM+zShRaNlCad2KkNa/WXPfqQa8RCG2Z94GdUil5/3bNh67cIP5+I
OTDOueP6LVyXomukaAy1gWvhGhL+nA7cy7PBULH2K6Rn0mULbdjSBdf+AkUR
S4qzPnFIF8WsD6sweZngX4+YrXeqt+z2IeH3Y+HYFu7DwvFfXSOFNzFuNCS8
NnA/32ZDdd/XDP1yrpu1odLWSppQolhiiSPjXXK9OPoZatpwRB/2aMQcO1i/
ug3bdq96wOBPad2O+0twP+YIwmsEcy29HnHMxFoc6w3c74pnP20mtVqeMETm
82tELPIpJU3skiJpw13mtlTViZo+ilMjhUHhOh6IPnar6gO/w2noE/eqB8d9
Qfj5dowjeKYKrqkWa3bdtBs+T3C9CM4v4fmBuIdpI6nT5k1+XaGNuMHqQtLO
CtjUiLUYUjQacYvZDm3nHoAJ69+GgpfOwssf/wKf/vgX/PTnVbh87RbHLxev
wpmf/4ZXPzkP61/5EqZsegcCFx6EcrE77NCGBn04rBHr+sDvcoraet8rfNyX
NI6gRvA8FVw7imeo4J4rfX5Em6E2cH4D9+CEEX5dz3pSp61tbbAxw5YuFHXy
4DTiOWoXRGW+Bk+99R38fuk6OGp//XsT9r//I4zIOw414p+yoY3i0oc1beww
fxe9JqehW+/XDR/7lcFgxJp9BeH3quF6UpxD1OfZbRvWbHguM545g+tvCwy1
WrxO+q+xnNuw0IZSzNDYrmptq0kf2jXiN+1ZyDt0Bn7/23FNqNnFKzdhw6tf
QcvZB7Rpwy59qPnSlg+3m/0j+MAp6vH71YNHnCX8WZK4lwTXBjUkfD6t1yLq
hnko5qO4BhfPl802Vvd7ldYbN031qjVtFKamLHQMsa4R70l7Ycuxr+HG7btF
rgu53bxzD5599wdoOmOfdW3YrY/d6rpQfb7I9cGvGzAO3nTfs03kKcLv38Xa
Mpzw876YO+gasTSMrZiH4vnKeO5yqqFS3ReM/bKvkcHiWI6D2tA4p1UcMaRc
7E5YtOtDroZ40Hbt1h1Ieu4TqDxqtxVtWNGHPXkqW8ep5FasPsigzeA8eP3d
8n5huP53PeHPQcX9iJg7uJRYLyydhs8LjK24lg3PoFpO3CvvNfRY/bd5DRIb
O9i2tKINa2uGLHRS9Pp4ZOY+ePvLPx64LuR25udLtJY/ZKc+7KzjbMYOQR/i
urJBmygbwXVgwe3yXi3wXNRsyijCz7HjWi29XudNXIuL+2FxrGoecXLZ7tRl
4W/mNXpWYocjulDUiMYcS6M+sPbG2rm02NWbd2DSpnfBGL3dPn3YOwaoIbdi
9UEGrAe3yIxrLpVq4ZpGHNPCtVq+hJ/30vMsvubAuVRcizuJumS90X/MVxh/
rcYOtXleCw08LaN4YwievTZn20m4e+9+SUvCwu7dvw85h86Aa8wO6/qwJ8ey
qQ3l3IrTxsANnD7IgLVQrseSP42u5XBMC88FxjVEWIvgc/O/rBGsOXBcD2sO
XJuTaWzc9R3DoA33zfpQix22tCHXhRWdFJE+jNHbYPWzn8CdUqgN1h4/9jW4
xOzUrg8ljcjnV1VjLTO+LY8doj76r6WsAY/gCd8YDEZcF0+fk9zzEufX/6t5
ljjPgfPjfSjLytdssN91UMEtzn+aY4cVbcTusaSY9GGgcQPr8HulXBtoeIWb
j36N51zZ0IfC+gMLbOShirHDnFuRAet4fUQWgCEy77570+5Yr+O5fThG04zw
a+/+izEE8yrc/4f7MGe4uLhsefnw4Z8PvP8D1IrfxehDQ+yQa0NJF9Y0UgT6
iMs7Ueic6vqtu9y8OY4Dz9v+AcRkvwERia9C95WvwNCs12Hm1vdhzctfwLtf
/QH/XL9daI2sfOYTqmsta9ZUdGGzRlOKHZa5lagPEpkHTn3SbzlX9cYzwXEv
O76HUcyz/kvG5lW4njN7yZIlH96nhm33ze//QNiSQ8q5ldXYoUEbWjRipz5a
zXke/i5ELf7JD39xfd9nyrPgNFR85qrPgRvod9aZsAdGFbwJxz77DQS32W13
7t6HwRmvK+vDQiNa13UqjFkp1eWy3IpE5gPplwekbw64hC+4YHRxx/MeMM/C
M8zwjIH/Up6FzwN8LuB5MEuDgoIOXrt27Q7bdtdv3YGFOz8A52HbLXMrxdhh
hzYkGimcPiqO3A0ffvunQ/3z9Pd/wYDUY+CCY0o2100pz4Mb6fV0fOxFePX0
eYdyuwv/3IBGU/fZrxGr2tAaO9YxsQP1kcvpg/TNArc2Qz8h/FpfPFu/MeHX
4/0X8iyMHfg8wOfC5PLlyz9++vTpC2rt9/zJn6D2hL0KuVUhYoctfVgbz2f6
ANYcG498ZXef/PfGHa5WcR3muC4s65/tEE3zsd8cWLfy4se/0Hp9l7pGNCHX
hpW6Q5JbCbGD5la8PrKB9MkCQ+/Uuy41m+KYL86t4zo8nDd82Nf5iutycR4Q
16WlpKamfmQrP/j29yvQZfkrlrlVYbQhz7Ec0AfWHPfszG1+vPAvdFr8op26
sK4NloZTnoO37JyTxHsYnvdm8WlDU+zIE2IH6iMTSEQ6OHeef4EYnfAsFNwv
grn4w16rYw6JNTmerTC7TZs2e69cuaJp4QWuW1q4+2NwGf6UHfrYy6A1hmjT
R9OZ++Hvq/atGfnil0tQf9JeDVpwXB94fRVHPQnPvvejXdf2/R//gkfcbvt1
oaoNhbxKY+wgERlAeqdTUsGlWR8cz8JaHedE8MyahzWGiGtI8KwX3BuTfezY
MbsaEZ/UBz/8BepO2s/oQ4s2rGjEAX1gzfH+OdWUUNEwBno/AG2IuMXuhMO0
JrHHpm95H1THe22OU6hoY7BSXmUrdqA+0oD0SgFjt2U3DBVq4TkoeP5+S8KP
6zyMMQRjB+aQnSkLhw8f/vI9BycLvqPPui4rj1nRh5I2VDRiV42+nXvfEo6x
2mM4Fhuw8AUHtWGHPmQaqTb2afj0x781X+e53/4B9+G7JJ9hoQkt2rA2XiVq
Q0PsQH2QnklgbDfyCyoJPAcb91jjvqCHLYag3nE9DcaOOCePKut9RxdcffeL
3+yVhskw31r01CfgOkIpLjB6GP6Mdo1o0MewnON2jadibh+/9q1CaMNxfSDt
F7zAjQdoMbytQemvWWrB1lkVWrShON/BjllZxg7SM5mSCIbuK+8Za/jh+Q4P
awwRYweOQyx0bTf8I/RHuSFrIWPfx3Drzj2HNIK99KXTv0G9qQfVtcFSSH00
mXEA/rxi3zzHE6+dK6Q2HNCIjJV7T2u+3gMnf+LmV2x/rlwXmFMp1OO28iob
sYP0WA2k+0owBiX8RCWB79fFswgepjpEHLPCPZQxzhVrrnfqnXKT8wn1j6F/
AfRPfBH+uOz4frqf/rwG3ZPekOpDro1C6qP8iCfhna/sqzlw3o/fe8H0c8Uz
s4pXI1gvaR33xXnOGjQvU9WE5B4UYoYtbVjkVdZjB+m+irICSNdl9w21W+H5
J6MJP5b1sKzvxfkOXGeG60jmuvuPPEUiMnmfoH/QV9RvDSbsguOf/wqOrtDA
PXMrnv0cXEc+q64PNY2ojvHy+jBE74D8l+yrOS5dvQWt58r2uappoxjGeeUs
2Pmh5mvnciyl71bcT2+vNgqsaIONHYmm2EG6cfqgMWTiz8RgxP24eH4mjoM+
DHPqOFeO71Me4lSher5T76RbnC8k+ljD+dA9ejOkPHfK4XwL7ZVPfgevaYcc
04fSGBbVx+DMN+xaW4U1x+g1b9qvjSKYJ1Sj9vg9mtdsFdBngfI1K5zTpVkb
8rwqx3pexcaObsupPpYC6fLYPUOd1k8Sfu2iOKdelg3jH+6Zxbnyaa5tot/j
fMDpI8tCH+hTA63p+ia+Ar/+dc1RiXD5Vs+UE0WiD9/pB2jud8Ou78e1sYah
as/ektEJzvW/8OHPmq7/5LkLYIyS68FKzGBrcbk2LOpxpuZAIlTyKlnsIOFL
UB9gaD/mOxpD8N3cuP4d12GU5TNPsIbCfYERzh6eqS49lv1r0gc+NzC+Yqzl
9LGW9yn6d/BmqD/xaTjyyXmH863bd+/ByufOguuo5xzWR/mRT9s9F41rscqP
2Fk02igyjfA/O3nTO5ruAeMMnsmlerajRcywoQ2bNYe8Jhdih6gNjB3hi6k+
HgUStuCeoaoP7lfH9ybg+Whlda+6OKaLa/jHuD3S5xXu3nul8v4Q9YHPE3y2
sPoQ9ka5DtsGq545DTcLcebHq5/+D7ynv2i3PgwxuyHj4Fm7vgvP12k+a7+V
vL24dGJbG0jbec9rzhMb4Fym/LxTi5ihpo31GrShlFeJNbkYO4S8SogdpPMi
1AeQlkNPE36sF9+ZUFbXnJjqcoOTyyLXznP/x907+oHTh1ij55lqdM6v6GPT
/qitYKDtGpF0BH7+86oj8uDsl7+uQy8x39Koj/7px7n131oN5w6G556wUdc+
SI1Y/ky1MU9q3hMfyu0vsPIeH7u0IcxzyGsONq/qpZJXsbGj80Kqj/lAOs28
Rdwr43whzhdUI2Uzx8K4h3sDB7h7tdlq7LHyPhc3OX2kgVqNLtWHuH9wG7eG
9+VT58HBrQ5cX1/53Blww3zLhj58Zhy0u+ZY98qXwrxBMetD81iw5TUY6fX9
dOFfTfczOP2o+jutlLShOlalUHOwY7lKeRVbk8tjR+g8ICFzgDTuhu/fwbMc
GpCyl2NhvMO4h3Od490Cx3zK3TM+GzCGsjW6Wg3C7j8Xno+4BnvJ06fhxi3H
862jn9F8C8e3VPTRcNrzcPoH7Wsy0HAtVoURu1Sf20WuD3s1wvwe7jnRYkMz
jjmmDXk9rqnmUIsdSyxjR+hcXh/+Y38lRmc8X7MVKXs5FsY7jHuhRlePxS7d
Ft/k7hmfDZIaJFNagyjmWOweW34uotsqmm9ddHx867dL12HGto+hzuTnTfrw
mvI8zNz+sd37J/785wY0nbHfal6jvd+rnDFdhPr4+LuLmu5LUR9atSGpORTG
ciV5lTjXwdTkptixWCF2UH10mgUkePpdUs0Pz8zCHKsqKVs5Fo5b1acMKOcX
vpd7DuA9c/pgcyz5PCGbY220PKOB2WNba+Jz3FpeR/MttDt373HrRRB7ag3R
sNaNynzdZt6vTRca3lPgqEaY7/ny/GVN92ahD5M21GoOtXkODeNVkppcQ+zo
NJPqYwaQpv1OYB8jZW8cC9ex4xqA+HIdx3/B3SfeM96/PMdix3lVxrGk+9DN
+whxL8j8XafgWiHyrcJY7otnNdXF9mtDo0bs1Id77HYaH7XF3Z4rX5Zpw5HY
oVSTp6vU5BpjR8hsQR/TMce6SJzdxhN+jLRcyXZ5zYZ5IJ4PGeRWueZjzp3n
X+f1sZS/f04f8hxLPo6lVqdLY4i4l7DziqPwwx+Oj285YniGSPm4XYXUh4Pv
u3FQHzhme+2mtrW8LWY+p55XaYkdWuc6NMcOoS7ncisaOzpOA9JhCtAcC9+1
E0D4eeiyUIPgmphalJ4VmnTeYcD7w+eAao4ljmPlMDmWrRjCaESYy6sxYR/s
P/mLw2d52GP4vg6/6fsUtKGgD4djR9Hro2/SEU33h7lmxbjtGmOH2niuwtpc
i/HcJI2xY75l7OgwlTIZiF/vVwi/t7AmKRs1COaBDSlDPQJHfsjdHz4HVHMs
hblCSQwR6pDBTAyR5Vniflvn2KdgFq2xr2p8RjpieEZV78QjKtqwRx9atFG0
+sg6+Lmme/yK1ihOUVvsz61UY4eVuUCbY1YLVGIH1UcQ1UebEecJP87rTcpG
DYJ5YHNnN48E15BpFznt43OAiyFCjmUax7I2F8LGEKWxLKXz4vj1t8HLjsC5
368UuTYwN4njzjEozLukSkYfbjE74KtftdXme97+rojq8kLMd3QRYoepLmdi
h5hbBU0CEjD+LqlQC98xhjVIaV+vKK5HDPCo7bfKGDrnPqf9sIVCDBFzrJW8
b5TqdMU6REOeJdNI1XHPwd73frL7fBE1O//XNei68lX7tFGc+rBnfJfSY+Ur
mu918sa3NY7pWsutrIzpqsYOdq58kULsYHIrjB2BCVQfE4B4d8Qze9tTKpLS
XYNg/ofr8sMrtOi9l7snzBvDhBjC1undV8tiSCZTh1iZD7HIs5Q0wuvEieZb
4ze+D78UYi0w5uJPvfU91Et41so+0+KqPYpGH7iW+OVTv2i6X9xb0HTaM4Wc
87CVW7Hr1xXWWXHrEFXGdE2xA/UxEeMHkGaRx4l5HqQ06wNrc1yv26984IiP
uXvCe8NngEWdrhRDVOZDFPMspVpE+Z0H1Sc8B0v2fGrXnOJt2k/2n/wJQpYe
BqNpH2Fh30Forz7sza2Ur6X7ysOaz1b84JsL4Dx0q+Pz5fbmVvaO6XJ1uZBb
BU7g9dF6+HliMOI7DGuT0r1nStwLNcwjdMpfnN4xhuAzQF6nK8YQoQ6xVqtb
vBdkmw2NmHXiGvc0dF11DFIPnIXXzvyPe3/y3//e4vb64TmbuK5k54nvYcLG
96AujRcGDWfvlp7cSvmaKo3aBZ//rH29zOyt7zmw1kpNH47mVhrqcjG38h8H
pN3oG6RinXjC1+ileV861kfNyletPcXYcco90nE6mGPIfNsxRDIfkquSZ8lr
EQWN2NAJi8eoPVBpzF4wxmg4e7dItPEgYgcPvqunwI6ziPA5UWvs7mJew66y
N1CSWynV5TOkdbmYW/mPBdJ+DJA6bfFd0XjGgVsJa8Ca4bkrbSv7BWcYcGwa
7wfvC++Rq9OVYojCfIipVlcYzzLVIgr1usW4VlG8Q89W7FDpr4XSR+G1gUzc
8I5d+4LXvnzW+poSu2sPe/c/qa21ks15YOwQc6v2YzF+APHptpPw62FL6zy6
OG/ewbNt/2c4jaPW8b4kdbpSDGHzrHTp2l7TeJZKLSKJI09YaoTViZpW7Hqn
RWG1Ufz6cIrebrc2/r1xG7wTni7cWt1C7p21utbKoi4Xcqv28bw+mkW+Sfgx
rAolKwNVQ33g+EGYW+shJziN24whS6UxRFOe5aBGJGf1a33XSynThgZ94HtB
dp/4zu53HSQ+e9rOPVC2ziRRO89K6x4Pa3X5RHPswNyq7SggLaO+JUYnPCPH
k5TOMSwc28U5/h6Vg+O/5e6BiyFT1WMI+oWbU18hiyFsnmWPRmTjWmo6kWhF
TRfW8ipHdKG17nAsfrjE7IDx69+GXzWuP2Tt618vg+fIndb14fA+jzR+rZ3K
mT2K+wO11uVi7Gg7EkirmMvErRKeP1pax3hxXK0OMRj6ugSMucjpWzGGCGNZ
kvkQpTxLpRZRHNNS0MiQJ7TppDC6KFJNOKYRj7idMGbNm9zZ8I4Yrpfpuvwl
B/cI2jgLkV2rKznvzcrecqW1Vkp1uRg72tHY0XYEkDbD75NKdaMIf05naVyH
heNqXp51Go5wDoi/zukbayhJDGHGskxz6ip5lri+V60WUdKIfOxXrpEoK+9r
tUcbxaYJ7TppPO1ZWPr0x9y58I4aLi2YieO5ts5eKLK6fLVKXV7I2NEmDudA
gNRuNY2U3jkQ1EeDaj5tproEjr3H6dtqDGHXZTF5lmk8S6EWsajXFXItUSNs
TWKhk21mHWg9g1lRF0WpA9s6qZ+wFyZtfAeOfvqr5nXq1iz7hc/BiZ0LdKju
UJnz6K0UO1TOJeliLXYo1B3+8tiB+ogFUr/jSsK/t6806gPXTjYy1Gw+j7tu
1LckhkyRxRClWl0tz7KiEbYekc+PSGKJik5szWmUkB6co7dBrbFPQb/kI5Dz
whnuLN+iWrqPtTtqwzn6iSLaQ6tSdzzI2NE6BkijLgWE37daGucIce68cbWW
3dO468brN8WQifw94rNAnFOX1OryPEutFrFHI5uk7S7XiWqtWzK6wLO+Hpm5
D0bmH4eNr34Jp6kerhfDvkg8N2/Bjg/AGKUWN9TyKjvWIirGDlt1h9qY1WQb
dYcQO1oNA9K4+y7Cn2dSWvXhVztgwHruutViCJdnzVTOs8TxLLEW4ebWC6kR
i1iyRdY3Hc2bthQK12FPQMjiQzBv+0nY994P3PlehTl3WIvh3trIlCOgXm9o
yas0rrWye8zKzvkONna0orGjZTQQv14HCb/3qDTuA+H0Ua19/ye461aLIRa1
+jzzeBZbi1jU6wrjvqxG5GO/4rPPFEs2W4knRagFlTOjXKK3gs8UrB/ehudP
/qj5rOiiMJwn3Pv2d1Bn/FNFp41CrSWxsg7R1ly5ON9hih0xfOxoORRIkz7H
SOnVB657aeLk12Mnp+k2I/j7wPtB3aP+5bU6l2eJ41lKtYjSmJYWjViLJZst
+4cD/V0LlUfsgB4rXoasg5/R+uEil9s8SMNy5aNv/4R+Sa+CIUrlXrTsf3Lk
3B4tc+WSNexa1lnJY0esOXa0iEJ94HkmuD62NOqDix/Ep+tOTtOSGCLmWROV
8yzVWoSt19n5dSu5FqcRhXxLrpNBCjopAuqOfxKmbHobDn7wI7cm+AFsh1c0
rOdjs18H9xiFGlxTLa6kjUKsYbdYZ6VxjW6gmFcJ66xMsWO4NHa0GAKkad+P
SOmNH4I+wndy14zaNmlkDK9/1TyLrUXEuXUrGumlphFxnp2tSdaa29ukkY3M
M7PwWilH++CQ9KNw7NNf7VrzVNR2heZsuDcW5/uM1mKfWswolDbs2P9kdS7Q
yjoSriaXx46hfOxoPhj1gedWNySlVx+NSaPwHVy8Q21jniWv1SV5lo1apLAa
Ucu3JPGE1QqjFw24Dt0CY/KPw1kH566Lwq7euAOHT/0CE9a9ycUu65pQ0IWa
Nvpb04aVM60cOSvUkZq8tZBXtYziY0fzQZhfnSKld/wKNetD9bGN07RFDLGW
Z7G1iFwjS6xoRD6uxazXUsy3bOlEOx0W7of3v/6jRPInfHfQ9te/pvnTa1B7
7G4wSPq/Epu068JubcjHq+RzHdZqcoV95Yrr163U5GLsaD6Q6iMC9VFa5z+4
+XPSqMsT3DVzGolhNDKKv0/JeFZRaiRdoW5XiCVynci1YkM37tGPQ/KzhXsH
nD2G8+S4fnDnG1/DxPUnoMnUPVQPm2R93hoK+mbvUTGfUjvLSkUb9q7PtXUm
idaanI0djwzAs7Cw/iit8+fcmbsGn/DHOT1zGhHyrDZsnqVSi+BeQ3bu0BGN
9FbRiFIsUdIJi4Jeao7eDq+c0vaOMkft76s34c2zv0HavtMQmfQyeE/cDcbB
Kv3cHuT3I48ZSrWGqjbsOIM9vKjyqhFMXsXU5Jw2aOxo1p/qo+frpPSuv+LW
71Zv128dd8147bbyLLEWUarXxTEtuzTCrGk01STCmSgWsUSuk7VMX7HUSy2q
jQ+/se/9zlrsEtXDSx/9BI/tfB86L34eqo54AgxK/bkosLgvpZjBzm8UUhs2
95QrnNcjn+tQmieX5FVC7GgWCcS3J56lWFrX73L7P2r5R2ZwejbFEHmeJa9F
cG5dqNfVxrSsacQ0P5LEaESoSbAtFWMJo5PINVKtKOjFc/gW+Pi7otHGLxf/
hSdPnINpG9+E1jP3gtMgpX5rO5Y5pgeleKEQM0xnS9urDQfOI7Enr1KqycXY
gfpo3O0ZUnr3f3D7B6u16vEop2e8dnmeJY5nWdQi4voTRzSisBZFHNtSiiWK
OmFjilQrxoHr4MnjXxdKE7ge8N0vf4fRea9BxZjHbeuhyGE0IdeFWsyQjOGy
9YbaWJXW8xbsyatGSfMqSewQtCHGjqb9cH3iJsLv8S6t+qhsrNNmNHe9okYk
eZatWkSo1zVphBn7Na3XspJvSeoSJuey0IkYV3i9jMg+YvdeVdHwvaJPv3kO
Oi18DlwGr5flcA8KuSaUdKGSTylqg40bVupxm2tzbeRVbe3Iq5AmfYE0DEsm
pXf/OVqFqr6BA5xbDLjHxTwuz1KrRWxohBvTktUjavMjpvVabE0iy7fYWMLq
xBRPLLVSb8xW+N2B/ao/XbgCK5/+AOrFb5P1y7UqeVwxoPidqIsCdV1YaCNT
ozbEOUCleQ72LBKF9VWKeZXSPKCVvKppX5z7AOIdjO9FL63nl6CVq+3bNtT5
kchrfAwRNcLWImr1ehFqRJJvqcUSZr7EQie5YKD95vFXz9ili4tXbsDENa9D
xWGbFHO1B88akMYKrbrIkmkjTaHeYLWh4ZweqzWHjfEqpbkONq9CbTSJAFLX
fygp3edfuRqc3XyNTfv8j7tuNs9iaxHVet0RjQjrtRRrElkskdclivGE10qb
6bvsWk944fJ1CJi9h+mHCrD9tTiRfG++DCu6UKs1HNaGfJ7D1voqtbwqWiGv
6m/Oq1Affr2BVG8SSErn3KBoOMZbr0Lr/me4a5ZrhMuzWI0MVxn3tVKPWMwh
Cmsa2bpdEktWm2OJWJdY1QmvlUMnv9OsjX+u3YKO8/Yq9EVbWNGSQ6h9j6gJ
uS6sxQz2PWgp4BSRAvXj8iFy6R6Yt/EIpD71FmTufQce3XwEhic9A83j88Cj
13KFOUAbaw9trq/SkFdh7PDt+TepWNeHlM65D9FwjLeGs1/3/bw++gr1k7wW
kY1pcWvhNWrEYp59Pr9OWjHfUoslycq1iUCbqdu52lqLYe0+Pv+ouX4RibSG
vTrSiux75JpQixdKuhBiRo3oPJiz8Si8/+V56hP1Pe/4Honvfv0L1h14FzpN
WQvGMCvrR2ibVgqfAY+u3Qd5Tx2FwJGrVNZXycar1PIqjB2Nu58lLh4491Ea
x65E48awPFv1zuGu2apGhvL3r1kjSvPscxTGthTyLUldYk0nvFY2vHhac+w4
+N634NQ/z6J+sSRPO9a0ZfP3Fb5bHiuUdCHTRoWBWbB855vw52X7xyfwvcBv
f/Y9RC/dBi6d51jUHMagBNj3+semn//3+k2InJltY30VO17VT5pX+fUC4tP1
EOHf/1HazaOCb+hY7pr9IqQaUarXrWqEqUcCmTlE03otpZqEzbeUYglbv8t0
QqlGn5cXNPaJ6zfvQOPxW2S1C1Pvs1jVTlGRo6wFE1k2dWGgdF3wJJw7r/3M
d2v24+9/weINL0D9AYtN8xxjV22zeG/RjZu3YdSS9WBoY2dehf3MtyeO7aaR
0v/+KDTXmn7tg+g13+OuvYmoEbYWURrTsqUR2VoUViM4bijJt1RiiakuYXIu
Vie0PolLfV5z2xcc/FioW4TaxaLO10qOA2j87D7WNCHNpZz7pMHSbcfhxq2i
f3/jP1dvwJOvnIS1z7wOl/+9rvgzN2/dhqnJW5W1gf0E587keZWvoI/6HYaT
0rnvQ27c+2vLteh7gbt2Ls8SNIL6wOeDXRoZo6IRZmwreKY03xL361rEEqX6
fZUk73rmhLb3AWB94jtus7TPSWp9mW4KpR97dJBl/m6La2M1wYxLUVypNnYc
+eyBvP/Xmt25cxfmZe4AA/YJMa+iMcfQYxVlNd9PxLxK1Ebj7tdJrRZtSelc
dyU3rEE8XZt0P8xdO6MRA+3/RtoHDcHThHprkJVcSxzXEuYQubUo4ppGtbpd
nm/JYokk55LHk5Xg3icZLv6j/GyT2wvvfQOGCHPNYiIiXVkzqvopJFa/K10W
J6SaEMdsKw7MoPdzrph7vnbDMY/V6/eCc5thYKDPO2OvJB7sO9gHxJqD6189
sPb4lrhVqEJKd23OWrlyTbqspLoGViOG1rHg1CsRnHrTe+08n4+h9mrEX64R
Nt+SjwHL6hJxjEsyFmzWif/kTZrbMCb5ANPPUoUan63zZbox6ceGdhzB4nvS
pHpQ0IS4B9a9XyrseeNsMfZ2xwzDWNaeN8ElIoXrL1y/6cXog4sdPYDrY406
7yVlo/YQzcXTL7gHadwNTBqheje0GAwuvVaDS+9EShI491gOBuz7FhqRzyGK
axoVxrYU8y1rsWQhf56lGEsYnUzJfUFT212+ehNqx+QycyrJ0v4n0ltNN8VB
qrIWOJIlmhDn+DBfWXfww2Lu6Y4b5nobD30I5fqm8H2G9h1j+1FsXgVcH/Pu
NIOU7nlBuRkr1m7k5dKkx2/c9fsKGqE5lmvPleBK79U1AkmmUJ2EzhJiyWBm
7NeGRti1v2qxhK1LFHXyqCTvWv/8SU3t9s7ZX2S1PVvjC5qxphtVDWnA1ueZ
tJDMXI9ZE+a1tqtg1rrDJXa+ij321LFPofKAVNpvVoOh1VCpNny63iW1W7cm
ZaP2EA3zwAruTbs/T3y68feBsZDel2uX+eBOtcHRJ4nL+RG3nsvBKXAcoxH5
WhSFut2Ub4nzJGwsYedKlHKu+bK861E4+tE3mtor97n3hNperO9XC3qxppkk
S/3IUevzaj9v8bmJllrg9LDavB6EYwV0mbMNrt645VB/vfjPVTh++ht4+shH
cODEp/DpN+fhzt2iPwuVtQNvnYVqA5PBgHMfXF6F2uiGa9q/pn3Ng5Sd2kM0
14rNwqcRn3BgNeLSYQKUo9oo1wdJ4umbzMVQxK3bo2BsN0KmEZWahNtDwuZb
1mIJm3Mp6CRsIXzx4x+a2mpC1kHzvCNT30vGw0zjx4xuRHoWEexnmrTAfD+j
B9O10uuuFZUO3/9m3/wGhplTX//CzflV7rUIDJ3M60WcO00D36hlkLX7CFy5
esP+zq/Rjn7wJVQPjDJrw6crkIaha0nZGNeVm7Gqb2Bzp8Zd/2U1Ymw5GMr3
WU1JhPJ9kSSefsmUFEoqh1v4fDBiTqU4/ivPtxRqd7YusaYTJu+6cOlfTe3U
a8F2WW2/nEFJMyuV9WOhIRt0l/f/VQqfv4LRg3BNXZeb1hBizffqh9ripGjX
b96GhesOgkvYHN5/7Hw4u0adPp+q95gFc3P2wBff/1YsY8UHjrwNTn49eG1g
v/IK7E3KVm4lGsa78q5+XU/w+hA0QmOIR8/HoELf1ZREqNAviZLME5kikAoV
+qdx/+3edQGtyUYr1ySmfIuNJQp1iaJOZkrzLqqTSyrzVqzhvG/ApHXm2l4y
DiboRURJNyyqGrKC0udItMB8v+malpj2LK3Ydsyu/og52MCFj8t0MZXZ95cg
W6PO7+Fw7zieWzPy9Y+/OyoFRbt77x54hQzj+1Ojzr8QzwaldT+tFnMp3yR8
Ds0RgdWIe/BEqNRvNSWRJzKJkszTP4WSCpUGIGmUdO7f5XsuAeeg8Xwc0RxL
tOjEXJ9o1kfCGnNdbxovXizTjFw3Cvqx0JAN5L+r+PlLzdcgXpNwjRELtlld
Yyi323fuQvSSrcq6YPducHubhP0b7Dpc2i7R8/MLIwcL++ffa1AjYCBwfapB
6EZiMJSlcSu5Gav6tGvo5BN+g7sfQSdOLQaCZ79V4BmZKJAEnv2RZEoKeA5A
UsFzIJJGSecZlMHpyT1sJq1RRgoaUYolzBiXRCcJVnXy9xXb665QH0EJBbLa
fpF0LMxCM4xuTNphUennEmS/I9eATAumvd/cPr5F0DAmDX67+I/mfoipUcqO
V8EgeXesNJcy64I9T8GsDafA8TB69c7CyMHCNj71At+PGna+S7yC8H2cZTG3
Ys3dzS/8BXo/YNJI465Qqft8qNp/NSWRksQzAEmmpEBVqg2eNKg6CEmHqoMz
BDKhCv3/ir2XgmunqWBoO0IWS9gxLiHn0qCTb37RdkZJvwVbmNperO8XCOvs
Bc10ZjWjoB0LFpsJXyz9f6u/J/sO7ruF6wjj97e6dnsUjp/Wvp8F7Y2Pz4F7
51kyXYi5lKAL035Yc8ww0FzYLXQGVOyzEqoMyYK9x4tu7hHXZ7XtNx64vtQw
7AtS2Uvbel2vIKS06sipQtPOQ+n9AKsRd/84qD4g0czAJIFknkEpAmlQfTCS
LpAB1YcgmZQsqB6VxWmmYu8l4BYylZ9vFDUiybmUdCLNu9765FtN7TQr7wBT
2wv1PYdcL3LdyPRjEXussUj6e+znWXzffNOZCIaweZD51Bt29cPfaZzxHrhY
posJQi7F6ILLpUZTn4/mnlMV6PMKn13Vh+bwRNH645eLDihB2Q6fOAkGPnbQ
3CpkHalQUz23qt/BQPGizKXaOEkZ/AD7vD1mqFivSRUX3y7fshox+HaD6v2W
Qs2BiQJJUHMQkiyQAjUHI6mUNKg5BEmnZEDNKCRTIAtqDkWyKTlQg/4bczeP
8Ll8vcLpRYNOaB/Y8dJ7mtppx8sf8HU9V9uL42BzZJpR0o0ceb/WgsLnmL5n
nvm7Q/i9e1FLtsFtO+YmbtBndMSsAnOsYPMoRhcG+t+uIdOhUsRS7hlVc1ge
1IzO5aHtwLdHFjxx+JSjcpAYjoVFjFkgxI5Q1Mc9ylHi3akP8Q525Xpa/Y4I
aiKBcoxyh+oCeAKfJ/UCS1gKquZczq/zCu6+GI1UCB4HtQclCiTxDE4WSIHa
Q5BUgTSoHZUukAG1hyKZAllQOzpbIAdqD8vlqEXB+FOZtmE5mis4BU1Q1wl9
Ni5a85ymtvr65z/ANWy2MKYj1vczZZpR0I1IqJKGtMD2fRbxO4TvFa6jSWyS
5jFrvg8CrHr8EBiClHXh1CEBytHnDj5/sP/XjsnnodrgfZ7D+19sC9oufqPy
IfuZt+G9sz/D2R/+B+f/vGzXGIFoZ879AE6Nw0VtmPHudJ/q4wOqi4WUQ5R/
qC6Aw6wNoNq4QKle0kJQMUNl3w4+To3CLnP3JGjEya8n1B6wHOoOTqQk8QxB
kikpPFGpAmlQdyiSDnWjkQxKJs+wLIFsqBuD5FByeWLzKPkcdWLyuNjj2WcZ
eHSdBy7Bk+lzcJypju8xJUtTW+FccfsxaXxtb6rvp5v1wjGT0Q2jHYl+Zqto
SY7Cz7Ofx35PMH8OAvbjD7/4ya4+eOT9s+DWabLwvBhv0kPlPsu5uF0H/Ti8
gPqyQPAp+lbwM+fzHL4NOIQ2EduItln9mAzwicuCpqNyof3ENdBz7hYYnbIX
5q97EZJ3HoMth07CS+99CW9/9h189dP/4Mq1m1zNcerMOQgeMpmNG+9T7lJt
4Jk++F5nBok2blNtvEd1MYfSgFKax4Gd3f26bDbpXtBI5ZAJ4DUkUSAJvKKQ
ZIEUnqGpAmk81Nc8GeA1LJMnJksgm5IDXrFILs/wPIF8SgF4xSFrOLCNa9Ac
rjLVTJ3+y7i5MC2WseuIUNtPMdf44niYhW7k2pmhEHtsofL74ryEOAZLMXaa
Dhv2v2WXNs5fuAyNBi+HCj0WQZXIVdyz3yturclPPAW8/zg/Cj7lfCz4m/N9
Nt8Ow7KEtsng24lrrzRzG4ptKrax2ObYB4T+UG/QSqjUur/QV8S40ektUrm+
O9VFAOVJqomrEl3U73CL6uIU5VGqDb+S7vR2mKFyk5AWxoaht8waCQWnJr3A
a+By8I5KBO+hSQLJPNEpAqk8w9IE0nno88hMJnjHZlGyBXLAe7hILnjH5ZkZ
kU8p4Bm5RmAtNBi1Fo5/+qPG/nQJKnedJdT3Qo3P1fmTlXUj146E6TZQ+71p
zOebvzN+9Q5uD7hWw/2CsckHwHvUOuqHdZwveATfiL5Cv7F+RL+KPkZ/m3yf
ybeH2DZie3GkmduTA9tXaG+x/bEvUKp2niTTBo0d9TvgGVfmOFC/YzP6Z18w
cWMtpazOiTi7Ng7bLckf6b1XCRkLjYYmUpKgUbRIskAKNBqWKpAGjWKQdDOx
GZRMnuFIFiWbJy6HksuQB41GIPk8IwsE1kCjUchaWLHjuOZ+tWDdC1Cp92Lw
6Daf1iO0vgmeBgaqD4NJL4xuTNqR68cRhM8JsqR13Gq4pGEeRzSsOdL2vAON
Rq+j97+O8wHPGt4voo9En6H/0I8mn+aY/c35PsvcHlzbZDDtJbQftiPXnilM
Oyfz7T6Up8GQleDctJes1gg+Sao0sjwfsX6HOeacKvALimsJ9O2iMEOVZmGt
jQ1DbnC5o3DfxsZdocGgZdCY+qfxMCTZTEyKQColjScWSRfIgMbDkUxoHIdk
CWRD4xEiOZRcnpF5Avk8owoE1nCEzta+rvWPS1eh/bQnoHH8RhM+YzZAA9qf
MO/A/BvHE3AsDsewq/RbwY0VVOz1GJTvvhDKdZ0P7uHzwK3LHHDtPAdcQmeZ
oXrDP3frMpf7OQR/p0KPR6kml3A1VNXIlVBjYBLUGpLGfVeT+LVwVuMaS9Fe
/fBb8ItfD41HrxVYY/KF2TeCr0y+E3zJ+VXwscnvWXxbDBfaBdvH1FZC+3Ft
KbTrsBSmvZP4PkCp1nm8ZR3uFTSEKK0l8QpqQLnK1eFegXdpndHlwXftojGj
e0UaQ0K3cnUVo5HKQbHgF5MkkMwTi6QIpILfcJE0SjpPXIZAJs8IJEsgG/xG
IjkCueA3CsnjGZ0vUGCiyZg1cOQjbfMgaE+9fgaajN0AfhwbVdgEfuOKCfxs
+h1N6X/bOxf3M605OkzfAn5j1gqs4WH8YfKR6DP0ocmfObyPOQSfi+1gaheh
nbg2S+PbD9vS1K5CO4ttTtvfZ8gycPbrzmqD0vEDUqmuclzwCjJQXbwkjFMh
mx9wty5KM1TwDfY1Nux0idWIoVEYNIhcAE1jkyjJPMORFIFUaBonkiaQDk1H
IBkCmTwjkSxKNs8oJEcgl2d0HkM+z5gCjrHp+zWf2Y55/oy1h2n/XE/ZILDR
Bpug6XgHwd9V+MzFT7xhcWaONcNxiLjk56ApjTlN49eYEXxg8onJR7lm35l8
KfiX83UW73exDbj2SOfbyNReQvthW5raNcXc3lzb07qj4whGF9hHgu9RDfQi
1tYh1gscLmgD+YNS+cF16SI3o3vj0NXCvZs04tEqEh6JWQ3NhydRkikp0DxO
JJVnBJJmZmS6QAbPqEyBLJ7R2Qw5PGNyGfJ44vNNtBy7Bk6d+1VzX7t89QZE
rXwGmlONcIwX2aDAxiLC/Jkj0w7QnFDbuBsazrWlP/0WNKf3aSIeKRAQfCH6
hvWX6EPWr6KvOb9nmNtCbBu2vcR2FNsV25hr6ySOxgMf5Z6VjDZw3HYfqVTP
es2NeqgX+CevjwAk9gH15WIxT98gT+dGIT+YNRLMaaRutwRoOSKJkiyQwjMy
lSGNZxSSLpBhZnSmQBbPmGyBHGgZL5LLMzaPId9EQtZ+zf0N7fyf/0C/xbuh
JdVFywksG4qV6NXPaD5zRbSjNH9sO2EdtBy3hlJghrl/k0/i88y+En2HfuT8
mWX2sehzth24dkkztxfbhmK7mtqZtnlcIlRoM1CujSu0r7fU1KnqBW4VtIE8
WcxduLjN4OEbNpTe/31u/Frwh1PjLvDI0KXQZmQyzygkRSBVIA3ajBZJNzMm
QyCTIQvaxItkU3KgzViRXIE8aDOOJZ/2nwJ445T2OgTt14tXIC7pWWhDddFm
ojU2FBL+c0al7qPasO/8z5//uAShMzZBm/FrKAVm6D3zCD5An5j8k8v7C33H
+TDb7FP0r8Tf1P+jM6TtIraVqf1SzIjtTKnfc4pcGzhmm0icXLTN69ULCKXs
pQwi9fxL83tyNFkV7+YuLo1C9pvmPgW/VGo7ENqOTIJ2VBvtqA/bjRZJZUiD
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
H5BrwgM=
"], {{0, 0}, {200., 187.}}, {0, 255},
ColorFunction->RGBColor,
ImageResolution->{72, 72}],
ImageSize->{Automatic, 28.3828125},
PlotRange->{{0, 200.}, {0, 187.}}]\)]]
In[94]:=
PythonObject["scipy"]
In[95]:=
DeleteObject[%["Session"]]

Dependencies

PythonObject["API","Dependencies"["package",assoc]]

specify dependencies for the package in the form <|"dep1"configuration1,|>.

Specify a dependency on the SciPy package:

In[96]:=
PythonObject["API", "Dependencies"[
  "mypackage", <|"scipy" -> ResourceFunction["SciPyObject"]|>]]

Configure

PythonObject["API","Configure"["package",{func1,func2,}]]

creates a configuration that includes all funci["package"].

Configuration functions funci are expected to be in the form "apif"[#,]&, where "apif" is one of the configuration functions.


In[97]:=
package = "TemporaryPackage"[<|"Name" -> "onefunction",
    "__init__.py" -> "from . import mod
from . mod import *
",
    "mod.py" -> "def my_function(x):
	return 'my_function called'
"|>];

Allow calling my_function by a different name:

In[98]:=
PythonObject["API", "Configure"[
  "onefunction", { "Rename"[#, "my_function" -> "Func"] &}]]
In[99]:=
p = PythonObject[package]
In[100]:=
p["Information"]
In[101]:=
p["Func"[1]]
In[102]:=
DeleteObject[p["Session"]]

Miscellaneous Utilities

PythonObject["API","CacheLocation"["package"]]

the location of a local cache for the package.

PythonObject["API","CacheLocation"[]]

the location of all local caches.

PythonObject["API","CacheLocation"[]] can be used instead of p["CacheLocation"] when the PythonObject p is not available.

The file referenced by "CacheLocation" may not exist on the file system:

In[103]:=
PythonObject["API", "CacheLocation"["none"]]
In[104]:=
FileExistsQ[%]

Create a local cache:

In[105]:=
p = PythonObject["camelcase", "LocalCache" -> True]
In[106]:=
cache = PythonObject["API", "CacheLocation"["camelcase"]]
In[107]:=
FileExistsQ[cache]

Delete the local cache:

In[108]:=
DeleteDirectory[cache, DeleteContents -> True]
In[109]:=
FileExistsQ[cache]

PythonObject["API","Reset"["package"]]

clears configurations and cached values for the package.

PythonObject["API","Reset"[]]

clears configurations and cached values for all packages.

PythonObject["API","Reset"[All]] is the same as PythonObject["API","Reset"[]].

By default, "Reset" clears both values defined in the current Wolfram Language session as well as persistent values. To clear only current session caches, use "Reset"[,False]. To clear only persistent caches, delete directories containing the caches.

Delete all the cached information to test the loading time for a package:

In[110]:=
PythonObject["API", "Reset"["requests"]]
In[111]:=
(p = PythonObject["requests", "LocalCache" -> True]) // AbsoluteTiming
In[112]:=
DeleteObject[p["Session"]]

Clear the current session cache:

In[113]:=
PythonObject["API", "Reset"["requests", False]]

Reloading the package is now faster since the local cache is used automatically:

In[114]:=
(p = PythonObject["requests"]) // AbsoluteTiming
In[115]:=
DeleteObject[p["Session"]]

PythonObject["API","MissingSignatures"[p]]

gives a list of callables in the PythonObject p with missing or incomplete signatures.

License Information