Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian committed Nov 9, 2023
1 parent 925f7ec commit 61982db
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 47 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ classifiers = [
dynamic = ["version"]
dependencies = [
"attrs>=22.2.0",
"rpds-py>=0.7.0",
"rpds.py>=0.12.0",
"url.py>=0.9.2",
]

[project.urls]
Expand Down
51 changes: 26 additions & 25 deletions referencing/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

from attrs import evolve, field
from rpds import HashTrieMap, HashTrieSet, List
from url import URL

from referencing import exceptions
from referencing._attrs import frozen
from referencing.typing import URI, Anchor as AnchorType, D, Mapping, Retrieve
from referencing.typing import Anchor as AnchorType, D, Mapping, Retrieve

EMPTY_UNCRAWLED: HashTrieSet[URI] = HashTrieSet()
EMPTY_PREVIOUS_RESOLVERS: List[URI] = List()
EMPTY_UNCRAWLED: HashTrieSet[URL] = HashTrieSet()
EMPTY_PREVIOUS_RESOLVERS: List[URL] = List()


class _MaybeInSubresource(Protocol[D]):
Expand All @@ -38,7 +39,7 @@ class Specification(Generic[D]):
name: str

#: Find the ID of a given document.
id_of: Callable[[D], URI | None]
id_of: Callable[[D], URL | None]

#: Retrieve the subresources of the given document (without traversing into
#: the subresources themselves).
Expand Down Expand Up @@ -138,7 +139,7 @@ def opaque(cls, contents: D) -> Resource[D]:
"""
return Specification.OPAQUE.create_resource(contents=contents)

def id(self) -> URI | None:
def id(self) -> URL | None:
"""
Retrieve this resource's (specification-specific) identifier.
"""
Expand Down Expand Up @@ -199,12 +200,12 @@ def pointer(self, pointer: str, resolver: Resolver[D]) -> Resolved[D]:
return Resolved(contents=contents, resolver=resolver) # type: ignore[reportUnknownArgumentType]


def _fail_to_retrieve(uri: URI):
def _fail_to_retrieve(uri: URL):
raise exceptions.NoSuchResource(ref=uri)


@frozen
class Registry(Mapping[URI, Resource[D]]):
class Registry(Mapping[URL, Resource[D]]):
r"""
A registry of `Resource`\ s, each identified by their canonical URIs.
Expand All @@ -228,16 +229,16 @@ class Registry(Mapping[URI, Resource[D]]):
even according to the retrieval logic.
"""

_resources: HashTrieMap[URI, Resource[D]] = field(
_resources: HashTrieMap[URL, Resource[D]] = field(
default=HashTrieMap(),
converter=HashTrieMap.convert, # type: ignore[reportGeneralTypeIssues]
alias="resources",
)
_anchors: HashTrieMap[tuple[URI, str], AnchorType[D]] = HashTrieMap() # type: ignore[reportGeneralTypeIssues]
_uncrawled: HashTrieSet[URI] = EMPTY_UNCRAWLED
_anchors: HashTrieMap[tuple[URL, str], AnchorType[D]] = HashTrieMap() # type: ignore[reportGeneralTypeIssues]
_uncrawled: HashTrieSet[URL] = EMPTY_UNCRAWLED
_retrieve: Retrieve[D] = field(default=_fail_to_retrieve, alias="retrieve")

def __getitem__(self, uri: URI) -> Resource[D]:
def __getitem__(self, uri: URL) -> Resource[D]:
"""
Return the (already crawled) `Resource` identified by the given URI.
"""
Expand All @@ -246,7 +247,7 @@ def __getitem__(self, uri: URI) -> Resource[D]:
except KeyError:
raise exceptions.NoSuchResource(ref=uri)

def __iter__(self) -> Iterator[URI]:
def __iter__(self) -> Iterator[URL]:
"""
Iterate over all crawled URIs in the registry.
"""
Expand Down Expand Up @@ -315,7 +316,7 @@ def __repr__(self) -> str:
summary = f"{pluralized}"
return f"<Registry ({size} {summary})>"

def get_or_retrieve(self, uri: URI) -> Retrieved[D, Resource[D]]:
def get_or_retrieve(self, uri: URL) -> Retrieved[D, Resource[D]]:
"""
Get a resource from the registry, crawling or retrieving if necessary.
Expand Down Expand Up @@ -345,7 +346,7 @@ def get_or_retrieve(self, uri: URI) -> Retrieved[D, Resource[D]]:
registry = registry.with_resource(uri, resource)
return Retrieved(registry=registry, value=resource)

def remove(self, uri: URI):
def remove(self, uri: URL):
"""
Return a registry with the resource identified by a given URI removed.
"""
Expand All @@ -361,7 +362,7 @@ def remove(self, uri: URI):
),
)

def anchor(self, uri: URI, name: str):
def anchor(self, uri: URL, name: str):
"""
Retrieve a given anchor from a resource which must already be crawled.
"""
Expand Down Expand Up @@ -389,7 +390,7 @@ def anchor(self, uri: URI, name: str):
)
raise exceptions.NoSuchAnchor(ref=uri, resource=resource, anchor=name)

def contents(self, uri: URI) -> D:
def contents(self, uri: URL) -> D:
"""
Retrieve the (already crawled) contents identified by the given URI.
"""
Expand Down Expand Up @@ -421,15 +422,15 @@ def crawl(self) -> Registry[D]:
uncrawled=EMPTY_UNCRAWLED,
)

def with_resource(self, uri: URI, resource: Resource[D]):
def with_resource(self, uri: URL, resource: Resource[D]):
"""
Add the given `Resource` to the registry, without crawling it.
"""
return self.with_resources([(uri, resource)])

def with_resources(
self,
pairs: Iterable[tuple[URI, Resource[D]]],
pairs: Iterable[tuple[URL, Resource[D]]],
) -> Registry[D]:
r"""
Add the given `Resource`\ s to the registry, without crawling them.
Expand All @@ -446,7 +447,7 @@ def with_resources(

def with_contents(
self,
pairs: Iterable[tuple[URI, D]],
pairs: Iterable[tuple[URL, D]],
**kwargs: Any,
) -> Registry[D]:
r"""
Expand Down Expand Up @@ -487,7 +488,7 @@ def combine(self, *registries: Registry[D]) -> Registry[D]:
retrieve=retrieve,
)

def resolver(self, base_uri: URI = "") -> Resolver[D]:
def resolver(self, base_uri: URL = "") -> Resolver[D]:
"""
Return a `Resolver` which resolves references against this registry.
"""
Expand Down Expand Up @@ -551,11 +552,11 @@ class Resolver(Generic[D]):
additional subresources and adding them to a new registry.
"""

_base_uri: URI = field(alias="base_uri")
_base_uri: URL = field(alias="base_uri")
_registry: Registry[D] = field(alias="registry")
_previous: List[URI] = field(default=List(), repr=False, alias="previous")
_previous: List[URL] = field(default=List(), repr=False, alias="previous")

def lookup(self, ref: URI) -> Resolved[D]:
def lookup(self, ref: URL) -> Resolved[D]:
"""
Resolve the given reference to the resource it points to.
Expand Down Expand Up @@ -610,14 +611,14 @@ def in_subresource(self, subresource: Resource[D]) -> Resolver[D]:
return self
return evolve(self, base_uri=urljoin(self._base_uri, id))

def dynamic_scope(self) -> Iterable[tuple[URI, Registry[D]]]:
def dynamic_scope(self) -> Iterable[tuple[URL, Registry[D]]]:
"""
In specs with such a notion, return the URIs in the dynamic scope.
"""
for uri in self._previous:
yield uri, self._registry

def _evolve(self, base_uri: URI, **kwargs: Any):
def _evolve(self, base_uri: URL, **kwargs: Any):
"""
Evolve, appending to the dynamic scope.
"""
Expand Down
9 changes: 5 additions & 4 deletions referencing/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
from referencing._attrs import frozen

if TYPE_CHECKING:
from url import URL

from referencing import Resource
from referencing.typing import URI


@frozen
Expand All @@ -23,7 +24,7 @@ class NoSuchResource(KeyError):
instantiable and *is* part of the public API of the package.
"""

ref: URI
ref: URL

def __eq__(self, other: Any) -> bool:
if self.__class__ is not other.__class__:
Expand Down Expand Up @@ -62,7 +63,7 @@ class Unretrievable(KeyError):
The given URI is not present in a registry, and retrieving it failed.
"""

ref: URI
ref: URL

def __eq__(self, other: Any) -> bool:
if self.__class__ is not other.__class__:
Expand Down Expand Up @@ -99,7 +100,7 @@ class Unresolvable(Exception):
A reference was unresolvable.
"""

ref: URI
ref: URL

def __eq__(self, other: Any) -> bool:
if self.__class__ is not other.__class__:
Expand Down
17 changes: 10 additions & 7 deletions referencing/jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
from __future__ import annotations

from collections.abc import Sequence, Set
from typing import Any, Iterable, Union
from typing import TYPE_CHECKING, Any, Iterable, Union

if TYPE_CHECKING:
from url import URL

from referencing import Anchor, Registry, Resource, Specification, exceptions
from referencing._attrs import frozen
from referencing._core import Resolved as _Resolved, Resolver as _Resolver
from referencing.typing import URI, Anchor as AnchorType, Mapping
from referencing.typing import Anchor as AnchorType, Mapping

#: A JSON Schema which is a JSON object
ObjectSchema = Mapping[str, Any]
Expand All @@ -30,24 +33,24 @@ class UnknownDialect(Exception):
If it's a custom ("unofficial") dialect, be sure you've registered it.
"""

uri: URI
uri: URL


def _dollar_id(contents: Schema) -> URI | None:
def _dollar_id(contents: Schema) -> URL | None:
if isinstance(contents, bool):
return
return contents.get("$id")


def _legacy_dollar_id(contents: Schema) -> URI | None:
def _legacy_dollar_id(contents: Schema) -> URL | None:
if isinstance(contents, bool) or "$ref" in contents:
return
id = contents.get("$id")
if id is not None and not id.startswith("#"):
return id


def _legacy_id(contents: ObjectSchema) -> URI | None:
def _legacy_id(contents: ObjectSchema) -> URL | None:
if "$ref" in contents:
return
id = contents.get("id")
Expand Down Expand Up @@ -555,7 +558,7 @@ def maybe_in_subresource(


def specification_with(
dialect_id: URI,
dialect_id: URL,
default: Specification[Any] = None, # type: ignore[reportGeneralTypeIssues]
) -> Specification[Any]:
"""
Expand Down
15 changes: 9 additions & 6 deletions referencing/retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from referencing import Resource

if TYPE_CHECKING:
from referencing.typing import URI, D, Retrieve
from url import URL

from referencing.typing import D, Retrieve

#: A serialized document (e.g. a JSON string)
_T = TypeVar("_T")
Expand All @@ -20,7 +22,7 @@ def to_cached_resource(
cache: Callable[[Retrieve[D]], Retrieve[D]] | None = None,
loads: Callable[[_T], D] = json.loads,
from_contents: Callable[[D], Resource[D]] = Resource.from_contents,
) -> Callable[[Callable[[URI], _T]], Retrieve[D]]:
) -> Callable[[Callable[[URL], _T]], Retrieve[D]]:
"""
Create a retriever which caches its return values from a simpler callable.
Expand All @@ -39,13 +41,14 @@ def to_cached_resource(
.. testcode::
from url import URL
from referencing import Registry
from referencing.typing import URI
import referencing.retrieval
@referencing.retrieval.to_cached_resource()
def retrieve(uri: URI):
def retrieve(uri: URL):
print(f"Retrieved {uri}")
# Normally, go get some expensive JSON from the network, a file ...
Expand Down Expand Up @@ -74,9 +77,9 @@ def retrieve(uri: URI):
if cache is None:
cache = lru_cache(maxsize=None)

def decorator(retrieve: Callable[[URI], _T]):
def decorator(retrieve: Callable[[URL], _T]):
@cache
def cached_retrieve(uri: URI):
def cached_retrieve(uri: URL):
response = retrieve(uri)
contents = loads(response)
return from_contents(contents)
Expand Down
7 changes: 3 additions & 4 deletions referencing/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@


if TYPE_CHECKING:
from referencing._core import Resolved, Resolver, Resource
from url import URL

#: A URI which identifies a `Resource`.
URI = str
from referencing._core import Resolved, Resolver, Resource

#: The type of documents within a registry.
D = TypeVar("D")
Expand All @@ -30,7 +29,7 @@ class Retrieve(Protocol[D]):
Does not make assumptions about where the resource might be coming from.
"""

def __call__(self, uri: URI) -> Resource[D]:
def __call__(self, uri: URL) -> Resource[D]:
"""
Retrieve the resource with the given URI.
Expand Down

0 comments on commit 61982db

Please sign in to comment.