[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2022-10-19 14:19:24 +00:00 committed by Anthony Sottile
parent 19dda7c9bd
commit 49abbf2485
4 changed files with 389 additions and 328 deletions

View File

@ -2,12 +2,16 @@
create errno-specific classes for IO or os calls. create errno-specific classes for IO or os calls.
""" """
import errno
import os
import sys
from types import ModuleType from types import ModuleType
import sys, os, errno
class Error(EnvironmentError): class Error(EnvironmentError):
def __repr__(self): def __repr__(self):
return "%s.%s %r: %s " %(self.__class__.__module__, return "{}.{} {!r}: {} ".format(
self.__class__.__module__,
self.__class__.__name__, self.__class__.__name__,
self.__class__.__doc__, self.__class__.__doc__,
" ".join(map(str, self.args)), " ".join(map(str, self.args)),
@ -15,11 +19,13 @@ class Error(EnvironmentError):
) )
def __str__(self): def __str__(self):
s = "[%s]: %s" %(self.__class__.__doc__, s = "[{}]: {}".format(
self.__class__.__doc__,
" ".join(map(str, self.args)), " ".join(map(str, self.args)),
) )
return s return s
_winerrnomap = { _winerrnomap = {
2: errno.ENOENT, 2: errno.ENOENT,
3: errno.ENOENT, 3: errno.ENOENT,
@ -32,11 +38,13 @@ _winerrnomap = {
5: errno.EACCES, # anything better? 5: errno.EACCES, # anything better?
} }
class ErrorMaker(ModuleType): class ErrorMaker(ModuleType):
"""lazily provides Exception classes for each possible POSIX errno """lazily provides Exception classes for each possible POSIX errno
(as defined per the 'errno' module). All such instances (as defined per the 'errno' module). All such instances
subclass EnvironmentError. subclass EnvironmentError.
""" """
Error = Error Error = Error
_errno2class = {} _errno2class = {}
@ -53,9 +61,11 @@ class ErrorMaker(ModuleType):
return self._errno2class[eno] return self._errno2class[eno]
except KeyError: except KeyError:
clsname = errno.errorcode.get(eno, "UnknownErrno%d" % (eno,)) clsname = errno.errorcode.get(eno, "UnknownErrno%d" % (eno,))
errorcls = type(Error)(clsname, (Error,), errorcls = type(Error)(
{'__module__':'py.error', clsname,
'__doc__': os.strerror(eno)}) (Error,),
{"__module__": "py.error", "__doc__": os.strerror(eno)},
)
self._errno2class[eno] = errorcls self._errno2class[eno] = errorcls
return errorcls return errorcls
@ -66,9 +76,9 @@ class ErrorMaker(ModuleType):
return func(*args, **kwargs) return func(*args, **kwargs)
except self.Error: except self.Error:
raise raise
except (OSError, EnvironmentError): except OSError:
cls, value, tb = sys.exc_info() cls, value, tb = sys.exc_info()
if not hasattr(value, 'errno'): if not hasattr(value, "errno"):
raise raise
__tracebackhide__ = False __tracebackhide__ = False
errno = value.errno errno = value.errno
@ -83,9 +93,9 @@ class ErrorMaker(ModuleType):
cls = self._geterrnoclass(_winerrnomap[errno]) cls = self._geterrnoclass(_winerrnomap[errno])
except KeyError: except KeyError:
raise value raise value
raise cls("%s%r" % (func.__name__, args)) raise cls(f"{func.__name__}{args!r}")
__tracebackhide__ = True __tracebackhide__ = True
error = ErrorMaker('_pytest._py.error') error = ErrorMaker("_pytest._py.error")
sys.modules[error.__name__] = error sys.modules[error.__name__] = error

View File

@ -1,24 +1,31 @@
""" """
local path implementation. local path implementation.
""" """
from __future__ import with_statement import atexit
from contextlib import contextmanager
import sys, os, atexit, io, uuid
from stat import S_ISLNK, S_ISDIR, S_ISREG
from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname
import warnings
import os
import sys
import posixpath
import fnmatch import fnmatch
import io
import os
import posixpath
import sys
import uuid
import warnings
from contextlib import contextmanager
from os.path import abspath
from os.path import dirname
from os.path import exists
from os.path import isabs
from os.path import isdir
from os.path import isfile
from os.path import islink
from os.path import normpath
from stat import S_ISDIR
from stat import S_ISLNK
from stat import S_ISREG
from . import error from . import error
# Moved from local.py. # Moved from local.py.
iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt') iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt")
try: try:
# FileNotFoundError might happen in py34, and is not available with py27. # FileNotFoundError might happen in py34, and is not available with py27.
@ -29,6 +36,7 @@ except NameError:
try: try:
from os import fspath from os import fspath
except ImportError: except ImportError:
def fspath(path): def fspath(path):
""" """
Return the string representation of the path. Return the string representation of the path.
@ -47,7 +55,7 @@ except ImportError:
try: try:
return path_type.__fspath__(path) return path_type.__fspath__(path)
except AttributeError: except AttributeError:
if hasattr(path_type, '__fspath__'): if hasattr(path_type, "__fspath__"):
raise raise
try: try:
import pathlib import pathlib
@ -57,11 +65,13 @@ except ImportError:
if isinstance(path, pathlib.PurePath): if isinstance(path, pathlib.PurePath):
return str(path) return str(path)
raise TypeError("expected str, bytes or os.PathLike object, not " raise TypeError(
+ path_type.__name__) "expected str, bytes or os.PathLike object, not " + path_type.__name__
)
class Checkers: class Checkers:
_depend_on_existence = 'exists', 'link', 'dir', 'file' _depend_on_existence = "exists", "link", "dir", "file"
def __init__(self, path): def __init__(self, path):
self.path = path self.path = path
@ -73,11 +83,11 @@ class Checkers:
raise NotImplementedError raise NotImplementedError
def dotfile(self): def dotfile(self):
return self.path.basename.startswith('.') return self.path.basename.startswith(".")
def ext(self, arg): def ext(self, arg):
if not arg.startswith('.'): if not arg.startswith("."):
arg = '.' + arg arg = "." + arg
return self.path.ext == arg return self.path.ext == arg
def exists(self): def exists(self):
@ -105,15 +115,14 @@ class Checkers:
try: try:
meth = getattr(self, name) meth = getattr(self, name)
except AttributeError: except AttributeError:
if name[:3] == 'not': if name[:3] == "not":
invert = True invert = True
try: try:
meth = getattr(self, name[3:]) meth = getattr(self, name[3:])
except AttributeError: except AttributeError:
pass pass
if meth is None: if meth is None:
raise TypeError( raise TypeError(f"no {name!r} checker available for {self.path!r}")
"no %r checker available for %r" % (name, self.path))
try: try:
if py.code.getrawcode(meth).co_argcount > 1: if py.code.getrawcode(meth).co_argcount > 1:
if (not meth(value)) ^ invert: if (not meth(value)) ^ invert:
@ -129,50 +138,58 @@ class Checkers:
if name in kw: if name in kw:
if kw.get(name): if kw.get(name):
return False return False
name = 'not' + name name = "not" + name
if name in kw: if name in kw:
if not kw.get(name): if not kw.get(name):
return False return False
return True return True
class NeverRaised(Exception): class NeverRaised(Exception):
pass pass
class PathBase(object):
class PathBase:
"""shared implementation for filesystem path objects.""" """shared implementation for filesystem path objects."""
Checkers = Checkers Checkers = Checkers
def __div__(self, other): def __div__(self, other):
return self.join(fspath(other)) return self.join(fspath(other))
__truediv__ = __div__ # py3k __truediv__ = __div__ # py3k
def basename(self): def basename(self):
"""basename part of path.""" """basename part of path."""
return self._getbyspec('basename')[0] return self._getbyspec("basename")[0]
basename = property(basename, None, None, basename.__doc__) basename = property(basename, None, None, basename.__doc__)
def dirname(self): def dirname(self):
"""dirname part of path.""" """dirname part of path."""
return self._getbyspec('dirname')[0] return self._getbyspec("dirname")[0]
dirname = property(dirname, None, None, dirname.__doc__) dirname = property(dirname, None, None, dirname.__doc__)
def purebasename(self): def purebasename(self):
"""pure base name of the path.""" """pure base name of the path."""
return self._getbyspec('purebasename')[0] return self._getbyspec("purebasename")[0]
purebasename = property(purebasename, None, None, purebasename.__doc__) purebasename = property(purebasename, None, None, purebasename.__doc__)
def ext(self): def ext(self):
"""extension of the path (including the '.').""" """extension of the path (including the '.')."""
return self._getbyspec('ext')[0] return self._getbyspec("ext")[0]
ext = property(ext, None, None, ext.__doc__) ext = property(ext, None, None, ext.__doc__)
def dirpath(self, *args, **kwargs): def dirpath(self, *args, **kwargs):
"""return the directory path joined with any given path arguments.""" """return the directory path joined with any given path arguments."""
return self.new(basename='').join(*args, **kwargs) return self.new(basename="").join(*args, **kwargs)
def read_binary(self): def read_binary(self):
"""read and return a bytestring from reading the path.""" """read and return a bytestring from reading the path."""
with self.open('rb') as f: with self.open("rb") as f:
return f.read() return f.read()
def read_text(self, encoding): def read_text(self, encoding):
@ -180,8 +197,7 @@ class PathBase(object):
with self.open("r", encoding=encoding) as f: with self.open("r", encoding=encoding) as f:
return f.read() return f.read()
def read(self, mode="r"):
def read(self, mode='r'):
"""read and return a bytestring from reading the path.""" """read and return a bytestring from reading the path."""
with self.open(mode) as f: with self.open(mode) as f:
return f.read() return f.read()
@ -189,14 +205,11 @@ class PathBase(object):
def readlines(self, cr=1): def readlines(self, cr=1):
"""read and return a list of lines from the path. if cr is False, the """read and return a list of lines from the path. if cr is False, the
newline will be removed from the end of each line.""" newline will be removed from the end of each line."""
if sys.version_info < (3, ): mode = "r"
mode = 'rU'
else: # python 3 deprecates mode "U" in favor of "newline" option
mode = 'r'
if not cr: if not cr:
content = self.read(mode) content = self.read(mode)
return content.split('\n') return content.split("\n")
else: else:
f = self.open(mode) f = self.open(mode)
try: try:
@ -206,9 +219,10 @@ newline will be removed from the end of each line. """
def load(self): def load(self):
"""(deprecated) return object unpickled from self.read()""" """(deprecated) return object unpickled from self.read()"""
f = self.open('rb') f = self.open("rb")
try: try:
import pickle import pickle
return error.checked_call(pickle.load, f) return error.checked_call(pickle.load, f)
finally: finally:
f.close() f.close()
@ -216,9 +230,7 @@ newline will be removed from the end of each line. """
def move(self, target): def move(self, target):
"""move this path to target.""" """move this path to target."""
if target.relto(self): if target.relto(self):
raise error.EINVAL( raise error.EINVAL(target, "cannot move path into a subdirectory of itself")
target,
"cannot move path into a subdirectory of itself")
try: try:
self.rename(target) self.rename(target)
except error.EXDEV: # invalid cross-device link except error.EXDEV: # invalid cross-device link
@ -247,7 +259,7 @@ newline will be removed from the end of each line. """
path.check(file=1, link=1) # a link pointing to a file path.check(file=1, link=1) # a link pointing to a file
""" """
if not kw: if not kw:
kw = {'exists': 1} kw = {"exists": 1}
return self.Checkers(self)._evaluate(kw) return self.Checkers(self)._evaluate(kw)
def fnmatch(self, pattern): def fnmatch(self, pattern):
@ -274,16 +286,15 @@ newline will be removed from the end of each line. """
to the given 'relpath'. to the given 'relpath'.
""" """
if not isinstance(relpath, (str, PathBase)): if not isinstance(relpath, (str, PathBase)):
raise TypeError("%r: not a string or path object" %(relpath,)) raise TypeError(f"{relpath!r}: not a string or path object")
strrelpath = str(relpath) strrelpath = str(relpath)
if strrelpath and strrelpath[-1] != self.sep: if strrelpath and strrelpath[-1] != self.sep:
strrelpath += self.sep strrelpath += self.sep
# assert strrelpath[-1] == self.sep # assert strrelpath[-1] == self.sep
# assert strrelpath[-2] != self.sep # assert strrelpath[-2] != self.sep
strself = self.strpath strself = self.strpath
if sys.platform == "win32" or getattr(os, '_name', None) == 'nt': if sys.platform == "win32" or getattr(os, "_name", None) == "nt":
if os.path.normcase(strself).startswith( if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)):
os.path.normcase(strrelpath)):
return strself[len(strrelpath) :] return strself[len(strrelpath) :]
elif strself.startswith(strrelpath): elif strself.startswith(strrelpath):
return strself[len(strrelpath) :] return strself[len(strrelpath) :]
@ -390,15 +401,17 @@ newline will be removed from the end of each line. """
sort if True will sort entries within each directory level. sort if True will sort entries within each directory level.
""" """
for x in Visitor(fil, rec, ignore, bf, sort).gen(self): yield from Visitor(fil, rec, ignore, bf, sort).gen(self)
yield x
def _sortlist(self, res, sort): def _sortlist(self, res, sort):
if sort: if sort:
if hasattr(sort, '__call__'): if hasattr(sort, "__call__"):
warnings.warn(DeprecationWarning( warnings.warn(
DeprecationWarning(
"listdir(sort=callable) is deprecated and breaks on python3" "listdir(sort=callable) is deprecated and breaks on python3"
), stacklevel=3) ),
stacklevel=3,
)
res.sort(sort) res.sort(sort)
else: else:
res.sort() res.sort()
@ -410,13 +423,14 @@ newline will be removed from the end of each line. """
def __fspath__(self): def __fspath__(self):
return self.strpath return self.strpath
class Visitor: class Visitor:
def __init__(self, fil, rec, ignore, bf, sort): def __init__(self, fil, rec, ignore, bf, sort):
if isinstance(fil, py.builtin._basestring): if isinstance(fil, py.builtin._basestring):
fil = FNMatcher(fil) fil = FNMatcher(fil)
if isinstance(rec, py.builtin._basestring): if isinstance(rec, py.builtin._basestring):
self.rec = FNMatcher(rec) self.rec = FNMatcher(rec)
elif not hasattr(rec, '__call__') and rec: elif not hasattr(rec, "__call__") and rec:
self.rec = lambda path: True self.rec = lambda path: True
else: else:
self.rec = rec self.rec = rec
@ -431,8 +445,9 @@ class Visitor:
except self.ignore: except self.ignore:
return return
rec = self.rec rec = self.rec
dirs = self.optsort([p for p in entries dirs = self.optsort(
if p.check(dir=1) and (rec is None or rec(p))]) [p for p in entries if p.check(dir=1) and (rec is None or rec(p))]
)
if not self.breadthfirst: if not self.breadthfirst:
for subdir in dirs: for subdir in dirs:
for p in self.gen(subdir): for p in self.gen(subdir):
@ -445,6 +460,7 @@ class Visitor:
for p in self.gen(subdir): for p in self.gen(subdir):
yield p yield p
class FNMatcher: class FNMatcher:
def __init__(self, pattern): def __init__(self, pattern):
self.pattern = pattern self.pattern = pattern
@ -452,9 +468,11 @@ class FNMatcher:
def __call__(self, path): def __call__(self, path):
pattern = self.pattern pattern = self.pattern
if (pattern.find(path.sep) == -1 and if (
iswin32 and pattern.find(path.sep) == -1
pattern.find(posixpath.sep) != -1): and iswin32
and pattern.find(posixpath.sep) != -1
):
# Running on Windows, the pattern has no Windows path separators, # Running on Windows, the pattern has no Windows path separators,
# and the pattern has one or more Posix path separators. Replace # and the pattern has one or more Posix path separators. Replace
# the Posix path separators with the Windows path separator. # the Posix path separators with the Windows path separator.
@ -465,22 +483,20 @@ class FNMatcher:
else: else:
name = str(path) # path.strpath # XXX svn? name = str(path) # path.strpath # XXX svn?
if not os.path.isabs(pattern): if not os.path.isabs(pattern):
pattern = '*' + path.sep + pattern pattern = "*" + path.sep + pattern
return fnmatch.fnmatch(name, pattern) return fnmatch.fnmatch(name, pattern)
if sys.version_info > (3,0):
def map_as_list(func, iter): def map_as_list(func, iter):
return list(map(func, iter)) return list(map(func, iter))
else:
map_as_list = map
ALLOW_IMPORTLIB_MODE = sys.version_info > (3, 5) ALLOW_IMPORTLIB_MODE = sys.version_info > (3, 5)
if ALLOW_IMPORTLIB_MODE: if ALLOW_IMPORTLIB_MODE:
import importlib import importlib
class Stat(object): class Stat:
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self._osstatresult, "st_" + name) return getattr(self._osstatresult, "st_" + name)
@ -493,6 +509,7 @@ class Stat(object):
if iswin32: if iswin32:
raise NotImplementedError("XXX win32") raise NotImplementedError("XXX win32")
import pwd import pwd
entry = error.checked_call(pwd.getpwuid, self.uid) entry = error.checked_call(pwd.getpwuid, self.uid)
return entry[0] return entry[0]
@ -502,6 +519,7 @@ class Stat(object):
if iswin32: if iswin32:
raise NotImplementedError("XXX win32") raise NotImplementedError("XXX win32")
import grp import grp
entry = error.checked_call(grp.getgrgid, self.gid) entry = error.checked_call(grp.getgrgid, self.gid)
return entry[0] return entry[0]
@ -512,9 +530,10 @@ class Stat(object):
return S_ISREG(self._osstatresult.st_mode) return S_ISREG(self._osstatresult.st_mode)
def islink(self): def islink(self):
st = self.path.lstat() self.path.lstat()
return S_ISLNK(self._osstatresult.st_mode) return S_ISLNK(self._osstatresult.st_mode)
class PosixPath(PathBase): class PosixPath(PathBase):
def chown(self, user, group, rec=0): def chown(self, user, group, rec=0):
"""change ownership to the given user and group. """change ownership to the given user and group.
@ -548,31 +567,39 @@ class PosixPath(PathBase):
relsource = self.__class__(value).relto(base) relsource = self.__class__(value).relto(base)
reldest = self.relto(base) reldest = self.relto(base)
n = reldest.count(self.sep) n = reldest.count(self.sep)
target = self.sep.join(('..', )*n + (relsource, )) target = self.sep.join(("..",) * n + (relsource,))
error.checked_call(os.symlink, target, self.strpath) error.checked_call(os.symlink, target, self.strpath)
def getuserid(user): def getuserid(user):
import pwd import pwd
if not isinstance(user, int): if not isinstance(user, int):
user = pwd.getpwnam(user)[2] user = pwd.getpwnam(user)[2]
return user return user
def getgroupid(group): def getgroupid(group):
import grp import grp
if not isinstance(group, int): if not isinstance(group, int):
group = grp.getgrnam(group)[2] group = grp.getgrnam(group)[2]
return group return group
FSBase = not iswin32 and PosixPath or PathBase FSBase = not iswin32 and PosixPath or PathBase
class LocalPath(FSBase): class LocalPath(FSBase):
"""object oriented interface to os.path and other local filesystem """object oriented interface to os.path and other local filesystem
related information. related information.
""" """
class ImportMismatchError(ImportError): class ImportMismatchError(ImportError):
"""raised on pyimport() if there is a mismatch of __file__'s""" """raised on pyimport() if there is a mismatch of __file__'s"""
sep = os.sep sep = os.sep
class Checkers(Checkers): class Checkers(Checkers):
def _stat(self): def _stat(self):
try: try:
@ -613,8 +640,10 @@ class LocalPath(FSBase):
try: try:
path = fspath(path) path = fspath(path)
except TypeError: except TypeError:
raise ValueError("can only pass None, Path instances " raise ValueError(
"or non-empty strings to LocalPath") "can only pass None, Path instances "
"or non-empty strings to LocalPath"
)
if expanduser: if expanduser:
path = os.path.expanduser(path) path = os.path.expanduser(path)
self.strpath = abspath(path) self.strpath = abspath(path)
@ -649,8 +678,7 @@ class LocalPath(FSBase):
return fspath(self) > fspath(other) return fspath(self) > fspath(other)
def samefile(self, other): def samefile(self, other):
""" return True if 'other' references the same file as 'self'. """return True if 'other' references the same file as 'self'."""
"""
other = fspath(other) other = fspath(other)
if not isabs(other): if not isabs(other):
other = abspath(other) other = abspath(other)
@ -658,8 +686,7 @@ class LocalPath(FSBase):
return True return True
if not hasattr(os.path, "samefile"): if not hasattr(os.path, "samefile"):
return False return False
return error.checked_call( return error.checked_call(os.path.samefile, self.strpath, other)
os.path.samefile, self.strpath, other)
def remove(self, rec=1, ignore_errors=False): def remove(self, rec=1, ignore_errors=False):
"""remove a file or directory (or a directory tree if rec=1). """remove a file or directory (or a directory tree if rec=1).
@ -672,9 +699,10 @@ class LocalPath(FSBase):
if iswin32: if iswin32:
self.chmod(0o700, rec=1) self.chmod(0o700, rec=1)
import shutil import shutil
error.checked_call( error.checked_call(
shutil.rmtree, self.strpath, shutil.rmtree, self.strpath, ignore_errors=ignore_errors
ignore_errors=ignore_errors) )
else: else:
error.checked_call(os.rmdir, self.strpath) error.checked_call(os.rmdir, self.strpath)
else: else:
@ -693,8 +721,8 @@ class LocalPath(FSBase):
mod = __import__(hashtype) mod = __import__(hashtype)
hash = getattr(mod, hashtype)() hash = getattr(mod, hashtype)()
except (AttributeError, ImportError): except (AttributeError, ImportError):
raise ValueError("Don't know how to compute %r hash" %(hashtype,)) raise ValueError(f"Don't know how to compute {hashtype!r} hash")
f = self.open('rb') f = self.open("rb")
try: try:
while 1: while 1:
buf = f.read(chunksize) buf = f.read(chunksize)
@ -720,28 +748,28 @@ class LocalPath(FSBase):
obj.strpath = self.strpath obj.strpath = self.strpath
return obj return obj
drive, dirname, basename, purebasename, ext = self._getbyspec( drive, dirname, basename, purebasename, ext = self._getbyspec(
"drive,dirname,basename,purebasename,ext") "drive,dirname,basename,purebasename,ext"
if 'basename' in kw: )
if 'purebasename' in kw or 'ext' in kw: if "basename" in kw:
if "purebasename" in kw or "ext" in kw:
raise ValueError("invalid specification %r" % kw) raise ValueError("invalid specification %r" % kw)
else: else:
pb = kw.setdefault('purebasename', purebasename) pb = kw.setdefault("purebasename", purebasename)
try: try:
ext = kw['ext'] ext = kw["ext"]
except KeyError: except KeyError:
pass pass
else: else:
if ext and not ext.startswith('.'): if ext and not ext.startswith("."):
ext = '.' + ext ext = "." + ext
kw['basename'] = pb + ext kw["basename"] = pb + ext
if ('dirname' in kw and not kw['dirname']): if "dirname" in kw and not kw["dirname"]:
kw['dirname'] = drive kw["dirname"] = drive
else: else:
kw.setdefault('dirname', dirname) kw.setdefault("dirname", dirname)
kw.setdefault('sep', self.sep) kw.setdefault("sep", self.sep)
obj.strpath = normpath( obj.strpath = normpath("%(dirname)s%(sep)s%(basename)s" % kw)
"%(dirname)s%(sep)s%(basename)s" % kw)
return obj return obj
def _getbyspec(self, spec): def _getbyspec(self, spec):
@ -749,26 +777,26 @@ class LocalPath(FSBase):
res = [] res = []
parts = self.strpath.split(self.sep) parts = self.strpath.split(self.sep)
args = filter(None, spec.split(',') ) args = filter(None, spec.split(","))
append = res.append append = res.append
for name in args: for name in args:
if name == 'drive': if name == "drive":
append(parts[0]) append(parts[0])
elif name == 'dirname': elif name == "dirname":
append(self.sep.join(parts[:-1])) append(self.sep.join(parts[:-1]))
else: else:
basename = parts[-1] basename = parts[-1]
if name == 'basename': if name == "basename":
append(basename) append(basename)
else: else:
i = basename.rfind('.') i = basename.rfind(".")
if i == -1: if i == -1:
purebasename, ext = basename, '' purebasename, ext = basename, ""
else: else:
purebasename, ext = basename[:i], basename[i:] purebasename, ext = basename[:i], basename[i:]
if name == 'purebasename': if name == "purebasename":
append(purebasename) append(purebasename)
elif name == 'ext': elif name == "ext":
append(ext) append(ext)
else: else:
raise ValueError("invalid part specification %r" % name) raise ValueError("invalid part specification %r" % name)
@ -782,7 +810,7 @@ class LocalPath(FSBase):
if args: if args:
path = path.join(*args) path = path.join(*args)
return path return path
return super(LocalPath, self).dirpath(*args, **kwargs) return super().dirpath(*args, **kwargs)
def join(self, *args, **kwargs): def join(self, *args, **kwargs):
"""return a new path by appending all 'args' as path """return a new path by appending all 'args' as path
@ -792,7 +820,7 @@ class LocalPath(FSBase):
sep = self.sep sep = self.sep
strargs = [fspath(arg) for arg in args] strargs = [fspath(arg) for arg in args]
strpath = self.strpath strpath = self.strpath
if kwargs.get('abs'): if kwargs.get("abs"):
newargs = [] newargs = []
for arg in reversed(strargs): for arg in reversed(strargs):
if isabs(arg): if isabs(arg):
@ -806,15 +834,15 @@ class LocalPath(FSBase):
arg = arg.strip(sep) arg = arg.strip(sep)
if iswin32: if iswin32:
# allow unix style paths even on windows. # allow unix style paths even on windows.
arg = arg.strip('/') arg = arg.strip("/")
arg = arg.replace('/', sep) arg = arg.replace("/", sep)
strpath = strpath + actual_sep + arg strpath = strpath + actual_sep + arg
actual_sep = sep actual_sep = sep
obj = object.__new__(self.__class__) obj = object.__new__(self.__class__)
obj.strpath = normpath(strpath) obj.strpath = normpath(strpath)
return obj return obj
def open(self, mode='r', ensure=False, encoding=None): def open(self, mode="r", ensure=False, encoding=None):
"""return an opened file with the given mode. """return an opened file with the given mode.
If ensure is True, create parent directories if needed. If ensure is True, create parent directories if needed.
@ -841,9 +869,10 @@ class LocalPath(FSBase):
return not kw["dir"] ^ isdir(self.strpath) return not kw["dir"] ^ isdir(self.strpath)
if "file" in kw: if "file" in kw:
return not kw["file"] ^ isfile(self.strpath) return not kw["file"] ^ isfile(self.strpath)
return super(LocalPath, self).check(**kw) return super().check(**kw)
_patternchars = set("*?[" + os.path.sep) _patternchars = set("*?[" + os.path.sep)
def listdir(self, fil=None, sort=None): def listdir(self, fil=None, sort=None):
"""list directory contents, possibly filter by the given fil func """list directory contents, possibly filter by the given fil func
and possibly sorted. and possibly sorted.
@ -892,8 +921,10 @@ class LocalPath(FSBase):
if stat: if stat:
copystat(self, target) copystat(self, target)
else: else:
def rec(p): def rec(p):
return p.check(link=0) return p.check(link=0)
for x in self.visit(rec=rec): for x in self.visit(rec=rec):
relpath = x.relto(self) relpath = x.relto(self)
newx = target.join(relpath) newx = target.join(relpath)
@ -917,8 +948,9 @@ class LocalPath(FSBase):
def dump(self, obj, bin=1): def dump(self, obj, bin=1):
"""pickle object into path location""" """pickle object into path location"""
f = self.open('wb') f = self.open("wb")
import pickle import pickle
try: try:
error.checked_call(pickle.dump, obj, f, bin) error.checked_call(pickle.dump, obj, f, bin)
finally: finally:
@ -936,7 +968,7 @@ class LocalPath(FSBase):
""" """
if ensure: if ensure:
self.dirpath().ensure(dir=1) self.dirpath().ensure(dir=1)
with self.open('wb') as f: with self.open("wb") as f:
f.write(data) f.write(data)
def write_text(self, data, encoding, ensure=False): def write_text(self, data, encoding, ensure=False):
@ -945,16 +977,16 @@ class LocalPath(FSBase):
""" """
if ensure: if ensure:
self.dirpath().ensure(dir=1) self.dirpath().ensure(dir=1)
with self.open('w', encoding=encoding) as f: with self.open("w", encoding=encoding) as f:
f.write(data) f.write(data)
def write(self, data, mode='w', ensure=False): def write(self, data, mode="w", ensure=False):
"""write data into path. If ensure is True create """write data into path. If ensure is True create
missing parent directories. missing parent directories.
""" """
if ensure: if ensure:
self.dirpath().ensure(dir=1) self.dirpath().ensure(dir=1)
if 'b' in mode: if "b" in mode:
if not py.builtin._isbytes(data): if not py.builtin._isbytes(data):
raise ValueError("can only process bytes") raise ValueError("can only process bytes")
else: else:
@ -991,12 +1023,12 @@ class LocalPath(FSBase):
then the path is forced to be a directory path. then the path is forced to be a directory path.
""" """
p = self.join(*args) p = self.join(*args)
if kwargs.get('dir', 0): if kwargs.get("dir", 0):
return p._ensuredirs() return p._ensuredirs()
else: else:
p.dirpath()._ensuredirs() p.dirpath()._ensuredirs()
if not p.check(file=1): if not p.check(file=1):
p.open('w').close() p.open("w").close()
return p return p
def stat(self, raising=True): def stat(self, raising=True):
@ -1036,7 +1068,6 @@ class LocalPath(FSBase):
error.checked_call(os.chdir, self.strpath) error.checked_call(os.chdir, self.strpath)
return old return old
@contextmanager @contextmanager
def as_cwd(self): def as_cwd(self):
""" """
@ -1060,7 +1091,7 @@ class LocalPath(FSBase):
return self.stat().atime return self.stat().atime
def __repr__(self): def __repr__(self):
return 'local(%r)' % self.strpath return "local(%r)" % self.strpath
def __str__(self): def __str__(self):
"""return string representation of the Path.""" """return string representation of the Path."""
@ -1072,7 +1103,7 @@ class LocalPath(FSBase):
if rec is True perform recursively. if rec is True perform recursively.
""" """
if not isinstance(mode, int): if not isinstance(mode, int):
raise TypeError("mode %r must be an integer" % (mode,)) raise TypeError(f"mode {mode!r} must be an integer")
if rec: if rec:
for x in self.visit(rec=rec): for x in self.visit(rec=rec):
error.checked_call(os.chmod, str(x), mode) error.checked_call(os.chmod, str(x), mode)
@ -1086,7 +1117,7 @@ class LocalPath(FSBase):
pkgpath = None pkgpath = None
for parent in self.parts(reverse=True): for parent in self.parts(reverse=True):
if parent.isdir(): if parent.isdir():
if not parent.join('__init__.py').exists(): if not parent.join("__init__.py").exists():
break break
if not isimportable(parent.basename): if not isimportable(parent.basename):
break break
@ -1127,18 +1158,15 @@ class LocalPath(FSBase):
if not self.check(): if not self.check():
raise error.ENOENT(self) raise error.ENOENT(self)
if ensuresyspath == 'importlib': if ensuresyspath == "importlib":
if modname is None: if modname is None:
modname = self.purebasename modname = self.purebasename
if not ALLOW_IMPORTLIB_MODE: if not ALLOW_IMPORTLIB_MODE:
raise ImportError( raise ImportError("Can't use importlib due to old version of Python")
"Can't use importlib due to old version of Python") spec = importlib.util.spec_from_file_location(modname, str(self))
spec = importlib.util.spec_from_file_location(
modname, str(self))
if spec is None: if spec is None:
raise ImportError( raise ImportError(
"Can't find module %s at location %s" % f"Can't find module {modname} at location {str(self)}"
(modname, str(self))
) )
mod = importlib.util.module_from_spec(spec) mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod) spec.loader.exec_module(mod)
@ -1164,10 +1192,10 @@ class LocalPath(FSBase):
return mod # we don't check anything as we might return mod # we don't check anything as we might
# be in a namespace package ... too icky to check # be in a namespace package ... too icky to check
modfile = mod.__file__ modfile = mod.__file__
if modfile[-4:] in ('.pyc', '.pyo'): if modfile[-4:] in (".pyc", ".pyo"):
modfile = modfile[:-1] modfile = modfile[:-1]
elif modfile.endswith('$py.class'): elif modfile.endswith("$py.class"):
modfile = modfile[:-9] + '.py' modfile = modfile[:-9] + ".py"
if modfile.endswith(os.path.sep + "__init__.py"): if modfile.endswith(os.path.sep + "__init__.py"):
if self.basename != "__init__.py": if self.basename != "__init__.py":
modfile = modfile[:-12] modfile = modfile[:-12]
@ -1176,8 +1204,8 @@ class LocalPath(FSBase):
except error.ENOENT: except error.ENOENT:
issame = False issame = False
if not issame: if not issame:
ignore = os.getenv('PY_IGNORE_IMPORTMISMATCH') ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH")
if ignore != '1': if ignore != "1":
raise self.ImportMismatchError(modname, modfile, self) raise self.ImportMismatchError(modname, modfile, self)
return mod return mod
else: else:
@ -1186,6 +1214,7 @@ class LocalPath(FSBase):
except KeyError: except KeyError:
# we have a custom modname, do a pseudo-import # we have a custom modname, do a pseudo-import
import types import types
mod = types.ModuleType(modname) mod = types.ModuleType(modname)
mod.__file__ = str(self) mod.__file__ = str(self)
sys.modules[modname] = mod sys.modules[modname] = mod
@ -1202,8 +1231,9 @@ class LocalPath(FSBase):
The process is directly invoked and not through a system shell. The process is directly invoked and not through a system shell.
""" """
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
argv = map_as_list(str, argv) argv = map_as_list(str, argv)
popen_opts['stdout'] = popen_opts['stderr'] = PIPE popen_opts["stdout"] = popen_opts["stderr"] = PIPE
proc = Popen([str(self)] + argv, **popen_opts) proc = Popen([str(self)] + argv, **popen_opts)
stdout, stderr = proc.communicate() stdout, stderr = proc.communicate()
ret = proc.wait() ret = proc.wait()
@ -1212,8 +1242,13 @@ class LocalPath(FSBase):
if ret != 0: if ret != 0:
if py.builtin._isbytes(stderr): if py.builtin._isbytes(stderr):
stderr = py.builtin._totext(stderr, sys.getdefaultencoding()) stderr = py.builtin._totext(stderr, sys.getdefaultencoding())
raise py.process.cmdexec.Error(ret, ret, str(self), raise py.process.cmdexec.Error(
stdout, stderr,) ret,
ret,
str(self),
stdout,
stderr,
)
return stdout return stdout
def sysfind(cls, name, checker=None, paths=None): def sysfind(cls, name, checker=None, paths=None):
@ -1231,21 +1266,22 @@ class LocalPath(FSBase):
else: else:
if paths is None: if paths is None:
if iswin32: if iswin32:
paths = os.environ['Path'].split(';') paths = os.environ["Path"].split(";")
if '' not in paths and '.' not in paths: if "" not in paths and "." not in paths:
paths.append('.') paths.append(".")
try: try:
systemroot = os.environ['SYSTEMROOT'] systemroot = os.environ["SYSTEMROOT"]
except KeyError: except KeyError:
pass pass
else: else:
paths = [path.replace('%SystemRoot%', systemroot) paths = [
for path in paths] path.replace("%SystemRoot%", systemroot) for path in paths
]
else: else:
paths = os.environ['PATH'].split(':') paths = os.environ["PATH"].split(":")
tryadd = [] tryadd = []
if iswin32: if iswin32:
tryadd += os.environ['PATHEXT'].split(os.pathsep) tryadd += os.environ["PATHEXT"].split(os.pathsep)
tryadd.append("") tryadd.append("")
for x in paths: for x in paths:
@ -1260,17 +1296,19 @@ class LocalPath(FSBase):
except error.EACCES: except error.EACCES:
pass pass
return None return None
sysfind = classmethod(sysfind) sysfind = classmethod(sysfind)
def _gethomedir(cls): def _gethomedir(cls):
try: try:
x = os.environ['HOME'] x = os.environ["HOME"]
except KeyError: except KeyError:
try: try:
x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH'] x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"]
except KeyError: except KeyError:
return None return None
return cls(x) return cls(x)
_gethomedir = classmethod(_gethomedir) _gethomedir = classmethod(_gethomedir)
# """ # """
@ -1282,6 +1320,7 @@ class LocalPath(FSBase):
(where tempfiles are usually created in) (where tempfiles are usually created in)
""" """
import tempfile import tempfile
return py.path.local(tempfile.gettempdir()) return py.path.local(tempfile.gettempdir())
@classmethod @classmethod
@ -1290,12 +1329,14 @@ class LocalPath(FSBase):
(which we created ourself). (which we created ourself).
""" """
import tempfile import tempfile
if rootdir is None: if rootdir is None:
rootdir = cls.get_temproot() rootdir = cls.get_temproot()
return cls(error.checked_call(tempfile.mkdtemp, dir=str(rootdir))) return cls(error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3, def make_numbered_dir(
lock_timeout=172800): # two days cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800
): # two days
"""return unique directory with a number greater than the current """return unique directory with a number greater than the current
maximum one. The number is assumed to start directly after prefix. maximum one. The number is assumed to start directly after prefix.
if keep is true directories with a number less than (maxnum-keep) if keep is true directories with a number less than (maxnum-keep)
@ -1306,6 +1347,7 @@ class LocalPath(FSBase):
rootdir = cls.get_temproot() rootdir = cls.get_temproot()
nprefix = prefix.lower() nprefix = prefix.lower()
def parse_num(path): def parse_num(path):
"""parse the number out of a path (if it matches the prefix)""" """parse the number out of a path (if it matches the prefix)"""
nbasename = path.basename.lower() nbasename = path.basename.lower()
@ -1318,18 +1360,21 @@ class LocalPath(FSBase):
def create_lockfile(path): def create_lockfile(path):
"""exclusively create lockfile. Throws when failed""" """exclusively create lockfile. Throws when failed"""
mypid = os.getpid() mypid = os.getpid()
lockfile = path.join('.lock') lockfile = path.join(".lock")
if hasattr(lockfile, 'mksymlinkto'): if hasattr(lockfile, "mksymlinkto"):
lockfile.mksymlinkto(str(mypid)) lockfile.mksymlinkto(str(mypid))
else: else:
fd = error.checked_call(os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) fd = error.checked_call(
with os.fdopen(fd, 'w') as f: os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644
)
with os.fdopen(fd, "w") as f:
f.write(str(mypid)) f.write(str(mypid))
return lockfile return lockfile
def atexit_remove_lockfile(lockfile): def atexit_remove_lockfile(lockfile):
"""ensure lockfile is removed at process exit""" """ensure lockfile is removed at process exit"""
mypid = os.getpid() mypid = os.getpid()
def try_remove_lockfile(): def try_remove_lockfile():
# in a fork() situation, only the last process should # in a fork() situation, only the last process should
# remove the .lock, otherwise the other processes run the # remove the .lock, otherwise the other processes run the
@ -1342,6 +1387,7 @@ class LocalPath(FSBase):
lockfile.remove() lockfile.remove()
except error.Error: except error.Error:
pass pass
atexit.register(try_remove_lockfile) atexit.register(try_remove_lockfile)
# compute the maximum number currently in use with the prefix # compute the maximum number currently in use with the prefix
@ -1381,7 +1427,7 @@ class LocalPath(FSBase):
except error.Error: except error.Error:
pass pass
garbage_prefix = prefix + 'garbage-' garbage_prefix = prefix + "garbage-"
def is_garbage(path): def is_garbage(path):
"""check if path denotes directory scheduled for removal""" """check if path denotes directory scheduled for removal"""
@ -1429,15 +1475,15 @@ class LocalPath(FSBase):
# make link... # make link...
try: try:
username = os.environ['USER'] #linux, et al username = os.environ["USER"] # linux, et al
except KeyError: except KeyError:
try: try:
username = os.environ['USERNAME'] #windows username = os.environ["USERNAME"] # windows
except KeyError: except KeyError:
username = 'current' username = "current"
src = str(udir) src = str(udir)
dest = src[:src.rfind('-')] + '-' + username dest = src[: src.rfind("-")] + "-" + username
try: try:
os.unlink(dest) os.unlink(dest)
except OSError: except OSError:
@ -1448,12 +1494,14 @@ class LocalPath(FSBase):
pass pass
return udir return udir
make_numbered_dir = classmethod(make_numbered_dir) make_numbered_dir = classmethod(make_numbered_dir)
def copymode(src, dest): def copymode(src, dest):
"""copy permission from src to dst.""" """copy permission from src to dst."""
import shutil import shutil
shutil.copymode(src, dest) shutil.copymode(src, dest)
@ -1461,14 +1509,15 @@ def copystat(src, dest):
"""copy permission, last modification time, """copy permission, last modification time,
last access time, and flags from src to dst.""" last access time, and flags from src to dst."""
import shutil import shutil
shutil.copystat(str(src), str(dest)) shutil.copystat(str(src), str(dest))
def copychunked(src, dest): def copychunked(src, dest):
chunksize = 524288 # half a meg of bytes chunksize = 524288 # half a meg of bytes
fsrc = src.open('rb') fsrc = src.open("rb")
try: try:
fdest = dest.open('wb') fdest = dest.open("wb")
try: try:
while 1: while 1:
buf = fsrc.read(chunksize) buf = fsrc.read(chunksize)
@ -1482,8 +1531,9 @@ def copychunked(src, dest):
def isimportable(name): def isimportable(name):
if name and (name[0].isalpha() or name[0] == '_'): if name and (name[0].isalpha() or name[0] == "_"):
name = name.replace("_", '') name = name.replace("_", "")
return not name or name.isalnum() return not name or name.isalnum()
local = LocalPath local = LocalPath

View File

@ -18,6 +18,7 @@ from typing import TypeVar
from typing import Union from typing import Union
import attr import attr
import py import py
# fmt: off # fmt: off

View File

@ -6,5 +6,5 @@ import sys
import _pytest._py.error as error import _pytest._py.error as error
import _pytest._py.path as path import _pytest._py.path as path
sys.modules['py.error'] = error sys.modules["py.error"] = error
sys.modules['py.path'] = path sys.modules["py.path"] = path