Example #1
0
    def process(self, uris: List[str], package: str):
        """
        Run main processes.

        :param uris: list of uris to process
        :param package: package name eg foo.bar.xxx
        """

        collections.apply(uris, self.process_schema)

        classes = [
            cls for classes in self.class_map.values() for cls in classes
        ]
        class_num, inner_num = self.count_classes(classes)
        if class_num:
            logger.info("Analyzer input: %d main and %d inner classes",
                        class_num, inner_num)
            self.assign_packages(package)

            classes = self.analyze_classes(classes)
            class_num, inner_num = self.count_classes(classes)
            logger.info("Analyzer output: %d main and %d inner classes",
                        class_num, inner_num)

            writer.designate(classes, self.output)
            if self.print:
                writer.print(classes, self.output)
            else:
                writer.write(classes, self.output)
        else:
            logger.warning("Analyzer returned zero classes!")
Example #2
0
    def write_file(self, uri: str, location: Optional[str], content: str):
        """
        Write the given uri and it's content according to the base path and if
        the uri is relative to first requested uri.

        Keep track of all the written file paths, in case we have to
        modify the location attribute in an upcoming schema/definition
        import.
        """
        common_path = os.path.commonpath((self.base_path or "", uri))
        if common_path:
            file_path = self.output.joinpath(
                Path(uri).relative_to(common_path))
        else:
            file_path = self.output.joinpath(Path(uri).name)

        content = self.adjust_imports(file_path.parent, content)
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text(content, encoding="utf-8")

        logger.info("Writing %s", file_path)
        self.downloaded[uri] = file_path

        if location:
            self.downloaded[location] = file_path
Example #3
0
    def write(self, classes: List[Class], output: str):
        engine = self.get_format(output)
        for file, package, buffer in engine.render(classes):
            if len(buffer.strip()) > 0:
                logger.info("Generating package: %s", package)

                file.parent.mkdir(parents=True, exist_ok=True)
                file.write_text(buffer)
Example #4
0
    def write(self, classes: List[Class], output: str):
        """Iterate over the designated generator outputs and create the
        necessary directories and files."""
        engine = self.get_format(output)
        for result in engine.render(classes):
            if len(result.source.strip()) > 0:
                logger.info("Generating package: %s", result.title)

                result.path.parent.mkdir(parents=True, exist_ok=True)
                result.path.write_text(result.source)
Example #5
0
    def parse_schema(self, uri: str, namespace: Optional[str]) -> Optional[Schema]:
        """Parse the given schema uri and return the schema tree object."""

        input_stream = self.load_resource(uri)
        if input_stream is None:
            return None

        logger.info("Parsing schema %s", os.path.basename(uri))
        parser = SchemaParser(target_namespace=namespace, location=uri)
        return parser.from_bytes(input_stream, Schema)
Example #6
0
    def write(self, classes: List[Class]):
        """Iterate over the designated generator outputs and create the
        necessary directories and files."""

        self.generator.designate(classes)
        for result in self.generator.render(classes):
            if result.source.strip():
                logger.info("Generating package: %s", result.title)

                result.path.parent.mkdir(parents=True, exist_ok=True)
                result.path.write_text(result.source, encoding="utf-8")
Example #7
0
    def generate_classes(self, schema: Schema) -> List[Class]:
        """Convert and return the given schema tree to classes."""
        uri = schema.location
        logger.info("Compiling schema %s", "..." if not uri else os.path.basename(uri))
        classes = SchemaMapper.map(schema)

        class_num, inner_num = self.count_classes(classes)
        if class_num > 0:
            logger.info("Builder: %d main and %d inner classes", class_num, inner_num)

        return classes
Example #8
0
    def generate_classes(self, schema: Schema, package: str):
        """Convert the given schema tree to codegen classes and use the writer
        factory to either generate or print the result code."""
        logger.info("Compiling schema...")
        classes = ClassBuilder(schema=schema, package=package).build()

        class_num, inner_num = self.count_classes(classes)
        if class_num > 0:
            logger.info("Builder: %d main and %d inner classes", class_num,
                        inner_num)

        return classes
Example #9
0
    def process_json_documents(self, uris: List[str]):
        """Process a list of json resources."""

        classes = []
        for uri in uris:
            input_stream = self.load_resource(uri)
            if input_stream:
                data = json.load(io.BytesIO(input_stream))
                logger.info("Parsing document %s", os.path.basename(uri))
                name = self.config.output.package.split(".")[-1]
                classes.extend(DictMapper.map(data, name))

        dirname = os.path.dirname(uris[0]) if uris else ""
        self.class_map[dirname] = ClassUtils.reduce(classes)
Example #10
0
    def process_xml_documents(self, uris: List[str]):
        """Process a list of xml resources."""

        classes = []
        parser = TreeParser()
        for uri in uris:
            input_stream = self.load_resource(uri)
            if input_stream:
                logger.info("Parsing document %s", os.path.basename(uri))
                any_element: AnyElement = parser.from_bytes(input_stream)
                classes.extend(ElementMapper.map(any_element))

        dirname = os.path.dirname(uris[0]) if uris else ""
        self.class_map[dirname] = ClassUtils.reduce(classes)
Example #11
0
    def process(self, schema_path: Path, package: str):
        classes = self.process_schema(schema_path, package)
        classes = self.analyze_classes(classes)
        class_num, inner_num = self.count_classes(classes)

        if not class_num:
            return logger.warning("Analyzer returned zero classes!")

        logger.info("Analyzer: %d main and %d inner classes", class_num,
                    inner_num)

        writer.designate(classes, self.output)
        if self.print:
            writer.print(classes, self.output)
        else:
            writer.write(classes, self.output)
Example #12
0
    def process_schema(self, uri: str, namespace: Optional[str] = None):
        """
        Parse and convert schema to codegen models.

        Avoid processing the same uri twice and fail silently if
        anything goes wrong with fetching and parsing the schema
        document.
        """
        if uri in self.processed:
            logger.debug("Skipping already processed: %s",
                         os.path.basename(uri))
        else:
            logger.info("Parsing schema %s", os.path.basename(uri))
            self.processed.append(uri)

            schema = self.parse_schema(uri, namespace)
            if schema:
                self.convert_schema(schema)
Example #13
0
    def wget(self, uri: str, location: Optional[str] = None):
        """Download handler for any uri input with circular protection."""

        if not (uri in self.downloaded or
                (location and location in self.downloaded)):
            self.downloaded[uri] = None
            self.downloaded[location] = None
            self.adjust_base_path(uri)

            logger.info("Fetching %s", uri)

            input_stream = urlopen(uri).read()  # nosec
            if uri.endswith("wsdl"):
                self.parse_definitions(uri, input_stream)
            else:
                self.parse_schema(uri, input_stream)

            self.write_file(uri, location, input_stream.decode())
Example #14
0
    def process_schema(self, uri: str, namespace: Optional[str] = None):
        """Recursively parse the given schema uri and all the imports and
        generate a class map indexed with the schema uri."""
        if uri in self.processed:
            logger.debug("Already processed skipping: %s", uri)
            return

        logger.info("Parsing schema %s", os.path.basename(uri))
        self.processed.append(uri)
        schema = self.parse_schema(uri, namespace)
        if schema is None:
            return

        for sub in schema.included():
            if sub.location:
                self.process_schema(sub.location, schema.target_namespace)

        self.class_map[uri] = self.generate_classes(schema)
Example #15
0
def init_config(**kwargs: Any):
    """Create or update a configuration file."""

    if kwargs["print"]:
        logger.setLevel(logging.ERROR)

    file_path = Path(kwargs["output"])
    if file_path.exists():
        config = GeneratorConfig.read(file_path)
        logger.info("Updating configuration file %s", kwargs["output"])
    else:
        logger.info("Initializing configuration file %s", kwargs["output"])
        config = GeneratorConfig.create()

    if kwargs["print"]:
        config.write(sys.stdout, config)
    else:
        with file_path.open("w") as fp:
            config.write(fp, config)
Example #16
0
    def adjust_base_path(self, uri: str):
        """
        Adjust base path for every new uri loaded.

        Example runs:
            - file:///schemas/air_v48_0/Air.wsdl -> file:///schemas/air_v48_0
            - file:///schemas/common_v48_0/CommonReqRsp.xsd -> file:///schemas
        """
        if not self.base_path:
            self.base_path = Path(uri).parent
            logger.info("Setting base path to %s", self.base_path)
        else:
            common_path = os.path.commonpath((self.base_path or "", uri))

            if common_path:
                common_path_path = Path(common_path)
                if common_path_path < self.base_path:
                    self.base_path = Path(common_path)
                    logger.info("Adjusting base path to %s", self.base_path)
Example #17
0
    def process_schema(
        self,
        schema_path: Path,
        package: str,
        target_namespace: Optional[str] = None,
    ) -> List[Class]:
        """Recursively parse the given schema and all it's included schemas and
        generate a list of classes."""
        classes = []
        if schema_path not in self.processed:
            self.processed.append(schema_path)
            logger.info("Parsing schema...")
            schema = self.parse_schema(schema_path, target_namespace)
            target_namespace = schema.target_namespace
            for sub in schema.included():
                included_classes = self.process_included(
                    sub, package, target_namespace)
                classes.extend(included_classes)

            classes.extend(self.generate_classes(schema, package))
        else:
            logger.debug("Already processed skipping: %s", schema_path.name)
        return classes
Example #18
0
    def process_classes(self):
        """Process the generated classes and write or print the final
        output."""
        classes = [
            cls for classes in self.class_map.values() for cls in classes
        ]
        class_num, inner_num = self.count_classes(classes)
        if class_num:
            logger.info("Analyzer input: %d main and %d inner classes",
                        class_num, inner_num)
            self.assign_packages()

            classes = self.analyze_classes(classes)
            class_num, inner_num = self.count_classes(classes)
            logger.info("Analyzer output: %d main and %d inner classes",
                        class_num, inner_num)

            writer = CodeWriter.from_config(self.config)
            if self.print:
                writer.print(classes)
            else:
                writer.write(classes)
        else:
            raise CodeGenerationError("Nothing to generate.")