Source code for netdumplings.dumpling

from enum import Enum
import json
import time
from typing import Any, Optional, Union

from .dumplingchef import DumplingChef
from .exceptions import InvalidDumpling, InvalidDumplingPayload


[docs]class DumplingDriver(Enum): """ The event driving the creation of a :class:`Dumpling`. ``DumplingDriver.packet`` a network packet being received by a :class:`DumplingChef`. ``DumplingDriver.interval`` a regular time interval poke being received by a :class:`DumplingChef`. """ packet = 1 interval = 2
[docs]class Dumpling: """ Represents a single Dumpling. Dumplings are usually created automatically by a dumpling kitchen from the payload returned by a :class:`~DumplingChef`. A Dumpling exposes the following attributes: * ``chef`` - the :class:`DumplingChef` which is responsible for the dumpling payload * ``chef_name`` - the name of the DumplingChef * ``kitchen`` - the name of the kitchen which created the Dumpling * ``driver`` - the :class:`DumplingDriver` for the dumpling * ``creation_time`` - when the dumpling was created (epoch milliseconds) * ``payload`` - the dumpling payload :param chef: The chef which created the dumpling payload (usually a :class:`DumplingChef` instance, but can be a string). :param driver: The event type that drove the dumpling to be created. :param creation_time: Dumpling creation time (epoch milliseconds). Defaults to current time. :param payload: The dumpling payload information. Can be anything (usually a dict) which is JSON-serializable. """ def __init__( self, *, chef: Union[DumplingChef, str], driver: DumplingDriver = DumplingDriver.packet, creation_time: Optional[float] = None, payload: Any) -> None: self.chef = chef try: self.chef_name = chef.name self.kitchen = chef.kitchen except AttributeError: self.chef_name = chef self.kitchen = None self.driver = driver self.creation_time = ( time.time() if creation_time is None else creation_time ) self.payload = payload def __repr__(self): if self.driver == DumplingDriver.packet: driver = 'DumplingDriver.packet' elif self.driver == DumplingDriver.interval: driver = 'DumplingDriver.interval' else: driver = repr(self.driver) payload = (None if self.payload is None else '<{}>'.format(type(self.payload).__name__)) return ( '{}(' 'chef={}, ' 'driver={}, ' 'creation_time={}, ' 'payload={})'.format( type(self).__name__, repr(self.chef), driver, self.creation_time, payload, ) )
[docs] @classmethod def from_json(cls, json_dumpling: str): """ A Dumpling factory which creates a Dumpling from a given ``json_dumpling`` string. The given ``json_dumpling`` is expected to be a dumpling which has already been JSON-serialized (presumably by a dumpling kitchen). :param json_dumpling: JSON string to create the Dumpling from. :return: A :class:`Dumpling` instance. :raise: :class:`InvalidDumpling` if ``json_dumpling`` could not be successfully converted into a Dumpling. """ try: dumpling_dict = json.loads(json_dumpling) except (json.decoder.JSONDecodeError, TypeError, ValueError) as e: raise InvalidDumpling( 'Could not interpret dumpling JSON: {}'.format(e) ) metadata = dumpling_dict['metadata'] try: if metadata['driver'] == 'packet': driver = DumplingDriver.packet elif metadata['driver'] == 'interval': driver = DumplingDriver.interval else: raise InvalidDumpling( "Dumpling driver was not 'packet' or 'interval'" ) dumpling = cls( chef=metadata['chef'], driver=driver, creation_time=metadata['creation_time'], payload=dumpling_dict['payload'], ) dumpling.kitchen = metadata['kitchen'] return dumpling except KeyError as e: raise InvalidDumpling( 'Dumpling JSON was missing key: {}'.format(e) )
[docs] def to_json(self) -> str: """ Creates a JSON-serialized dumpling string from the Dumpling. :return: A JSON string representation of the Dumpling. :raise: :class:`InvalidDumplingPayload` if the Dumpling payload cannot be JSON-serialized. """ dumpling = { 'metadata': { 'chef': self.chef_name, 'kitchen': self.kitchen.name if self.kitchen else None, 'creation_time': self.creation_time, 'driver': self.driver.name, }, 'payload': self.payload, } try: dumpling_serialized = json.dumps(dumpling) except TypeError as e: raise InvalidDumplingPayload( 'Could not create dumpling: {}'.format(e) ) return dumpling_serialized