File: mixins.py

package info (click to toggle)
python-xsdata 24.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,936 kB
  • sloc: python: 29,257; xml: 404; makefile: 27; sh: 6
file content (98 lines) | stat: -rw-r--r-- 3,231 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import abc
import datetime
from pathlib import Path
from typing import Dict, Iterator, List, NamedTuple

from xsdata import __version__
from xsdata.codegen.models import Class
from xsdata.exceptions import CodeGenerationError
from xsdata.models.config import GeneratorConfig
from xsdata.utils.collections import group_by
from xsdata.utils.package import module_path, package_path


class GeneratorResult(NamedTuple):
    """
    Generator easy access output wrapper.

    :param path: file path to be written
    :param title: result title for misc usage
    :param source: source code/output to be written
    """

    path: Path
    title: str
    source: str


class AbstractGenerator(abc.ABC):
    """Abstract code generator class."""

    __slots__ = "config"

    def __init__(self, config: GeneratorConfig):
        """
        Generator constructor.

        :param config Generator configuration
        """
        self.config = config

    def module_name(self, module: str) -> str:
        """Convert the given module name to match the generator conventions."""
        return module

    def package_name(self, package: str) -> str:
        """Convert the given module name to match the generator conventions."""
        return package

    @abc.abstractmethod
    def render(self, classes: List[Class]) -> Iterator[GeneratorResult]:
        """Return an iterator of the generated results."""

    @classmethod
    def group_by_package(cls, classes: List[Class]) -> Dict[Path, List[Class]]:
        """Group the given list of classes by the target package directory."""
        return group_by(classes, lambda x: package_path(x.target_module))

    @classmethod
    def group_by_module(cls, classes: List[Class]) -> Dict[Path, List[Class]]:
        """Group the given list of classes by the target module directory."""
        return group_by(classes, lambda x: module_path(x.target_module))

    def render_header(self) -> str:
        """Generate a header for the writer to prepend on the output files."""
        if not self.config.output.include_header:
            return ""

        now = datetime.datetime.now().isoformat(sep=" ", timespec="seconds")
        return (
            f'"""This file was generated by xsdata, v{__version__}, on {now}'
            f"\n\nGenerator: {self.__class__.__qualname__}\n"
            f"See: https://xsdata.readthedocs.io/\n"
            '"""\n'
        )

    def normalize_packages(self, classes: List[Class]):
        """
        Normalize the target package and module names by the given output
        generator.

        :param classes: a list of codegen class instances
        """
        modules = {}
        packages = {}
        for obj in classes:
            if obj.package is None or obj.module is None:
                raise CodeGenerationError(
                    f"Class `{obj.name}` has not been assigned to a package"
                )

            if obj.module not in modules:
                modules[obj.module] = self.module_name(obj.module)

            if obj.package not in packages:
                packages[obj.package] = self.package_name(obj.package)

            obj.module = modules[obj.module]
            obj.package = packages[obj.package]