import pprint
[docs]class ParseNode(object):
"""
"""
name = None
parent = None
children = None
"""
:type: list[pyprotobuf.nodes.ParseNode]
"""
comment = None
""" A comment associated with this node.
:type: :class:`pyprotobuf.nodes.CommentNode`
"""
_options = None
def __init__(self):
self.children = []
self.parent = None
self.name = None
self.type = None
self.comment = None
# dependencies used for topological sorting
self.dependencies = set()
# a map used to store options keys/values
self._options = {}
[docs] def get_file(self):
for parent in self.get_parents():
if isinstance(parent, FileNode):
return parent
[docs] def add_child(self, c):
assert isinstance(c, ParseNode), "Child must be a ParseNode. Got %s" % type(c)
self.children.append(c)
c.parent = self
[docs] def get_children(self):
return self.children
[docs] def get_child(self, index):
return self.children[index]
[docs] def get_full_typename(self):
fqn = [str(self.name) if self.name else '']
parent = self.parent
while parent is not None:
if parent.name:
fqn.append(str(parent.name))
parent = parent.parent
return '.'.join(list(reversed(fqn)))
[docs] def add_dependency(self, dep):
"""
:param dep: The node to add a dependency to.
:type dep: pyprotobuf.nodes.ParseNode
"""
self.dependencies.add(dep)
for parent in self.get_parents():
parent.add_dependency(dep)
[docs] def get_dependencies(self):
"""
:rtype: list[pyprotobuf.nodes.ParseNode]
"""
return self.dependencies
[docs] def get_parents(self):
"""
:rtype: list[pyprotobuf.nodes.ParseNode]
"""
parent = self.parent
while parent is not None:
yield parent
parent = parent.parent
[docs] def get_root(self):
return list(self.get_parents())[-1]
[docs] def get_full_name(self):
assert self.name is not None, ("Name must not be none", self)
fqn = [str(self.name)]
parent = self.parent
while parent is not None:
if parent.name:
fqn.append(str(parent.name))
parent = parent.parent
return '.'.join(list(reversed(fqn)))
[docs] def get_children_of_type(self, node_class):
"""
:type node_class: T
:rtype: list[V <= T]
"""
return filter(lambda x: isinstance(x, node_class), self.children)
def __repr__(self):
return '%s("%s", %r)' % (
self.__class__.__name__, self.name, self.children)#','.join([repr(f) for f in self.children]))
[docs] def resolve_name(self, name):
for child in self.children:
if child.name == name:
return child
return None
[docs] def has_option(self, name):
return name in self._options
[docs] def set_option(self, name, value):
self._options[name] = value
[docs] def get_option(self, *args):
return self._options.get(*args)
[docs] def to_proto(self):
raise NotImplementedError
[docs]class InvalidChildTypeError(Exception):
def __init__(self, child, accepted_types):
super(InvalidChildTypeError, self).__init__("Invalid child %s. Expected one of %s" % (child, accepted_types))
[docs]class RootNode(ParseNode):
def __init__(self):
super(RootNode, self).__init__()
self.packages = {}
[docs] def get_package(self, name):
return self.packages.get(name, None)
[docs] def add_child(self, c):
if not isinstance(c, Package):
raise InvalidChildTypeError(c, [Package])
super(RootNode, self).add_child(c)
def __repr__(self):
return '%s(\n%r)' % (self.__class__.__name__, self.children)
[docs]class FileNode(ParseNode):
filename = None
package_name = None
def __repr__(self):
return 'FileNode(filename="%s")' % self.filename
[docs] def get_imports(self):
return self.get_children_of_type(ImportNode)
[docs]class Package(ParseNode):
name = ''
def __init__(self):
super(Package, self).__init__()
self.file_map = {}
[docs] def add_child(self, c):
assert isinstance(c, FileNode), "Child must be a filenode"
self.file_map[c.filename] = c
super(Package, self).add_child(c)
[docs] def is_named(self):
return self.name is not None
def __repr__(self):
return '%s(%s,\n %s)' % (self.__class__.__name__, self.name, pprint.pformat(self.children))
[docs]class PackageDefinition(ParseNode):
name = None
[docs] def to_proto(self):
return 'package %s;' % self.name
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self.name)
[docs]class ServiceNode(ParseNode):
name = None
[docs] def to_proto(self):
string = []
for child in self.children:
if isinstance(child, MessageNode):
string.append(child.tostr(1))
else:
string.append(str(child))
string = '\n'.join([(' ' + f) for f in string])
return "service %s {\n%s\n%s}" % (self.name, string, ' ')
[docs]class MethodNode(ParseNode):
name = None
request_type = None
response_type = None
def __str__(self):
return "rpc %s (%s) returns (%s);" % (self.name, self.request_type, self.response_type)
[docs]class MessageNode(ParseNode):
name = None
[docs] def tostr(self, depth):
string = []
for child in self.children:
if isinstance(child, MessageNode):
string.append(child.tostr(depth + 1))
else:
string.append(str(child))
string = '\n'.join([(' ' * depth + f) for f in string])
return "message %s {\n%s\n%s}" % (self.name, string, ' ' * (depth - 1))
[docs] def to_proto(self):
return self.tostr(1)
[docs]class FieldDescriptorNode(ParseNode):
"""
Properties:
label: One of "repeated", "optional" or "required".
number: The tag/number/id of the field.
name: The name of the field.
type: Can be a string (Enum of proto types), MessageNode or EnumNode.
"""
label = None
number = None
name = None
type = None
[docs] class LabelType(object):
REPEATED = 'repeated'
OPTIONAL = 'optional'
REQUIRED = 'required'
def __repr__(self):
return 'FieldDescriptorNode(number="%s", name="%s", label="%s", type="%s")' % (self.number, self.name,
self.label, self.type)
[docs] def to_proto(self):
t = self.type
if isinstance(self.type, MessageNode):
t = self.type.name
return '{0} {1} {2} = {3};'.format(self.label, t, self.name, self.number)
[docs]class EnumNode(ParseNode):
name = None
def __repr__(self):
map = {}
for c in self.children:
map[str(c.name)] = str(c.value)
return 'EnumNode("%s", %s)' % (self.name, map)
[docs] def has(self, key):
for c in self.children:
if c.name == key:
return True
return False
[docs] def get(self, key):
for c in self.children:
if c.name == key:
return c
return None
[docs] def to_proto(self):
return 'enum %s { %s }' % (self.name, ' '.join(map(str, self.children)) )
[docs]class EnumAssignmentNode(ParseNode):
name = None
value = None
[docs] def to_proto(self):
return '{0} = {1};'.format(self.name, self.value)
[docs]class OptionNode(ParseNode):
name = None
value = None
[docs] def to_proto(self):
return 'option {0} = {1};'.format(self.name, self.value)
[docs]class ExtendNode(ParseNode):
name = None
def __init__(self):
"""
"""
super(ExtendNode, self).__init__()
@property
def message_node(self):
"""
:rtype: MessageNode
"""
return self._message_node
@message_node.setter
[docs] def message_node(self, v):
"""
:type v: MessageNode
"""
self._message_node = v
[docs] def to_proto(self):
return 'extend %s { %s }' % (self.name, ' '.join(map(str, self.children)) )
[docs]class OptionalNode(ParseNode):
type = None
[docs] def to_proto(self):
return 'optional {0} {1} = {2};'.format(self.name, self.type, self.value)
[docs]class SyntaxNode(ParseNode):
[docs] def to_proto(self):
return 'syntax = "{0}";'.format(self.value)
[docs]class ImportNode(ParseNode):
""" value: path
"""
value = None
# the file node is attached to this in the compiler
file_node = None
public = False
[docs] def to_proto(self):
return 'import "{0}";'.format(self.value)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self.value)
[docs]class ExtensionsNode(ParseNode):
[docs] def to_proto(self):
return 'extension {0} to {1};'.format(self.start, self.end)
[docs]class Types(object):
BOOL = 'bool'
STRING = 'string'
INT32 = 'int32'
INT64 = 'int64'
UINT32 = 'uint32'
UINT64 = 'uint64'
SINT32 = 'sint32'
SINT64 = 'sint64'
FIXED32 = 'fixed32'
FIXED64 = 'fixed64'
SFIXED32 = 'sfixed32'
SFIXED64 = 'sfixed64'
DOUBLE = 'double'
FLOAT = 'float'
BYTES = 'bytes'
ENUM = 'enum'
MESSAGE = 'messsage'
GROUP = 'group'
TYPES = [
Types.BOOL,
Types.STRING,
Types.INT32,
Types.INT64,
Types.UINT32,
Types.UINT64,
Types.SINT32,
Types.SINT64,
Types.FIXED32,
Types.FIXED64,
Types.SFIXED32,
Types.SFIXED64,
Types.DOUBLE,
Types.FLOAT,
Types.BYTES,
Types.ENUM,
Types.MESSAGE,
Types.GROUP
]
NUMERIC_TYPES = [
Types.INT32,
Types.INT64,
Types.UINT32,
Types.UINT64,
Types.SINT32,
Types.SINT64,
Types.FIXED32,
Types.FIXED64,
Types.SFIXED32,
Types.SFIXED64,
Types.DOUBLE,
Types.FLOAT
]