herosdevices.helper
===================

.. py:module:: herosdevices.helper

.. autoapi-nested-parse::

   Helper functions for writing hardware drivers.









Module Contents
---------------

.. py:data:: SPAM
   :value: 5


.. py:class:: Logger(name, level=NOTSET)

   Bases: :py:obj:`logging.Logger`


   Extend logger to include a spam level for debugging device communication.


   .. py:method:: setLevel(level: str | int, globally: bool = False) -> None

      Set the logging level of this logger.  level must be an int or a str.



   .. py:method:: spam(msg: str, *args, **kwargs) -> None

      Log a message with severity SPAM, even lower than DEBUG.



.. py:data:: format_str
   :value: '%(asctime)-15s %(name)s: %(message)s'


.. py:data:: log

.. py:data:: SI_PREFIX_EXP

.. py:function:: limits(lower: float, upper: float) -> collections.abc.Callable[[float], str | bool]

   Create a function which checks if a value is within the specified range.

   :param lower: The lower bound of the valid range.
   :param upper: The upper bound of the valid range.

   :returns: A function that takes a value and returns True if within the range, or a message
             indicating it's out of range.


.. py:function:: limits_int(lower: int, upper: int) -> collections.abc.Callable[[int], str | bool]

   Create a function to check if a value is within a specified range and is an integer.

   :param lower: The lower bound of the valid range.
   :param upper: The upper bound of the valid range.

   :returns: A function that takes a value and returns True if within the range and is an integer,
             or a message indicating why it's invalid.


.. py:function:: explicit(values: list[Any]) -> collections.abc.Callable[[Any], str | bool]

   Create a function to check if a value is in a list of allowed values.

   :param values: A list of allowed values.

   :returns: A function that takes a value and returns True if within the list, or a message
             indicating it's not in the list.


.. py:function:: extract_regex(pattern: str) -> collections.abc.Callable[[str], str]

   Create a function to extract a value from a string via regex pattern matching.

   :param regex: regex pattern string.

   :returns: A function that takes a string and returns the first match group.


.. py:function:: sanitize_member_name(name: str) -> str

   Convert an arbitrary string into a valid Python identifier.

   The function replaces characters that are not allowed in Python
   variable names with underscores, ensures the identifier does not
   start with a digit, and avoids Python reserved keywords.

   :param name: Input string to sanitize.

   :returns: A valid Python identifier derived from the input string.


.. py:function:: transform_unit(in_unit: str, out_unit: str) -> collections.abc.Callable[[float, bool], float]

   Create a function to transform a value from one unit to another using SI prefixes.

   :param in_unit: The input unit (e.g., 'k' for kilo, 'm' for milli). Use 'base' for no prefix.
   :param out_unit: The output unit (e.g., 'k' for kilo, 'm' for milli). Use 'base' for no prefix.

   :returns: A function that transforms a given value from the input unit to the output unit,
             optionally allowing reverse transformation (second argument True).


.. py:function:: merge_dicts(dict1: dict, dict2: dict) -> dict

   Recursively merge two dicts of dicts.


.. py:function:: get_or_create_dynamic_subclass(base_cls: Any, *args: Any, **kwargs: Any) -> type

   Return a cached dynamic subclass of ``base_cls`` based on the input arguments.

   This helper generates a subclass of ``base_cls`` whose identity is determined by ``*args`` and ``**kwargs``.
   The argument signature is serialized into a hash, which is then used as both a cache key and the dynamic subclass
   name. If the subclass for a given argument combination already exists, it is returned from cache.

   The generated subclass replaces ``__new__`` with a dummy implementation to prevent recursive invocation of
   ``base_cls.__new__``.

   :param base_cls: The base class to derive from. Must be passed positionally.
   :param \*args: Positional values that should influence subclass identity.
   :param \*\*kwargs: Keyword values that should influence subclass identity.

   :returns: A dynamically generated subclass of ``base_cls``.

   :raises TypeError: If arguments cannot be serialized for hashing.


.. py:function:: add_class_descriptor(cls: type, attr_name: str, descriptor) -> None

   Add a descriptor to a class.

   This is a simple helper function which uses `setattr` to add an attribute to the class and then also calls
   `__set_name__` on the attribute.

   :param cls: Class to add the descriptor to
   :param attr_name: Name of the attribute the descriptor will be added to
   :param descriptor: The descriptor to be added


.. py:function:: mark_driver(name: str | None = None, info: str | None = None, state: str = 'unknown', additional_docs: list | None = None, requires: dict | None = None, product_page: str | None = None) -> collections.abc.Callable

   Mark a class as a driver.

   This decorator can be used to mark a class as a driver and attach meta data to it, which is then accessed by the
   sphinx documentation. All drivers marked with this decorator will be listed on the "Hardware" page. Wraps the
   ``__init__`` function of the decorated class to check if all required packages are installed.

   :param state: State of the driver, can be "alpha" for very untested code "beta" for tested but under active development
                 or "stable" for well tested and stable drivers.
   :param name: Name of the represented hardware as it should appear in the doc.
   :param info: Small info line which is shown as a subtitle in the doc.
   :param additional_docs: List of additional ``.rst`` files that are added to the documentation. For example to document
                           complicated vendor library installation procedures.
   :param requires: List of additional packages that are required to use the driver, given in the form of a dictionary
                    with the package name used in an import statement as key and a PyPi package name or url to the package as
                    value. The import name is used to check if the required package is available.
   :param product_page: URL to the vendor product page


.. py:function:: cast_iterable(it: collections.abc.Iterable, target_type: Any) -> list

   Convert an iterable to a list of specified target type.

   :param it: An iterable object containing elements to be converted.
   :param target_type: The type to which each element in the iterable should be converted.

   :returns: A list containing elements from the input iterable converted to the target type.

   .. rubric:: Example

   >>> cast_iterable([1, 2, 3], str)
   ['1', '2', '3']
   >>> cast_iterable(['1', '2', '3'], int)
   [1, 2, 3]


