diff extensions/kwarchive.py @ 331:99b44c866f4f

Ported the kwarchive extension to Python3 and tested with Mercurial 5.0.1
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 12 Jul 2019 15:26:45 +0200
parents 793161e48af2
children 04deddddc6e5
line wrap: on
line diff
--- a/extensions/kwarchive.py	Fri Jul 12 12:05:34 2019 +0200
+++ b/extensions/kwarchive.py	Fri Jul 12 15:26:45 2019 +0200
@@ -115,18 +115,18 @@
         _dateutil = util
 
 
-testedwith = "4.3.1 4.3.2 4.4.1 4.4.2 4.5.2 4.6.1 4.8.1 4.9 5.0.1"
+testedwith = b"4.3.1 4.3.2 4.4.1 4.4.2 4.5.2 4.6.1 4.8.1 4.9 5.0.1"
 
 
 SHORTENED_HG_SCHEMES = {
-    "ssh": "hg+ssh",
-    "http": "hg+http",
-    "https": "hg+https",
-    "file": "file",
+    b"ssh": b"hg+ssh",
+    b"http": b"hg+http",
+    b"https": b"hg+https",
+    b"file": b"file",
 }
 
 
-KWARCHIVE_CONFIG = ".hgkwarchive"
+KWARCHIVE_CONFIG = b".hgkwarchive"
 
 
 cmdtable = {}
@@ -149,32 +149,32 @@
     try:
         fn = __file__
     except NameError:
-        return "<unknown>"
+        return b"<unknown>"
     else:
         try:
             # this is pure Python standard functionality: no util.posixfile
             verdata = open(os.path.join(os.path.dirname(fn),
                                         "../VERSION"),
-                           "r").read()
-            return re.search("^(.*)", verdata,).group(1)
+                           "rb").read()
+            return re.search(b"^(.*)", verdata,).group(1)
         except OSError:
-            return "<not found>"
+            return b"<not found>"
 
 
 @command(
-    'kwarchive',
+    b"kwarchive",
     [
-        ('', 'no-decode', None, _('do not pass files through decoders')),
-        ('p', 'prefix', '', _('directory prefix for files in archive'), _('PREFIX')),
-        ('r', 'rev', '', _('revision to distribute'), _('REV')),
-        ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
-        ('', 'path', 'default', _('the canonical repository to use'), _('PATH')),
-        ('', "kwconfig", '', _('an alternate pattern configuration configuration file (possibly used for subrepos also)'), _('PATTERNCONFIG')),
-        ('', "no-shorten-path", None, _("don't shorten the path urls")),
-        ('', "path-filter", "short", _("determine how the path will be printed")),
-        ('', "user-filter", "user", _("the part of the user to be printed"), _("USERFILTER"))
+        (b'', b"no-decode", None, _(b"do not pass files through decoders")),
+        (b'p', b"prefix", b'', _(b"directory prefix for files in archive"), _(b"PREFIX")),
+        (b'r', b"rev", b'', _(b"revision to distribute"), _(b"REV")),
+        (b't', b"type", b'', _(b"type of distribution to create"), _(b"TYPE")),
+        (b'', b"path", b"default", _(b"the canonical repository to use"), _(b"PATH")),
+        (b'', b"kwconfig", b'', _(b"an alternate pattern configuration configuration file (possibly used for subrepos also)"), _(b"PATTERNCONFIG")),
+        (b'', b"no-shorten-path", None, _(b"don't shorten the path urls")),
+        (b'', b"path-filter", b"short", _(b"determine how the path will be printed")),
+        (b'', b"user-filter", b"user", _(b"the part of the user to be printed"), _(b"USERFILTER"))
     ] + cmdutil.subrepoopts + cmdutil.walkopts,
-    _('[OPTION]... DEST'),
+    _(b"[OPTION]... DEST"),
     inferrepo=True)
 def kwarchive(ui, repo, dest, **opts):
     '''create an unversioned archive of a repository revision with some keywords expanded
@@ -244,23 +244,23 @@
     '''
 
     opts = pycompat.byteskwargs(opts)
-    ctx = scmutil.revsingle(repo, opts.get('rev'))
+    ctx = scmutil.revsingle(repo, opts.get(b"rev"))
     if not ctx:
-        raise error.Abort(_('no Mercurial revision found: please specify a revision'))
+        raise error.Abort(_(b"no Mercurial revision found: please specify a revision"))
     node = ctx.node()
     dest = makefilename_compat(ctx, dest)
     if os.path.realpath(dest) == repo.root:
-        raise error.Abort(_('repository root cannot be destination'))
+        raise error.Abort(_(b"repository root cannot be destination"))
 
-    kind = opts.get('type') or archival.guesskind(dest) or 'files'
-    prefix = opts.get('prefix')
+    kind = opts.get(b"type") or archival.guesskind(dest) or b"files"
+    prefix = opts.get(b"prefix")
 
-    if dest == '-':
-        if kind == 'files':
-            raise error.Abort(_('cannot archive plain files to stdout'))
+    if dest == b'-':
+        if kind == "files":
+            raise error.Abort(_(b"cannot archive plain files to stdout"))
         dest = makefileobj_compat(ctx, dest)
         if not prefix:
-            prefix = os.path.basename(repo.root) + '-%h'
+            prefix = os.path.basename(repo.root) + b'-%h'
 
     prefix = makefilename_compat(ctx, prefix)
     matchfn = scmutil.match(ctx, [], opts)
@@ -277,14 +277,14 @@
                 ctx,
                 ac,
                 archival.tidyprefix(dest, kind, prefix),
-                hgpath=opts.get("path"),
+                hgpath=opts.get(b"path"),
                 path_filter=get_checked_path_filter_option(opts),
                 user_filter=get_checked_user_filter_option(opts),
-                subrepos=opts.get("subrepos"),
-                kwconfig=opts.get("kwconfig")))
+                subrepos=opts.get(b"subrepos"),
+                kwconfig=opts.get(b"kwconfig")))
 
-    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
-                     matchfn, prefix, subrepos=opts.get('subrepos'))
+    archival.archive(repo, dest, node, kind, not opts.get(b"no_decode"),
+                     matchfn, prefix, subrepos=opts.get(b"subrepos"))
 
     # XXX FIXME: Should the original methods be restored here?
 
@@ -295,17 +295,17 @@
 
 
 @command(
-    'kwprint',
+    b"kwprint",
     [
-        ('r', 'rev', '', _('revision to distribute'), _('REV')),
-        ('', 'path', 'default', _('the canonical repository to use'), _('PATH')),
-        ('', "no-shorten-path", None, _("don't shorten the path urls")),
-        ('', "path-filter", "short", _("determine how the path will be printed")),
-        ('', "user-filter", "user", _("the part of the user to be printed"), _("USERFILTER")),
-        ('', "reST", None, _("output in reST substitution definition syntax")),
-        ('', "no-file", None, _("don't show file-dependent keywords")),
+        (b'r', b"rev", b'', _(b"revision to distribute"), _(b"REV")),
+        (b'', b"path", b"default", _(b"the canonical repository to use"), _(b"PATH")),
+        (b'', b"no-shorten-path", None, _(b"don't shorten the path urls")),
+        (b'', b"path-filter", b"short", _(b"determine how the path will be printed")),
+        (b'', b"user-filter", b"user", _(b"the part of the user to be printed"), _(b"USERFILTER")),
+        (b'', b"reST", None, _(b"output in reST substitution definition syntax")),
+        (b'', b"no-file", None, _(b"don't show file-dependent keywords")),
     ] + cmdutil.subrepoopts + cmdutil.walkopts,
-    _('[OPTION]...'),
+    _(b"[OPTION]..."),
     inferrepo=True)
 def kwprint(ui, repo, **opts):
     '''print the file-independent keywords and for an example file-dependent
@@ -351,33 +351,33 @@
     '''
 
     opts = pycompat.byteskwargs(opts)
-    ctx = scmutil.revsingle(repo, opts.get('rev'))
+    ctx = scmutil.revsingle(repo, opts.get(b"rev"))
     if not ctx:
-        raise error.Abort(_('no Mercurial revision found: please specify a revision'))
+        raise error.Abort(_(b"no Mercurial revision found: please specify a revision"))
     node = ctx.node()
 
-    prefix = makefilename_compat(ctx, "")
+    prefix = makefilename_compat(ctx, b'')
 
     keywords = make_node_keywords(
         ui, ctx,
-        hgpath=opts.get("path"),
+        hgpath=opts.get(b"path"),
         path_filter=get_checked_path_filter_option(opts),
         user_filter=get_checked_user_filter_option(opts))
     # make file-dependent keywords for an example file
-    if not opts.get("no_file"):
+    if not opts.get(b"no_file"):
         file_keywords = make_file_keywords(
             keywords,
-            "dir1/dir2/test.file",
-            "a4dd6f4b22e11fec41158eec187630c24a43120a")
+            b"dir1/dir2/test.file",
+            b"a4dd6f4b22e11fec41158eec187630c24a43120a")
     else:
         file_keywords = None
     _kwprint_keywords(ui, keywords, file_keywords,
-                      opts.get("reST"),
-                      opts.get("no_file"))
-    if opts.get("subrepos"):
+                      opts.get(b"reST"),
+                      opts.get(b"no_file"))
+    if opts.get(b"subrepos"):
         _kwprint_subrepos(ctx, ui,
-                          opts.get("reST"),
-                          opts.get("no_file"),
+                          opts.get(b"reST"),
+                          opts.get(b"no_file"),
                           path_filter=get_checked_path_filter_option(opts),
                           user_filter=get_checked_user_filter_option(opts))
 
@@ -387,22 +387,22 @@
     for key in sorted(keywords.keys()):
         if rest:
             if keywords[key]:
-                ui.write(".. |VCS%s| replace:: %s\n" % (key, keywords[key]))
+                ui.write(b".. |VCS%s| replace:: %s\n" % (key, keywords[key]))
             else:
                 #
                 # empty replacements are not allowed:
                 # write a non-breaking space instead
                 #
-                ui.write(".. |VCS%s| unicode:: 0xA0\n" % key)
+                ui.write(b".. |VCS%s| unicode:: 0xA0\n" % key)
         else:
-            ui.write("$%s: %s $\n" % (key, keywords[key]))
+            ui.write(b"$%s: %s $\n" % (key, keywords[key]))
     if not no_file:
-        ui.write("\n")
+        ui.write(b"\n")
         for key in sorted(file_keywords.keys()):
             if rest:
-                ui.write(".. |VCS%s| replace:: %s\n" % (key, file_keywords[key]))
+                ui.write(b".. |VCS%s| replace:: %s\n" % (key, file_keywords[key]))
             else:
-                ui.write("$%s: %s $\n" % (key, file_keywords[key]))
+                ui.write(b"$%s: %s $\n" % (key, file_keywords[key]))
 
 
 def _kwprint_subrepos(ctx, ui, rest, no_file, path_filter, user_filter):
@@ -410,7 +410,7 @@
     for subpath in sorted(ctx.substate):
         substate = ctx.substate[subpath]
         # skip on non-Mercurial subrepos
-        if substate[2] != "hg":
+        if substate[2] != b"hg":
             continue
         subrep = ctx.workingsub(subpath)
         subctx = subrep._getctx()
@@ -423,11 +423,11 @@
         if not no_file:
             file_keywords = make_file_keywords(
                 keywords,
-                "dir3/dir4/test-s.file",
-                "ffffffff22e11fec41158eec187630c24a43120a")
+                b"dir3/dir4/test-s.file",
+                b"ffffffff22e11fec41158eec187630c24a43120a")
         else:
             file_keywords = None
-        ui.write("\n\n")
+        ui.write(b"\n\n")
         _kwprint_keywords(ui, keywords, file_keywords, rest, no_file)
         # Recursively check for other subrepos
         _kwprint_subrepos(subctx, ui, rest, no_file, path_filter, user_filter)
@@ -462,15 +462,15 @@
 
 
 def make_keyword_filter(ui, ctx, archive_class, prefix,
-                        hgpath="default",
-                        path_filter="short",
-                        user_filter="user",
+                        hgpath=b"default",
+                        path_filter=b"short",
+                        user_filter=b"user",
                         subrepos=False,
-                        kwconfig=""):
-
+                        kwconfig=b""):
+    assert isinstance(archive_class, str), "must be a native string"
     filterdata_by_subrepos = {
-        "": _make_repo_filterdata(ui, ctx, hgpath, None, kwconfig,
-                                  path_filter, user_filter),
+        b'': _make_repo_filterdata(ui, ctx, hgpath, None, kwconfig,
+                                   path_filter, user_filter),
     }
     if subrepos:
         _amend_filterdata_by_subrepos(filterdata_by_subrepos,
@@ -513,8 +513,8 @@
         _predef_keywords = keywords.copy()
         _predef_keywords.update(file_keywords)
         # This prevents unwanted keyword expansion here
-        _MARKER_RCS = '$'
-        _MARKER_RST = '|'
+        _MARKER_RCS = b'$'
+        _MARKER_RST = b'|'
         for kw, value in itertools.chain(keyword_substitutions.items(), keywords.items(), file_keywords.items()):
             #
             # Non-empty keyword_substitutions are an implicit whitelist and
@@ -524,7 +524,12 @@
                 if kw in keyword_substitutions:
                     kwds = [kw]
                     if value:
-                        value = value.format(**_predef_keywords)
+                        if not pycompat.ispy3:
+                            value = value.format(**_predef_keywords)
+                        else:
+                            # Assume UTF-8 strings in config files
+                            _predef_keywords_u = dict((k.decode("utf-8"), v.decode("utf-8")) for k, v in _predef_keywords.items())
+                            value = value.decode("utf-8").format(**_predef_keywords_u).encode("utf-8")
                     else:
                         value = _predef_keywords[kw]
                 else:
@@ -535,13 +540,13 @@
                 kwds = [kw]
             for kw in kwds:
                 if matcher_rcs(rel_name_in_subrepo):
-                    filekw = "%s%s%s" % (_MARKER_RCS, kw, _MARKER_RCS)
-                    filevalue = "%s%s: %s %s" \
+                    filekw = b"%s%s%s" % (_MARKER_RCS, kw, _MARKER_RCS)
+                    filevalue = b"%s%s: %s %s" \
                                 % (_MARKER_RCS, kw, value, _MARKER_RCS)
                     data = data.replace(filekw, filevalue)
                 if matcher_rst(rel_name_in_subrepo):
-                    filekw = "%sVCS%s%s" % (_MARKER_RST, kw, _MARKER_RST)
-                    filevalue = "%s" % value   # always convert to a string
+                    filekw = b"%sVCS%s%s" % (_MARKER_RST, kw, _MARKER_RST)
+                    filevalue = b"%s" % value   # always convert to a string
                     data = data.replace(filekw, filevalue)
         return data
 
@@ -579,37 +584,37 @@
         patterns = []
         patterns_rcs = []
         patterns_rst = []
-        if cfg.items("patterns"):
-            for pattern, styles in cfg.items("patterns"):
-                styles = [s.strip() for s in styles.upper().split(",")]
-                if "YES" in styles or "INCLUDE" in styles:
+        if cfg.items(b"patterns"):
+            for pattern, styles in cfg.items(b"patterns"):
+                styles = [s.strip() for s in styles.upper().split(b',')]
+                if b"YES" in styles or b"INCLUDE" in styles:
                     include.append(pattern)
-                elif "NO" in styles or "EXCLUDE" in styles:
+                elif b"NO" in styles or b"EXCLUDE" in styles:
                     exclude.append(pattern)
                 else:
                     patterns.append(pattern)
-                if "REST" in styles or "RST" in styles \
-                        or "RCS" in styles:
-                    if "RCS" in styles:
+                if b"REST" in styles or b"RST" in styles \
+                        or b"RCS" in styles:
+                    if b"RCS" in styles:
                         patterns_rcs.append(pattern)
-                    if "REST" in styles or "RST" in styles:
+                    if b"REST" in styles or b"RST" in styles:
                         patterns_rst.append(pattern)
                 else:
                     # default to RCS if no keyword style is given
                     patterns_rcs.append(pattern)
-            matcher = match.match(ctx.repo().root, '', patterns=patterns,
+            matcher = match.match(ctx.repo().root, b'', patterns=patterns,
                                   include=include, exclude=exclude)
         else:
-            matcher = match.never(ctx.repo().root, '')
+            matcher = match.never(ctx.repo().root, b'')
 
         #
         # An empty patterns_rcs does not mean that match_rcs is always
         # true.
         #
         if not patterns_rcs:
-            matcher_rcs = match.never(ctx.repo().root, '')
+            matcher_rcs = match.never(ctx.repo().root, b'')
         else:
-            matcher_rcs = match.match(ctx.repo().root, '',
+            matcher_rcs = match.match(ctx.repo().root, b'',
                                       patterns=patterns_rcs,
                                       include=[], exclude=[])
         #
@@ -617,9 +622,9 @@
         # true.
         #
         if not patterns_rst:
-            matcher_rst = match.never(ctx.repo().root, '')
+            matcher_rst = match.never(ctx.repo().root, b'')
         else:
-            matcher_rst = match.match(ctx.repo().root, '',
+            matcher_rst = match.match(ctx.repo().root, b'',
                                       patterns=patterns_rst,
                                       include=[], exclude=[])
 
@@ -632,7 +637,7 @@
         #
         keyword_substitutions = {}
 
-        for alias, value in cfg.items("keywords"):
+        for alias, value in cfg.items(b"keywords"):
             keyword_substitutions[alias] = value
 
         #
@@ -654,13 +659,13 @@
     for subpath in sorted(ctx.substate):
         substate = ctx.substate[subpath]
         # skip on non-Mercurial subrepos
-        if substate[2] != "hg":
+        if substate[2] != b"hg":
             continue
         subrep = ctx.workingsub(subpath)
         subctx = subrep._getctx()
 
         # Really amend
-        filterdata_by_subrepos[subrepo.subrelpath(subrep) + '/'] = \
+        filterdata_by_subrepos[subrepo.subrelpath(subrep) + b'/'] = \
             _make_repo_filterdata(ui, subctx, None, substate[0], kwconfig,
                                   path_filter, user_filter)
 
@@ -672,56 +677,57 @@
 
 
 def make_node_keywords(ui, ctx,
-                       hgpath="default",
-                       path_filter="short",
-                       user_filter="user",
+                       hgpath=b"default",
+                       path_filter=b"short",
+                       user_filter=b"user",
                        hglocation=None):
     """Make all the node-specific (i.e. file-path independent) keywords
 
     """
     if (hglocation is not None) and hgpath:
         raise ValueError("either `hgpath' or `hglocation` can be set")
-    if (hglocation is not None) or (hgpath and hgpath != "."):
+    if (hglocation is not None) or (hgpath and hgpath != b"."):
         if hglocation is None:
             hglocation = ui.paths[hgpath].loc
         try:
-            if path_filter == "full":
+            if path_filter == b"full":
                 path_uri = bytes(util.url(hglocation))
-            elif path_filter == "nopwd":
+            elif path_filter == b"nopwd":
                 path_uri = util.hidepassword(hglocation)
-            elif path_filter == "nouser":
+            elif path_filter == b"nouser":
                 path_url = util.url(hglocation)
                 path_url.user = None
                 path_url.passwd = None
                 path_uri = bytes(path_url)
-            elif path_filter == "short":
+            elif path_filter == b"short":
                 path_url = util.url(hglocation)
                 path_url.scheme = SHORTENED_HG_SCHEMES.get(path_url.scheme,
-                                                           "hg")
+                                                           b"hg")
                 path_url.user = None
                 path_url.passwd = None
                 path_url.host = stripped_hostname(path_url.host)
                 path_url.port = None
                 path_uri = bytes(path_url)
-            elif path_filter == "last":
-                path_url = str(util.url(hglocation)).split("/")
-                path_uri = bytes(b".../"+path_url[-1])
+            elif path_filter == b"last":
+                path_url = str(util.url(hglocation)).split('/')
+                path_uri = pycompat.sysbytes(".../"+path_url[-1])
             else:
-                raise error.Abort("path-filter `%s' not implemented"
+                raise error.Abort(b"path-filter `%s' not implemented"
                                   % path_filter)
         except LookupError:
-            raise error.Abort(_("remote repository named `%s' not found") % hgpath)
+            raise error.Abort(_(b"remote repository named `%s' not found") % hgpath)
     else:
         path_uri = ctx.repo().root
-        if path_filter == "last":
+        if path_filter == b"last":
             m = max(path_uri.rfind(b"/"), path_uri.rfind(b"\\"))
             if m >= 0:
                 path_uri = b".../" + path_uri[m+1:]
     if path_uri.startswith(b"\\\\"):
         # Make an URL from a Windows UNC path
         path_uri = b"file:///" + path_uri.replace(b'\\', b'/')
-    elif len(path_uri) >= 2 and (b'A' <= path_uri[0].upper() <= b'Z') \
-            and path_uri[1] == b':':
+    elif len(path_uri) >= 2 \
+         and ((pycompat.ispy3 and ((b'A' <= pycompat.bytechr(path_uri[0]) <= b'Z') or (b'a' <= pycompat.bytechr(path_uri[0]) <= b'z'))) or ((not pycompat.ispy3 and (b'A' <= path_uri[0].upper() <= b'Z')))) \
+         and path_uri[1] == b':':
         # make an URL from a Windows path with drive letter
         path_uri = b"file:///" + path_uri.replace(b'\\', b'/')
     elif path_uri.startswith(b'/'):
@@ -729,62 +735,62 @@
         path_uri = b"file://" + path_uri
     if context_mapping_api:
         mapping = {
-            'repo': ctx.repo(),
-            'ctx': ctx,
-            'cache': {}
+            b'repo': ctx.repo(),
+            b'ctx': ctx,
+            b'cache': {}
         }
         latesttags = templatekw.getlatesttags(_FakeRenderContext(), mapping)
     else:
         latesttags = templatekw.getlatesttags(ctx.repo(), ctx, {})
     keywords = {
-        "HGrepoid": ctx.repo()[ctx.repo().lookup('0')].hex(),   # repo id
-        "HGpath": path_uri,     # XXX FIXME: Should Archive an alias of this
-        "HGbranch": ctx.branch(),
-        "HGtags": " ".join([tag for tag in ctx.tags() if tag != "tip"]),
-        "HGlatesttags": " ".join(latesttags[2]),
-        "HGlatesttagdistance": latesttags[1],
-        "HGlatesttagdate": templatefilters.isodatesec(_dateutil.makedate(latesttags[0])),
-        "HGlatesttagjustdate": templatefilters.shortdate(_dateutil.makedate(latesttags[0])),
-        "HGbookmarks": " ".join([bm if not bm.startswith('*') else bm[1:]
-                                 for bm in ctx.bookmarks() if bm != "@"]),
-        "State": ctx.phasestr(),
-        "HGrevision": ctx.hex(),
-        "Revision": templatefilters.short(ctx.hex()),
-        "Date": templatefilters.isodatesec(ctx.date()),
-        "JustDate": templatefilters.shortdate(ctx.date()),
+        b"HGrepoid": ctx.repo()[ctx.repo().lookup(b'0')].hex(),   # repo id
+        b"HGpath": path_uri,     # XXX FIXME: Should Archive an alias of this
+        b"HGbranch": ctx.branch(),
+        b"HGtags": b' '.join([tag for tag in ctx.tags() if tag != b"tip"]),
+        b"HGlatesttags": b" ".join(latesttags[2]),
+        b"HGlatesttagdistance": pycompat.sysbytes(str(latesttags[1])),
+        b"HGlatesttagdate": templatefilters.isodatesec(_dateutil.makedate(latesttags[0])),
+        b"HGlatesttagjustdate": templatefilters.shortdate(_dateutil.makedate(latesttags[0])),
+        b"HGbookmarks": b' '.join([bm if not bm.startswith(b'*') else bm[1:]
+                                  for bm in ctx.bookmarks() if bm != b"@"]),
+        b"State": ctx.phasestr(),
+        b"HGrevision": ctx.hex(),
+        b"Revision": templatefilters.short(ctx.hex()),
+        b"Date": templatefilters.isodatesec(ctx.date()),
+        b"JustDate": templatefilters.shortdate(ctx.date()),
         # compatibility alias for `JustDate'
-        "HGshortdate": templatefilters.shortdate(ctx.date()),
+        b"HGshortdate": templatefilters.shortdate(ctx.date()),
     }
-    if user_filter == "user":
-        keywords["Author"] = templatefilters.emailuser(ctx.user())
-    elif user_filter == "person":
-        keywords["Author"] = templatefilters.utf8(
-            templatefilters.person(ctx.user()).replace(' ', '+'))
-    elif user_filter == "email":
-        keywords["Author"] = templatefilters.email(ctx.user())
-    elif user_filter in ("full", "none",):
+    if user_filter == b"user":
+        keywords[b"Author"] = templatefilters.emailuser(ctx.user())
+    elif user_filter == b"person":
+        keywords[b"Author"] = templatefilters.utf8(
+            templatefilters.person(ctx.user()).replace(b' ', b'+'))
+    elif user_filter == b"email":
+        keywords[b"Author"] = templatefilters.email(ctx.user())
+    elif user_filter in (b"full", b"none",):
         #
         # "none" is retained for compatibility reasons and now an
         # alias for "full".
         # But make it **one** word because that is meant in the RCS spec.
         #
-        keywords["Author"] = templatefilters.utf8(ctx.user().replace(' ', '+'))
+        keywords[b"Author"] = templatefilters.utf8(ctx.user().replace(b' ', b'+'))
     else:
-        raise error.Abort(_("unknown user filter"))
+        raise error.Abort(_(b"unknown user filter"))
 
     return keywords
 
 
 def make_file_keywords(keywords, rel_name, nodeid):
     return {
-        "HGsource": keywords["HGpath"] + '/' + rel_name,
-        "Source": rel_name,
-        "File": templatefilters.basename(rel_name),
-        "Header": "%s %s %s %s %s" % (rel_name, keywords["Revision"], keywords["Date"], keywords["Author"], keywords["State"]),
-        "HGid": "%s %s %s %s %s" % (keywords["HGpath"] + '/' + rel_name, keywords["Revision"], keywords["Date"], keywords["Author"], keywords["State"]),
-        "HGheader": "%s %s %s %s %s" % (keywords["HGpath"] + '/' + rel_name, keywords["HGrevision"], keywords["Date"], keywords["Author"], keywords["State"]),
-        "Id": "%s %s %s %s %s" % (templatefilters.basename(rel_name), keywords["Revision"], keywords["Date"], keywords["Author"], keywords["State"]),
-        "HGnodeid": nodeid or "",
+        b"HGsource": keywords[b"HGpath"] + b'/' + rel_name,
+        b"Source": rel_name,
+        b"File": templatefilters.basename(rel_name),
+        b"Header": b"%s %s %s %s %s" % (rel_name, keywords[b"Revision"], keywords[b"Date"], keywords[b"Author"], keywords[b"State"]),
+        b"HGid": b"%s %s %s %s %s" % (keywords[b"HGpath"] + b'/' + rel_name, keywords[b"Revision"], keywords[b"Date"], keywords[b"Author"], keywords[b"State"]),
+        b"HGheader": b"%s %s %s %s %s" % (keywords[b"HGpath"] + b'/' + rel_name, keywords[b"HGrevision"], keywords[b"Date"], keywords[b"Author"], keywords[b"State"]),
+        b"Id": b"%s %s %s %s %s" % (templatefilters.basename(rel_name), keywords[b"Revision"], keywords[b"Date"], keywords[b"Author"], keywords[b"State"]),
+        b"HGnodeid": nodeid or b'',
     }
 
 
@@ -792,30 +798,36 @@
     """Return `hostname` without any domain port information"""
     if not hostname:
         return hostname
-    idx = hostname.find('.')
+    idx = hostname.find(b'.')
     if idx < 0:
         return hostname
     return hostname[:idx]
 
 
 def get_checked_user_filter_option(opts):
-    uf = opts.get("user_filter")
+    uf = opts.get(b"user_filter")
     # "none" is retained for compatibility reasons and now an alias for "full"
-    if uf not in ("person", "user", "email", "full", "none"):
+    if uf not in (b"person", b"user", b"email", b"full", b"none"):
         raise error.Abort(
-            _("user filter must be any of `user', `person', `email' or `none'"))
+            _(b"user filter must be any of `user', `person', `email' or `none'"))
     return uf
 
 
 def get_checked_path_filter_option(opts):
-    pf = opts.get("path_filter")
-    if pf not in ("full", "nopwd", "nouser", "short", "last"):
+    pf = opts.get(b"path_filter")
+    if pf not in (b"full", b"nopwd", b"nouser", b"short", b"last"):
         raise error.Abort(
-            _("path filter must be any of `full', `nopwd', `nouser`, `short' or `last'"))
+            _(b"path filter must be any of `full', `nopwd', `nouser`, `short' or `last'"))
     return pf
 
 
-if "ctx" in inspect.getargspec(cmdutil.makefilename).args:
+if hasattr(inspect, "getfullargspec"):
+    # PY3
+    _has_makefilename_ctx = "ctx" in inspect.getfullargspec(cmdutil.makefilename).args
+else:
+    _has_makefilename_ctx = "ctx" in inspect.getargspec(cmdutil.makefilename).args
+
+if _has_makefilename_ctx:
 
     # Mercurial >= 4.6