Skip to content

URI & Schemes API

pathlib_next.uri

Uri(*uris, **options)

Bases: Pathname

A pure (no I/O) RFC 3986 URI, lazily parsed into source (scheme/ userinfo/host/port), path, query, and fragment on first access. Join semantics (multiple constructor args, or /) are pathlib- joinpath-like, not RFC 3986 reference resolution -- see _load_parts's docstring and docs/divergences.md.

Source code in src/pathlib_next/uri/__init__.py
 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
def __init__(self, *uris: UriLike, **options):
    if self._raw_uris or self._initiated:
        return
    _uris: list[str | Uri] = []
    for uri in uris:
        if not uri:
            uri = ""
        if isinstance(uri, Uri):
            _uris.append(uri)
        elif isinstance(uri, (_pathlib.Path, Path)):
            try:
                uri = uri.as_uri()
            except ValueError:
                # as_uri() raises ValueError for a relative path.
                uri = f"file:{_uriencode(uri.as_posix(), safe='/')}"
            _uris.append(uri)
        elif isinstance(uri, (_pathlib.PurePath, Pathname)):
            _uris.append(f"{_uriencode(uri.as_posix(), safe='/')}")
        elif hasattr(uri, "as_uri"):
            path = uri.as_uri
            if callable(path):
                path = path()
            _uris.append(path)
        elif isinstance(uri, str):
            _uris.append(uri)
        elif isinstance(uri, bytes):
            _uris.append(uri.decode())
        else:
            path = None
            try:
                path = os.fspath(uri)
            except (TypeError, NotImplementedError):
                pass
            if not isinstance(path, str):
                raise TypeError(
                    "argument should be a str or an os.PathLike "
                    "object where __fspath__ returns a str, "
                    f"not {type(path).__name__!r}"
                )
            # Only __fspath__ is guaranteed here -- posix-normalize the
            # string itself rather than assuming an as_posix() method.
            posix = _pathlib.PurePath(path).as_posix()
            _uris.append(f"{_uriencode(posix, safe='/')}")
    self._raw_uris = _uris

normalized_path property

Return the normalized path using posixpath rules.

parent property

The logical parent of the path.

parts property

The tuple of URI components: (source, path, query, fragment).

__str__()

Return the string representation of the path, suitable for passing to system calls.

Source code in src/pathlib_next/uri/__init__.py
254
255
256
257
def __str__(self):
    """Return the string representation of the path, suitable for
    passing to system calls."""
    return self.as_uri(sanitize=True)

is_absolute()

True if the path is absolute.

Source code in src/pathlib_next/uri/__init__.py
406
407
408
def is_absolute(self):
    """True if the path is absolute."""
    return bool(self.source) and self.path.startswith("/")

is_local()

Return True if the URI points to a local resource.

Source code in src/pathlib_next/uri/__init__.py
452
453
454
def is_local(self):
    """Return True if the URI points to a local resource."""
    return self.source.is_local()

is_relative_to(other)

Return True if the path is relative to another path or False.

Source code in src/pathlib_next/uri/__init__.py
410
411
412
413
414
415
416
417
418
419
420
421
422
def is_relative_to(self, other: UriLike):
    """Return True if the path is relative to another path or False."""
    other = other if isinstance(other, Uri) else Uri(self, _ROOT, other)
    if not (
        (other.source == self.source)
        or not (bool(self.source) and bool(other.source))
    ):
        return False
    # Segment-wise prefix comparison: a naive startswith() on the raw
    # strings would report "/foo/bar2" as relative to "/foo/bar".
    _other = _segments_of(other.normalized_path)
    _self = _segments_of(self.normalized_path)
    return _self[: len(_other)] == _other

with_fragment(fragment)

Return a new URI with the fragment replaced.

Source code in src/pathlib_next/uri/__init__.py
356
357
358
def with_fragment(self, fragment: str):
    """Return a new URI with the fragment replaced."""
    return self._from_parsed_parts(self.source, self.path, self.query, fragment)

with_path(path)

Return a new URI with the path replaced.

Source code in src/pathlib_next/uri/__init__.py
341
342
343
344
345
346
347
348
def with_path(self, path: str | Pathname):
    """Return a new URI with the path replaced."""
    return self._from_parsed_parts(
        self.source,
        path.as_posix() if isinstance(path, Pathname) else path,
        self.query,
        self.fragment,
    )

with_query(query)

Return a new URI with the query replaced.

Source code in src/pathlib_next/uri/__init__.py
350
351
352
353
354
def with_query(self, query: str):
    """Return a new URI with the query replaced."""
    if not isinstance(query, Query):
        query = Query(query)
    return self._from_parsed_parts(self.source, self.path, query, self.fragment)

with_segments(*segments)

Return a new URI with the path segments replaced.

Source code in src/pathlib_next/uri/__init__.py
335
336
337
338
339
def with_segments(self, *segments: str):
    """Return a new URI with the path segments replaced."""
    if not segments:
        return self.with_path("")
    return self.with_path("/".join(segments))

with_source(source)

Return a new URI with the source replaced.

Source code in src/pathlib_next/uri/__init__.py
331
332
333
def with_source(self, source: Source):
    """Return a new URI with the source replaced."""
    return self._from_parsed_parts(source, self.path, self.query, self.fragment)

UriPath(*uris, **options)

Bases: Uri, Path

Uri + Path (I/O) + scheme dispatch. UriPath(...) constructs the concrete subclass registered for the URI's scheme (via __SCHEMES) -- e.g. UriPath("http://...") returns an HttpPath. Subclass this and set __SCHEMES to add a new scheme (Track B of extending this library; see docs/guides/extending.md); implement the I/O surface (_listdir or _scandir, stat, _open, ...) documented in docs/guides/extending.md. Prefer overriding _scandir() over _listdir() when the listing call already returns type/size/mtime metadata (PROPFIND, MLSD, listdir_attr, an S3 list page, ...) -- walk()/glob() then answer is_dir() on the results for free, without a stat request per entry.

Source code in src/pathlib_next/uri/__init__.py
 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
def __init__(self, *uris: UriLike, **options):
    if self._raw_uris or self._initiated:
        return
    _uris: list[str | Uri] = []
    for uri in uris:
        if not uri:
            uri = ""
        if isinstance(uri, Uri):
            _uris.append(uri)
        elif isinstance(uri, (_pathlib.Path, Path)):
            try:
                uri = uri.as_uri()
            except ValueError:
                # as_uri() raises ValueError for a relative path.
                uri = f"file:{_uriencode(uri.as_posix(), safe='/')}"
            _uris.append(uri)
        elif isinstance(uri, (_pathlib.PurePath, Pathname)):
            _uris.append(f"{_uriencode(uri.as_posix(), safe='/')}")
        elif hasattr(uri, "as_uri"):
            path = uri.as_uri
            if callable(path):
                path = path()
            _uris.append(path)
        elif isinstance(uri, str):
            _uris.append(uri)
        elif isinstance(uri, bytes):
            _uris.append(uri.decode())
        else:
            path = None
            try:
                path = os.fspath(uri)
            except (TypeError, NotImplementedError):
                pass
            if not isinstance(path, str):
                raise TypeError(
                    "argument should be a str or an os.PathLike "
                    "object where __fspath__ returns a str, "
                    f"not {type(path).__name__!r}"
                )
            # Only __fspath__ is guaranteed here -- posix-normalize the
            # string itself rather than assuming an as_posix() method.
            posix = _pathlib.PurePath(path).as_posix()
            _uris.append(f"{_uriencode(posix, safe='/')}")
    self._raw_uris = _uris

backend property

The connection or session state backend instance.

with_backend(backend)

Return a new path instance sharing the same backend state.

Source code in src/pathlib_next/uri/__init__.py
639
640
641
def with_backend(self, backend):
    """Return a new path instance sharing the same backend state."""
    return self._from_parsed_parts(*self.parts, backend=backend)

pathlib_next.uri.source

Source

Bases: NamedTuple

A URI's scheme/userinfo/host/port -- everything before the path. Falsy (bool(source) is False) when every field is empty/None.

is_local() cached

Whether host resolves to this machine.

Caches per unique Source (Source is an immutable value type), since this does a DNS lookup (socket.gethostbyname) -- never call it on a hot path uncached.

Source code in src/pathlib_next/uri/source.py
256
257
258
259
260
261
262
263
264
265
266
267
268
269
@_functools.lru_cache(maxsize=256)
def is_local(self):
    """Whether `host` resolves to this machine.

    Caches per unique Source (Source is an immutable value type), since
    this does a DNS lookup (socket.gethostbyname) -- never call it on a
    hot path uncached.
    """
    host = self.host
    if not host or host == "localhost":
        return True
    if isinstance(host, str):
        host = _ip.ip_address(_socket.gethostbyname(host))
    return host.is_loopback or host in _utils.get_machine_ips()

pathlib_next.uri.query

Query

Bases: str

A URI query string (str subclass) that can also be built from a dict/list of pairs and decoded back with to_dict()/iteration.