Python types ############ .. _wrappers: Available wrappers ================== All major Python types are available as thin SystemVerilog wrapper classes. These can also be used as function parameters -- see :ref:`python_objects_as_args`. Available types include : :class:`py_bool`, :class:`py_int`, :class:`py_float`, :class:`py_str`, :class:`py_bytes`, :class:`py_tuple`, :class:`py_list`, :class:`py_dict`, :class:`py_none` .. _instantiating_compound_types: Instantiating compound Python types from SystemVerilog ====================================================== Dictionaries can be initialized in the :class:`py_dict` methods using the :func:`create` or :func:`empty` methods: .. code-block:: systemverilog import pystim_pkg::*; ... typedef pystim_pkg::pystim py; ... py_pair p0 = new(py::int_(-3), py::str_("three")); py_pair p1 = new(py::str_("foo"), py::float_(42.3)); py_dict d = py_dict::create({p0, p1}); py_dict empty_d = py_dict::create_empty(); empty_d.put(py::str_("key"), py::int_(42)); .. note:: Dictionary keys must be of a string type. A tuple of python objects can be instantiated using :func:`create` or :func:`empty` methods of :class:`py_tuple`: .. code-block:: systemverilog py_tuple tuple = py_tuple::create({py::int_(15), py::none_(), py::str_("spam")}); py_tuple empty_tup = py_tuple::create_empty(); empty_tup.put(py::int_(42)); A list of python objects can be instantiated using :func:`create` or :func:`empty` Casting back and forth ====================== In this kind of mixed code, it is often necessary to convert base SystemVerilog types to Python wrappers types, which can be done using :func:`pystim_pkg::int_`, :func:`pystim_pkg::float_`, :func:`pystim_pkg::str_`, :func:`pystim_pkg::bytes_`, :func:`pystim_pkg::list_`, :func:`pystim_pkg::tuple_`, :func:`pystim_pkg::dict_`, :func:`pystim_pkg::none_` and similar functions. .. code-block:: systemverilog int my_int = ...; py::py_int obj = py::int_(my_int); The reverse direction uses the following syntax: .. code-block:: systemverilog py_object obj = ...; int my_int = obj.cast_int().get_value(); When conversion fails, both directions fails with the cast error. .. _python_libs: Accessing Python libraries from SystemVerilog ============================================= It is also possible to import objects defined in the Python standard library or available in the current Python environment (``sys.path``) and work with these in SystemVerilog. In this case the SystemVerilog object represents the pointer to the Python object. .. note:: All allocated SystemVerilog python object handlers should be disposed with :func:`delete` method at the end of usage to avoid memory leaks. The SystemVerilog garbage collector does not automatically deallocate CPython objects. This example obtains a reference to the Python ``Decimal`` class. .. code-block:: systemverilog import pystim_pkg::*; // Equivalent to "from decimal import Decimal" py_object Decimal = py_module::import_("decimal").attr("Decimal").obtain(); .. code-block:: systemverilog // Try to import scipy py_object scipy = py_module::import_("scipy"); py_object version = scipy.attr("__version__").obtain(); .. _calling_python_functions: Calling Python functions ======================== It is also possible to call Python functions and methods with :func:`attr()`. .. code-block:: systemverilog // Construct a Python object of class Decimal py_object pi = Decimal.call(py::float_(3.14159)); .. code-block:: systemverilog // Use Python to make our directories py_object os = py_module::import_("os"); py_object makedirs = os.attr("makedirs"); makedirs.call(py::str_("/tmp/path/to/somewhere")); .. _calling_python_methods: Calling Python methods ======================== To call an object's method, one can again use ``.attr`` to obtain access to the Python method. .. code-block:: systemverilog // Calculate e^π in decimal py::object exp_pi = pi.attr("exp").call(); py::print(exp_pi)); In the example above ``pi.attr("exp").call()`` is a *bound method*: it will always call the method for that same instance of the class. Alternately one can create an *unbound method* via the Python class (instead of instance) and pass the ``self`` object explicitly, followed by other arguments. .. code-block:: systemverilog py::object decimal_exp = Decimal.attr("exp"); // Compute the e^n for n=0..4 for (int n = 0; n < 5; n++) { py_object d = Decimal.call(n); py::print(decimal_exp.call(d)); d.delete(); } decimal_exp.delete(); .. _unpack_args: Unpacking arguments =================== Unpacking of ``*args`` and ``**kwargs`` is also possible and can be mixed with other arguments: .. code-block:: python def f(number, message, obj): ... # function code .. code-block:: systemverilog // * unpacking py_tuple args = py::tuple_({py::int_(1234), py::str_("hello"), some_instance}); f.call(.args(args)); // ** unpacking py_dict kwargs = py::dict("number"=1234, "message"="hello", "obj"=some_instance); f.call(.kwargs(kwargs)); // mixed keywords, * and ** unpacking py_tuple args = py::make_tuple("hello"); py_dict kwargs = py::dict("obj"=some_instance); f.call(args, kwargs); .. note:: Generalized unpacking according to PEP448_ not supported. .. seealso:: The file :file:`example/test_pytypes` contains a complete example that demonstrates passing native Python types in more detail. The file :file:`example/test_callbacks` presents a few examples of calling Python functions from SystemVerilog, including keywords arguments and unpacking. .. _PEP448: https://www.python.org/dev/peps/pep-0448/ .. _implicit_casting: Implicit casting ================ When using the SystemVerilog interface for Python types, or calling Python functions, objects of type :class:`py_object` are returned. It is possible to invoke implicit conversions to subclasses like :class:`dict`. The same holds for the proxy objects returned by ``obj.call()`` or ``obj.attr()``. Casting to subtypes improves code readability and allows values to be passed to SystemVerilog functions that require a specific subtype rather than a generic :class:`py_object`. .. code-block:: systemverilog #include using namespace pybind11::literals; py::module_ os = py::module_::import("os"); py::module_ path = py::module_::import("os.path"); // like 'import os.path as path' py::module_ np = py::module_::import("numpy"); // like 'import numpy as np' py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); py::print(py::str("Current directory: ") + curdir_abs); py::dict environ = os.attr("environ"); py::print(environ["HOME"]); py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); py::print(py::repr(arr + py::int_(1))); These implicit conversions are available for subclasses of :class:`py_object`; there is no need to call ``obj.cast()`` explicitly as for custom classes, see :ref:`casting_back_and_forth`. .. _casting_back_and_forth: Casting back and forth ====================== .. note:: This functionality still not implemented in current version of PyStim. .. note:: The following section is only relevant for custom classes. For built-in Python types, see :ref:`implicit_casting`. In this kind of mixed code, it is often necessary to convert arbitrary SystemVerilog types to Python, which can be done using :func:`py::cast`: .. code-block:: cpp MyClass *cls = ...; py::object obj = py::cast(cls); The reverse direction uses the following syntax: .. code-block:: cpp py::object obj = ...; MyClass *cls = obj.cast(); When conversion fails, both directions throw the exception :class:`cast_error`. .. note:: If a trivial conversion via move constructor is not possible, both implicit and explicit casting (calling ``obj.cast()``) will attempt a "rich" conversion. For instance, ``py::list env = os.attr("environ");`` will succeed and is equivalent to the Python code ``env = list(os.environ)`` that produces a list of the dict keys. .. TODO: Adapt text once PR #2349 has landed Handling exceptions =================== Python exceptions from wrapper classes will be thrown as a ``py::py_error``. See :ref:`Handling exceptions from Python in SystemVerilog ` for more information on handling exceptions raised when calling C++ wrapper classes.