# imports import typing # bsfs imports from bsfs.utils import URI, typename # exports __all__: typing.Sequence[str] = ( 'ClosedNamespace', 'Namespace', ) ## code ## class Namespace(): """A namespace consists of a common prefix that is used in a set of URIs. Note that the prefix must include the separator between path and fragment (typically a '#' or a '/'). """ # namespace prefix. prefix: URI # fragment separator. fsep: str # path separator. psep: str def __init__(self, prefix: URI, fsep: str = '#', psep: str = '/'): # ensure prefix type prefix = URI(prefix) # truncate fragment separator while prefix.endswith(fsep): prefix = URI(prefix[:-1]) # truncate path separator while prefix.endswith(psep): prefix = URI(prefix[:-1]) # store members self.prefix = prefix self.fsep = fsep self.psep = psep def __eq__(self, other: typing.Any) -> bool: return isinstance(other, type(self)) \ and self.prefix == other.prefix \ and self.fsep == other.fsep \ and self.psep == other.psep def __hash__(self) -> int: return hash((type(self), self.prefix, self.fsep, self.psep)) def __str__(self) -> str: return str(self.prefix) def __repr__(self) -> str: return f'{typename(self)}({self.prefix}, {self.fsep}, {self.psep})' def __getattr__(self, fragment: str) -> URI: """Return prefix + fragment.""" return URI(self.prefix + self.fsep + fragment) def __getitem__(self, fragment: str) -> URI: """Alias for getattr(self, fragment).""" return self.__getattr__(fragment) def __add__(self, value: typing.Any) -> 'Namespace': """Concatenate another namespace to this one.""" if not isinstance(value, str): return NotImplemented return Namespace(self.prefix + self.psep + value, self.fsep, self.psep) class ClosedNamespace(Namespace): """Namespace that covers a restricted set of URIs.""" # set of permissible fragments. fragments: typing.Set[str] def __init__(self, prefix: URI, *args: str, fsep: str = '#', psep: str = '/'): super().__init__(prefix, fsep, psep) self.fragments = set(args) def __eq__(self, other: typing.Any) -> bool: return super().__eq__(other) and self.fragments == other.fragments def __hash__(self) -> int: return hash((type(self), self.prefix, tuple(sorted(self.fragments)))) def __getattr__(self, fragment: str) -> URI: """Return prefix + fragment or raise a KeyError if the fragment is not part of this namespace.""" if fragment not in self.fragments: raise KeyError(f'{fragment} is not a valid fragment of namespace {self.prefix}') return super().__getattr__(fragment) ## EOF ##