pycyphal.dsdl package
Module contents
This module is used for automatic generation of Python classes from DSDL type definitions and also for various manipulations on them. Auto-generated classes have a high-level application-facing API and built-in auto-generated serialization and deserialization routines.
The serialization code heavily relies on NumPy and the data alignment analysis implemented in PyDSDL. Some of the technical details are covered in the following posts:
The main entity of this module is the function compile()
.
- pycyphal.dsdl.compile(root_namespace_directory: None | str | Path = None, lookup_directories: list[str | Path] | None = None, output_directory: None | str | Path = None, allow_unregulated_fixed_port_id: bool = False) GeneratedPackageInfo | None [source]
This function runs the Nunavut transpiler converting a specified DSDL root namespace into a Python package. In the generated package, nested DSDL namespaces are represented as Python subpackages, DSDL types as Python classes, type version numbers as class name suffixes separated via underscores (like
Type_1_0
), constants as class attributes, fields as properties. For a more detailed information on how to use generated types refer to the Nunavut documentation.Generated packages do not automatically import their nested subpackages. For example, if the application needs to use
uavcan.node.Heartbeat.1.0
, it has toimport uavcan.node
explicitly; doing justimport uavcan
is not sufficient.If the source definition contains identifiers, type names, namespace components, or other entities whose names are listed in
nunavut.lang.py.PYTHON_RESERVED_IDENTIFIERS
, the compiler applies substitution by suffixing such entities with an underscore_
. A small subset of applications may require access to a generated entity without knowing in advance whether its name is a reserved identifier or not (i.e., whether it’s prefixed or not). To simplify usage, the generatednunavut_support
module provides functionsget_attribute
andset_attribute
that provide access to the generated class/object attributes using their original names before substitution. Likewise, theget_model
function can find a generated type even if any of its name components are prefixed; e.g., a DSDL typestr.Type.1.0
would be imported asstr_.Type_1_0
.Tip
Production applications should compile their DSDL namespaces as part of the package build process. This can be done by overriding the
build_py
command insetup.py
and invoking this function from there.Tip
Configure your IDE to index the compilation output directory as a source directory to enable code completion. For PyCharm: right click the directory –> “Mark Directory as” –> “Sources Root”.
- Parameters:
root_namespace_directory – The source DSDL root namespace directory path. The last component of the path is the name of the root namespace. For example, to generate package for the root namespace
uavcan
, the path would be likefoo/bar/uavcan
. If set to None, only thenunavut_support
module will be generated.lookup_directories – An iterable of DSDL root namespace directory paths where to search for referred DSDL definitions. The format of each path is the same as for the previous parameter; i.e., the last component of each path is a DSDL root namespace name. If you are generating code for a vendor-specific DSDL root namespace, make sure to provide at least the path to the standard
uavcan
namespace directory here.output_directory – The generated Python package directory will be placed into this directory. If not specified or None, the current working directory is used. For example, if this argument equals
foo/bar
, and the DSDL root namespace name isuavcan
, the top-level__init__.py
of the generated package will end up infoo/bar/uavcan/__init__.py
. The directory tree will be created automatically if it does not exist (likemkdir -p
). If the destination exists, it will be silently written over. Applications that compile DSDL lazily are recommended to shard the output directory by the library version number to avoid compatibility issues with code generated by older versions of the library. Don’t forget to add the output directory toPYTHONPATH
.allow_unregulated_fixed_port_id – If True, the compiler will not reject unregulated data types with fixed port-ID. If you are not sure what it means, do not use it, and read the Cyphal specification first.
- Returns:
An instance of
GeneratedPackageInfo
describing the generated package, unless the root namespace is empty, in which case it’s None.- Raises:
OSError
if required operations on the file system could not be performed;pydsdl.InvalidDefinitionError
if the source DSDL definitions are invalid;pydsdl.InternalError
if there is a bug in the DSDL processing front-end;ValueError
if any of the arguments are otherwise invalid.
The following table is an excerpt from the Cyphal specification. Observe that unregulated fixed port identifiers are prohibited by default, but it can be overridden.
Scope
Regulated
Unregulated
Public
Standard and contributed (e.g., vendor-specific) definitions. Fixed port identifiers are allowed; they are called “regulated port-IDs”.
Definitions distributed separately from the Cyphal specification. Fixed port identifiers are not allowed.
Private
Nonexistent category.
Definitions that are not available to anyone except their authors. Fixed port identifiers are permitted (although not recommended); they are called “unregulated fixed port-IDs”.
- pycyphal.dsdl.compile_all(root_namespace_directories: Iterable[str | Path], output_directory: None | str | Path = None, *, allow_unregulated_fixed_port_id: bool = False) list[GeneratedPackageInfo] [source]
This is a simple convenience wrapper over
compile()
that addresses a very common use case where the application needs to compile multiple inter-dependent namespaces.- Parameters:
root_namespace_directories –
compile()
will be invoked once for each directory in the list, using all of them as look-up dirs for each other. They may be ordered arbitrarily. Directories that contain no DSDL definitions are ignored.output_directory – See
compile()
.allow_unregulated_fixed_port_id – See
compile()
.
- Returns:
A list of of
GeneratedPackageInfo
, one per non-empty root namespace directory.
>>> import sys >>> import pathlib >>> import importlib >>> import pycyphal >>> compiled_dsdl_dir = pathlib.Path(".lazy_compiled", pycyphal.__version__) >>> compiled_dsdl_dir.mkdir(parents=True, exist_ok=True) >>> sys.path.insert(0, str(compiled_dsdl_dir)) >>> try: ... import sirius_cyber_corp ... import uavcan.si.sample.volumetric_flow_rate ... except (ImportError, AttributeError): ... _ = pycyphal.dsdl.compile_all( ... [ ... DEMO_DIR / "custom_data_types/sirius_cyber_corp", ... DEMO_DIR / "public_regulated_data_types/uavcan", ... DEMO_DIR / "public_regulated_data_types/reg/", ... ], ... output_directory=compiled_dsdl_dir, ... ) ... importlib.invalidate_caches() ... import sirius_cyber_corp ... import uavcan.si.sample.volumetric_flow_rate
- class pycyphal.dsdl.GeneratedPackageInfo(path: 'pathlib.Path', models: 'Sequence[pydsdl.CompositeType]', name: 'str')[source]
Bases:
object
- models: Sequence[CompositeType]
List of PyDSDL objects describing the source DSDL definitions. This can be used for arbitrarily complex introspection and reflection.
- __match_args__ = ('path', 'models', 'name')
- pycyphal.dsdl.install_import_hook(lookup_directories: Iterable[str | Path] | None = None, output_directory: None | str | Path = None, allow_unregulated_fixed_port_id: bool | None = None) None [source]
Installs python import hook, which automatically compiles any DSDL if package is not found.
A default import hook is automatically installed when pycyphal is imported. To opt out, set environment variable
PYCYPHAL_NO_IMPORT_HOOK=True
before importing pycyphal.- Parameters:
lookup_directories – List of directories where to look for DSDL sources. If not provided, it is sourced from
CYPHAL_PATH
environment variable.output_directory – Directory to output compiled DSDL packages. If not provided,
PYCYPHAL_PATH
environment variable is used. If that is not available either, a default~/.pycyphal
(or other OS equivalent) directory is used.allow_unregulated_fixed_port_id – If True, the compiler will not reject unregulated data types with fixed port-ID. If not provided, it will be sourced from
CYPHAL_ALLOW_UNREGULATED_FIXED_PORT_ID
variable or default to False.
- pycyphal.dsdl.serialize(obj: Any) Iterable[memoryview] [source]
A wrapper over
nunavut_support.serialize
. Thenunavut_support
module will be generated automatically if it is not importable.
-
pycyphal.dsdl.deserialize(dtype: Type[
pycyphal.dsdl.T
], fragmented_serialized_representation: Sequence[memoryview])pycyphal.dsdl.T
| None [source] A wrapper over
nunavut_support.deserialize
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.get_model(class_or_instance: Any) CompositeType [source]
A wrapper over
nunavut_support.get_model
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.get_class(model: CompositeType) type [source]
A wrapper over
nunavut_support.get_class
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.get_extent_bytes(class_or_instance: Any) int [source]
A wrapper over
nunavut_support.get_extent_bytes
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.get_fixed_port_id(class_or_instance: Any) int | None [source]
A wrapper over
nunavut_support.get_fixed_port_id
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.get_attribute(obj: Any, name: str) Any [source]
A wrapper over
nunavut_support.get_attribute
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.set_attribute(obj: Any, name: str, value: Any) None [source]
A wrapper over
nunavut_support.set_attribute
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.is_serializable(dtype: Any) bool [source]
A wrapper over
nunavut_support.is_serializable
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.is_message_type(dtype: Any) bool [source]
A wrapper over
nunavut_support.is_message_type
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.is_service_type(dtype: Any) bool [source]
A wrapper over
nunavut_support.is_service_type
. Thenunavut_support
module will be generated automatically if it is not importable.
- pycyphal.dsdl.to_builtin(obj: object) Dict[str, Any] [source]
A wrapper over
nunavut_support.to_builtin
. Thenunavut_support
module will be generated automatically if it is not importable.
-
pycyphal.dsdl.update_from_builtin(destination:
pycyphal.dsdl.T
, source: Any)pycyphal.dsdl.T
[source] A wrapper over
nunavut_support.update_from_builtin
. Thenunavut_support
module will be generated automatically if it is not importable.