Mercurial > public > mercurial-scm > hg
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) |