Python Expressions
==================

Katana uses Python to evaluate its expressions. Code that would evaluate as an
expression within Python can be used as an expression within Katana. The
following page lists functions and variables that are specific to the
expression editor and also lists which modules are imported. This is not meant
to be an introduction to Python but a quick reference for those wishing to
leverage some of the expression specific functions and variables.

Available Modules
-----------------

A few python modules are available for use in parameter expressions:

=====================  ================================  ==================================================
Module                 Namespace                         Example
=====================  ================================  ==================================================
:mod:`re`              ``import re``                     ``re.sub(r'x', ' by ', '2048x2048')``
:mod:`array`           ``import array``                  ``array.array('u', "efficient").buffer_info()[1]``
:mod:`os.path`         ``from os import path``           ``path.exists(getenv("HOME", "") + "/shaders")``
:mod:`math`            ``from math import *``            ``cos(pi/3)``
:mod:`ExpressionMath`  ``from ExpressionMath import *``  ``randval(0, 1, frame)``
=====================  ================================  ==================================================


Parameters
----------


.. py:function:: getNode(nodeName)

    Returns a *parameter reference* for the given node's top-level
    parameter. For example::

        >>> getNode("Transform3D").translate.i0
        150

    .. note::

        When *nodeName* is a string literal, the expression will automatically
        be updated if the referenced node is renamed.


.. py:function:: getParam(nodeNameAndParameterPath)

    Returns a *parameter reference* for the given node/parameter path.
    For example::

        >>> getParam("Transform3D.translate.i0")
        150

    .. note::

        When *nodeNameAndParameterPath* is a string literal, the expression
        will automatically be updated if the referenced node is renamed.

.. py:function:: getParamRelative(parameterReference, relativePath)

    Returns a *parameter reference* for the parameter related to
    *parameterReference* by *relativePath*.

    *relativePath* should be a slash-delimited parameter path, where each
    component is one of:

    - ``.`` (the current parameter)
    - ``..`` (the parent parameter)
    - A parameter name

    For example::

        >>> getParamRelative(getParam("Transform3D.translate.i0"), "../i1")
        150

.. py:data:: self

    Evaluates to a *parameter reference* for the current parameter. For
    example::

        >>> self.getParent().user.foo
        "bar"

Parameter References
''''''''''''''''''''

.. py:data:: <path.to.param>

    Evaluates to a *parameter reference* for parameter at the given path.
    For example::

        >>> translate.i0
        150

    Child parameters references can be retrieved by
    accessing attributes, for example the expression ``transform.translate.i0``
    is equivalent to ``((transform).translate).i0``.

    If a parameter expression evaluates to a parameter reference, the actual
    value of the parameter is retrieved by calling ``str()`` or ``float()``,
    depending on the type of the expressioned parameter

.. py:method:: <path.to.param>.getNode()

    Returns the node instance that owns the referenced parameter.

.. py:method:: <path.to.param>.getParent()

    Returns a *parameter reference* for the top-level parameter of
    the parent of the node that owns the referenced parameter.

.. py:method:: <path.to.param>.getNodeName()

    Returns the name of the node that owns the referenced parameter

.. py:method:: <path.to.param>.getChildren()

    Returns a list of child parameters of the referenced parameter

.. py:method:: <path.to.param>.isNumber()

    Returns a boolean indicating whether the referenced parameter is a
    number

Project Settings
''''''''''''''''

Project settings are stored as parameters on the *root node* (the top level of the node graph) and shown in the
:kat:ui:`Project Settings` tab. They can be accessed similarly to parameters on any other node. The ``project`` object
contains additional attributes without corresponding parameters; these are described below.

.. py:data:: project

    Evaluates to a *parameter reference* describing values in the
    **Project Settings** tab. For example::

        >>> project.inTime
        1001
        >>> project.outTime
        1500
        >>> project.width
        1920
        >>> project.height
        1080
        >>> project.assetID
        "/path/to/project.katana"

.. py:data:: project.<path.to.param>

    Evaluates to a *parameter reference* for a parameter on the
    root node, as shown in the :kat:ui:`Project Settings` tab.

.. py:data:: project.width

    Evaluates to the width of the currently selected resolution.

.. py:data:: project.height

    Evaluates to the height of the currently selected resolution.

.. py:data:: project.file

    Evaluates to the filesystem path of current project.

.. py:data:: project.dir

    Evaluates to the filesystem path of the directory that contains the
    current project.

.. py:data:: project.assetID

    Evaluates to the name of the file or the ID of the asset that the
    current project was loaded from.

.. py:data:: project.originalAssetID

    Evaluates to the name of the original file or the ID of the original
    asset that the current project was last saved as before loading.

Resolving Assets
----------------

.. py:function:: assetIdForScope(assetId, scope)

    Returns a partial asset ID scoped to the specified level in the asset
    hierarchy.

    *scope* should be one of:

    - ``kAssetFieldName``
    - ``kAssetFieldVersion``

    Under the hood, this function calls the current asset plug-in's
    ``getAssetIdForScope()`` method.

.. py:function:: assetAttr(assetId, attrName, default=None)

    Returns metadata associated with an asset.

    Under the hood, this function calls the current asset plug-in's
    ``getAssetAttributes()`` method.

.. py:function:: resolveAsset(assetId, default=None)

    Returns the resolved asset string for the given asset ID.

    Under the hood, this function calls the current asset plug-in's
    ``resolveAllAssets()`` method.

.. py:function:: resolvePath(path, frame, default=None)

    Returns the resolved asset string for the given asset ID, with environment
    variables and file sequences resolved.

    Under the hood, this function calls the current asset plug-in's
    ``resolvePath()`` method.


Renderers
---------

.. py:data:: renderers

    List of names of available renderers.

    .. versionadded:: 3.0v1


Resolutions
-----------

.. py:function:: getres(resolutionName)

    Returns a (width, height) tuple for the resolution named *resolutionName*::

        >>> str(getres('hd'))
        (1920, 1080)

.. py:function:: getresdict(resolutionName)

    Returns a dictionary of information for the resolution named
    *resolutionName*::

        >>> str(getresdict('hd'))
        "{'name': 'HD', 'yres': 1080, 'groupname': 'TV', 'xres': 1920, 'proxyname': '', 'aspectRatio': 1.7777777910232544, 'fullname': 'Miscellaneous'}"


Directories
-----------

.. py:data:: tmpDir

    The path to the operating system's temporary directory

    .. versionadded:: 2.5v1

.. py:data:: katanaTmpDir

    The path to Katana's temporary directory

    .. versionadded:: 2.5v1

.. py:data:: katanaRootDir

    The path to Katana's installation directory

    .. versionadded:: 2.5v1


Node-specific Functions
-----------------------

.. py:function:: scenegraphLocationFromNode(node)

    Applicability: 3D nodes that create locations

    Returns the scene graph location created by the given node object *node*::

        >>> scenegraphLocationFromNode(getNode('PrimitiveCreate'))
        "/root/world/geo/primitive"

.. py:function:: getRenderLocation(node, renderPass)

    Applicability: *Render*, *ImageWrite*

    Returns the file asset path created by the given *node* for the render pass
    *renderPass*::

        >>> getRenderLocation(getNode('shadow_key'), 'primary')
        "/tmp/shadow_key.exr"

.. py:function:: getDisplayWindow(port=0, node=None, time=None)

    Applicability: 2D nodes

    Returns an object representing the display window at the given node, port
    and time. This object has the following attributes:

    - ``x0``
    - ``y0``
    - ``x1``
    - ``y1``
    - ``width``
    - ``height``

    If not given, *port* defaults to the first input port on the node. If the
    port is not found amongst the input ports, it will be looked up amongst the
    output ports.

    If not given, *node* defaults to the current node.

    If not given, *time* defaults to the current time.

.. py:function:: getXform(node=None, time=None)

    Applicability: 2D nodes

    Returns a :class:`Imath.M44f` object representing the 2D transformation at
    the given node and time.

    If not given, *node* defaults to the current node.

    If not given, *time* defaults to the current time.

.. py:function:: getXformR(node=None, time=None)

    Applicability: 2D nodes

    Returns an integer representing the 2D rotation at the given node and time.

    If not given, *node* defaults to the current node.

    If not given, *time* defaults to the current time.

.. py:function:: getXformS(node=None, time=None)

    Applicability: 2D nodes

    Returns a :class:`Image.Vec3` object representing to 2D scale at the given
    node and time

    If not given, *node* defaults to the current node.

    If not given, *time* defaults to the current time.

.. py:function:: getXformT(node=None, time=None)

    Applicability: 2D nodes

    Returns a :class:`Image.Vec3` object representing to 2D translation at the
    given node and time

    If not given, *node* defaults to the current node.

    If not given, *time* defaults to the current time.


Graph State
-----------

.. py:function:: getVar(varName)

    Gets the value of the named Graph State Variable in the Local Graph State, where possible.

    If the evaluation context does not provide Local Graph State, the Global Graph State is queried. For example, this
    is the case when ``getVar()`` is evaluated in the context of the UI, or when a Parameter is evaluated via
    ``Parameter.getValue()`` in a user script. See
    `NodegraphAPI.StackedLocalGraphState <../Scripting/WorkingWithProjects.html#NodegraphAPI.StackedLocalGraphState>`_.

    :type varName: ``str``
    :rtype: ``str``, ``int``, or ``float``
    :param varName: The name of the Graph State Variable.
    :return: The value of the named Graph State Variable.
    :raises TypeError: If the type of ``varName`` is not ``str``.
    :raises Exception: If the name of the variable cannot be found.
    :since: Katana 4.5v1

.. py:function:: getFrameTime()

    Gets the frame time in the Local Graph State, where possible.

    If the evaluation context does not provide Local Graph State, the Global Graph State is queried. For example, this
    is the case when ``getVar()`` is evaluated in the context of the UI, or when a Parameter is evaluated via
    ``Parameter.getValue()`` in a user script. See
    `NodegraphAPI.StackedLocalGraphState <../Scripting/WorkingWithProjects.html#NodegraphAPI.StackedLocalGraphState>`_.

    :rtype: ``float``
    :return: The Graph State Frame Time.
    :since: Katana 4.5v1

.. py:function:: matchGraphStateVariable(variableName, pattern, matchMethod)

    Evaluates whether the value of the named Graph State Variable in the Local Graph State matches the given ``pattern``
    using the specified ``matchMethod``.

    If the evaluation context does not provide Local Graph State, the Global Graph State is queried. For example, this
    is the case when ``matchGraphStateVariable()`` is evaluated in the context of the UI, or when a Parameter is
    evaluated via ``Parameter.getValue()`` in a user script. See
    `NodegraphAPI.StackedLocalGraphState <../Scripting/WorkingWithProjects.html#NodegraphAPI.StackedLocalGraphState>`_.

    :type variableName: ``str``
    :type pattern: ``str``
    :type matchMethod: ``str``
    :rtype: ``bool``
    :param variableName: The name of the Graph State Variable.
    :param pattern: The pattern to be matched. Typically a CEL statement.
    :param matchMethod: The match method. Currently only ``'CEL'`` is supported.
    :return: Whether the value of the Graph State Variable was matched.
    :raises ValueError: If ``matchMethod`` is not ``'CEL'``.
    :since: Katana 4.5v1

.. py:function:: matchGlobalGraphStateVariable(variableName, pattern, matchMethod)

    Evaluates whether the value of the named Graph State Variable matches the given ``pattern`` using the specified
    ``matchMethod``.

    :type variableName: ``str``
    :type pattern: ``str``
    :type matchMethod: ``str``
    :rtype: ``bool``
    :param variableName: The name of the Graph State Variable.
    :param pattern: The pattern to be matched. Typically a CEL statement.
    :param matchMethod: The match method. Currently only ``'CEL'`` is supported.
    :return: Whether the value of the Graph State Variable was matched.
    :raises ValueError: If ``matchMethod`` is not ``'CEL'``.


Miscellaneous
-------------

.. py:data:: sampleTime

  |sparkles| **New in Katana 4.5v1**

  The floating point frame time at which the parameter is being evaluated: the ``time`` argument given to
  ``Parameter.getValue()``.

.. py:data:: frame

  .. warning:: |warning| **Deprecated since Katana 4.5v1**: Use :py:data:`sampleTime`.

  The floating point frame time at which the parameter is being evaluated: the ``time`` argument given to
  ``Parameter.getValue()``.

.. py:data:: nodeName

    The name of the node that the parameter is a part of.

.. py:data:: katanaRelease

    The Katana release identifier *with* the maintenance version, e.g.
    ``3.0v1``.

.. py:data:: katanaVersion

    The internal Katana version number, e.g. ``3.0.1.000002``, which is stored
    in the node graph document of a Katana project file, in order to determine
    which upgrade scripts to run when loading the Katana project in a future
    release.

.. py:data:: katanaBranch

    The Katana release identifier *without* the maintenance version, e.g.
    ``3.0``.

.. py:data:: isLinux

    A boolean flag that is ``True`` on Linux and ``False`` otherwise.

    .. versionadded:: 2.5v1

.. py:data:: isWindows

    A boolean flag that is ``True`` on Windows and ``False`` otherwise.

    .. versionadded:: 2.5v1

.. py:function:: fcurve(fileName, curveName, frameNum)

    Returns the value stored within the FCurve file *fileName* from the curve
    *curveName* for the given frame *frameNum*::

        >>> fcurve("/tmp/fcurve.xml", "lgt_spot.shaders.lightParams.intensity.value", frame)
        0.1

    The FCurve file should be an XML file such as those generated by the menu
    option **Export FCurve...** which is obtained by right-clicking on a float
    parameter within the **Parameters** tab.

.. py:function:: getenv(envVarName, defaultValue)

    Returns the value of the environment variable *envVarName* or if not found
    the default *defaultValue*::

        >>> getenv("HOME", "/tmp")
        '/home/rms'

.. py:function:: getExrAttr(fileName, attrHeader, frame, default=None)

    Returns the string value of the attribute *attrHeader* within the file
    *fileName* for the given frame *frame*, or *default* if the attribute is
    not found::

        >>> getExrAttr('/tmp/beauty.exr', 'compression', frame)
        'zips'

.. py:function:: isLookFileBaking()

    Returns ``True`` if the expression is being evaluated during the baking of
    a Look File, otherwise ``False``.

    Equivalent to :py:obj:`Nodes3DAPI.LookFileBaking.GetGlobalBakeState`.


.. |sparkles| unicode:: U+2728
.. |warning| replace:: ⚠️