Skip to content

oapi.model

ModelModule

ModelModule(
    open_api: (
        str | sob.abc.Readable | oapi.oas.model.OpenAPI
    ),
    get_class_name_from_pointer: collections.abc.Callable[
        [str, str], str
    ] = oapi.model.get_default_class_name_from_pointer,
    docstring: str | None = None,
)

This class parses an Open API document and generates a module defining classes to represent each schema defined in the Open API document as a subclass of sob.Object, sob.Array, or sob.Dictionary.

Parameters:

  • open_api (str | sob.abc.Readable | oapi.oas.model.OpenAPI) –

    An OpenAPI document. This can be a URL, file-path, an HTTP response (http.client.HTTPResponse), a file object, or an instance of oapi.oas.OpenAPI.

  • get_class_name_from_pointer (collections.abc.Callable[[str, str], str], default: oapi.model.get_default_class_name_from_pointer ) –

    This argument defaults to oapi.model.get_default_class_name_from_pointer. If an alternate function is provided, it should accept two arguments, both being str instances. The first argument is a JSON pointer, or concatenated relative URL + JSON pointer, and the second being either an empty string or a parameter name, where applicable. The function should return a str which is a valid, unique, class name.

  • docstring (str | None, default: None ) –

    If provided, this string will be inserts as the module docstring.

Source code in src/oapi/model.py
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
def __init__(
    self,
    open_api: str | sob.abc.Readable | OpenAPI,
    get_class_name_from_pointer: Callable[
        [str, str], str
    ] = get_default_class_name_from_pointer,
    docstring: str | None = None,
) -> None:
    message: str
    self._parser = _ModuleParser()
    self._modeler: _Modeler
    if isinstance(open_api, str):
        if os.path.exists(open_api):
            self._modeler = _get_path_modeler(open_api)
        else:
            self._modeler = _get_url_modeler(open_api)
    elif isinstance(open_api, sob.abc.Readable):
        self._modeler = _get_io_modeler(open_api)
    elif isinstance(open_api, OpenAPI):
        self._modeler = _get_open_api_modeler(open_api)
    else:
        message = (
            f"`{get_calling_function_qualified_name()}` requires an "
            f"instance of `str`, `{get_qualified_name(OpenAPI)}`, or "
            "a file-like object for the `open_api` parameter—not: "
            f"{open_api!r}"
        )
        raise TypeError(message)
    self._modeler.docstring = docstring
    self._modeler.get_class_name_from_pointer = get_class_name_from_pointer

save

save(path: str | pathlib.Path) -> None

This method will save the generated module to a given path. If there is an existing module at that path--the existing module will be imported and the pre-existing class names will be utilized for any schemas defined by elements residing at the same location as the documented JSON pointer in the pre-existing classes' docstrings.

Source code in src/oapi/model.py
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
def save(self, path: str | Path) -> None:
    """
    This method will save the generated module to a given path. If there is
    an existing module at that path--the existing module will be imported
    and the pre-existing class names will be utilized for any schemas
    defined by elements residing at the same location as the documented
    JSON pointer in the pre-existing classes' docstrings.
    """
    # Make sure that any matching, existing classes use the same names
    self._parse_existing_module(path)
    model_source: str = str(self)
    # Save the module
    with open(path, "w") as model_io:
        model_io.write(model_source)

get_default_class_name_from_pointer

get_default_class_name_from_pointer(
    pointer: str,
    name: str = "",
    log: (
        logging.Logger
        | collections.abc.Callable[[str], None]
        | None
    ) = None,
) -> str

This function infers a class name from a JSON pointer (or from a relative URL concatenated with a JSON pointer) + parameter name (or empty string when a parameter name is not applicable). This function is the default naming function used by oapi.model.Module.

Parameters:

  • pointer (str) –

    A JSON pointer referencing a schema within an OpenAPI document, or a concatenation of a relative URL + "#" + a JSON pointer.

  • name (str, default: '' ) –

    The parameter name, or "" if the element is not a parameter.

  • log (logging.Logger | collections.abc.Callable[[str], None] | None, default: None ) –

    A logger, or a callback function for log messages

Examples:

print( ... get_default_class_name_from_pointer( ... "#/paths/~1directory~1sub-directory~1name/get/parameters/1", ... name="argument-name", ... ) ... ) DirectorySubDirectoryNameGetArgumentName

print( ... get_default_class_name_from_pointer( ... "#/paths/~1directory~1sub-directory~1name/get/parameters/1" # ... "/item", ... name="argument-name", ... ) ... ) DirectorySubDirectoryNameGetArgumentNameItem

Source code in src/oapi/model.py
 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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def get_default_class_name_from_pointer(
    pointer: str,
    name: str = "",
    log: Logger | Callable[[str], None] | None = None,
) -> str:
    """
    This function infers a class name from a JSON pointer (or from a
    relative URL concatenated with a JSON pointer) + parameter name (or
    empty string when a parameter name is not applicable). This function is
    the default naming function used by `oapi.model.Module`.

    Parameters:
        pointer: A JSON pointer referencing a schema within an OpenAPI
            document, or a concatenation of a relative URL + "#" + a JSON
            pointer.
        name: The parameter name, or "" if the element is not a
            parameter.
        log: A logger, or a callback function for log messages

    Examples:

    >>> print(
    ...     get_default_class_name_from_pointer(
    ...         "#/paths/~1directory~1sub-directory~1name/get/parameters/1",
    ...         name="argument-name",
    ...     )
    ... )
    DirectorySubDirectoryNameGetArgumentName

    >>> print(
    ...     get_default_class_name_from_pointer(
    ...         "#/paths/~1directory~1sub-directory~1name/get/parameters/1"  #
    ...         "/item",
    ...         name="argument-name",
    ...     )
    ... )
    DirectorySubDirectoryNameGetArgumentNameItem
    """
    relative_url: str = ""
    if "#" in pointer:
        relative_url, pointer = pointer.split("#", 1)
    class_name_: str = pointer.lstrip("/")
    pattern: str
    repl: str
    for pattern, repl in (
        (
            r"/([^\/]+)/responses/200/(content/[^\/]+/)?schema\b",
            r"/\1/response",
        ),
        (
            r"/([^\/]+)/responses/(\d+)/(content/[^\/]+/)?schema\b",
            r"/\1/response/\2",
        ),
        (
            r"/(anyOf|allOf|oneOf)/\d+/",
            "/",
        ),
        (
            r"(?:^components)?(/|^)parameters/([^/]+)/schema(/|$)",
            r"\1\2\3",
        ),
        (
            r"^(components/[^/]+/|definitions/|paths/)",
            "/",
        ),
        (
            r"/properties/",
            "/",
        ),
        (
            r"/items(/|$)",
            r"/item\1",
        ),
        (
            r"~1",
            "/",
        ),
        (
            r"~0",
            "~",
        ),
    ):
        class_name_ = re.sub(pattern, repl, class_name_)
    # For parameters, include the parameter name in the class name *if* the
    # parameter is defined inline (if it's not defined inline, the path to
    # the parameter definition will usually be sufficiently descriptive),
    # and don't include "/parameters/" or the parameter # in the class
    # name.
    if name and not (
        pointer.startswith(("/components/parameters/", "/definitions/"))
    ):
        parameters_pattern: Pattern = re.compile(r"/parameters/\d+((?:/.+)?)$")
        if parameters_pattern.search(class_name_):
            class_name_ = parameters_pattern.sub(f"/{name}/\\1", class_name_)
        else:
            class_name_ = f"{class_name_}/{name}"
    if relative_url:
        class_name_ = f"{relative_url}/{class_name_}"
    if log is not None:  # pragma: no cover
        message: str = (
            f"{pointer} -> {class_name_} (JSON Pointer -> Class Name)"
        )
        if isinstance(log, Logger):
            log.info(message)
        elif callable(log):
            log(message)
    return sob.utilities.get_class_name(class_name_)

write_model_module

write_model_module(
    model_path: str | pathlib.Path,
    *,
    open_api: (
        str | sob.abc.Readable | oapi.oas.model.OpenAPI
    ),
    get_class_name_from_pointer: collections.abc.Callable[
        [str, str], str
    ] = oapi.model.get_default_class_name_from_pointer,
    docstring: str | None = None
) -> None

This function creates or updates a module defining classes to represent each schema in an Open API document as a subclass of sob.Object, sob.Array, or sob.Dictionary.

Parameters:

  • model_path (str | pathlib.Path) –

    The file path where the model module will be saved (created or updated).

  • open_api (str | sob.abc.Readable | oapi.oas.model.OpenAPI) –

    An OpenAPI document. This can be a URL, file-path, an HTTP response (http.client.HTTPResponse), a file object, or an instance of oapi.oas.model.OpenAPI.

  • get_class_name_from_pointer (collections.abc.Callable[[str, str], str], default: oapi.model.get_default_class_name_from_pointer ) –

    This argument defaults to oapi.model.get_default_class_name_from_pointer. If an alternate function is provided, it should accept two arguments, both being str instances. The first argument is a JSON pointer, or concatenated relative URL + JSON pointer, and the second being either an empty string or a parameter name, where applicable. The function should return a str which is a valid, unique, class name.

  • docstring (str | None, default: None ) –

    If provided, this string will be inserted at the top of the module as the docstring.

Source code in src/oapi/model.py
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
def write_model_module(
    model_path: str | Path,
    *,
    open_api: str | sob.abc.Readable | OpenAPI,
    get_class_name_from_pointer: Callable[
        [str, str], str
    ] = get_default_class_name_from_pointer,
    docstring: str | None = None,
) -> None:
    """
    This function creates or updates a module defining classes to represent
    each schema in an Open API document as a subclass of `sob.Object`,
    `sob.Array`, or `sob.Dictionary`.

    Parameters:
        model_path: The file path where the model module will be saved
            (created or updated).
        open_api: An OpenAPI document. This can be a URL, file-path, an
            HTTP response (`http.client.HTTPResponse`), a file object, or an
            instance of `oapi.oas.model.OpenAPI`.
        get_class_name_from_pointer: This argument defaults to
            `oapi.model.get_default_class_name_from_pointer`. If an alternate
            function is provided, it should accept two arguments, both being
            `str` instances. The first argument is a JSON pointer, or
            concatenated relative URL + JSON pointer, and the second being
            either an empty string or a parameter name, where applicable.
            The function should return a `str` which is a valid, unique,
            class name.
        docstring: If provided, this string will be inserted at the top of the
            module as the docstring.
    """
    locals_: dict[str, Any] = dict(locals())
    locals_.pop("model_path")
    # Increase the recursion limit to allow deep models to be processed
    recursion_limit: int = sys.getrecursionlimit()
    sys.setrecursionlimit(recursion_limit * 100)
    try:
        model_module: ModelModule = ModelModule(**locals_)
        model_module.save(model_path)
    finally:
        sys.setrecursionlimit(recursion_limit)