.. _ogn_tutorial_simpleDataPy:


Tutorial 10 - Simple Data Node in Python
========================================

The simple data node creates one input attribute and one output attribute of each of the simple types, where "simple"
refers to data types that have a single component and are not arrays. (e.g. "float" is simple, "float[3]" is not, nor is
"float[]"). See also :ref:`ogn_tutorial_simpleData` for a similar example in C++.

Automatic Python Node Registration
----------------------------------
By implementing the standard Carbonite extension interfact in Python, OmniGraph will know to scan your Python import
path for to recursively scan the directory, import all Python node files it finds, and register those nodes.
It will also deregister those nodes when the extension shuts down. Here is an example of the directory structure for
an extension with a single node in it. (For extensions that have a `premake5.lua` build script this will be in the
build directory. For standalone extensions it is in your source directory.)

.. code-block:: text

    omni.my.extension/
        omni/
            my/
                extension/
                    nodes/
                        OgnMyNode.ogn
                        OgnMyNode.py



OgnTutorialSimpleDataPy.ogn
---------------------------
The *ogn* file shows the implementation of a node named "omni.graph.tutorials.SimpleDataPy", which has one
input and one output attribute of each simple type.

.. literalinclude:: ../../../../../source/extensions/omni.graph.tutorials/tutorials/tutorial10/OgnTutorialSimpleDataPy.ogn
    :linenos:
    :language: json

OgnTutorialSimpleDataPy.py
--------------------------
The *py* file contains the implementation of the compute method, which modifies
each of the inputs in a simple way to create outputs that have different values.

.. literalinclude:: ../../../../../source/extensions/omni.graph.tutorials/tutorials/tutorial10/OgnTutorialSimpleDataPy.py
    :linenos:
    :language: python

Note how the attribute values are available through the ``OgnTutorialSimpleDataPyDatabase`` class. The generated
interface creates access methods for every attribute, named for the attribute itself. They are all implemented as Python
properties, where inputs only have get methods and outputs have both get and set methods.

Pythonic Attribute Data
-----------------------
Three subsections are creating in the generated database class. The main section implements the node type ABI methods
and uses introspection on your node class to call any versions of the ABI methods you have defined (see later
tutorials for examples of how this works).

The other two subsections are classes containing attribute access properties for inputs and outputs. For naming
consistency the class members are called *inputs* and *outputs*. For example, you can access the value of the input
attribute named *foo* by referencing ``db.inputs.foo``.


Pythonic Attribute Access
-------------------------
In the USD file the attribute names are automatically namespaced as *inputs:FOO* or *outputs:BAR*. In the Python
interface the colon is illegal so the contained classes above are used to make use of the dot-separated equivalent,
as *inputs.FOO* or *outputs.BAR*.

While the underlying data types are stored in their exact form there is conversion when they are passed back to Python
as Python has a more limited set of data types, though they all have compatible ranges. For this class, these are the
types the properties provide:

+-------------------+---------------+
| Database Property | Returned Type |
+===================+===============+
| inputs.a_bool     | bool          |
+-------------------+---------------+
| inputs.a_half     | float         |
+-------------------+---------------+
| inputs.a_int      | int           |
+-------------------+---------------+
| inputs.a_int64    | int           |
+-------------------+---------------+
| inputs.a_float    | float         |
+-------------------+---------------+
| inputs.a_double   | float         |
+-------------------+---------------+
| inputs.a_token    | str           |
+-------------------+---------------+
| outputs.a_bool    | bool          |
+-------------------+---------------+
| outputs.a_half    | float         |
+-------------------+---------------+
| outputs.a_int     | int           |
+-------------------+---------------+
| outputs.a_int64   | int           |
+-------------------+---------------+
| outputs.a_float   | float         |
+-------------------+---------------+
| outputs.a_double  | float         |
+-------------------+---------------+
| outputs.a_token   | str           |
+-------------------+---------------+

The data returned are all references to the real data in the Fabric, our managed memory store, pointed to the
correct location at evaluation time.

Python Helpers
--------------
A few helpers are provided in the database class definition to help make coding with it more natural.

Python logging
++++++++++++++
Two helper functions are providing in the database class to help provide more information when the compute method of
a node has failed. Two methods are provided, both taking a formatted string describing the problem.

:py:meth:`log_error(message)<omni.graph.core.Database.log_error>` is used when the compute has run into some
inconsistent or unexpected data, such as two input arrays that are supposed to have the same size but do not,
like the normals and vertexes on a mesh.

:py:meth:`log_warning(message)<omni.graph.core.Database.log_warning>` can be used when the compute has hit an unusual
case but can still provide a consistent output for it, for example the deformation of an empty mesh would result in an
empty mesh and a warning since that is not a typical use for the node.


Direct Pythonic ABI Access
++++++++++++++++++++++++++
All of the generated database classes provide access to the underlying *INodeType* ABI for those rare situations
where you want to access the ABI directly. There are two members provided, which correspond to the objects passed
in to the ABI compute method.

There is the graph evaluation context member, :py:attr:`db.abi_context<omni.graph.core.Database.abi_context>`,
for accessing the underlying OmniGraph evaluation context and its interface.

There is also the OmniGraph node member, :py:attr:`db.abi_node<omni.graph.core.Database.abi_node>`, for accessing
the underlying OmniGraph node object and its interface.
