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) |