Skip to content

oapi.oas.references

This module provides functionality for resolving references within an instance of oapi.oas.OpenAPI.

The following will replace all references in the Open API document open_api_document with the objects targeted by the ref property of the reference.

Example:

import yaml
from urllib.request import urlopen
from oapi.oas import OpenAPI
from oapi.oas.references import Resolver


with urlopen(
    "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/3.1.1/"
    "examples/v3.0/callback-example.yaml"
) as response:
    open_api_document = OpenAPI(
        yaml.safe_load(response)
    )

resolver = Resolver(open_api_document)
resolver.dereference()

Resolver

Resolver(
    root: oapi.oas.model.OpenAPI,
    url: str | None = None,
    urlopen: typing.Callable = oapi.oas.references._urlopen,
)

This class should be used, with an instance of oapi.oas.OpenAPI, to resolve references.

Parameters:

  • root (oapi.oas.model.OpenAPI) –

    The OpenAPI document against which pointers will be resolved.

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

    The URL or file path from where root was retrieved. The base URL for relative paths will be the directory above this URL. This will not typically be needed, as it can be inferred from most Model instances.

  • urlopen (typing.Callable, default: oapi.oas.references._urlopen ) –

    If provided, this should be a function taking one argument (a str), which can be used in lieu of urllib.request.urlopen to retrieve a document and return an instance of a sub-class of IOBase (such as http.client.HTTPResponse). This should be used if authentication is needed in order to retrieve external references in the document, or if local file paths will be referenced instead of web URL's (use open as the value for the urlopen parameter in this case).

Source code in src/oapi/oas/references.py
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
def __init__(
    self,
    root: OpenAPI,
    url: str | None = None,
    urlopen: Callable = _urlopen,
) -> None:
    # Ensure arguments are of the correct types
    if not callable(urlopen):
        raise TypeError(urlopen)
    if not isinstance(root, OpenAPI):
        raise TypeError(root)
    if not ((url is None) or isinstance(url, str)):
        raise TypeError(url)
    # This is the function used to open external pointer references
    self.urlopen = urlopen
    # Infer the URL from the `OpenAPI` document, if not explicitly provided
    if url is None:
        url = sob.get_model_url(root) or ""
    self.url = url
    # This is the primary document--the one we are resolving
    document: _Document = _Document(self, root, url)
    # Store the primary document both by URL and under the key "#" (for
    # convenient reference)
    self.documents = {url: document}
    if url != "":
        self.documents[""] = document

get_document

get_document(url: str) -> oapi.oas.references._Document

Retrieve a document by URL, or use the cached document if previously retrieved

Source code in src/oapi/oas/references.py
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
def get_document(self, url: str) -> _Document:
    """
    Retrieve a document by URL, or use the cached document if previously
    retrieved
    """
    if url not in self.documents:
        try:
            with self.urlopen(url) as response:
                self.documents[url] = _Document(
                    self, sob.unmarshal(sob.deserialize(response)), url=url
                )
        except (HTTPError, FileNotFoundError) as error:
            sob.errors.append_exception_text(error, f": {url}")
            raise
    return self.documents[url]

dereference

dereference() -> None

Dereference the primary document

Source code in src/oapi/oas/references.py
413
414
415
416
417
def dereference(self) -> None:
    """
    Dereference the primary document
    """
    self.documents[""].dereference_all()

resolve

resolve(
    pointer: str,
    types: (
        sob.abc.Types
        | collections.abc.Sequence[type | sob.abc.Property]
    ) = (),
    *,
    dereference: bool = False
) -> sob.abc.Model

Retrieve an object at the specified pointer

Source code in src/oapi/oas/references.py
419
420
421
422
423
424
425
426
427
428
429
430
431
432
def resolve(
    self,
    pointer: str,
    types: sob.abc.Types | Sequence[type | sob.abc.Property] = (),
    *,
    dereference: bool = False,
) -> sob.abc.Model:
    """
    Retrieve an object at the specified pointer
    """
    url, pointer = self.documents[""].get_url_pointer(pointer)
    return self.documents[url].resolve(
        pointer, types, dereference=dereference
    )

resolve_reference

resolve_reference(
    reference: oapi.oas.model.Reference,
    types: (
        sob.abc.Types
        | collections.abc.Sequence[type | sob.abc.Property]
    ) = (),
) -> sob.abc.Model

Retrieve a referenced object.

Parameters:

Source code in src/oapi/oas/references.py
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
def resolve_reference(
    self,
    reference: Reference,
    types: sob.abc.Types | Sequence[type | sob.abc.Property] = (),
) -> sob.abc.Model:
    """
    Retrieve a referenced object.

    Parameters:
        reference:
        types:
    """
    message: str
    url: str = sob.get_model_url(reference) or ""
    if not reference.ref:
        raise ValueError(reference)
    pointer: str = urljoin(
        sob.get_model_pointer(reference) or "",
        reference.ref,
    )
    resolved_model: sob.abc.Model = self.get_document(url).resolve(
        pointer, types
    )
    if resolved_model is reference or (
        isinstance(resolved_model, Reference)
        and resolved_model.ref == reference.ref
    ):
        message = f"`Reference` instance is self-referential: {pointer}"
        raise OAPIReferenceLoopError(message)
    if isinstance(resolved_model, Reference):
        resolved_model = self.resolve_reference(
            resolved_model, types=types
        )
    return resolved_model

get_relative_url

get_relative_url(url: str) -> str

Given a URL, return that URL relative to the base document

Source code in src/oapi/oas/references.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
def get_relative_url(self, url: str) -> str:
    """
    Given a URL, return that URL relative to the base document
    """
    relative_url: str = ""
    if url:
        parse_result: ParseResult = urlparse(url)
        # Determine if the URL is absolute or relative
        if parse_result.netloc or parse_result.scheme == "file":
            # Only include the relative URL if it is not the root document
            if url == self.url:
                relative_url = ""
            else:
                relative_url = sob.utilities.get_url_relative_to(
                    url, self.url
                )
        else:
            relative_url = url
    return relative_url