Update jaraco-windows to 3.9.2

Also updates:
- importlib-metadata-0.7
- jaraco-windows
- jaraco.classes-1.5
- jaraco.collections-1.6.0
- jaraco.functools-1.20
- jaraco.structures-1.1.2
- jaraco.text-1.10.1
- jaraco.ui-1.6
- more-itertools-4.3.0
- path.py-11.5.0
- six-1.12.0
This commit is contained in:
Labrys of Knossos 2018-12-15 01:17:06 -05:00
commit 8d43b8ea39
92 changed files with 7515 additions and 996 deletions

View file

@ -1,25 +1,3 @@
#
# Copyright (c) 2010 Mikhail Gusarov
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
"""
path.py - An object representing a path to a file or directory.
@ -29,8 +7,18 @@ Example::
from path import Path
d = Path('/home/guido/bin')
# Globbing
for f in d.files('*.py'):
f.chmod(0o755)
# Changing the working directory:
with Path("somewhere"):
# cwd in now `somewhere`
...
# Concatenate paths with /
foo_txt = Path("bar") / "foo.txt"
"""
from __future__ import unicode_literals
@ -41,7 +29,6 @@ import os
import fnmatch
import glob
import shutil
import codecs
import hashlib
import errno
import tempfile
@ -50,8 +37,10 @@ import operator
import re
import contextlib
import io
from distutils import dir_util
import importlib
import itertools
import platform
import ntpath
try:
import win32security
@ -77,22 +66,17 @@ string_types = str,
text_type = str
getcwdu = os.getcwd
def surrogate_escape(error):
"""
Simulate the Python 3 ``surrogateescape`` handler, but for Python 2 only.
"""
chars = error.object[error.start:error.end]
assert len(chars) == 1
val = ord(chars)
val += 0xdc00
return __builtin__.unichr(val), error.end
if PY2:
import __builtin__
string_types = __builtin__.basestring,
text_type = __builtin__.unicode
getcwdu = os.getcwdu
codecs.register_error('surrogateescape', surrogate_escape)
map = itertools.imap
filter = itertools.ifilter
FileNotFoundError = OSError
itertools.filterfalse = itertools.ifilterfalse
@contextlib.contextmanager
def io_error_compat():
@ -107,7 +91,8 @@ def io_error_compat():
##############################################################################
__all__ = ['Path', 'CaseInsensitivePattern']
__all__ = ['Path', 'TempDir', 'CaseInsensitivePattern']
LINESEPS = ['\r\n', '\r', '\n']
@ -119,8 +104,8 @@ U_NL_END = re.compile(r'(?:{0})$'.format(U_NEWLINE.pattern))
try:
import pkg_resources
__version__ = pkg_resources.require('path.py')[0].version
import importlib_metadata
__version__ = importlib_metadata.version('path.py')
except Exception:
__version__ = 'unknown'
@ -131,7 +116,7 @@ class TreeWalkWarning(Warning):
# from jaraco.functools
def compose(*funcs):
compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs))
compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs)) # noqa
return functools.reduce(compose_two, funcs)
@ -170,6 +155,60 @@ class multimethod(object):
)
class matchers(object):
# TODO: make this class a module
@staticmethod
def load(param):
"""
If the supplied parameter is a string, assum it's a simple
pattern.
"""
return (
matchers.Pattern(param) if isinstance(param, string_types)
else param if param is not None
else matchers.Null()
)
class Base(object):
pass
class Null(Base):
def __call__(self, path):
return True
class Pattern(Base):
def __init__(self, pattern):
self.pattern = pattern
def get_pattern(self, normcase):
try:
return self._pattern
except AttributeError:
pass
self._pattern = normcase(self.pattern)
return self._pattern
def __call__(self, path):
normcase = getattr(self, 'normcase', path.module.normcase)
pattern = self.get_pattern(normcase)
return fnmatch.fnmatchcase(normcase(path.name), pattern)
class CaseInsensitive(Pattern):
"""
A Pattern with a ``'normcase'`` property, suitable for passing to
:meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
:meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
For example, to get all files ending in .py, .Py, .pY, or .PY in the
current directory::
from path import Path, matchers
Path('.').files(matchers.CaseInsensitive('*.py'))
"""
normcase = staticmethod(ntpath.normcase)
class Path(text_type):
"""
Represents a filesystem path.
@ -214,16 +253,6 @@ class Path(text_type):
"""
return cls
@classmethod
def _always_unicode(cls, path):
"""
Ensure the path as retrieved from a Python API, such as :func:`os.listdir`,
is a proper Unicode string.
"""
if PY3 or isinstance(path, text_type):
return path
return path.decode(sys.getfilesystemencoding(), 'surrogateescape')
# --- Special Python methods.
def __repr__(self):
@ -277,6 +306,9 @@ class Path(text_type):
def __exit__(self, *_):
os.chdir(self._old_dir)
def __fspath__(self):
return self
@classmethod
def getcwd(cls):
""" Return the current working directory as a path object.
@ -330,23 +362,45 @@ class Path(text_type):
return self.expandvars().expanduser().normpath()
@property
def namebase(self):
def stem(self):
""" The same as :meth:`name`, but with one file extension stripped off.
For example,
``Path('/home/guido/python.tar.gz').name == 'python.tar.gz'``,
but
``Path('/home/guido/python.tar.gz').namebase == 'python.tar'``.
>>> Path('/home/guido/python.tar.gz').stem
'python.tar'
"""
base, ext = self.module.splitext(self.name)
return base
@property
def namebase(self):
warnings.warn("Use .stem instead of .namebase", DeprecationWarning)
return self.stem
@property
def ext(self):
""" The file extension, for example ``'.py'``. """
f, ext = self.module.splitext(self)
return ext
def with_suffix(self, suffix):
""" Return a new path with the file suffix changed (or added, if none)
>>> Path('/home/guido/python.tar.gz').with_suffix(".foo")
Path('/home/guido/python.tar.foo')
>>> Path('python').with_suffix('.zip')
Path('python.zip')
>>> Path('filename.ext').with_suffix('zip')
Traceback (most recent call last):
...
ValueError: Invalid suffix 'zip'
"""
if not suffix.startswith('.'):
raise ValueError("Invalid suffix {suffix!r}".format(**locals()))
return self.stripext() + suffix
@property
def drive(self):
""" The drive specifier, for example ``'C:'``.
@ -437,8 +491,9 @@ class Path(text_type):
@multimethod
def joinpath(cls, first, *others):
"""
Join first to zero or more :class:`Path` components, adding a separator
character (:samp:`{first}.module.sep`) if needed. Returns a new instance of
Join first to zero or more :class:`Path` components,
adding a separator character (:samp:`{first}.module.sep`)
if needed. Returns a new instance of
:samp:`{first}._next_class`.
.. seealso:: :func:`os.path.join`
@ -516,7 +571,7 @@ class Path(text_type):
# --- Listing, searching, walking, and matching
def listdir(self, pattern=None):
def listdir(self, match=None):
""" D.listdir() -> List of items in this directory.
Use :meth:`files` or :meth:`dirs` instead if you want a listing
@ -524,46 +579,39 @@ class Path(text_type):
The elements of the list are Path objects.
With the optional `pattern` argument, this only lists
items whose names match the given pattern.
With the optional `match` argument, a callable,
only return items whose names match the given pattern.
.. seealso:: :meth:`files`, :meth:`dirs`
"""
if pattern is None:
pattern = '*'
return [
self / child
for child in map(self._always_unicode, os.listdir(self))
if self._next_class(child).fnmatch(pattern)
]
match = matchers.load(match)
return list(filter(match, (
self / child for child in os.listdir(self)
)))
def dirs(self, pattern=None):
def dirs(self, *args, **kwargs):
""" D.dirs() -> List of this directory's subdirectories.
The elements of the list are Path objects.
This does not walk recursively into subdirectories
(but see :meth:`walkdirs`).
With the optional `pattern` argument, this only lists
directories whose names match the given pattern. For
example, ``d.dirs('build-*')``.
Accepts parameters to :meth:`listdir`.
"""
return [p for p in self.listdir(pattern) if p.isdir()]
return [p for p in self.listdir(*args, **kwargs) if p.isdir()]
def files(self, pattern=None):
def files(self, *args, **kwargs):
""" D.files() -> List of the files in this directory.
The elements of the list are Path objects.
This does not walk into subdirectories (see :meth:`walkfiles`).
With the optional `pattern` argument, this only lists files
whose names match the given pattern. For example,
``d.files('*.pyc')``.
Accepts parameters to :meth:`listdir`.
"""
return [p for p in self.listdir(pattern) if p.isfile()]
return [p for p in self.listdir(*args, **kwargs) if p.isfile()]
def walk(self, pattern=None, errors='strict'):
def walk(self, match=None, errors='strict'):
""" D.walk() -> iterator over files and subdirs, recursively.
The iterator yields Path objects naming each child item of
@ -593,6 +641,8 @@ class Path(text_type):
raise ValueError("invalid errors parameter")
errors = vars(Handlers).get(errors, errors)
match = matchers.load(match)
try:
childList = self.listdir()
except Exception:
@ -603,7 +653,7 @@ class Path(text_type):
return
for child in childList:
if pattern is None or child.fnmatch(pattern):
if match(child):
yield child
try:
isdir = child.isdir()
@ -615,92 +665,26 @@ class Path(text_type):
isdir = False
if isdir:
for item in child.walk(pattern, errors):
for item in child.walk(errors=errors, match=match):
yield item
def walkdirs(self, pattern=None, errors='strict'):
def walkdirs(self, *args, **kwargs):
""" D.walkdirs() -> iterator over subdirs, recursively.
With the optional `pattern` argument, this yields only
directories whose names match the given pattern. For
example, ``mydir.walkdirs('*test')`` yields only directories
with names ending in ``'test'``.
The `errors=` keyword argument controls behavior when an
error occurs. The default is ``'strict'``, which causes an
exception. The other allowed values are ``'warn'`` (which
reports the error via :func:`warnings.warn()`), and ``'ignore'``.
"""
if errors not in ('strict', 'warn', 'ignore'):
raise ValueError("invalid errors parameter")
return (
item
for item in self.walk(*args, **kwargs)
if item.isdir()
)
try:
dirs = self.dirs()
except Exception:
if errors == 'ignore':
return
elif errors == 'warn':
warnings.warn(
"Unable to list directory '%s': %s"
% (self, sys.exc_info()[1]),
TreeWalkWarning)
return
else:
raise
for child in dirs:
if pattern is None or child.fnmatch(pattern):
yield child
for subsubdir in child.walkdirs(pattern, errors):
yield subsubdir
def walkfiles(self, pattern=None, errors='strict'):
def walkfiles(self, *args, **kwargs):
""" D.walkfiles() -> iterator over files in D, recursively.
The optional argument `pattern` limits the results to files
with names that match the pattern. For example,
``mydir.walkfiles('*.tmp')`` yields only files with the ``.tmp``
extension.
"""
if errors not in ('strict', 'warn', 'ignore'):
raise ValueError("invalid errors parameter")
try:
childList = self.listdir()
except Exception:
if errors == 'ignore':
return
elif errors == 'warn':
warnings.warn(
"Unable to list directory '%s': %s"
% (self, sys.exc_info()[1]),
TreeWalkWarning)
return
else:
raise
for child in childList:
try:
isfile = child.isfile()
isdir = not isfile and child.isdir()
except:
if errors == 'ignore':
continue
elif errors == 'warn':
warnings.warn(
"Unable to access '%s': %s"
% (self, sys.exc_info()[1]),
TreeWalkWarning)
continue
else:
raise
if isfile:
if pattern is None or child.fnmatch(pattern):
yield child
elif isdir:
for f in child.walkfiles(pattern, errors):
yield f
return (
item
for item in self.walk(*args, **kwargs)
if item.isfile()
)
def fnmatch(self, pattern, normcase=None):
""" Return ``True`` if `self.name` matches the given `pattern`.
@ -710,8 +694,8 @@ class Path(text_type):
attribute, it is applied to the name and path prior to comparison.
`normcase` - (optional) A function used to normalize the pattern and
filename before matching. Defaults to :meth:`self.module`, which defaults
to :meth:`os.path.normcase`.
filename before matching. Defaults to :meth:`self.module`, which
defaults to :meth:`os.path.normcase`.
.. seealso:: :func:`fnmatch.fnmatch`
"""
@ -730,10 +714,32 @@ class Path(text_type):
of all the files users have in their :file:`bin` directories.
.. seealso:: :func:`glob.glob`
.. note:: Glob is **not** recursive, even when using ``**``.
To do recursive globbing see :func:`walk`,
:func:`walkdirs` or :func:`walkfiles`.
"""
cls = self._next_class
return [cls(s) for s in glob.glob(self / pattern)]
def iglob(self, pattern):
""" Return an iterator of Path objects that match the pattern.
`pattern` - a path relative to this directory, with wildcards.
For example, ``Path('/users').iglob('*/bin/*')`` returns an
iterator of all the files users have in their :file:`bin`
directories.
.. seealso:: :func:`glob.iglob`
.. note:: Glob is **not** recursive, even when using ``**``.
To do recursive globbing see :func:`walk`,
:func:`walkdirs` or :func:`walkfiles`.
"""
cls = self._next_class
return (cls(s) for s in glob.iglob(self / pattern))
#
# --- Reading or writing an entire file at once.
@ -882,15 +888,9 @@ class Path(text_type):
translated to ``'\n'``. If ``False``, newline characters are
stripped off. Default is ``True``.
This uses ``'U'`` mode.
.. seealso:: :meth:`text`
"""
if encoding is None and retain:
with self.open('U') as f:
return f.readlines()
else:
return self.text(encoding, errors).splitlines(retain)
return self.text(encoding, errors).splitlines(retain)
def write_lines(self, lines, encoding=None, errors='strict',
linesep=os.linesep, append=False):
@ -931,14 +931,15 @@ class Path(text_type):
to read the file later.
"""
with self.open('ab' if append else 'wb') as f:
for l in lines:
isUnicode = isinstance(l, text_type)
for line in lines:
isUnicode = isinstance(line, text_type)
if linesep is not None:
pattern = U_NL_END if isUnicode else NL_END
l = pattern.sub('', l) + linesep
line = pattern.sub('', line) + linesep
if isUnicode:
l = l.encode(encoding or sys.getdefaultencoding(), errors)
f.write(l)
line = line.encode(
encoding or sys.getdefaultencoding(), errors)
f.write(line)
def read_md5(self):
""" Calculate the md5 hash for this file.
@ -952,8 +953,8 @@ class Path(text_type):
def _hash(self, hash_name):
""" Returns a hash object for the file at the current path.
`hash_name` should be a hash algo name (such as ``'md5'`` or ``'sha1'``)
that's available in the :mod:`hashlib` module.
`hash_name` should be a hash algo name (such as ``'md5'``
or ``'sha1'``) that's available in the :mod:`hashlib` module.
"""
m = hashlib.new(hash_name)
for chunk in self.chunks(8192, mode="rb"):
@ -1176,7 +1177,8 @@ class Path(text_type):
gid = grp.getgrnam(gid).gr_gid
os.chown(self, uid, gid)
else:
raise NotImplementedError("Ownership not available on this platform.")
msg = "Ownership not available on this platform."
raise NotImplementedError(msg)
return self
def rename(self, new):
@ -1236,7 +1238,8 @@ class Path(text_type):
self.rmdir()
except OSError:
_, e, _ = sys.exc_info()
if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST:
bypass_codes = errno.ENOTEMPTY, errno.EEXIST, errno.ENOENT
if e.errno not in bypass_codes:
raise
return self
@ -1277,9 +1280,8 @@ class Path(text_type):
file does not exist. """
try:
self.unlink()
except OSError:
_, e, _ = sys.exc_info()
if e.errno != errno.ENOENT:
except FileNotFoundError as exc:
if PY2 and exc.errno != errno.ENOENT:
raise
return self
@ -1306,11 +1308,16 @@ class Path(text_type):
return self._next_class(newpath)
if hasattr(os, 'symlink'):
def symlink(self, newlink):
def symlink(self, newlink=None):
""" Create a symbolic link at `newlink`, pointing here.
If newlink is not supplied, the symbolic link will assume
the name self.basename(), creating the link in the cwd.
.. seealso:: :func:`os.symlink`
"""
if newlink is None:
newlink = self.basename()
os.symlink(self, newlink)
return self._next_class(newlink)
@ -1368,30 +1375,60 @@ class Path(text_type):
cd = chdir
def merge_tree(self, dst, symlinks=False, *args, **kwargs):
def merge_tree(
self, dst, symlinks=False,
# *
update=False,
copy_function=shutil.copy2,
ignore=lambda dir, contents: []):
"""
Copy entire contents of self to dst, overwriting existing
contents in dst with those in self.
If the additional keyword `update` is True, each
`src` will only be copied if `dst` does not exist,
or `src` is newer than `dst`.
Pass ``symlinks=True`` to copy symbolic links as links.
Note that the technique employed stages the files in a temporary
directory first, so this function is not suitable for merging
trees with large files, especially if the temporary directory
is not capable of storing a copy of the entire source tree.
Accepts a ``copy_function``, similar to copytree.
To avoid overwriting newer files, supply a copy function
wrapped in ``only_newer``. For example::
src.merge_tree(dst, copy_function=only_newer(shutil.copy2))
"""
update = kwargs.pop('update', False)
with tempdir() as _temp_dir:
# first copy the tree to a stage directory to support
# the parameters and behavior of copytree.
stage = _temp_dir / str(hash(self))
self.copytree(stage, symlinks, *args, **kwargs)
# now copy everything from the stage directory using
# the semantics of dir_util.copy_tree
dir_util.copy_tree(stage, dst, preserve_symlinks=symlinks,
update=update)
dst = self._next_class(dst)
dst.makedirs_p()
if update:
warnings.warn(
"Update is deprecated; "
"use copy_function=only_newer(shutil.copy2)",
DeprecationWarning,
stacklevel=2,
)
copy_function = only_newer(copy_function)
sources = self.listdir()
_ignored = ignore(self, [item.name for item in sources])
def ignored(item):
return item.name in _ignored
for source in itertools.filterfalse(ignored, sources):
dest = dst / source.name
if symlinks and source.islink():
target = source.readlink()
target.symlink(dest)
elif source.isdir():
source.merge_tree(
dest,
symlinks=symlinks,
update=update,
copy_function=copy_function,
ignore=ignore,
)
else:
copy_function(source, dest)
self.copystat(dst)
#
# --- Special stuff from os
@ -1410,19 +1447,23 @@ class Path(text_type):
# in-place re-writing, courtesy of Martijn Pieters
# http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/
@contextlib.contextmanager
def in_place(self, mode='r', buffering=-1, encoding=None, errors=None,
newline=None, backup_extension=None):
def in_place(
self, mode='r', buffering=-1, encoding=None, errors=None,
newline=None, backup_extension=None,
):
"""
A context in which a file may be re-written in-place with new content.
A context in which a file may be re-written in-place with
new content.
Yields a tuple of :samp:`({readable}, {writable})` file objects, where `writable`
replaces `readable`.
Yields a tuple of :samp:`({readable}, {writable})` file
objects, where `writable` replaces `readable`.
If an exception occurs, the old file is restored, removing the
written data.
Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only read-only-modes are
allowed. A :exc:`ValueError` is raised on invalid modes.
Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only
read-only-modes are allowed. A :exc:`ValueError` is raised
on invalid modes.
For example, to add line numbers to a file::
@ -1448,22 +1489,28 @@ class Path(text_type):
except os.error:
pass
os.rename(self, backup_fn)
readable = io.open(backup_fn, mode, buffering=buffering,
encoding=encoding, errors=errors, newline=newline)
readable = io.open(
backup_fn, mode, buffering=buffering,
encoding=encoding, errors=errors, newline=newline,
)
try:
perm = os.fstat(readable.fileno()).st_mode
except OSError:
writable = open(self, 'w' + mode.replace('r', ''),
writable = open(
self, 'w' + mode.replace('r', ''),
buffering=buffering, encoding=encoding, errors=errors,
newline=newline)
newline=newline,
)
else:
os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
if hasattr(os, 'O_BINARY'):
os_mode |= os.O_BINARY
fd = os.open(self, os_mode, perm)
writable = io.open(fd, "w" + mode.replace('r', ''),
writable = io.open(
fd, "w" + mode.replace('r', ''),
buffering=buffering, encoding=encoding, errors=errors,
newline=newline)
newline=newline,
)
try:
if hasattr(os, 'chmod'):
os.chmod(self, perm)
@ -1516,6 +1563,23 @@ class Path(text_type):
return functools.partial(SpecialResolver, cls)
def only_newer(copy_func):
"""
Wrap a copy function (like shutil.copy2) to return
the dst if it's newer than the source.
"""
@functools.wraps(copy_func)
def wrapper(src, dst, *args, **kwargs):
is_newer_dst = (
dst.exists()
and dst.getmtime() >= src.getmtime()
)
if is_newer_dst:
return dst
return copy_func(src, dst, *args, **kwargs)
return wrapper
class SpecialResolver(object):
class ResolverScope:
def __init__(self, paths, scope):
@ -1584,14 +1648,15 @@ class Multi:
)
class tempdir(Path):
class TempDir(Path):
"""
A temporary directory via :func:`tempfile.mkdtemp`, and constructed with the
same parameters that you can use as a context manager.
A temporary directory via :func:`tempfile.mkdtemp`, and
constructed with the same parameters that you can use
as a context manager.
Example:
Example::
with tempdir() as d:
with TempDir() as d:
# do stuff with the Path object "d"
# here the directory is deleted automatically
@ -1606,19 +1671,27 @@ class tempdir(Path):
def __new__(cls, *args, **kwargs):
dirname = tempfile.mkdtemp(*args, **kwargs)
return super(tempdir, cls).__new__(cls, dirname)
return super(TempDir, cls).__new__(cls, dirname)
def __init__(self, *args, **kwargs):
pass
def __enter__(self):
return self
# TempDir should return a Path version of itself and not itself
# so that a second context manager does not create a second
# temporary directory, but rather changes CWD to the location
# of the temporary directory.
return self._next_class(self)
def __exit__(self, exc_type, exc_value, traceback):
if not exc_value:
self.rmtree()
# For backwards compatibility.
tempdir = TempDir
def _multi_permission_mask(mode):
"""
Support multiple, comma-separated Unix chmod symbolic modes.
@ -1626,7 +1699,8 @@ def _multi_permission_mask(mode):
>>> _multi_permission_mask('a=r,u+w')(0) == 0o644
True
"""
compose = lambda f, g: lambda *args, **kwargs: g(f(*args, **kwargs))
def compose(f, g):
return lambda *args, **kwargs: g(f(*args, **kwargs))
return functools.reduce(compose, map(_permission_mask, mode.split(',')))
@ -1692,31 +1766,56 @@ def _permission_mask(mode):
return functools.partial(op_map[op], mask)
class CaseInsensitivePattern(text_type):
class CaseInsensitivePattern(matchers.CaseInsensitive):
def __init__(self, value):
warnings.warn(
"Use matchers.CaseInsensitive instead",
DeprecationWarning,
stacklevel=2,
)
super(CaseInsensitivePattern, self).__init__(value)
class FastPath(Path):
def __init__(self, *args, **kwargs):
warnings.warn(
"Use Path, as FastPath no longer holds any advantage",
DeprecationWarning,
stacklevel=2,
)
super(FastPath, self).__init__(*args, **kwargs)
def patch_for_linux_python2():
"""
A string with a ``'normcase'`` property, suitable for passing to
:meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
:meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
For example, to get all files ending in .py, .Py, .pY, or .PY in the
current directory::
from path import Path, CaseInsensitivePattern as ci
Path('.').files(ci('*.py'))
As reported in #130, when Linux users create filenames
not in the file system encoding, it creates problems on
Python 2. This function attempts to patch the os module
to make it behave more like that on Python 3.
"""
if not PY2 or platform.system() != 'Linux':
return
@property
def normcase(self):
return __import__('ntpath').normcase
try:
import backports.os
except ImportError:
return
########################
# Backward-compatibility
class path(Path):
def __new__(cls, *args, **kwargs):
msg = "path is deprecated. Use Path instead."
warnings.warn(msg, DeprecationWarning)
return Path.__new__(cls, *args, **kwargs)
class OS:
"""
The proxy to the os module
"""
def __init__(self, wrapped):
self._orig = wrapped
def __getattr__(self, name):
return getattr(self._orig, name)
def listdir(self, *args, **kwargs):
items = self._orig.listdir(*args, **kwargs)
return list(map(backports.os.fsdecode, items))
globals().update(os=OS(os))
__all__ += ['path']
########################
patch_for_linux_python2()