diff --git a/contrib/import-checker.py b/contrib/import-checker.py --- a/contrib/import-checker.py +++ b/contrib/import-checker.py @@ -10,7 +10,7 @@ # Import a minimal set of stdlib modules needed for list_stdlib_modules() # to work when run from a virtualenv. The modules were chosen empirically # so that the return value matches the return value without virtualenv. -if True: # disable lexical sorting checks +if True: # disable lexical sorting checks try: import BaseHTTPServer as basehttpserver except ImportError: @@ -21,40 +21,39 @@ # Whitelist of modules that symbols can be directly imported from. allowsymbolimports = ( - '__future__', - 'bzrlib', - 'hgclient', - 'mercurial', - 'mercurial.hgweb.common', - 'mercurial.hgweb.request', - 'mercurial.i18n', - 'mercurial.node', + "__future__", + "bzrlib", + "hgclient", + "mercurial", + "mercurial.hgweb.common", + "mercurial.hgweb.request", + "mercurial.i18n", + "mercurial.node", # for revlog to re-export constant to extensions - 'mercurial.revlogutils.constants', + "mercurial.revlogutils.constants", # for cffi modules to re-export pure functions - 'mercurial.pure.base85', - 'mercurial.pure.bdiff', - 'mercurial.pure.mpatch', - 'mercurial.pure.osutil', - 'mercurial.pure.parsers', + "mercurial.pure.base85", + "mercurial.pure.bdiff", + "mercurial.pure.mpatch", + "mercurial.pure.osutil", + "mercurial.pure.parsers", # third-party imports should be directly imported - 'mercurial.thirdparty', - 'mercurial.thirdparty.attr', - 'mercurial.thirdparty.zope', - 'mercurial.thirdparty.zope.interface', + "mercurial.thirdparty", + "mercurial.thirdparty.attr", + "mercurial.thirdparty.zope", + "mercurial.thirdparty.zope.interface", ) # Whitelist of symbols that can be directly imported. -directsymbols = ( - 'demandimport', -) +directsymbols = ("demandimport",) # Modules that must be aliased because they are commonly confused with # common variables and can create aliasing and readability issues. requirealias = { - 'ui': 'uimod', + "ui": "uimod", } + def usingabsolute(root): """Whether absolute imports are being used.""" if sys.version_info[0] >= 3: @@ -62,13 +61,14 @@ for node in ast.walk(root): if isinstance(node, ast.ImportFrom): - if node.module == '__future__': + if node.module == "__future__": for n in node.names: - if n.name == 'absolute_import': + if n.name == "absolute_import": return True return False + def walklocal(root): """Recursively yield all descendant nodes but not in a different scope""" todo = collections.deque(ast.iter_child_nodes(root)) @@ -80,6 +80,7 @@ todo.extend(ast.iter_child_nodes(node)) yield node, newscope + def dotted_name_of_path(path): """Given a relative path to a source file, return its dotted module name. @@ -88,11 +89,12 @@ >>> dotted_name_of_path('zlibmodule.so') 'zlib' """ - parts = path.replace(os.sep, '/').split('/') - parts[-1] = parts[-1].split('.', 1)[0] # remove .py and .so and .ARCH.so - if parts[-1].endswith('module'): + parts = path.replace(os.sep, "/").split("/") + parts[-1] = parts[-1].split(".", 1)[0] # remove .py and .so and .ARCH.so + if parts[-1].endswith("module"): parts[-1] = parts[-1][:-6] - return '.'.join(parts) + return ".".join(parts) + def fromlocalfunc(modulename, localmods): """Get a function to examine which locally defined module the @@ -158,43 +160,48 @@ ('foo.bar', 'foo.bar.__init__', True) """ if not isinstance(modulename, str): - modulename = modulename.decode('ascii') - prefix = '.'.join(modulename.split('.')[:-1]) + modulename = modulename.decode("ascii") + prefix = ".".join(modulename.split(".")[:-1]) if prefix: - prefix += '.' + prefix += "." + def fromlocal(name, level=0): # name is false value when relative imports are used. if not name: # If relative imports are used, level must not be absolute. assert level > 0 - candidates = ['.'.join(modulename.split('.')[:-level])] + candidates = [".".join(modulename.split(".")[:-level])] else: if not level: # Check relative name first. candidates = [prefix + name, name] else: - candidates = ['.'.join(modulename.split('.')[:-level]) + - '.' + name] + candidates = [ + ".".join(modulename.split(".")[:-level]) + "." + name + ] for n in candidates: if n in localmods: return (n, n, False) - dottedpath = n + '.__init__' + dottedpath = n + ".__init__" if dottedpath in localmods: return (n, dottedpath, True) return False + return fromlocal + def populateextmods(localmods): """Populate C extension modules based on pure modules""" newlocalmods = set(localmods) for n in localmods: - if n.startswith('mercurial.pure.'): - m = n[len('mercurial.pure.'):] - newlocalmods.add('mercurial.cext.' + m) - newlocalmods.add('mercurial.cffi._' + m) + if n.startswith("mercurial.pure."): + m = n[len("mercurial.pure.") :] + newlocalmods.add("mercurial.cext." + m) + newlocalmods.add("mercurial.cffi._" + m) return newlocalmods + def list_stdlib_modules(): """List the modules present in the stdlib. @@ -227,18 +234,18 @@ yield m # These modules only exist on windows, but we should always # consider them stdlib. - for m in ['msvcrt', '_winreg']: + for m in ["msvcrt", "_winreg"]: yield m - yield '__builtin__' - yield 'builtins' # python3 only - yield 'importlib.abc' # python3 only - yield 'importlib.machinery' # python3 only - yield 'importlib.util' # python3 only - for m in 'fcntl', 'grp', 'pwd', 'termios': # Unix only + yield "__builtin__" + yield "builtins" # python3 only + yield "importlib.abc" # python3 only + yield "importlib.machinery" # python3 only + yield "importlib.util" # python3 only + for m in "fcntl", "grp", "pwd", "termios": # Unix only yield m - for m in 'cPickle', 'datetime': # in Python (not C) on PyPy + for m in "cPickle", "datetime": # in Python (not C) on PyPy yield m - for m in ['cffi']: + for m in ["cffi"]: yield m stdlib_prefixes = {sys.prefix, sys.exec_prefix} # We need to supplement the list of prefixes for the search to work @@ -262,28 +269,33 @@ for libpath in sys.path: # We want to walk everything in sys.path that starts with something in # stdlib_prefixes, but not directories from the hg sources. - if (os.path.abspath(libpath).startswith(sourceroot) - or not any(libpath.startswith(p) for p in stdlib_prefixes)): + if os.path.abspath(libpath).startswith(sourceroot) or not any( + libpath.startswith(p) for p in stdlib_prefixes + ): continue for top, dirs, files in os.walk(libpath): for i, d in reversed(list(enumerate(dirs))): - if (not os.path.exists(os.path.join(top, d, '__init__.py')) - or top == libpath and d in ('hgdemandimport', 'hgext', - 'mercurial')): + if ( + not os.path.exists(os.path.join(top, d, "__init__.py")) + or top == libpath + and d in ("hgdemandimport", "hgext", "mercurial") + ): del dirs[i] for name in files: - if not name.endswith(('.py', '.so', '.pyc', '.pyo', '.pyd')): + if not name.endswith((".py", ".so", ".pyc", ".pyo", ".pyd")): continue - if name.startswith('__init__.py'): + if name.startswith("__init__.py"): full_path = top else: full_path = os.path.join(top, name) - rel_path = full_path[len(libpath) + 1:] + rel_path = full_path[len(libpath) + 1 :] mod = dotted_name_of_path(rel_path) yield mod + stdlib_modules = set(list_stdlib_modules()) + def imported_modules(source, modulename, f, localmods, ignore_nested=False): """Given the source of a file as a string, yield the names imported by that file. @@ -344,7 +356,7 @@ """ fromlocal = fromlocalfunc(modulename, localmods) for node in ast.walk(ast.parse(source, f)): - if ignore_nested and getattr(node, 'col_offset', 0) > 0: + if ignore_nested and getattr(node, "col_offset", 0) > 0: continue if isinstance(node, ast.Import): for n in node.names: @@ -368,7 +380,7 @@ continue modnotfound = False - prefix = absname + '.' + prefix = absname + "." for n in node.names: found = fromlocal(prefix + n.name) if not found: @@ -381,6 +393,7 @@ # lookup yield dottedpath + def verify_import_convention(module, source, localmods): """Verify imports match our established coding convention. @@ -398,6 +411,7 @@ else: return verify_stdlib_on_own_line(root) + def verify_modern_convention(module, root, localmods, root_col_offset=0): """Verify a file conforms to the modern import convention rules. @@ -426,8 +440,8 @@ and readability problems. See `requirealias`. """ if not isinstance(module, str): - module = module.decode('ascii') - topmodule = module.split('.')[0] + module = module.decode("ascii") + topmodule = module.split(".")[0] fromlocal = fromlocalfunc(module, localmods) # Whether a local/non-stdlib import has been performed. @@ -441,19 +455,26 @@ seenlevels = set() for node, newscope in walklocal(root): + def msg(fmt, *args): return (fmt % args, node.lineno) + if newscope: # Check for local imports in function - for r in verify_modern_convention(module, node, localmods, - node.col_offset + 4): + for r in verify_modern_convention( + module, node, localmods, node.col_offset + 4 + ): yield r elif isinstance(node, ast.Import): # Disallow "import foo, bar" and require separate imports # for each module. if len(node.names) > 1: - yield msg('multiple imported names: %s', - ', '.join(n.name for n in node.names)) + yield msg( + "multiple imported names: %s", + ", ".join( + n.name for n in node.names + ), + ) name = node.names[0].name asname = node.names[0].asname @@ -463,51 +484,61 @@ # Ignore sorting rules on imports inside blocks. if node.col_offset == root_col_offset: if lastname and name < lastname and laststdlib == stdlib: - yield msg('imports not lexically sorted: %s < %s', - name, lastname) + yield msg( + "imports not lexically sorted: %s < %s", name, lastname + ) lastname = name laststdlib = stdlib # stdlib imports should be before local imports. if stdlib and seenlocal and node.col_offset == root_col_offset: - yield msg('stdlib import "%s" follows local import: %s', - name, seenlocal) + yield msg( + 'stdlib import "%s" follows local import: %s', + name, + seenlocal, + ) if not stdlib: seenlocal = name # Import of sibling modules should use relative imports. - topname = name.split('.')[0] + topname = name.split(".")[0] if topname == topmodule: - yield msg('import should be relative: %s', name) + yield msg("import should be relative: %s", name) if name in requirealias and asname != requirealias[name]: - yield msg('%s module must be "as" aliased to %s', - name, requirealias[name]) + yield msg( + '%s module must be "as" aliased to %s', + name, + requirealias[ + name + ], + ) elif isinstance(node, ast.ImportFrom): # Resolve the full imported module name. if node.level > 0: - fullname = '.'.join(module.split('.')[:-node.level]) + fullname = ".".join(module.split(".")[: -node.level]) if node.module: - fullname += '.%s' % node.module + fullname += ".%s" % node.module else: assert node.module fullname = node.module - topname = fullname.split('.')[0] + topname = fullname.split(".")[0] if topname == topmodule: - yield msg('import should be relative: %s', fullname) + yield msg("import should be relative: %s", fullname) # __future__ is special since it needs to come first and use # symbol import. - if fullname != '__future__': + if fullname != "__future__": if not fullname or ( fullname in stdlib_modules and fullname not in localmods - and fullname + '.__init__' not in localmods): - yield msg('relative import of stdlib module') + and fullname + ".__init__" not in localmods + ): + yield msg("relative import of stdlib module") else: seenlocal = fullname @@ -515,20 +546,27 @@ # must occur before non-symbol imports. found = fromlocal(node.module, node.level) if found and found[2]: # node.module is a package - prefix = found[0] + '.' - symbols = (n.name for n in node.names - if not fromlocal(prefix + n.name)) + prefix = found[0] + "." + symbols = ( + n.name for n in node.names if not fromlocal(prefix + n.name) + ) else: symbols = (n.name for n in node.names) symbols = [sym for sym in symbols if sym not in directsymbols] if node.module and node.col_offset == root_col_offset: if symbols and fullname not in allowsymbolimports: - yield msg('direct symbol import %s from %s', - ', '.join(symbols), fullname) + yield msg( + "direct symbol import %s from %s", + ", ".join( + symbols + ), + fullname, + ) if symbols and seennonsymbollocal: - yield msg('symbol import follows non-symbol import: %s', - fullname) + yield msg( + "symbol import follows non-symbol import: %s", fullname + ) if not symbols and fullname not in stdlib_modules: seennonsymbollocal = True @@ -536,15 +574,19 @@ assert node.level # Only allow 1 group per level. - if (node.level in seenlevels - and node.col_offset == root_col_offset): - yield msg('multiple "from %s import" statements', - '.' * node.level) + if ( + node.level in seenlevels + and node.col_offset == root_col_offset + ): + yield msg( + 'multiple "from %s import" statements', "." * node.level + ) # Higher-level groups come before lower-level groups. if any(node.level > l for l in seenlevels): - yield msg('higher-level import should come first: %s', - fullname) + yield msg( + "higher-level import should come first: %s", fullname + ) seenlevels.add(node.level) @@ -554,14 +596,25 @@ for n in node.names: if lastentryname and n.name < lastentryname: - yield msg('imports from %s not lexically sorted: %s < %s', - fullname, n.name, lastentryname) + yield msg( + "imports from %s not lexically sorted: %s < %s", + fullname, + n.name, + lastentryname, + ) lastentryname = n.name if n.name in requirealias and n.asname != requirealias[n.name]: - yield msg('%s from %s must be "as" aliased to %s', - n.name, fullname, requirealias[n.name]) + yield msg( + '%s from %s must be "as" aliased to %s', + n.name, + fullname, + requirealias[ + n.name + ], + ) + def verify_stdlib_on_own_line(root): """Given some python source, verify that stdlib imports are done @@ -580,13 +633,24 @@ for n in node.names: from_stdlib[n.name in stdlib_modules].append(n.name) if from_stdlib[True] and from_stdlib[False]: - yield ('mixed imports\n stdlib: %s\n relative: %s' % - (', '.join(sorted(from_stdlib[True])), - ', '.join(sorted(from_stdlib[False]))), node.lineno) + yield ( + "mixed imports\n stdlib: %s\n relative: %s" + % ( + ", ".join( + sorted(from_stdlib[True]) + ), + ", ".join( + sorted(from_stdlib[False]) + ), + ), + node.lineno, + ) + class CircularImport(Exception): pass + def checkmod(mod, imports): shortest = {} visit = [[mod]] @@ -601,6 +665,7 @@ continue visit.append(path + [i]) + def rotatecycle(cycle): """arrange a cycle so that the lexicographically first module listed first @@ -611,6 +676,7 @@ idx = cycle.index(lowest) return cycle[idx:] + cycle[:idx] + [lowest] + def find_cycles(imports): """Find cycles in an already-loaded import graph. @@ -634,9 +700,11 @@ cycles.add(" -> ".join(rotatecycle(cycle))) return cycles + def _cycle_sortkey(c): return len(c), c + def embedded(f, modname, src): """Extract embedded python code @@ -670,14 +738,15 @@ if not name: # use 'doctest.py', in order to make already existing # doctest above pass instantly - name = 'doctest.py' + name = "doctest.py" # "starts" is "line number" (1-origin), but embedded() is # expected to return "line offset" (0-origin). Therefore, this # yields "starts - 1". if not isinstance(modname, str): - modname = modname.decode('utf8') + modname = modname.decode("utf8") yield code, "%s[%d]" % (modname, starts), name, starts - 1 + def sources(f, modname): """Yields possibly multiple sources from a filepath @@ -689,20 +758,21 @@ the input file. """ py = False - if not f.endswith('.t'): - with open(f, 'rb') as src: + if not f.endswith(".t"): + with open(f, "rb") as src: yield src.read(), modname, f, 0 py = True - if py or f.endswith('.t'): - with open(f, 'rb') as src: + if py or f.endswith(".t"): + with open(f, "rb") as src: for script, modname, t, line in embedded(f, modname, src): - yield script, modname.encode('utf8'), t, line + yield script, modname.encode("utf8"), t, line + def main(argv): - if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2): - print('Usage: %s {-|file [file] [file] ...}') + if len(argv) < 2 or (argv[1] == "-" and len(argv) > 2): + print("Usage: %s {-|file [file] [file] ...}") return 1 - if argv[1] == '-': + if argv[1] == "-": argv = argv[:1] argv.extend(l.rstrip() for l in sys.stdin.readlines()) localmodpaths = {} @@ -715,19 +785,23 @@ for localmodname, source_path in sorted(localmodpaths.items()): if not isinstance(localmodname, bytes): # This is only safe because all hg's files are ascii - localmodname = localmodname.encode('ascii') + localmodname = localmodname.encode("ascii") for src, modname, name, line in sources(source_path, localmodname): try: used_imports[modname] = sorted( - imported_modules(src, modname, name, localmods, - ignore_nested=True)) - for error, lineno in verify_import_convention(modname, src, - localmods): + imported_modules( + src, modname, name, localmods, ignore_nested=True + ) + ) + for error, lineno in verify_import_convention( + modname, src, localmods + ): any_errors = True - print('%s:%d: %s' % (source_path, lineno + line, error)) + print("%s:%d: %s" % (source_path, lineno + line, error)) except SyntaxError as e: - print('%s:%d: SyntaxError: %s' % - (source_path, e.lineno + line, e)) + print( + "%s:%d: SyntaxError: %s" % (source_path, e.lineno + line, e) + ) cycles = find_cycles(used_imports) if cycles: firstmods = set() @@ -738,10 +812,11 @@ # of cycles that are effectively duplicates. if first in firstmods: continue - print('Import cycle:', c) + print("Import cycle:", c) firstmods.add(first) any_errors = True return any_errors != 0 -if __name__ == '__main__': + +if __name__ == "__main__": sys.exit(int(main(sys.argv))) diff --git a/mercurial/cacheutil.py b/mercurial/cacheutil.py --- a/mercurial/cacheutil.py +++ b/mercurial/cacheutil.py @@ -8,14 +8,15 @@ from . import repoview + def cachetocopy(srcrepo): """return the list of cache file valuable to copy during a clone""" # In local clones we're copying all nodes, not just served # ones. Therefore copy all branch caches over. - cachefiles = ['branch2'] - cachefiles += ['branch2-%s' % f for f in repoview.filtertable] - cachefiles += ['rbc-names-v1', 'rbc-revs-v1'] - cachefiles += ['tags2'] - cachefiles += ['tags2-%s' % f for f in repoview.filtertable] - cachefiles += ['hgtagsfnodes1'] + cachefiles = ["branch2"] + cachefiles += ["branch2-%s" % f for f in repoview.filtertable] + cachefiles += ["rbc-names-v1", "rbc-revs-v1"] + cachefiles += ["tags2"] + cachefiles += ["tags2-%s" % f for f in repoview.filtertable] + cachefiles += ["hgtagsfnodes1"] return cachefiles diff --git a/mercurial/diffhelper.py b/mercurial/diffhelper.py --- a/mercurial/diffhelper.py +++ b/mercurial/diffhelper.py @@ -14,6 +14,7 @@ pycompat, ) + def addlines(fp, hunk, lena, lenb, a, b): """Read lines from fp into the hunk @@ -30,38 +31,40 @@ for i in pycompat.xrange(num): s = fp.readline() if not s: - raise error.ParseError(_('incomplete hunk')) + raise error.ParseError(_("incomplete hunk")) if s == "\\ No newline at end of file\n": fixnewline(hunk, a, b) continue - if s == '\n' or s == '\r\n': + if s == "\n" or s == "\r\n": # Some patches may be missing the control char # on empty lines. Supply a leading space. - s = ' ' + s + s = " " + s hunk.append(s) - if s.startswith('+'): + if s.startswith("+"): b.append(s[1:]) - elif s.startswith('-'): + elif s.startswith("-"): a.append(s) else: b.append(s[1:]) a.append(s) + def fixnewline(hunk, a, b): """Fix up the last lines of a and b when the patch has no newline at EOF""" l = hunk[-1] # tolerate CRLF in last line - if l.endswith('\r\n'): + if l.endswith("\r\n"): hline = l[:-2] else: hline = l[:-1] - if hline.startswith((' ', '+')): + if hline.startswith((" ", "+")): b[-1] = hline[1:] - if hline.startswith((' ', '-')): + if hline.startswith((" ", "-")): a[-1] = hline hunk[-1] = hline + def testhunk(a, b, bstart): """Compare the lines in a with the lines in b diff --git a/mercurial/dirstateguard.py b/mercurial/dirstateguard.py --- a/mercurial/dirstateguard.py +++ b/mercurial/dirstateguard.py @@ -15,8 +15,9 @@ util, ) + class dirstateguard(util.transactional): - '''Restore dirstate at unexpected failure. + """Restore dirstate at unexpected failure. At the construction, this class does: @@ -27,21 +28,25 @@ is invoked before ``close()``. This just removes the backup file at ``close()`` before ``release()``. - ''' + """ def __init__(self, repo, name): self._repo = repo self._active = False self._closed = False - self._backupname = 'dirstate.backup.%s.%d' % (name, id(self)) - self._narrowspecbackupname = ('narrowspec.backup.%s.%d' % - (name, id(self))) + self._backupname = "dirstate.backup.%s.%d" % (name, id(self)) + self._narrowspecbackupname = "narrowspec.backup.%s.%d" % ( + name, + id( + self + ), + ) repo.dirstate.savebackup(repo.currenttransaction(), self._backupname) narrowspec.savewcbackup(repo, self._narrowspecbackupname) self._active = True def __del__(self): - if self._active: # still active + if self._active: # still active # this may occur, even if this class is used correctly: # for example, releasing other resources like transaction # may raise exception before ``dirstateguard.release`` in @@ -49,27 +54,33 @@ self._abort() def close(self): - if not self._active: # already inactivated - msg = (_("can't close already inactivated backup: %s") - % self._backupname) + if not self._active: # already inactivated + msg = ( + _("can't close already inactivated backup: %s") + % self._backupname + ) raise error.Abort(msg) - self._repo.dirstate.clearbackup(self._repo.currenttransaction(), - self._backupname) + self._repo.dirstate.clearbackup( + self._repo.currenttransaction(), self._backupname + ) narrowspec.clearwcbackup(self._repo, self._narrowspecbackupname) self._active = False self._closed = True def _abort(self): narrowspec.restorewcbackup(self._repo, self._narrowspecbackupname) - self._repo.dirstate.restorebackup(self._repo.currenttransaction(), - self._backupname) + self._repo.dirstate.restorebackup( + self._repo.currenttransaction(), self._backupname + ) self._active = False def release(self): if not self._closed: - if not self._active: # already inactivated - msg = (_("can't release already inactivated backup: %s") - % self._backupname) + if not self._active: # already inactivated + msg = ( + _("can't release already inactivated backup: %s") + % self._backupname + ) raise error.Abort(msg) self._abort() diff --git a/mercurial/httpconnection.py b/mercurial/httpconnection.py --- a/mercurial/httpconnection.py +++ b/mercurial/httpconnection.py @@ -43,8 +43,9 @@ # requires authentication. Since we can't know until we try # once whether authentication will be required, just lie to # the user and maybe the push succeeds suddenly at 50%. - self._progress = ui.makeprogress(_('sending'), unit=_('kb'), - total=(self.length // 1024 * 2)) + self._progress = ui.makeprogress( + _("sending"), unit=_("kb"), total=(self.length // 1024 * 2) + ) def read(self, *args, **kwargs): ret = self._data.read(*args, **kwargs) @@ -61,35 +62,36 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.close() + # moved here from url.py to avoid a cycle def readauthforuri(ui, uri, user): uri = pycompat.bytesurl(uri) # Read configuration groups = {} - for key, val in ui.configitems('auth'): - if key in ('cookiefile',): + for key, val in ui.configitems("auth"): + if key in ("cookiefile",): continue - if '.' not in key: + if "." not in key: ui.warn(_("ignoring invalid [auth] key '%s'\n") % key) continue - group, setting = key.rsplit('.', 1) + group, setting = key.rsplit(".", 1) gdict = groups.setdefault(group, {}) - if setting in ('username', 'cert', 'key'): + if setting in ("username", "cert", "key"): val = util.expandpath(val) gdict[setting] = val # Find the best match - scheme, hostpath = uri.split('://', 1) + scheme, hostpath = uri.split("://", 1) bestuser = None bestlen = 0 bestauth = None for group, auth in groups.iteritems(): - if user and user != auth.get('username', user): + if user and user != auth.get("username", user): # If a username was set in the URI, the entry username # must either match it or be unset continue - prefix = auth.get('prefix') + prefix = auth.get("prefix") if not prefix: continue @@ -104,18 +106,26 @@ prefixurl.user = None prefix = bytes(prefixurl) - p = prefix.split('://', 1) + p = prefix.split("://", 1) if len(p) > 1: schemes, prefix = [p[0]], p[1] else: - schemes = (auth.get('schemes') or 'https').split() - if ((prefix == '*' or hostpath.startswith(prefix)) and - (len(prefix) > bestlen or (len(prefix) == bestlen and - not bestuser and 'username' in auth)) - and scheme in schemes): + schemes = (auth.get("schemes") or "https").split() + if ( + (prefix == "*" or hostpath.startswith(prefix)) + and ( + len(prefix) > bestlen + or ( + len(prefix) == bestlen + and not bestuser + and "username" in auth + ) + ) + and scheme in schemes + ): bestlen = len(prefix) bestauth = group, auth - bestuser = auth.get('username') + bestuser = auth.get("username") if user and not bestuser: - auth['username'] = user + auth["username"] = user return bestauth diff --git a/mercurial/minifileset.py b/mercurial/minifileset.py --- a/mercurial/minifileset.py +++ b/mercurial/minifileset.py @@ -15,47 +15,57 @@ pycompat, ) + def _sizep(x): # i18n: "size" is a keyword expr = filesetlang.getstring(x, _("size requires an expression")) return fileset.sizematcher(expr) + def _compile(tree): if not tree: raise error.ParseError(_("missing argument")) op = tree[0] - if op == 'withstatus': + if op == "withstatus": return _compile(tree[1]) - elif op in {'symbol', 'string', 'kindpat'}: - name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern')) - if name.startswith('**'): # file extension test, ex. "**.tar.gz" + elif op in {"symbol", "string", "kindpat"}: + name = filesetlang.getpattern(tree, {"path"}, _("invalid file pattern")) + if name.startswith("**"): # file extension test, ex. "**.tar.gz" ext = name[2:] for c in pycompat.bytestr(ext): - if c in '*{}[]?/\\': - raise error.ParseError(_('reserved character: %s') % c) + if c in "*{}[]?/\\": + raise error.ParseError(_("reserved character: %s") % c) return lambda n, s: n.endswith(ext) - elif name.startswith('path:'): # directory or full path test - p = name[5:] # prefix + elif name.startswith("path:"): # directory or full path test + p = name[5:] # prefix pl = len(p) - f = lambda n, s: n.startswith(p) and (len(n) == pl - or n[pl:pl + 1] == '/') + f = lambda n, s: n.startswith(p) and ( + len(n) == pl or n[pl : pl + 1] == "/" + ) return f - raise error.ParseError(_("unsupported file pattern: %s") % name, - hint=_('paths must be prefixed with "path:"')) - elif op in {'or', 'patterns'}: + raise error.ParseError( + _("unsupported file pattern: %s") + % name, + hint=_( + 'paths must be prefixed with "path:"' + ), + ) + elif op in {"or", "patterns"}: funcs = [_compile(x) for x in tree[1:]] return lambda n, s: any(f(n, s) for f in funcs) - elif op == 'and': + elif op == "and": func1 = _compile(tree[1]) func2 = _compile(tree[2]) return lambda n, s: func1(n, s) and func2(n, s) - elif op == 'not': + elif op == "not": return lambda n, s: not _compile(tree[1])(n, s) - elif op == 'func': + elif op == "func": symbols = { - 'all': lambda n, s: True, - 'none': lambda n, s: False, - 'size': lambda n, s: _sizep(tree[2])(s), + "all": lambda n, s: True, + "none": lambda n, s: False, + "size": lambda n, s: _sizep(tree[2])( + s + ), } name = filesetlang.getsymbol(tree[1]) @@ -63,14 +73,23 @@ return symbols[name] raise error.UnknownIdentifier(name, symbols.keys()) - elif op == 'minus': # equivalent to 'x and not y' + elif op == "minus": # equivalent to 'x and not y' func1 = _compile(tree[1]) func2 = _compile(tree[2]) return lambda n, s: func1(n, s) and not func2(n, s) - elif op == 'list': - raise error.ParseError(_("can't use a list in this context"), - hint=_('see \'hg help "filesets.x or y"\'')) - raise error.ProgrammingError('illegal tree: %r' % (tree,)) + elif op == "list": + raise error.ParseError( + _( + "can't use a list in this context" + ), + hint=_( + "see 'hg help \"filesets.x or y\"'" + ), + ) + raise error.ProgrammingError( + "illegal tree: %r" % (tree,) + ) + def compile(text): """generate a function (path, size) -> bool from filter specification. diff --git a/mercurial/node.py b/mercurial/node.py --- a/mercurial/node.py +++ b/mercurial/node.py @@ -20,6 +20,7 @@ except binascii.Error as e: raise TypeError(e) + nullrev = -1 # In hex, this is '0000000000000000000000000000000000000000' nullid = b"\0" * 20 @@ -28,20 +29,21 @@ # Phony node value to stand-in for new files in some uses of # manifests. # In hex, this is '2121212121212121212121212121212121212121' -newnodeid = '!!!!!!!!!!!!!!!!!!!!' +newnodeid = "!!!!!!!!!!!!!!!!!!!!" # In hex, this is '3030303030303030303030303030306164646564' -addednodeid = '000000000000000added' +addednodeid = "000000000000000added" # In hex, this is '3030303030303030303030306d6f646966696564' -modifiednodeid = '000000000000modified' +modifiednodeid = "000000000000modified" wdirfilenodeids = {newnodeid, addednodeid, modifiednodeid} # pseudo identifiers for working directory # (they are experimental, so don't add too many dependencies on them) -wdirrev = 0x7fffffff +wdirrev = 0x7FFFFFFF # In hex, this is 'ffffffffffffffffffffffffffffffffffffffff' wdirid = b"\xff" * 20 wdirhex = hex(wdirid) + def short(node): return hex(node[:6]) diff --git a/mercurial/policy.py b/mercurial/policy.py --- a/mercurial/policy.py +++ b/mercurial/policy.py @@ -21,18 +21,34 @@ # By default, fall back to the pure modules so the in-place build can # run without recompiling the C extensions. This will be overridden by # __modulepolicy__ generated by setup.py. -policy = b'allow' +policy = b"allow" _packageprefs = { # policy: (versioned package, pure package) - b'c': (r'cext', None), - b'allow': (r'cext', r'pure'), - b'cffi': (r'cffi', None), - b'cffi-allow': (r'cffi', r'pure'), - b'py': (None, r'pure'), + b"c": ( + r"cext", + None, + ), + b"allow": ( + r"cext", + r"pure", + ), + b"cffi": ( + r"cffi", + None, + ), + b"cffi-allow": ( + r"cffi", + r"pure", + ), + b"py": ( + None, + r"pure", + ), } try: from . import __modulepolicy__ + policy = __modulepolicy__.modulepolicy except ImportError: pass @@ -41,15 +57,16 @@ # # The canonical way to do this is to test platform.python_implementation(). # But we don't import platform and don't bloat for it here. -if r'__pypy__' in sys.builtin_module_names: - policy = b'cffi' +if r"__pypy__" in sys.builtin_module_names: + policy = b"cffi" # Environment variable can always force settings. if sys.version_info[0] >= 3: - if r'HGMODULEPOLICY' in os.environ: - policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8') + if r"HGMODULEPOLICY" in os.environ: + policy = os.environ[r"HGMODULEPOLICY"].encode(r"utf-8") else: - policy = os.environ.get(r'HGMODULEPOLICY', policy) + policy = os.environ.get(r"HGMODULEPOLICY", policy) + def _importfrom(pkgname, modname): # from . import (where . is looked through this module) @@ -58,42 +75,74 @@ try: fakelocals[modname] = mod = getattr(pkg, modname) except AttributeError: - raise ImportError(r'cannot import name %s' % modname) + raise ImportError(r"cannot import name %s" % modname) # force import; fakelocals[modname] may be replaced with the real module - getattr(mod, r'__doc__', None) + getattr(mod, r"__doc__", None) return fakelocals[modname] + # keep in sync with "version" in C modules _cextversions = { - (r'cext', r'base85'): 1, - (r'cext', r'bdiff'): 3, - (r'cext', r'mpatch'): 1, - (r'cext', r'osutil'): 4, - (r'cext', r'parsers'): 12, + ( + r"cext", + r"base85", + ): 1, + ( + r"cext", + r"bdiff", + ): 3, + ( + r"cext", + r"mpatch", + ): 1, + ( + r"cext", + r"osutil", + ): 4, + ( + r"cext", + r"parsers", + ): 12, } # map import request to other package or module _modredirects = { - (r'cext', r'charencode'): (r'cext', r'parsers'), - (r'cffi', r'base85'): (r'pure', r'base85'), - (r'cffi', r'charencode'): (r'pure', r'charencode'), - (r'cffi', r'parsers'): (r'pure', r'parsers'), + (r"cext", r"charencode"): ( + r"cext", + r"parsers", + ), + (r"cffi", r"base85"): ( + r"pure", + r"base85", + ), + (r"cffi", r"charencode"): ( + r"pure", + r"charencode", + ), + (r"cffi", r"parsers"): ( + r"pure", + r"parsers", + ), } + def _checkmod(pkgname, modname, mod): expected = _cextversions.get((pkgname, modname)) - actual = getattr(mod, r'version', None) + actual = getattr(mod, r"version", None) if actual != expected: - raise ImportError(r'cannot import module %s.%s ' - r'(expected version: %d, actual: %r)' - % (pkgname, modname, expected, actual)) + raise ImportError( + r"cannot import module %s.%s " + r"(expected version: %d, actual: %r)" + % (pkgname, modname, expected, actual) + ) + def importmod(modname): """Import module according to policy and check API version""" try: verpkg, purepkg = _packageprefs[policy] except KeyError: - raise ImportError(r'invalid HGMODULEPOLICY %r' % policy) + raise ImportError(r"invalid HGMODULEPOLICY %r" % policy) assert verpkg or purepkg if verpkg: pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname)) diff --git a/mercurial/pushkey.py b/mercurial/pushkey.py --- a/mercurial/pushkey.py +++ b/mercurial/pushkey.py @@ -14,48 +14,70 @@ phases, ) + def _nslist(repo): n = {} for k in _namespaces: n[k] = "" if not obsolete.isenabled(repo, obsolete.exchangeopt): - n.pop('obsolete') + n.pop("obsolete") return n -_namespaces = {"namespaces": (lambda *x: False, _nslist), - "bookmarks": (bookmarks.pushbookmark, bookmarks.listbookmarks), - "phases": (phases.pushphase, phases.listphases), - "obsolete": (obsolete.pushmarker, obsolete.listmarkers), - } + +_namespaces = { + "namespaces": ( + lambda *x: False, + _nslist, + ), + "bookmarks": ( + bookmarks.pushbookmark, + bookmarks.listbookmarks, + ), + "phases": ( + phases.pushphase, + phases.listphases, + ), + "obsolete": ( + obsolete.pushmarker, + obsolete.listmarkers, + ), +} + def register(namespace, pushkey, listkeys): _namespaces[namespace] = (pushkey, listkeys) + def _get(namespace): return _namespaces.get(namespace, (lambda *x: False, lambda *x: {})) + def push(repo, namespace, key, old, new): - '''should succeed iff value was old''' + """should succeed iff value was old""" pk = _get(namespace)[0] return pk(repo, key, old, new) + def list(repo, namespace): - '''return a dict''' + """return a dict""" lk = _get(namespace)[1] return lk(repo) + encode = encoding.fromlocal decode = encoding.tolocal + def encodekeys(keys): """encode the content of a pushkey namespace for exchange over the wire""" - return '\n'.join(['%s\t%s' % (encode(k), encode(v)) for k, v in keys]) + return "\n".join(["%s\t%s" % (encode(k), encode(v)) for k, v in keys]) + def decodekeys(data): """decode the content of a pushkey namespace from exchange over the wire""" result = {} for l in data.splitlines(): - k, v = l.split('\t') + k, v = l.split("\t") result[decode(k)] = decode(v) return result diff --git a/mercurial/rcutil.py b/mercurial/rcutil.py --- a/mercurial/rcutil.py +++ b/mercurial/rcutil.py @@ -24,47 +24,64 @@ systemrcpath = scmplatform.systemrcpath userrcpath = scmplatform.userrcpath + def _expandrcpath(path): - '''path could be a file or a directory. return a list of file paths''' + """path could be a file or a directory. return a list of file paths""" p = util.expandpath(path) if os.path.isdir(p): join = os.path.join - return sorted(join(p, f) for f, k in util.listdir(p) - if f.endswith('.rc')) + return sorted( + join(p, f) for f, k in util.listdir(p) if f.endswith(".rc") + ) return [p] + def envrcitems(env=None): - '''Return [(section, name, value, source)] config items. + """Return [(section, name, value, source)] config items. The config items are extracted from environment variables specified by env, used to override systemrc, but not userrc. If env is not provided, encoding.environ will be used. - ''' + """ if env is None: env = encoding.environ checklist = [ - ('EDITOR', 'ui', 'editor'), - ('VISUAL', 'ui', 'editor'), - ('PAGER', 'pager', 'pager'), + ( + "EDITOR", + "ui", + "editor", + ), + ( + "VISUAL", + "ui", + "editor", + ), + ( + "PAGER", + "pager", + "pager", + ), ] result = [] for envname, section, configname in checklist: if envname not in env: continue - result.append((section, configname, env[envname], '$%s' % envname)) + result.append((section, configname, env[envname], "$%s" % envname)) return result + def defaultrcpath(): - '''return rc paths in default.d''' + """return rc paths in default.d""" path = [] - defaultpath = os.path.join(util.datapath, 'default.d') + defaultpath = os.path.join(util.datapath, "default.d") if os.path.isdir(defaultpath): path = _expandrcpath(defaultpath) return path + def rccomponents(): - '''return an ordered [(type, obj)] about where to load configs. + """return an ordered [(type, obj)] about where to load configs. respect $HGRCPATH. if $HGRCPATH is empty, only .hg/hgrc of current repo is used. if $HGRCPATH is not set, the platform default will be used. @@ -74,26 +91,27 @@ type could be either 'path' or 'items', if type is 'path', obj is a string, and is the config file path. if type is 'items', obj is a list of (section, name, value, source) that should fill the config directly. - ''' - envrc = ('items', envrcitems()) + """ + envrc = ("items", envrcitems()) - if 'HGRCPATH' in encoding.environ: + if "HGRCPATH" in encoding.environ: # assume HGRCPATH is all about user configs so environments can be # overridden. _rccomponents = [envrc] - for p in encoding.environ['HGRCPATH'].split(pycompat.ospathsep): + for p in encoding.environ["HGRCPATH"].split(pycompat.ospathsep): if not p: continue - _rccomponents.extend(('path', p) for p in _expandrcpath(p)) + _rccomponents.extend(("path", p) for p in _expandrcpath(p)) else: - normpaths = lambda paths: [('path', os.path.normpath(p)) for p in paths] + normpaths = lambda paths: [("path", os.path.normpath(p)) for p in paths] _rccomponents = normpaths(defaultrcpath() + systemrcpath()) _rccomponents.append(envrc) _rccomponents.extend(normpaths(userrcpath())) return _rccomponents + def defaultpagerenv(): - '''return a dict of default environment variables and their values, + """return a dict of default environment variables and their values, intended to be set before starting a pager. - ''' - return {'LESS': 'FRX', 'LV': '-c'} + """ + return {"LESS": "FRX", "LV": "-c"} diff --git a/mercurial/rewriteutil.py b/mercurial/rewriteutil.py --- a/mercurial/rewriteutil.py +++ b/mercurial/rewriteutil.py @@ -16,7 +16,8 @@ revset, ) -def precheck(repo, revs, action='rewrite'): + +def precheck(repo, revs, action="rewrite"): """check if revs can be rewritten action is used to control the error message. @@ -30,7 +31,7 @@ if len(repo[None].parents()) > 1: raise error.Abort(_("cannot %s while merging") % action) - publicrevs = repo.revs('%ld and public()', revs) + publicrevs = repo.revs("%ld and public()", revs) if publicrevs: msg = _("cannot %s public changesets") % (action) hint = _("see 'hg help phases' for details") @@ -40,6 +41,7 @@ if newunstable: raise error.Abort(_("cannot %s changeset with children") % action) + def disallowednewunstable(repo, revs): """Checks whether editing the revs will create new unstable changesets and are we allowed to create them. diff --git a/mercurial/scmposix.py b/mercurial/scmposix.py --- a/mercurial/scmposix.py +++ b/mercurial/scmposix.py @@ -16,49 +16,64 @@ # $MORE variable, but there's no compatible option with Linux 'more'. Given # OS X is widely used and most modern Unix systems would have 'less', setting # 'less' as the default seems reasonable. -fallbackpager = 'less' +fallbackpager = "less" + def _rcfiles(path): - rcs = [os.path.join(path, 'hgrc')] - rcdir = os.path.join(path, 'hgrc.d') + rcs = [os.path.join(path, "hgrc")] + rcdir = os.path.join(path, "hgrc.d") try: - rcs.extend([os.path.join(rcdir, f) - for f, kind in util.listdir(rcdir) - if f.endswith(".rc")]) + rcs.extend( + [ + os.path.join(rcdir, f) + for f, kind in util.listdir(rcdir) + if f.endswith(".rc") + ] + ) except OSError: pass return rcs + def systemrcpath(): path = [] - if pycompat.sysplatform == 'plan9': - root = 'lib/mercurial' + if pycompat.sysplatform == "plan9": + root = "lib/mercurial" else: - root = 'etc/mercurial' + root = "etc/mercurial" # old mod_python does not set sys.argv - if len(getattr(sys, 'argv', [])) > 0: + if len(getattr(sys, "argv", [])) > 0: p = os.path.dirname(os.path.dirname(pycompat.sysargv[0])) - if p != '/': + if p != "/": path.extend(_rcfiles(os.path.join(p, root))) - path.extend(_rcfiles('/' + root)) + path.extend(_rcfiles("/" + root)) return path + def userrcpath(): - if pycompat.sysplatform == 'plan9': - return [encoding.environ['home'] + '/lib/hgrc'] + if pycompat.sysplatform == "plan9": + return [encoding.environ["home"] + "/lib/hgrc"] elif pycompat.isdarwin: - return [os.path.expanduser('~/.hgrc')] + return [os.path.expanduser("~/.hgrc")] else: - confighome = encoding.environ.get('XDG_CONFIG_HOME') + confighome = encoding.environ.get("XDG_CONFIG_HOME") if confighome is None or not os.path.isabs(confighome): - confighome = os.path.expanduser('~/.config') + confighome = os.path.expanduser("~/.config") - return [os.path.expanduser('~/.hgrc'), - os.path.join(confighome, 'hg', 'hgrc')] + return [ + os.path.expanduser( + "~/.hgrc" + ), + os.path.join( + confighome, "hg", "hgrc" + ), + ] + def termsize(ui): try: import termios + TIOCGWINSZ = termios.TIOCGWINSZ # unavailable on IRIX (issue3449) except (AttributeError, ImportError): return 80, 24 @@ -71,8 +86,8 @@ continue if not os.isatty(fd): continue - arri = fcntl.ioctl(fd, TIOCGWINSZ, '\0' * 8) - height, width = array.array(r'h', arri)[:2] + arri = fcntl.ioctl(fd, TIOCGWINSZ, "\0" * 8) + height, width = array.array(r"h", arri)[:2] if width > 0 and height > 0: return width, height except ValueError: diff --git a/mercurial/scmwindows.py b/mercurial/scmwindows.py --- a/mercurial/scmwindows.py +++ b/mercurial/scmwindows.py @@ -11,51 +11,55 @@ try: import _winreg as winreg + winreg.CloseKey except ImportError: import winreg # MS-DOS 'more' is the only pager available by default on Windows. -fallbackpager = 'more' +fallbackpager = "more" + def systemrcpath(): - '''return default os-specific hgrc search path''' + """return default os-specific hgrc search path""" rcpath = [] filename = win32.executablepath() # Use mercurial.ini found in directory with hg.exe - progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') + progrc = os.path.join(os.path.dirname(filename), "mercurial.ini") rcpath.append(progrc) # Use hgrc.d found in directory with hg.exe - progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d') + progrcd = os.path.join(os.path.dirname(filename), "hgrc.d") if os.path.isdir(progrcd): for f, kind in util.listdir(progrcd): - if f.endswith('.rc'): + if f.endswith(".rc"): rcpath.append(os.path.join(progrcd, f)) # else look for a system rcpath in the registry - value = util.lookupreg('SOFTWARE\\Mercurial', None, - winreg.HKEY_LOCAL_MACHINE) + value = util.lookupreg( + "SOFTWARE\\Mercurial", None, winreg.HKEY_LOCAL_MACHINE + ) if not isinstance(value, str) or not value: return rcpath value = util.localpath(value) for p in value.split(pycompat.ospathsep): - if p.lower().endswith('mercurial.ini'): + if p.lower().endswith("mercurial.ini"): rcpath.append(p) elif os.path.isdir(p): for f, kind in util.listdir(p): - if f.endswith('.rc'): + if f.endswith(".rc"): rcpath.append(os.path.join(p, f)) return rcpath + def userrcpath(): - '''return os-specific hgrc search path to the user dir''' - home = os.path.expanduser('~') - path = [os.path.join(home, 'mercurial.ini'), - os.path.join(home, '.hgrc')] - userprofile = encoding.environ.get('USERPROFILE') + """return os-specific hgrc search path to the user dir""" + home = os.path.expanduser("~") + path = [os.path.join(home, "mercurial.ini"), os.path.join(home, ".hgrc")] + userprofile = encoding.environ.get("USERPROFILE") if userprofile and userprofile != home: - path.append(os.path.join(userprofile, 'mercurial.ini')) - path.append(os.path.join(userprofile, '.hgrc')) + path.append(os.path.join(userprofile, "mercurial.ini")) + path.append(os.path.join(userprofile, ".hgrc")) return path + def termsize(ui): return win32.termsize() diff --git a/mercurial/stack.py b/mercurial/stack.py --- a/mercurial/stack.py +++ b/mercurial/stack.py @@ -12,6 +12,7 @@ scmutil, ) + def getstack(repo, rev=None): """return a sorted smartrev of the stack containing either rev if it is not None or the current working directory parent. @@ -20,9 +21,9 @@ the revision and are not merges. """ if rev is None: - rev = '.' + rev = "." - revspec = 'reverse(only(%s) and not public() and not ::merge())' + revspec = "reverse(only(%s) and not public() and not ::merge())" revset = revsetlang.formatspec(revspec, rev) revisions = scmutil.revrange(repo, [revset]) revisions.sort() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 80 +exclude = 'build/|wheelhouse/|dist/|packages/|\.hg/|\.mypy_cache/|\.venv/|mercurial/thirdparty/|hgext/fsmonitor/pywatchman/|contrib/python-zstandard/'