Skip to content

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 12:58

pathlib-next 0.10.0 is the core alone: the path classes, pathlib_next.http,
FTPPath and the archive paths, with no dependency, no extra and no console
script. The URI layer, every scheme that needs a library, and the uripath
command are distributions of their own that depend on it. Under Changed, the
migration comes in this order: the table of names, then every change of
behaviour with what to do about it.

Added

  • pathlib_next.http: HTTP requests on the standard library, with a
    transport you can replace, and no URI.
    import pathlib_next.http needs no
    extra and no third-party library (it loads http.client and ssl;
    import pathlib_next still loads neither). HTTPClient(transport=None, *, headers=None, timeout=(10, 60), user_agent=None) sends request(method, url, *, data=None, headers=None, stream=False, allow_redirects=None, timeout, auth=None) through a transport and follows redirects: GET,
    HEAD and OPTIONS up to 30, with Authorization dropped for good by a hop
    to another scheme, host or port; every other method is sent again once after
    a 307 or 308 to the same origin and is otherwise an OSError(EIO) naming the
    status. A transport is any object with send(method, url, *, headers, body=None, timeout, stream=False) that returns an HTTPResponse, and a
    close(); HTTPTransport(*, ssl_context=None, verify=True, proxies=None) is
    the built-in one on http.client (TLS verified by default, proxies= as an
    object indexed with the request URL, one idle connection per thread and
    server). The package also holds the helpers a path built on it needs:
    raise_for_status, translate_errors (a failed request as the OSError
    type pathlib raises), read_limited, json_body and drop_body (a reply
    read whole is held to MAX_REPLY_BYTES, 8 MiB after decoding, and the
    setting is assignable), peer_text, split_userinfo, server_size and
    is_redirect. PATHLIB_NEXT_HTTP_TRANSPORT names the transport a client
    builds when it is given none: builtin, or the name of one an installed
    distribution declares in the entry-point group pathlib_next.http_transports
    (requests, from the pathlib-next-requests distribution; transport_names()
    lists them without importing any and get_transport(name) loads one).
    prepare_request() makes the checks of the transport contract for a
    transport of your own.
  • pathlib_next.http.testing: the suite that holds a transport to the
    promises of the built-in one.
    With the distribution that provides the
    transport and pytest installed, python -m pathlib_next.http.testing --http-transports requests -q runs the tests that depend on the transport
    against it, over loopback servers scripted at the byte level. The test
    files ship in the wheel. It is testing support and not part of the stable
    API: its contents, including test names, fixtures and helper signatures, may
    change in any release, and pathlib_next.http never imports it.
  • pathlib_next.ftp: FTPPath and FTPBackend, an FTP tree that needs no
    URI and no extra.
    FTPPath("/pub/x", host="h", user="u", tls=True) builds
    the path's own FTPBackend; FTPPath("/pub/x", backend=backend) shares one
    connection cache between paths built separately (or plugs in a
    BaseFTPBackend of your own), and giving both is a TypeError. Nothing
    connects until the first operation. FTPBackend(host, port=21, *, user, password, tls, timeout, ssl_context, verify) keeps a control connection per
    thread, is a context manager, and close() ends every connection it holds.
    str() is the remote path, repr() names the endpoint and never shows a
    password, and two paths are one tree when they share a backend or name the
    same host, port, account and security. pathlib_next.FTPPath is imported on
    first use, so import pathlib_next still loads neither ftplib nor ssl.
  • pathlib_next.archive: ZipPath, TarPath and ArchivePath, the members
    of an archive file that is any Path, with no URI and no extra.

    ZipPath("d/x", archive="a.zip") takes the archive as a Path, or a str
    or os.PathLike (a LocalPath); the segments are the member path, read
    from the archive root. ZipPath and TarPath fix the format, ArchivePath
    reads it from the extension or the first bytes on first use, and building a
    path reads nothing. One handle serves each archive file and format, so paths
    built separately see each other's writes. A zip that is a local file is
    written in place (write_bytes, mkdir, unlink, rmdir, rename,
    rm(recursive=True), batch()); every other archive is read-only, in
    memory or on any other Path. str() is the member path, repr() names the
    archive, and as_uri() is zip:<archive uri>!/<member>.
    ArchivePath.backend_for() gives the handle to pass as archive=.
    pathlib_next.ZipPath, TarPath and ArchivePath are imported on first use.
  • pathlib_next.providers.TreePath, the base FTPPath and the archive
    paths are built on, for a path whose names are POSIX segments below the
    root of a tree a backend serves.
  • pathlib_next.providers.name_stem(), name_suffix() and
    name_suffixes().
    One path component split into stem and suffix as
    pathlib of the running interpreter splits it, for a class that computes
    those itself and must agree with Pathname.
  • pathlib_next.exceptions: PathError, the base of the exceptions the
    package defines.
    NonRelativePatternError (an absolute glob pattern) is
    now a PathError as well as a NotImplementedError and a ValueError, so
    every except clause that caught it still does. I/O failures stay the
    OSError types pathlib raises. PathError, NonRelativePatternError and
    MemPath are exported from pathlib_next.
  • pathlib_next.mempath, pathlib_next.sync and pathlib_next.testing
    declare __all__
    , so from pathlib_next.sync import * publishes
    PathSyncer, SyncEvent and PathAndStat and nothing else, and a type
    checker reads those names as the module's exports.
  • pathlib_next.providers.DiscardOnError, a mixin for a write stream that
    commits when it is closed: its __exit__ calls discard() when the with
    block raised, then closes. CommitOnClose inherits it and has a
    discard(); see the entry under Changed.

Changed

  • Where every name went. Change each import and each install as the table
    says. There is no alias for an old name: the old import raises
    ModuleNotFoundError or ImportError, and a name that is not in the table
    was never public. The URI layer, the schemes and the command line are the
    distributions uripath, sftppath, objpath, repopath and
    pathlib-next-requests, which each document their own changes of behaviour;
    the core's own moves come first.

    Was Is now
    pathlib_next.utils.sync (PathSyncer, SyncEvent, PathAndStat) pathlib_next.sync
    pathlib_next.utils.glob (glob, select, full_match, parse_pattern, ...) pathlib_next.glob
    pathlib_next.utils.glob.NonRelativePatternError pathlib_next.NonRelativePatternError (defined in pathlib_next.exceptions)
    pathlib_next.utils.checksum (md5, sha256, stream, native) pathlib_next.checksum
    pathlib_next.utils.md5, pathlib_next.utils.sha256 pathlib_next.checksum.md5, pathlib_next.checksum.sha256
    pathlib_next.utils.archive, pathlib_next.utils.make_archive, pathlib_next.utils.unpack_archive pathlib_next.archive.make_archive, pathlib_next.archive.unpack_archive
    pathlib_next.utils.archive.detect_format (also _detect_format) pathlib_next.archive.detect_format
    pathlib_next.utils.stat.FileStat pathlib_next.FileStat
    pathlib_next.utils.as_mode pathlib_next.providers.parse_mode
    pathlib_next.utils.as_owner pathlib_next.providers.parse_owner
    pathlib_next.utils.UNCHANGED pathlib_next.providers.UNCHANGED
    pathlib_next.utils.as_error_handler, as_error_predicate, OfferedOnce pathlib_next.providers.as_error_handler, as_error_predicate, OfferedOnce
    pathlib_next.utils.is_safe_child_name, is_windows_flavoured pathlib_next.providers.is_safe_child_name, is_windows_flavoured
    pathlib_next.utils.notimplemented pathlib_next.providers.notimplemented
    pathlib_next.utils.LRU pathlib_next.providers.LRU
    pathlib_next.utils._commit.CommitOnClose (also _CommitOnClose) pathlib_next.providers.CommitOnClose
    pathlib_next.path.check_follow, contains, copy_error_predicate (also _check_follow, _contains) pathlib_next.providers.check_follow, contains, copy_error_predicate
    pathlib_next.utils.sizeof_fmt, pathlib_next.utils.parsedate removed: no replacement is public
    the package pathlib_next.uri the distribution uripath, import name uripath
    pathlib_next.Uri, pathlib_next.UriPath (and pathlib_next.uri.Uri, UriPath, UriLike) uripath.URI, uripath.URIPath, uripath.URILike
    Path, Pathname, FileStat imported from pathlib_next.uri the same names in pathlib_next
    pathlib_next.uri.source.Source uripath.Source (also uripath.source.Source)
    pathlib_next.uri.source.compose_uri, compose_host, percent_encode, is_drive, SAFE_PATH, ERRORS the same names in uripath.source
    pathlib_next.uri.same_authority uripath.source.same_authority
    pathlib_next.uri.DerivedBackend uripath.schemes.DerivedBackend
    pathlib_next.uri.query.Query; Query(text), Query.decode() uripath.Query, no longer a str; Query.parse(text), Query.to_pairs()
    Source.from_str(text, strict); Source.as_str(sanitize=True), as_str(sanitize=False) Source.parse(text, *, strict=True); str(source), source.reveal()
    pathlib_next.uri.schemes.file.FileUri, schemes.data.DataUri uripath.schemes.FileURIPath, DataURIPath
    schemes.archive.ZipUri, TarUri, ArchiveUri, ArchiveZipUri, ArchiveTarUri uripath.schemes.ZipURIPath, TarURIPath, ArchiveURIPath, ArchiveZipURIPath, ArchiveTarURIPath
    schemes.ftp.FtpPath uripath.schemes.FTPURIPath for ftp:; pathlib_next.FTPPath for a path with no URI
    schemes.ftp.FtpBackend, BaseFtpBackend uripath.schemes.FTPURIBackend, BaseFTPURIBackend (pathlib_next.ftp.FTPBackend belongs to one host)
    schemes.ftp.DEFAULT_TIMEOUT, IDLE_PROBE_SECONDS; schemes.archive.MAX_LINKS, LinkTarget pathlib_next.ftp and pathlib_next.archive, which define them
    schemes.archive._base.MEMBER_SPOOL_BYTES uripath.schemes.MEMBER_SPOOL_BYTES (pathlib_next.archive.MEMBER_SPOOL_BYTES for ZipPath)
    schemes.http.HttpPath, HttpBackend, HttpWriteStream, HttpAppendStream uripath.schemes.HTTPURIPath, HTTPBackend, HTTPWriteStream, HTTPAppendStream
    schemes.http.HTTPTransport, HTTPResponse pathlib_next.http.HTTPTransport, HTTPResponse
    schemes.http.MAX_LISTING_BYTES pathlib_next.http.MAX_REPLY_BYTES (assign it there)
    schemes.http.RequestsTransport pathlib_next_requests.RequestsTransport, from the distribution pathlib-next-requests
    schemes.dav.DavPath uripath.schemes.DAVURIPath
    schemes.sftp.SftpPath sftppath.SFTPPath for a path built from keywords; sftppath.uri.SFTPURIPath for sftp:
    schemes.sftp.SftpBackend, AsyncsshSftpBackend, BaseSftpBackend, CheckFileSftpBackend sftppath.SFTPBackend, AsyncSSHBackend, BaseSFTPBackend, CheckFileSFTPBackend
    schemes.s3.S3Path, S3Backend, BaseS3Backend objpath.S3Path, S3Backend, BaseS3Backend; objpath.uri.S3URIPath for s3:
    schemes.gs.GsPath, GsBackend, BaseGsBackend objpath.GSPath, GSBackend, BaseGSBackend; objpath.uri.GSURIPath for gs:
    schemes.az.AzPath, AzBackend, BaseAzBackend objpath.AZPath, AZBackend, BaseAZBackend; objpath.uri.AZURIPath for az:
    schemes.github.GitHubPath, schemes.gitlab.GitLabPath repopath.GitHubPath, GitLabPath for paths with no URI; repopath.uri.GitHubURIPath, GitLabURIPath for github:, gitlab:
    schemes.git.GitPath (git:); GitHubGitPath, GitLabGitPath (git+github:, git+gitlab:) repopath.RepoPath, repopath.uri.RepoURIPath (repo:); github: and gitlab: replace git+github: and git+gitlab:; git: itself is gone
    schemes.github.RepoBackend; schemes._gitrepo.BaseRepoBackend; InsecureTransportWarning repopath.RepoBackend, BaseRepoBackend, InsecureTransportWarning
    pathlib_next.tools.uripath (main(argv, *, stdin, stdout, stderr), build_parser()) uripath.cli.main(argv=None); there is no parser to build
    the script uripath = pathlib_next.tools.uripath:main uripath = uripath.cli:main, which needs uripath[cli]; python -m uripath runs it too
    the entry-point group pathlib_next.schemes the group uripath.schemes
    the environment variable PATHLIB_NEXT_SFTP_BACKEND SFTPPATH_BACKEND, read by sftppath; the old name is not read
    the extra pathlib-next[uri] install uripath
    pathlib-next[http] nothing for pathlib_next.http; uripath for http: and dav: paths; pathlib-next-requests for a requests.Session or PATHLIB_NEXT_HTTP_TRANSPORT=requests
    pathlib-next[sftp] sftppath[paramiko]
    pathlib-next[sftp-async] sftppath[asyncssh]
    pathlib-next[s3], [gs], [az] objpath[s3], objpath[gs], objpath[az]
    github: and gitlab: through pathlib-next[uri] or [http] repopath[uri]; repopath alone for GitHubPath and GitLabPath with no URI
    git://github.com/o/r/f, git+github://host/o/r/f, git+gitlab://host/o/r/f repo://github.com/o/r/f, github://host/o/r/f, gitlab://host/o/r/f
    the uripath command, installed with pathlib-next[uri] uripath[cli]
    sftp:, s3:, gs: or az: paths through UriPath with the matching extra add the uri extra to the provider (sftppath[uri], objpath[uri], repopath[uri]), or install uripath beside it

    pathlib_next.path, pathlib_next.glob, pathlib_next.sync and
    pathlib_next.testing are now packages whose private modules hold the code;
    they export the same names, and pathlib_next.path.Path is still
    pathlib_next.Path. A module or name that starts with an underscore is not
    part of the API. Code that builds a class on Path takes its helpers from
    pathlib_next.providers.

  • A write stream that commits on close is not committed when its with block
    raised.
    FTPPath and the members of a zip (ZipPath, ArchivePath) hold
    what is written and send it when the file is closed. A with path.open("wb") as f: block that wrote part of a file and then raised used to close the
    stream on the way out, so the part replaced the complete remote file. Now a
    block that ends in an exception (any BaseException, KeyboardInterrupt
    included) discards what was written: the target keeps its previous content, or
    is not created, and nothing is sent (no STOR or rewrite; a ZipPath inside
    batch() does not stage the member, and the block's other changes still
    commit). The same holds in text mode, where the text the wrapper still held is
    dropped, and for write_bytes(), write_text(), copy() and PathSyncer,
    which open their target in a with block. A block that ends normally, and a
    stream closed with close() outside a with block, commit as before, and an
    error raised by the commit still propagates and is not retried. LocalPath
    and MemPath write through as pathlib does and are unchanged: a failed
    block leaves what was written. If you relied on the partial upload, close
    the stream yourself instead of raising through the block (f.close() commits
    what was written). A path of your own whose _open() returns a stream that
    commits on close gets the same behaviour by returning
    providers.CommitOnClose, or by giving its stream class
    providers.DiscardOnError as its first base and a discard() that drops what
    was written; a stream that has discard() and not the mixin is discarded in
    text mode only. A failed open() of a text file (an unknown encoding=) no
    longer creates an empty file on such a backend.

  • A path class that does not define __fspath__ is not an os.PathLike.
    Every Pathname (MemPath, FTPPath, ZipPath, TarPath, ArchivePath
    and any class built on Path) was registered with os.PathLike, so
    isinstance(path, os.PathLike) was true while os.fspath(path), open(path)
    and pathlib.Path(path) raised NotImplementedError: Method not implemented: __fspath__. Now only a class that defines __fspath__ is one (LocalPath,
    PosixPathname, WindowsPathname, and a class of your own that defines it):
    isinstance(MemPath("/a"), os.PathLike) is false and
    os.fspath(MemPath("/a")) is a TypeError, as for any object. FsPathLike
    stays a protocol for annotations, is no longer a base of Pathname and is no
    longer registered with os.PathLike. What to do: catch TypeError where you
    caught NotImplementedError from os.fspath(), ask isinstance(path, os.PathLike) instead of assuming it, and define __fspath__ on a class of
    your own that has a local name.

  • touch(mode=...) raises where the mode cannot be applied.
    MemPath("/f").touch(mode=0o600), and touch() with a mode on any class
    without chmod(), created the file and returned None, dropping the mode
    without a word. It now raises NotImplementedError and removes the file it
    created. An existing file is left as it is, its mode included, as pathlib
    does. What to do: call touch() without mode on such a class, and chmod()
    where the class has one.

Removed

  • The URI layer, its schemes and the uripath command. pathlib_next.uri
    (Uri, UriPath, Source, Query, every scheme) and pathlib_next.tools
    are gone, pathlib_next no longer exports Uri or UriPath, and
    import pathlib_next never looks for uritools or netimps. The package
    declares no extra but dev and docs: the extras uri, http, sftp,
    sftp-async, s3, gs and az, the entry-point group
    pathlib_next.schemes and the uripath script are removed. A project that
    imported one of those names, installed one of those extras or ran the command
    installs the distribution the table names. Two distributions defining one
    script would collide, which is why the command is not kept here.

Full Changelog: v0.9.14...v0.10.0