Example #1
0
    def _determine_clashing_namespaces(self, py_namespaces_by_target):
        """Check for any overlapping namespaces."""
        namespaces_by_files = defaultdict(list)
        for target, all_namespaces in py_namespaces_by_target.items():
            for (path, namespace) in all_namespaces:
                namespaces_by_files[namespace].append((target, path))

        clashing_namespaces = {
            namespace: all_paths
            for namespace, all_paths in namespaces_by_files.items()
            if len(all_paths) > 1
        }
        if clashing_namespaces:
            pretty_printed_clashing = '\n'.join(
                '{}: [{}]'.format(
                    namespace, ', '.join(
                        '({}, {})'.format(t.address.spec, path)
                        for (t, path) in all_paths))
                for namespace, all_paths in clashing_namespaces.items())
            error = self.ClashingNamespaceError("""\
Clashing namespaces for python thrift library sources detected in build graph. This will silently
overwrite previously generated python sources with generated sources from thrift files declaring the
same python namespace. This is an upstream WONTFIX in thrift, see:
      https://issues.apache.org/jira/browse/THRIFT-515
Errors:
{}
""".format(pretty_printed_clashing))
            if self.get_options().strict:
                raise error
            else:
                self.context.log.warn(str(error))
        return namespaces_by_files
    def _extract_all_python_namespaces(self, thrift_file_sources_by_target):
        """Extract the python namespace from each thrift source file."""
        py_namespaces_by_target = OrderedDict()
        failing_py_thrift_by_target = defaultdict(list)
        for t, all_content in thrift_file_sources_by_target.items():
            py_namespaces_by_target[t] = []
            for (path, content) in all_content:
                try:
                    py_namespaces_by_target[t].append(
                        # File content is provided as a binary string, so we have to decode it.
                        (path,
                         self._extract_py_namespace_from_content(
                             t, path, content.decode('utf-8'))))
                except self.NamespaceParseFailure:
                    failing_py_thrift_by_target[t].append(path)

        if failing_py_thrift_by_target:
            # We dump the output to a file here because the output can be very long in some repos.
            no_py_namespace_output_file = os.path.join(
                self.workdir, 'no-python-namespace-output.txt')

            pretty_printed_failures = '\n'.join(
                '{}: [{}]'.format(t.address.spec, ', '.join(paths))
                for t, paths in failing_py_thrift_by_target.items())
            error = self.NamespaceExtractionError(
                no_py_namespace_output_file, """\
Python namespaces could not be extracted from some thrift sources. Declaring a `namespace py` in
thrift sources for python thrift library targets will soon become required.

{} python library target(s) contained thrift sources not declaring a python namespace. The targets
and/or files which need to be edited will be dumped to: {}
""".format(len(failing_py_thrift_by_target), no_py_namespace_output_file))

            safe_file_dump(no_py_namespace_output_file,
                           '{}\n'.format(pretty_printed_failures),
                           mode='w')

            if self.get_options().strict:
                raise error
            else:
                self.context.log.warn(error)
        return py_namespaces_by_target