Function Repository Resource:

PythonEvaluateWithDependencies

Source Notebook

Evaluate an expression automatically while installing required Python packages

Contributed by: Wolfram Staff

ResourceFunction["PythonEvaluateWithDependencies"][spec,cmds]

evaluates the Python commands cmds in a system satifying the specification spec and tries to install any required Python packages that are not yet installed.

ResourceFunction["PythonEvaluateWithDependencies"][expr]

evaluates the expression expr that calls ExternalEvaluate to execute Python commands.

ResourceFunction["PythonEvaluateWithDependencies"][,info]

returns the specified additional information info.

Details and Options

The Python system specification spec and the Python commands cmds can be the same as in ExternalEvaluate.
In ResourceFunction["PythonEvaluateWithDependencies"][expr], the evaluated form of expr can be anything, as long as there are no errors encountered in Python or an arbitrary expression containing one or more Failure objects returned by ExternalEvaluate.
To install missing packages, ResourceFunction["PythonEvaluateWithDependencies"] calls the resource function PythonPackageInstall, printing a temporary cell while installing a package.
Successful installation of missing packages can be verified with the resource function PythonPackageList.
ResourceFunction["PythonEvaluateWithDependencies"] returns a Failure object if the installation of any missing package fails.
ResourceFunction["PythonEvaluateWithDependencies"] takes the option "NameSubstitutions" that allows one to install a specific version of a given package or install a package with a different name instead of the name referred to in the Python code.
info can be any of the following:
Allan Association of the result and a list of installed packages
Noneonly the result of evaluation (default)
ResourceFunction["PythonEvaluateWithDependencies"] has attribute HoldFirst.
As packages are installed automatically, you should only use ResourceFunction["PythonEvaluateWithDependencies"] to evaluate a Python code that comes from trusted sources.

Examples

Basic Examples (2) 

The Python package "camelcase" used in this example is not installed in this system:

In[1]:=
ResourceFunction["PythonPackageList"][]["camelcase"]
Out[1]=

Evaluate the Python code, installing the package automatically:

In[2]:=
code = "from camelcase import CamelCase as c
c().hump('hello world')"
Out[2]=
In[3]:=
ResourceFunction["PythonEvaluateWithDependencies"]["Python", code]
Out[3]=

The package is now installed:

In[4]:=
ResourceFunction["PythonPackageList"][]["camelcase"]
Out[4]=

Uninstall the package to clean up:

In[5]:=
ResourceFunction["PythonPackageUninstall"]["camelcase"]
Out[5]=

Evaluate an expression that contains a call to ExternalEvaluate:

In[6]:=
func[code_] := {ExternalEvaluate["Python", code]}
In[7]:=
code = "from camelcase import CamelCase as c
print('hello')
c().hump('hello world')";
In[8]:=
ResourceFunction["PythonEvaluateWithDependencies"][func[code]]
Out[8]=

Clean up:

In[9]:=
ResourceFunction["PythonPackageUninstall"]["camelcase"]
Out[9]=

Scope (2) 

Install as many Python packages as necessary to successfully evaluate an expression:

In[10]:=
code = "from camelcase import CamelCase as c
import requests
r = requests.get('https://wolfram.com')
[c().hump('hello world'),r.status_code]"
Out[10]=
In[11]:=
ResourceFunction["PythonEvaluateWithDependencies"]["Python", code]
Out[11]=

Uninstall the packages to clean up:

In[12]:=
ResourceFunction["PythonPackageUninstall"][{"requests", "camelcase"}]
Out[12]=

Obtain both the result of evaluation and a list of installed packages:

In[13]:=
ResourceFunction[
 "PythonEvaluateWithDependencies"]["Python", "import tst", All]
Out[13]=

Uninstall the added packages to clean up:

In[14]:=
ResourceFunction["PythonPackageUninstall"][%["InstalledDependencies"]]
Out[14]=

Options (2) 

Use the option "NameSubstitutions" to install a specific version of a package:

In[15]:=
ResourceFunction[
 "PythonEvaluateWithDependencies"]["Python", "import camelcase", "NameSubstitutions" -> {"camelcase" -> "camelcase==0.2"}]

Clean up:

In[16]:=
ResourceFunction["PythonPackageUninstall"]["camelcase"]
Out[16]=

Applications (2) 

PythonEvaluateWithDependencies is most useful when the Python code requirements are not known:

In[17]:=
code = "from camelcase import CamelCase as c\nc().hump('hello world')";

Start a session and make an inventory of installed packages:

In[18]:=
session = StartExternalSession["Python"]
Out[18]=
In[19]:=
installed = ResourceFunction["PythonPackageList"][session];

Evaluate the black box code:

In[20]:=
ResourceFunction["PythonEvaluateWithDependencies"][session, code]
Out[20]=

See the newly installed package:

In[21]:=
Complement[ResourceFunction["PythonPackageList"][session], installed]
Out[21]=

Uninstall it:

In[22]:=
ResourceFunction["PythonPackageUninstall"][session, %]
Out[22]=

End the session:

In[23]:=
DeleteObject[session]

Use the resource function CreatePythonVirtualEnvironment to ensure that packages installed by PythonEvaluateWithDependencies will not interfere with your working Python system:

In[24]:=
id = ResourceFunction["CreatePythonVirtualEnvironment"][
  "pythonPackageSetX"]
Out[24]=
In[25]:=
session = StartExternalSession[{"Python", "Target" -> ResourceFunction["FindPythonExecutable"][id]}]
Out[25]=
In[26]:=
ResourceFunction[
 "PythonEvaluateWithDependencies"][session, "import requests\nprint('done')"]

The "requests" package is installed in your new virtual environment:

In[27]:=
ResourceFunction["PythonPackageList"][id]
Out[27]=

Typically, environments are used in multiple Wolfram Language sessions and deleted afterwards:

In[28]:=
ResourceFunction[
  "DeletePythonVirtualEnvironment"]["pythonPackageSetX"]
Out[28]=
In[29]:=
DeleteObject[session]

Properties and Relations (2) 

The resource function PythonPackageInstall can be used instead of PythonEvaluateWithDependencies when the package requirements are known beforehand:

In[30]:=
code = "from camelcase import CamelCase as c
c().hump('hello world')";
In[31]:=
ResourceFunction["PythonPackageInstall"]["camelcase"]
Out[31]=
In[32]:=
ExternalEvaluate["Python", code]
Out[32]=

Uninstall the package:

In[33]:=
ResourceFunction["PythonPackageUninstall"]["camelcase"]
Out[33]=

Possible Issues (1) 

PythonEvaluateWithDependencies fails if a missing Python package cannot be installed:

In[34]:=
ResourceFunction[
 "PythonEvaluateWithDependencies"]["Python", "import foo"]
Out[34]=

Version History

  • 1.0.1 – 25 January 2022

Related Resources

Author Notes

In the option "NameSubstitutions", the only currently supported syntax for specifying the version is "name==version". To be compatible with PythonPackageList, we should eventually support a rule "name""version" and a list and association of rules.

License Information