跳转到主要内容

相较于标准pathlib模块和pathlib2包的改进

项目描述

ruamel.std.pathlib

包ruamel.std.pathlib是一个用于扩展Python标准pathlib模块的即插即用替代品。

您可以简单地替换

from pathlib import PosixPath, Path

from ruamel.std.pathlib import PosixPath, Path

image image image

从0.8.0版本开始,ruamel.std.pathlib不再支持Python 2

额外的路径功能

  • 将Path上的remove别名设置为unlink
  • 向Path添加来自shutil的copy()rmtree()
  • 向path添加hash(),返回文件内容的hashlib.new()值(默认为'sha224',可以提供其他算法作为参数: print(p.hash('md5').hexdigest())
  • 向一个(目录)Path添加pushd()popd(),这保留了一个可能包含多个条目的堆栈。
  • 向Path添加rmtree(),在Path上调用shutil.rmtree()
  • 向一个(目录)Path添加chdir()
  • 向Path添加stabilise(),等待它不再改变,可以设置duration(默认5秒),recheck(默认0.2秒),以及如果尚未稳定,则显示一次的消息。

blake3

当你安装了blake3(这不是此包的依赖项)并执行import ruamel.std.pathlib.blake3时,你还可以在Path实例上使用该方法。

json

当你执行import ruamel.std.pathlib.json时,这将按顺序加载orjson/ujson/json到一个Path,这样你就可以执行data = path.json.load()path.json.dump(data)

msgpack

当你安装了ruamel.ext.msgpack并执行import ruamel.std.pathlib.msgpack并且有一个Path实例path时,你可以执行data = path.msgpack.load()path.msgpack.dump(data)

tar

当您已安装 zstandard 并执行 import ruamel.std.pathlib.tar,并且拥有一个 Path 实例 path 时,您可以对 .tar.zst 文件执行 with path.tar.open() 操作,实现内存中的解压缩。

转换助手

如果您刚开始使用标准的 pathlib 库,一次性更改所有内容会很麻烦,同时还需要将所有调用 os.path.joinos.renameos.path.dirname 的参数封装在 str() 中。

通过创建一个名为 plPathLibConversionHelper 实例,您可以将 os.path.join() 更改为 pl.path.join() 等,然后开始传递 Path 实例而不是字符串。

PathLibConversionHelper 目前支持以下对 os 的替换:

: os.pathshutil 和内置函数

    .chdir()             replaces: os.chdir()
    .copy()              replaces: shutil.copy()
    .glob()              replaces: glob.glob()
    .listdir()           replaces: os.listdir()
    .makedirs()          replaces: os.makedirs()
    .mkstemp()           replaces: tempfile.mkstemp()
    .open()              replaces: built-in open()
    .path.basename()     replaces: os.path.basename()
    .path.dirname()      replaces: os.path.dirname()
    .path.exists()       replaces: os.path.exists()
    .path.expanduser()   replaces: os.path.expanduser()
    .path.getmtime()     replaces: os.path.getmtime()
    .path.isdir()        replaces: os.path.isdir()
    .path.join()         replaces: os.path.join()
    .path.splitext()     replaces: os.path.splitext()
    .remove()            replaces: os.remove()
    .rename()            replaces: os.rename()
    .rmdir()             replaces: os.rmdir()
    .rmtree()            replaces: shutil.rmtree()
    .walk()              replaces: os.walk()

在创建 PathLibConversionHelper() 实例时,您可以提供检查级别。

  • 如果检查值非零,所有调用都将被记录,并且调用可以通过 pl.dump(stream, show_all=False) 输出,例如在程序结束时。这将包括未使用 Path 的调用次数(以及如果 show_all=True,仅使用 Path 的调用次数)。
  • 如果检查值大于 1,则立即输出第一次使用。

如果您开始使用以下代码

# coding: utf-8

import os
import glob
import tempfile
import shutil
import random


class TempDir(object):
    """self removing (unless keep=True) temporary directory"""
    def __init__(self, keep=False, basedir=None, prefix=None):
        self._keep = keep
        # mkdtemp creates with permissions rwx------
        kw = dict(dir=basedir)
        if prefix is not None:
            kw['prefix'] = prefix
        # mkdtemp doesn't do the right thing if None is passed in
        # as it has prefix=template in definition
        self._tmpdir = tempfile.mkdtemp(**kw)

    def remove(self):
        shutil.rmtree(self._tmpdir)

    def chdir(self):
        os.chdir(self._tmpdir)

    def tempfilename(self, extension=''):
        fd, name = tempfile.mkstemp(suffix=extension, dir=self._tmpdir)
        os.close(fd)
        return name

    def tempfilename2(self, extension=''):
        while True:
            name = os.path.join(
                self._tmpdir,
                '%08d' % random.randint(0, 100000) + extension
            )
            if not os.path.exists(name):
                break
        return name

    @property
    def directory(self):
        return self._tmpdir

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not self._keep:
            self.remove()


def main():
    """contrived example using TempDir"""
    org_dir = os.getcwd()
    with TempDir() as td:
        for n in range(3):
            t1 = td.tempfilename(extension='.sample')
            with open(t1, 'w') as fp:
                fp.write('content\n')
        t2 = td.tempfilename2(extension='.sample2')
        with open(t2, 'w') as fp:
            fp.write('content\n')
        os.chdir(td.directory)
        count = 0
        for file_name in glob.glob('*.samp*'):
            full_name = os.path.join(os.getcwd(), file_name)  # noqa
            # print(full_name)
            count += 1
        os.chdir('/tmp')  # not using Path
        os.chdir(org_dir)
    print('{} files found in temporary directory'.format(count))

main()

您将得到

4 files found in temporary directory

当您开始将 TempDir() 更改为将实际目录作为 Path 存储时,事情会立即开始出错

# coding: utf-8

import os
import glob
import tempfile
import shutil
import random

from ruamel.std.pathlib import Path                                   # added


class TempDir(object):
    """self removing (unless keep=True) temporary directory"""
    def __init__(self, keep=False, basedir=None, prefix=None):
        self._keep = keep
        # mkdtemp creates with permissions rwx------
        kw = dict(dir=basedir)
        if prefix is not None:
            kw['prefix'] = prefix
        # mkdtemp doesn't do the right thing if None is passed in
        # as it has prefix=template in definition
        self._tmpdir = Path(tempfile.mkdtemp(**kw))                   # changed

    def remove(self):
        shutil.rmtree(self._tmpdir)

    def chdir(self):
        os.chdir(self._tmpdir)

    def tempfilename(self, extension=''):
        fd, name = tempfile.mkstemp(suffix=extension, dir=self._tmpdir)
        os.close(fd)
        return name

    def tempfilename2(self, extension=''):
        while True:
            name = os.path.join(
                self._tmpdir,
                '%08d' % random.randint(0, 100000) + extension
            )
            if not os.path.exists(name):
                break
        return name

    @property
    def directory(self):
        return self._tmpdir

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not self._keep:
            self.remove()


def main():
    """contrived example using TempDir"""
    org_dir = os.getcwd()
    with TempDir() as td:
        for n in range(3):
            t1 = td.tempfilename(extension='.sample')
            with open(t1, 'w') as fp:
                fp.write('content\n')
        t2 = td.tempfilename2(extension='.sample2')
        with open(t2, 'w') as fp:
            fp.write('content\n')
        os.chdir(td.directory)
        count = 0
        for file_name in glob.glob('*.samp*'):
            full_name = os.path.join(os.getcwd(), file_name)  # noqa
            # print(full_name)
            count += 1
        os.chdir('/tmp')  # not using Path
        os.chdir(org_dir)
    print('{} files found in temporary directory'.format(count))

main()

有一些错误

Traceback (most recent call last):
  File "_example/stage1.py", line 80, in <module>
    main()
  File "_example/stage1.py", line 77, in main
    os.chdir(org_dir)
  File "_example/stage1.py", line 56, in __exit__
    self.remove()
  File "_example/stage1.py", line 27, in remove
    shutil.rmtree(self._tmpdir)
  File "/opt/python/2.7.13/lib/python2.7/shutil.py", line 228, in rmtree
    if os.path.islink(path):
  File "/home/venv/dev/lib/python2.7/posixpath.py", line 135, in islink
    st = os.lstat(path)
TypeError: coercing to Unicode: need string or buffer, PosixPath found

而不是一次性更改程序中的所有使用,并希望它再次工作,您替换了标准模块中的例程

# coding: utf-8

import os
import glob
import tempfile
import shutil                       # noqa
import random

from ruamel.std.pathlib import Path, PathLibConversionHelper            # changed
pl = PathLibConversionHelper()                                          # added


class TempDir(object):
    """self removing (unless keep=True) temporary directory"""
    def __init__(self, keep=False, basedir=None, prefix=None):
        self._keep = keep
        # mkdtemp creates with permissions rwx------
        kw = dict(dir=basedir)
        if prefix is not None:
            kw['prefix'] = prefix
        # mkdtemp doesn't do the right thing if None is passed in
        # as it has prefix=template in definition
        self._tmpdir = Path(tempfile.mkdtemp(**kw))

    def remove(self):
        pl.rmtree(self._tmpdir)

    def chdir(self):
        os.chdir(self._tmpdir)

    def tempfilename(self, extension=''):
        fd, name = pl.mkstemp(suffix=extension, dir=self._tmpdir)     # changed
        os.close(fd)
        return name

    def tempfilename2(self, extension=''):
        while True:
            name = pl.path.join(
                self._tmpdir,
                '%08d' % random.randint(0, 100000) + extension
            )
            if not pl.path.exists(name):                              # changed
                break
        return name

    @property
    def directory(self):
        return self._tmpdir

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not self._keep:
            self.remove()


def main():
    """contrived example using TempDir"""
    org_dir = os.getcwd()
    with TempDir() as td:
        for n in range(3):
            t1 = td.tempfilename(extension='.sample')
            with open(t1, 'w') as fp:
                fp.write('content\n')
        t2 = td.tempfilename2(extension='.sample2')
        with pl.open(t2, 'w') as fp:
            c = 'content\n'                                           # added
            if not isinstance(fp, file):                              # added
                c = unicode(c)                                        # added
            fp.write(c)                                               # changed
        pl.chdir(td.directory)
        count = 0
        for file_name in glob.glob('*.samp*'):
            full_name = pl.path.join(os.getcwd(), file_name)  # noqa  # changed
            # print(full_name)
            count += 1
        pl.chdir('/tmp')  # not using Path
        pl.chdir(org_dir)                                             # changed
    print('{} files found in temporary directory'.format(count))

main()

(再次)

4 files found in temporary directory

只需将 self._tempdir 的创建改回原始版本

self._tmpdir = tempfile.mkdtemp(**kw)

输出保持不变

4 files found in temporary directory

如果您现在将 pl 的创建改为

pl = PathLibConversionHelper(check=2)

您将得到以下输出

update .mkstemp to use Path.mkstemp() [_example/stage3.py:34 / Path (True,)]
update .path.join to use "/" [_example/stage3.py:42 / Path (True, False)]
update .exists to use Path.exists() [_example/stage3.py:44 / Path (True,)]
update .open to use Path.open() [_example/stage3.py:69 / Path (True,)]
update .chdir to use Path.chdir() or os.chdir(str(Path)) [_example/stage3.py:74 / Path (True,)]
update .path.join to use "/" [_example/stage3.py:77 / Path (False, False)]
update .chdir to use Path.chdir() or os.chdir(str(Path)) [_example/stage3.py:80 / Path (False,)]
update .chdir to use Path.chdir() or os.chdir(str(Path)) [_example/stage3.py:81 / Path (False,)]
update .rmtree to use Path.rmtree() or shutil.rmtree(str(Path)) [_example/stage3.py:28 / Path (True,)]
4 files found in temporary directory

如果您使用 check=1 并在最后执行 pl.dump(),您将得到

4 files found in temporary directory
update .path.join to use "/" [_example/stage4.py:42 / 1 / Path (True, False)]
update .chdir to use Path.chdir() or os.chdir(str(Path)) [_example/stage4.py:81 / 1 / Path (False,)]
update .path.join to use "/" [_example/stage4.py:77 / 4 / Path (False, False)]
update .chdir to use Path.chdir() or os.chdir(str(Path)) [_example/stage4.py:80 / 1 / Path (False,)]

显示您仍然使用的基于字符串的路径/文件名。

消息部分 file_name.py: 123 / 2 / Path (True, False) 表示在 file_name.py 中的第 123 行有两次调用,并且它们调用时第一个参数是 Path,第二个不是 Path(当将 os.path.join() 替换为 Path 的 / 连接运算符时,这是一个很好的起点,对于其他情况,您可能还需要将第二个参数转换为 Path 实例)。

扩展 PathLibConversionHelper

如果 PathLibConversionHelper 还没有包含某个特定函数,您可以轻松地继承它并添加自己的。

from ruamel.std.pathlib import Path, PathLibConversionHelper


class MyPLCH(PathLibConversionHelper):
    # an example, ruamel.std.pathlib already adds mkstemp
    def mkstemp(self, suffix="", prefix=None, dir=None, text=False):
        import tempfile
        # would be much better if prefix defaults to built-in value (int, None, string)
        if prefix is None:
            prefix = tempfile.template
        self.__add_usage(dir, 'update .mkstemp to use Path.mkstemp()')
        if isinstance(dir, Path):
            dir = str(dir)
        return tempfile.mkstemp(suffix, prefix, dir, text)

pl = MyPLCH(check=1)

self.add_usage() 的第一个参数用于确定是否使用了 Path。这应该是一个包含所有相关变量(可以是 Path 实例也可以不是)的列表。如果列表只有一个元素,则不需要作为列表传入(如示例所示)。第二个参数应该是一个字符串,其中包含有关进一步消除对 .mkstemp() 调用的帮助信息。

项目详情


下载文件

下载您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源代码分发

ruamel.std.pathlib-0.13.0.tar.gz (25.5 kB 查看散列)

上传于 源代码

构建的发行版

ruamel.std.pathlib-0.13.0-py3-none-any.whl (14.5 kB 查看哈希值)

上传于 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面