Source code for zoonado.protocol.part

from __future__ import unicode_literals

import operator

from .primitives import Primitive


[docs]class Part(object): """ Composable building block used to define Zookeeper protocol parts. Behaves much like the `Primitive` class but has named "sub parts" stored in a ``parts`` class attribute, that can hold any `Part` or `Primitive` subclass. """ parts = () def __init__(self, **kwargs): part_names = set([item[0] for item in self.parts]) for name, value in kwargs.items(): if name not in part_names: raise ValueError("Unknown part name: '%s'" % name) part_names.discard(name) setattr(self, name, value) for name in part_names: setattr(self, name, None)
[docs] def render(self, parts=None): """ Returns a two-element tuple with the ``struct`` format and values. Iterates over the applicable sub-parts and calls `render()` on them, accumulating the format string and values. Optionally takes a subset of parts to render, default behavior is to render all sub-parts belonging to the class. """ if not parts: parts = self.parts fmt = [] data = [] for name, part_class in parts: if issubclass(part_class, Primitive): part = part_class(getattr(self, name, None)) else: part = getattr(self, name, None) part_format, part_data = part.render() fmt.extend(part_format) data.extend(part_data) return "".join(fmt), data
@classmethod
[docs] def parse(cls, buff, offset): """ Given a buffer and offset, returns the parsed value and new offset. Calls `parse()` on the given buffer for each sub-part in order and creates a new instance with the results. """ values = {} for name, part in cls.parts: value, new_offset = part.parse(buff, offset) values[name] = value offset = new_offset return cls(**values), offset
def __eq__(self, other): """ `Part` instances are equal if all of their sub-parts are also equal. """ return all([ getattr(self, part_name) == getattr(other, part_name) for part_name, part_class in self.parts ]) def __ne__(self, other): return not self.__eq__(other) def __str__(self): def subpart_string(part_info): part_name, part_class = part_info if not part_class.__name__.startswith("VectorOf"): value = getattr(self, part_name, None) if value and part_class.__name__ == "Buffer": value = value.decode("utf-8", errors="replace") return "%s=%s" % (part_name, value) return "%s=[%s]" % ( part_name, ", ".join([ str(item) for item in getattr(self, part_name, []) ]) ) return "%s(%s)" % ( self.__class__.__name__, ", ".join([ subpart_string(part) for part in sorted(self.parts, key=operator.itemgetter(0)) ]) )