comparison mercurial/util.py @ 45942:89a2afe31e82

formating: upgrade to black 20.8b1 This required a couple of small tweaks to un-confuse black, but now it works. Big formatting changes come from: * Dramatically improved collection-splitting logic upstream * Black having a strong (correct IMO) opinion that """ is better than ''' Differential Revision: https://phab.mercurial-scm.org/D9430
author Augie Fackler <raf@durin42.com>
date Fri, 27 Nov 2020 17:03:29 -0500
parents a0791bfd9cfa
children 59fa3890d40a
comparison
equal deleted inserted replaced
45941:346af7687c6f 45942:89a2afe31e82
1262 self._copied = getattr(self, '_copied', 0) + 1 1262 self._copied = getattr(self, '_copied', 0) + 1
1263 return self 1263 return self
1264 1264
1265 1265
1266 class sortdict(collections.OrderedDict): 1266 class sortdict(collections.OrderedDict):
1267 '''a simple sorted dictionary 1267 """a simple sorted dictionary
1268 1268
1269 >>> d1 = sortdict([(b'a', 0), (b'b', 1)]) 1269 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1270 >>> d2 = d1.copy() 1270 >>> d2 = d1.copy()
1271 >>> d2 1271 >>> d2
1272 sortdict([('a', 0), ('b', 1)]) 1272 sortdict([('a', 0), ('b', 1)])
1274 >>> list(d2.keys()) # should still be in last-set order 1274 >>> list(d2.keys()) # should still be in last-set order
1275 ['b', 'a'] 1275 ['b', 'a']
1276 >>> d1.insert(1, b'a.5', 0.5) 1276 >>> d1.insert(1, b'a.5', 0.5)
1277 >>> d1 1277 >>> d1
1278 sortdict([('a', 0), ('a.5', 0.5), ('b', 1)]) 1278 sortdict([('a', 0), ('a.5', 0.5), ('b', 1)])
1279 ''' 1279 """
1280 1280
1281 def __setitem__(self, key, value): 1281 def __setitem__(self, key, value):
1282 if key in self: 1282 if key in self:
1283 del self[key] 1283 del self[key]
1284 super(sortdict, self).__setitem__(key, value) 1284 super(sortdict, self).__setitem__(key, value)
1759 if prop in obj.__dict__: 1759 if prop in obj.__dict__:
1760 del obj.__dict__[prop] 1760 del obj.__dict__[prop]
1761 1761
1762 1762
1763 def increasingchunks(source, min=1024, max=65536): 1763 def increasingchunks(source, min=1024, max=65536):
1764 '''return no less than min bytes per chunk while data remains, 1764 """return no less than min bytes per chunk while data remains,
1765 doubling min after each chunk until it reaches max''' 1765 doubling min after each chunk until it reaches max"""
1766 1766
1767 def log2(x): 1767 def log2(x):
1768 if not x: 1768 if not x:
1769 return 0 1769 return 0
1770 i = 0 1770 i = 0
1831 # PyPy runs slower with gc disabled 1831 # PyPy runs slower with gc disabled
1832 nogc = lambda x: x 1832 nogc = lambda x: x
1833 1833
1834 1834
1835 def pathto(root, n1, n2): 1835 def pathto(root, n1, n2):
1836 '''return the relative path from one place to another. 1836 """return the relative path from one place to another.
1837 root should use os.sep to separate directories 1837 root should use os.sep to separate directories
1838 n1 should use os.sep to separate directories 1838 n1 should use os.sep to separate directories
1839 n2 should use "/" to separate directories 1839 n2 should use "/" to separate directories
1840 returns an os.sep-separated path. 1840 returns an os.sep-separated path.
1841 1841
1842 If n1 is a relative path, it's assumed it's 1842 If n1 is a relative path, it's assumed it's
1843 relative to root. 1843 relative to root.
1844 n2 should always be relative to root. 1844 n2 should always be relative to root.
1845 ''' 1845 """
1846 if not n1: 1846 if not n1:
1847 return localpath(n2) 1847 return localpath(n2)
1848 if os.path.isabs(n1): 1848 if os.path.isabs(n1):
1849 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]: 1849 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1850 return os.path.join(root, localpath(n2)) 1850 return os.path.join(root, localpath(n2))
1890 b'zfs', 1890 b'zfs',
1891 } 1891 }
1892 1892
1893 1893
1894 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): 1894 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1895 '''copy a file, preserving mode and optionally other stat info like 1895 """copy a file, preserving mode and optionally other stat info like
1896 atime/mtime 1896 atime/mtime
1897 1897
1898 checkambig argument is used with filestat, and is useful only if 1898 checkambig argument is used with filestat, and is useful only if
1899 destination file is guarded by any lock (e.g. repo.lock or 1899 destination file is guarded by any lock (e.g. repo.lock or
1900 repo.wlock). 1900 repo.wlock).
1901 1901
1902 copystat and checkambig should be exclusive. 1902 copystat and checkambig should be exclusive.
1903 ''' 1903 """
1904 assert not (copystat and checkambig) 1904 assert not (copystat and checkambig)
1905 oldstat = None 1905 oldstat = None
1906 if os.path.lexists(dest): 1906 if os.path.lexists(dest):
1907 if checkambig: 1907 if checkambig:
1908 oldstat = checkambig and filestat.frompath(dest) 1908 oldstat = checkambig and filestat.frompath(dest)
2015 } 2015 }
2016 _winreservedchars = b':*?"<>|' 2016 _winreservedchars = b':*?"<>|'
2017 2017
2018 2018
2019 def checkwinfilename(path): 2019 def checkwinfilename(path):
2020 r'''Check that the base-relative path is a valid filename on Windows. 2020 r"""Check that the base-relative path is a valid filename on Windows.
2021 Returns None if the path is ok, or a UI string describing the problem. 2021 Returns None if the path is ok, or a UI string describing the problem.
2022 2022
2023 >>> checkwinfilename(b"just/a/normal/path") 2023 >>> checkwinfilename(b"just/a/normal/path")
2024 >>> checkwinfilename(b"foo/bar/con.xml") 2024 >>> checkwinfilename(b"foo/bar/con.xml")
2025 "filename contains 'con', which is reserved on Windows" 2025 "filename contains 'con', which is reserved on Windows"
2037 >>> checkwinfilename(b"../bar") 2037 >>> checkwinfilename(b"../bar")
2038 >>> checkwinfilename(b"foo\\") 2038 >>> checkwinfilename(b"foo\\")
2039 "filename ends with '\\', which is invalid on Windows" 2039 "filename ends with '\\', which is invalid on Windows"
2040 >>> checkwinfilename(b"foo\\/bar") 2040 >>> checkwinfilename(b"foo\\/bar")
2041 "directory name ends with '\\', which is invalid on Windows" 2041 "directory name ends with '\\', which is invalid on Windows"
2042 ''' 2042 """
2043 if path.endswith(b'\\'): 2043 if path.endswith(b'\\'):
2044 return _(b"filename ends with '\\', which is invalid on Windows") 2044 return _(b"filename ends with '\\', which is invalid on Windows")
2045 if b'\\/' in path: 2045 if b'\\/' in path:
2046 return _(b"directory name ends with '\\', which is invalid on Windows") 2046 return _(b"directory name ends with '\\', which is invalid on Windows")
2047 for n in path.replace(b'\\', b'/').split(b'/'): 2047 for n in path.replace(b'\\', b'/').split(b'/'):
2173 _re2 = bool(re2.match(r'\[([^\[]+)\]', b'[ui]')) 2173 _re2 = bool(re2.match(r'\[([^\[]+)\]', b'[ui]'))
2174 except ImportError: 2174 except ImportError:
2175 _re2 = False 2175 _re2 = False
2176 2176
2177 def compile(self, pat, flags=0): 2177 def compile(self, pat, flags=0):
2178 '''Compile a regular expression, using re2 if possible 2178 """Compile a regular expression, using re2 if possible
2179 2179
2180 For best performance, use only re2-compatible regexp features. The 2180 For best performance, use only re2-compatible regexp features. The
2181 only flags from the re module that are re2-compatible are 2181 only flags from the re module that are re2-compatible are
2182 IGNORECASE and MULTILINE.''' 2182 IGNORECASE and MULTILINE."""
2183 if _re2 is None: 2183 if _re2 is None:
2184 self._checkre2() 2184 self._checkre2()
2185 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0: 2185 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
2186 if flags & remod.IGNORECASE: 2186 if flags & remod.IGNORECASE:
2187 pat = b'(?i)' + pat 2187 pat = b'(?i)' + pat
2193 pass 2193 pass
2194 return remod.compile(pat, flags) 2194 return remod.compile(pat, flags)
2195 2195
2196 @propertycache 2196 @propertycache
2197 def escape(self): 2197 def escape(self):
2198 '''Return the version of escape corresponding to self.compile. 2198 """Return the version of escape corresponding to self.compile.
2199 2199
2200 This is imperfect because whether re2 or re is used for a particular 2200 This is imperfect because whether re2 or re is used for a particular
2201 function depends on the flags, etc, but it's the best we can do. 2201 function depends on the flags, etc, but it's the best we can do.
2202 ''' 2202 """
2203 global _re2 2203 global _re2
2204 if _re2 is None: 2204 if _re2 is None:
2205 self._checkre2() 2205 self._checkre2()
2206 if _re2: 2206 if _re2:
2207 return re2.escape 2207 return re2.escape
2213 2213
2214 _fspathcache = {} 2214 _fspathcache = {}
2215 2215
2216 2216
2217 def fspath(name, root): 2217 def fspath(name, root):
2218 '''Get name in the case stored in the filesystem 2218 """Get name in the case stored in the filesystem
2219 2219
2220 The name should be relative to root, and be normcase-ed for efficiency. 2220 The name should be relative to root, and be normcase-ed for efficiency.
2221 2221
2222 Note that this function is unnecessary, and should not be 2222 Note that this function is unnecessary, and should not be
2223 called, for case-sensitive filesystems (simply because it's expensive). 2223 called, for case-sensitive filesystems (simply because it's expensive).
2224 2224
2225 The root should be normcase-ed, too. 2225 The root should be normcase-ed, too.
2226 ''' 2226 """
2227 2227
2228 def _makefspathcacheentry(dir): 2228 def _makefspathcacheentry(dir):
2229 return {normcase(n): n for n in os.listdir(dir)} 2229 return {normcase(n): n for n in os.listdir(dir)}
2230 2230
2231 seps = pycompat.ossep 2231 seps = pycompat.ossep
2299 and path.endswith(pycompat.osaltsep) 2299 and path.endswith(pycompat.osaltsep)
2300 ) 2300 )
2301 2301
2302 2302
2303 def splitpath(path): 2303 def splitpath(path):
2304 '''Split path by os.sep. 2304 """Split path by os.sep.
2305 Note that this function does not use os.altsep because this is 2305 Note that this function does not use os.altsep because this is
2306 an alternative of simple "xxx.split(os.sep)". 2306 an alternative of simple "xxx.split(os.sep)".
2307 It is recommended to use os.path.normpath() before using this 2307 It is recommended to use os.path.normpath() before using this
2308 function if need.''' 2308 function if need."""
2309 return path.split(pycompat.ossep) 2309 return path.split(pycompat.ossep)
2310 2310
2311 2311
2312 def mktempcopy(name, emptyok=False, createmode=None, enforcewritable=False): 2312 def mktempcopy(name, emptyok=False, createmode=None, enforcewritable=False):
2313 """Create a temporary file with the same contents from name 2313 """Create a temporary file with the same contents from name
2457 def __ne__(self, other): 2457 def __ne__(self, other):
2458 return not self == other 2458 return not self == other
2459 2459
2460 2460
2461 class atomictempfile(object): 2461 class atomictempfile(object):
2462 '''writable file object that atomically updates a file 2462 """writable file object that atomically updates a file
2463 2463
2464 All writes will go to a temporary copy of the original file. Call 2464 All writes will go to a temporary copy of the original file. Call
2465 close() when you are done writing, and atomictempfile will rename 2465 close() when you are done writing, and atomictempfile will rename
2466 the temporary copy to the original name, making the changes 2466 the temporary copy to the original name, making the changes
2467 visible. If the object is destroyed without being closed, all your 2467 visible. If the object is destroyed without being closed, all your
2468 writes are discarded. 2468 writes are discarded.
2469 2469
2470 checkambig argument of constructor is used with filestat, and is 2470 checkambig argument of constructor is used with filestat, and is
2471 useful only if target file is guarded by any lock (e.g. repo.lock 2471 useful only if target file is guarded by any lock (e.g. repo.lock
2472 or repo.wlock). 2472 or repo.wlock).
2473 ''' 2473 """
2474 2474
2475 def __init__(self, name, mode=b'w+b', createmode=None, checkambig=False): 2475 def __init__(self, name, mode=b'w+b', createmode=None, checkambig=False):
2476 self.__name = name # permanent name 2476 self.__name = name # permanent name
2477 self._tempname = mktempcopy( 2477 self._tempname = mktempcopy(
2478 name, 2478 name,
3363 3363
3364 timedcm._nested = 0 3364 timedcm._nested = 0
3365 3365
3366 3366
3367 def timed(func): 3367 def timed(func):
3368 '''Report the execution time of a function call to stderr. 3368 """Report the execution time of a function call to stderr.
3369 3369
3370 During development, use as a decorator when you need to measure 3370 During development, use as a decorator when you need to measure
3371 the cost of a function, e.g. as follows: 3371 the cost of a function, e.g. as follows:
3372 3372
3373 @util.timed 3373 @util.timed
3374 def foo(a, b, c): 3374 def foo(a, b, c):
3375 pass 3375 pass
3376 ''' 3376 """
3377 3377
3378 def wrapper(*args, **kwargs): 3378 def wrapper(*args, **kwargs):
3379 with timedcm(pycompat.bytestr(func.__name__)) as time_stats: 3379 with timedcm(pycompat.bytestr(func.__name__)) as time_stats:
3380 result = func(*args, **kwargs) 3380 result = func(*args, **kwargs)
3381 stderr = procutil.stderr 3381 stderr = procutil.stderr
3402 (b'b', 1), 3402 (b'b', 1),
3403 ) 3403 )
3404 3404
3405 3405
3406 def sizetoint(s): 3406 def sizetoint(s):
3407 '''Convert a space specifier to a byte count. 3407 """Convert a space specifier to a byte count.
3408 3408
3409 >>> sizetoint(b'30') 3409 >>> sizetoint(b'30')
3410 30 3410 30
3411 >>> sizetoint(b'2.2kb') 3411 >>> sizetoint(b'2.2kb')
3412 2252 3412 2252
3413 >>> sizetoint(b'6M') 3413 >>> sizetoint(b'6M')
3414 6291456 3414 6291456
3415 ''' 3415 """
3416 t = s.strip().lower() 3416 t = s.strip().lower()
3417 try: 3417 try:
3418 for k, u in _sizeunits: 3418 for k, u in _sizeunits:
3419 if t.endswith(k): 3419 if t.endswith(k):
3420 return int(float(t[: -len(k)]) * u) 3420 return int(float(t[: -len(k)]) * u)
3422 except ValueError: 3422 except ValueError:
3423 raise error.ParseError(_(b"couldn't parse size: %s") % s) 3423 raise error.ParseError(_(b"couldn't parse size: %s") % s)
3424 3424
3425 3425
3426 class hooks(object): 3426 class hooks(object):
3427 '''A collection of hook functions that can be used to extend a 3427 """A collection of hook functions that can be used to extend a
3428 function's behavior. Hooks are called in lexicographic order, 3428 function's behavior. Hooks are called in lexicographic order,
3429 based on the names of their sources.''' 3429 based on the names of their sources."""
3430 3430
3431 def __init__(self): 3431 def __init__(self):
3432 self._hooks = [] 3432 self._hooks = []
3433 3433
3434 def add(self, source, hook): 3434 def add(self, source, hook):
3441 results.append(hook(*args)) 3441 results.append(hook(*args))
3442 return results 3442 return results
3443 3443
3444 3444
3445 def getstackframes(skip=0, line=b' %-*s in %s\n', fileline=b'%s:%d', depth=0): 3445 def getstackframes(skip=0, line=b' %-*s in %s\n', fileline=b'%s:%d', depth=0):
3446 '''Yields lines for a nicely formatted stacktrace. 3446 """Yields lines for a nicely formatted stacktrace.
3447 Skips the 'skip' last entries, then return the last 'depth' entries. 3447 Skips the 'skip' last entries, then return the last 'depth' entries.
3448 Each file+linenumber is formatted according to fileline. 3448 Each file+linenumber is formatted according to fileline.
3449 Each line is formatted according to line. 3449 Each line is formatted according to line.
3450 If line is None, it yields: 3450 If line is None, it yields:
3451 length of longest filepath+line number, 3451 length of longest filepath+line number,
3452 filepath+linenumber, 3452 filepath+linenumber,
3453 function 3453 function
3454 3454
3455 Not be used in production code but very convenient while developing. 3455 Not be used in production code but very convenient while developing.
3456 ''' 3456 """
3457 entries = [ 3457 entries = [
3458 (fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func)) 3458 (fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
3459 for fn, ln, func, _text in traceback.extract_stack()[: -skip - 1] 3459 for fn, ln, func, _text in traceback.extract_stack()[: -skip - 1]
3460 ][-depth:] 3460 ][-depth:]
3461 if entries: 3461 if entries:
3473 f=procutil.stderr, 3473 f=procutil.stderr,
3474 otherf=procutil.stdout, 3474 otherf=procutil.stdout,
3475 depth=0, 3475 depth=0,
3476 prefix=b'', 3476 prefix=b'',
3477 ): 3477 ):
3478 '''Writes a message to f (stderr) with a nicely formatted stacktrace. 3478 """Writes a message to f (stderr) with a nicely formatted stacktrace.
3479 Skips the 'skip' entries closest to the call, then show 'depth' entries. 3479 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3480 By default it will flush stdout first. 3480 By default it will flush stdout first.
3481 It can be used everywhere and intentionally does not require an ui object. 3481 It can be used everywhere and intentionally does not require an ui object.
3482 Not be used in production code but very convenient while developing. 3482 Not be used in production code but very convenient while developing.
3483 ''' 3483 """
3484 if otherf: 3484 if otherf:
3485 otherf.flush() 3485 otherf.flush()
3486 f.write(b'%s%s at:\n' % (prefix, msg.rstrip())) 3486 f.write(b'%s%s at:\n' % (prefix, msg.rstrip()))
3487 for line in getstackframes(skip + 1, depth=depth): 3487 for line in getstackframes(skip + 1, depth=depth):
3488 f.write(prefix + line) 3488 f.write(prefix + line)