Source code for pyprotobuf.compiler

from pyprotobuf.commentresolver import CommentResolver
from pyprotobuf.dependencysorter import DependencySorter
from pyprotobuf.extensionsresolver import ExtensionsResolver
from pyprotobuf.nameresolver import NameResolver
from pyprotobuf.optionresolver import OptionResolver
from pyprotobuf.parser import ProtoParser
import copy
import logging
import os
import pyprotobuf.nodes as nodes


logger = logging.getLogger('pyprotobuf.Compiler')


class CompilerInput(object):
    filename = None
    ast = None

    def __init__(self, filename):
        self.filename = filename

    def get_source(self):
        return open(self.filename, 'r').read()


class StringCompilerInput(CompilerInput):
    def __init__(self, filename, data):
        super(StringCompilerInput, self).__init__(filename)
        self.data = data

    def get_source(self):
        return self.data


[docs]class Compiler(object): logger = logging.getLogger('pyprotobuf.Compiler') passes = [ OptionResolver, NameResolver, ExtensionsResolver, DependencySorter, CommentResolver ] def __init__(self): self.parser = ProtoParser() self.import_paths = [os.getcwd(), os.path.join(os.path.dirname(os.path.abspath(__file__)), 'proto')] self.imports = {} self.log_level = logging.INFO self._inputs = [] def set_inputs(self, inputs): """ :type inputs: list(CompilerInput) """ self._inputs = inputs def compile(self): """ Return a RootNode containing all the PackageNode->FileNode :rtype: pyprotobuf.nodes.RootNode """ # self.root = nodes.RootNode() self.traversal_path = [] for input in self._inputs: input.ast = file_node = self._precompile_file(input.get_source(), input.filename) self._add_file_to_package(file_node) for compiler_pass_class in self.passes: compiler_pass = compiler_pass_class(self) compiler_pass.set_log_level(self.log_level) compiler_pass.process(self.root) return self.root def _add_file_to_package(self, filenode): # resolve an add other packages before adding ours self._resolve_imports(filenode) package_name = filenode.package_name package = self.root.get_package(package_name) if not package: package = nodes.Package() package.name = package_name self.root.add_child(package) package.add_child(filenode) def _precompile_file(self, string, filename): file_node = self.parser.parse_string(string) # so the generators know the path file_node.filename = filename return file_node def _resolve_import_path(self, path, file_node=None): """ :param path: Path to resolve :param file_node: File node to resolve relative paths from. """ if path.startswith('/'): return path # search through each of the paths # if specified, starting relative to the directory of the package import_paths = copy.copy(self.import_paths) if file_node is not None: assert isinstance(file_node, nodes.FileNode) import_paths.insert(0, os.path.dirname(file_node.filename)) split_path = path.split(os.pathsep) for import_path in import_paths: test_path = os.path.join(import_path, *split_path) if os.path.exists(test_path): return test_path raise Exception("Could not find import %s in paths %s" % (path, import_paths)) def _resolve_imports(self, file_node): """ :type file_node: pyprotobuf.nodes.FileNode """ assert isinstance(file_node, nodes.FileNode) # find any import nodes for child in file_node.get_children_of_type(nodes.ImportNode): path = self._resolve_import_path(child.value, file_node) if path in self.imports: file_node = self.imports[path] else: file_node = self._import_file(path) self.logger.info('Importing %s from %s: %s %s', path, file_node.filename, file_node, file_node.children) self.imports[path] = file_node # attach the imported FileNode to the ImportNode # XXX: unused child.file_node = file_node if path in self.traversal_path: self.traversal_path.append(path) raise Exception("Circular import %s" % self.traversal_path) # visit other imports before we add ours self.traversal_path.append(path) self._resolve_imports(file_node) self.traversal_path.pop() self._add_file_to_package(file_node) def _import_file(self, filename): """ Return the FileNode node at path. :rtype: pyprotobuf.nodes.FileNode """ if filename in self.traversal_path: raise Exception("Circular import %s from %s" % (filename, self.traversal_path)) self.traversal_path.append(filename) with open(filename, 'r') as f: string = f.read() filenode = self.parser.parse_string(string) filenode.filename = filename self._resolve_imports(filenode) self.traversal_path.pop() return filenode