Source code for nc5ng.types.datapoint

"""Base Datapoint Types

``DataPointType`` Metaclass
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: nc5ng.types.DataPointType
  :members:

"""

import logging



[docs]class DataPointType(type): """ Metaclass for DataPoints, defines class creation and class/hierarchy member variables The meta-class in part, hides some of the more rote requirements of our datapoint type from the actual object hierarchy This allows some magic like run-time casting to the correct datapoint without knowing the type specifically, and a persisitent library-wide memory backed database for quick retrieval and to minimize replication To understand the meta class, there are a number of tutorials available online, roughly speaking this class is what is used to "create" the DataPoint class, and allows us to manipulate the class without needing any implementation details. Defining a new DataPointType Hierarchy simply requires using this class as the metaclass class NewDataPoint(metaclass=DataPointType): pass By creating a new DataPointType, the following changes will be done to the final class - The type will have a database (dictionary) of types registered with the base class - Each type and subtype will have a point container (default set) created - The shorthand name will be generated from the class name - Instance Creation will be overidden and allow creation of any other data type by specifting the type shorthand as an argument Class Configuration: Each new type has some meta-configuration available 1. To override data point registration - Create a `@classmethod` `__register__(cls, point)` to overide how a new point is registered/saved - Create a class member `_point_store` to change the underlying storage type (from set) 2. Override shorthand name by specific '_type_shorthand' explicitly in the class Any class that uses this type as a metaclass will be registered """ @classmethod def __prepare__(metacls, name, bases, **kargs): """ Prepare the new class, here for completeness """ logging.debug("Preparing Class %s"%name) return super().__prepare__(name, bases, **kargs) @property def type_shorthand(cls): """ Get the class shorthand name""" return cls._type_shorthand @property def point_store(cls): """ Return the type-specific Point Buffer""" return cls._point_store @property def point_database(cls): """ Return the Root Database of all DataPoints in this Hierarchy""" return cls._cbdb def __new__(metacls, name, bases, namespace, **kargs): """ Create a new data point type, called on class load Creates class attributes level point set for storage metaclass __new__ is executed on load time for every class that uses it, it is executed after __prepare__ which constructs the class object """ logging.debug("Creating Class %s"%name) cls = super().__new__(metacls, name, bases, namespace) if not(hasattr(cls, '_cbdb')): logging.debug("Creating Data Point Database") cls._cbdb = dict() # Shorthand Name cls._type_shorthand = name.lower() while cls._type_shorthand in cls._cbdb: cls._type_shorthand = cls._type_shorthand + "_" # in case of name conflict, add underscore # Point Storage cls._point_store = namespace.get('_point_store', set() ) logging.debug("Registering new Data Point Type %s with shorthand %s"%(name, cls._type_shorthand)) cls._cbdb[cls.type_shorthand]={'type':cls, 'points': cls._point_store } return cls def __init__(cls, name, bases, namespace): """ Initialize a new FileBacked Class This is a slot method for class creation, __init__ is called when class is defined (load time) \param cls - reference to new type, similiar to @classmethod \param name - new class name \param bases - base classes \param namespace - new class attributes \param Parser - BaseFileParser underlying this type \param **kwargs - keywords passed to Parser initialization """ logging.debug("Creating Data Point Class %s"%name) super().__init__(name, bases, namespace) def __register__(cls, point): """ Register a point with the class buffer """ cls._point_store.add(point) def __call__(cls, *args, **kw): """ Create a new Point in this hierarchy""" """ if typename is not None: if typename not in cls._cbdb: raise TypeError("Invalid Data Point Type with Shorthand %s"%typename) cls = cls._cbdb[typename]['type'] """ if args and (args[0] in cls.point_database.keys()): typename=args[0] elif kw and ('type' in kw): typename = kw['type'] else: typename = cls.type_shorthand if typename == cls.type_shorthand: point = super().__call__(*args, **kw) if not getattr(point, 'ephemeral', False): cls.__register__(point) elif typename in cls.point_database.keys(): point = cls.point_database[typename]['type'].__call__(*args, **kw) else: return None return point