# HG changeset patch # User FUJIWARA Katsunori # Date 1466437839 -32400 # Node ID 844f72885fb9f4a2c4a7eda72549f4d772f87179 # Parent d0ae5b8f80dc115064e66e4ed1dfd848c4f7d1b0 check-code: detect "missing _() in ui message" more exactly Before this patch, "missing _() in ui message" rule overlooks translatable message, which starts with other than alphabet. To detect "missing _() in ui message" more exactly, this patch improves the regexp with assumptions below. - sequence consisting of below might precede "translatable message" in same string token - formatting string, which starts with '%' - escaped character, which starts with 'b' (as replacement of '\\'), or - characters other than '%', 'b' and 'x' (as replacement of alphabet) - any string tokens might precede a string token, which contains "translatable message" This patch builds an input file, which is used to examine "missing _() in ui message" detection, before '"$check_code" stringjoin.py' in test-contrib-check-code.t, because this reduces amount of change churn in subsequent patch. This patch also applies "()" instead of "_()" on messages below to hide false-positives: - messages for ui.debug() or debug commands/tools - contrib/debugshell.py - hgext/win32mbcs.py (ui.write() is used, though) - mercurial/commands.py - _debugchangegroup - debugindex - debuglocks - debugrevlog - debugrevspec - debugtemplate - untranslatable messages - doc/gendoc.py (ReST specific text) - hgext/hgk.py (permission string) - hgext/keyword.py (text written into configuration file) - mercurial/cmdutil.py (formatting strings for JSON) diff -r d0ae5b8f80dc -r 844f72885fb9 contrib/check-code.py --- a/contrib/check-code.py Wed Jun 22 21:30:49 2016 +0100 +++ b/contrib/check-code.py Tue Jun 21 00:50:39 2016 +0900 @@ -329,7 +329,20 @@ # rules depending on implementation of repquote() (r' x+[xpqo%APM][\'"]\n\s+[\'"]x', 'string join across lines with no space'), - (r'ui\.(status|progress|write|note|warn)\([\'\"]x', + (r'''(?x)ui\.(status|progress|write|note|warn)\( + [ \t\n#]* + (?# any strings/comments might precede a string, which + # contains translatable message) + ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)* + (?# sequence consisting of below might precede translatable message + # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ... + # - escaped character: "\\", "\n", "\0" ... + # - character other than '%', 'b' as '\', and 'x' as alphabet) + (['"]|\'\'\'|""") + ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x + (?# this regexp can't use [^...] style, + # because _preparepats forcibly adds "\n" into [^...], + # even though this regexp wants match it against "\n")''', "missing _() in ui message (use () to hide false-positives)"), ], # warnings diff -r d0ae5b8f80dc -r 844f72885fb9 contrib/debugshell.py --- a/contrib/debugshell.py Wed Jun 22 21:30:49 2016 +0100 +++ b/contrib/debugshell.py Tue Jun 21 00:50:39 2016 +0900 @@ -52,7 +52,7 @@ with demandimport.deactivated(): __import__(pdbmap[debugger]) except ImportError: - ui.warn("%s debugger specified but %s module was not found\n" + ui.warn(("%s debugger specified but %s module was not found\n") % (debugger, pdbmap[debugger])) debugger = 'pdb' diff -r d0ae5b8f80dc -r 844f72885fb9 doc/gendoc.py --- a/doc/gendoc.py Wed Jun 22 21:30:49 2016 +0100 +++ b/doc/gendoc.py Tue Jun 21 00:50:39 2016 +0900 @@ -117,11 +117,11 @@ ui.write(_("This section contains help for extensions that are " "distributed together with Mercurial. Help for other " "extensions is available in the help system.")) - ui.write("\n\n" + ui.write(("\n\n" ".. contents::\n" " :class: htmlonly\n" " :local:\n" - " :depth: 1\n\n") + " :depth: 1\n\n")) for extensionname in sorted(allextensionnames()): mod = extensions.load(ui, extensionname, None) diff -r d0ae5b8f80dc -r 844f72885fb9 hgext/hgk.py --- a/hgext/hgk.py Wed Jun 22 21:30:49 2016 +0100 +++ b/hgext/hgk.py Tue Jun 21 00:50:39 2016 +0900 @@ -81,13 +81,13 @@ for f in modified: # TODO get file permissions - ui.write(":100664 100664 %s %s M\t%s\t%s\n" % + ui.write((":100664 100664 %s %s M\t%s\t%s\n") % (short(mmap[f]), short(mmap2[f]), f, f)) for f in added: - ui.write(":000000 100664 %s %s N\t%s\t%s\n" % + ui.write((":000000 100664 %s %s N\t%s\t%s\n") % (empty, short(mmap2[f]), f, f)) for f in removed: - ui.write(":100664 000000 %s %s D\t%s\t%s\n" % + ui.write((":100664 000000 %s %s D\t%s\t%s\n") % (short(mmap[f]), empty, f, f)) ## diff -r d0ae5b8f80dc -r 844f72885fb9 hgext/keyword.py --- a/hgext/keyword.py Wed Jun 22 21:30:49 2016 +0100 +++ b/hgext/keyword.py Tue Jun 21 00:50:39 2016 +0900 @@ -455,7 +455,7 @@ uisetup(ui) reposetup(ui, repo) - ui.write('[extensions]\nkeyword =\n') + ui.write(('[extensions]\nkeyword =\n')) demoitems('keyword', ui.configitems('keyword')) demoitems('keywordset', ui.configitems('keywordset')) demoitems('keywordmaps', kwmaps.iteritems()) diff -r d0ae5b8f80dc -r 844f72885fb9 hgext/win32mbcs.py --- a/hgext/win32mbcs.py Wed Jun 22 21:30:49 2016 +0100 +++ b/hgext/win32mbcs.py Tue Jun 21 00:50:39 2016 +0900 @@ -192,5 +192,5 @@ # command line options is not yet applied when # extensions.loadall() is called. if '--debug' in sys.argv: - ui.write("[win32mbcs] activated with encoding: %s\n" + ui.write(("[win32mbcs] activated with encoding: %s\n") % _encoding) diff -r d0ae5b8f80dc -r 844f72885fb9 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Wed Jun 22 21:30:49 2016 +0100 +++ b/mercurial/cmdutil.py Tue Jun 21 00:50:39 2016 +0900 @@ -1405,24 +1405,24 @@ self.ui.write(",\n {") if self.ui.quiet: - self.ui.write('\n "rev": %s' % jrev) - self.ui.write(',\n "node": %s' % jnode) + self.ui.write(('\n "rev": %s') % jrev) + self.ui.write((',\n "node": %s') % jnode) self.ui.write('\n }') return - self.ui.write('\n "rev": %s' % jrev) - self.ui.write(',\n "node": %s' % jnode) - self.ui.write(',\n "branch": "%s"' % j(ctx.branch())) - self.ui.write(',\n "phase": "%s"' % ctx.phasestr()) - self.ui.write(',\n "user": "%s"' % j(ctx.user())) - self.ui.write(',\n "date": [%d, %d]' % ctx.date()) - self.ui.write(',\n "desc": "%s"' % j(ctx.description())) - - self.ui.write(',\n "bookmarks": [%s]' % + self.ui.write(('\n "rev": %s') % jrev) + self.ui.write((',\n "node": %s') % jnode) + self.ui.write((',\n "branch": "%s"') % j(ctx.branch())) + self.ui.write((',\n "phase": "%s"') % ctx.phasestr()) + self.ui.write((',\n "user": "%s"') % j(ctx.user())) + self.ui.write((',\n "date": [%d, %d]') % ctx.date()) + self.ui.write((',\n "desc": "%s"') % j(ctx.description())) + + self.ui.write((',\n "bookmarks": [%s]') % ", ".join('"%s"' % j(b) for b in ctx.bookmarks())) - self.ui.write(',\n "tags": [%s]' % + self.ui.write((',\n "tags": [%s]') % ", ".join('"%s"' % j(t) for t in ctx.tags())) - self.ui.write(',\n "parents": [%s]' % + self.ui.write((',\n "parents": [%s]') % ", ".join('"%s"' % c.hex() for c in ctx.parents())) if self.ui.debugflag: @@ -1430,26 +1430,26 @@ jmanifestnode = 'null' else: jmanifestnode = '"%s"' % hex(ctx.manifestnode()) - self.ui.write(',\n "manifest": %s' % jmanifestnode) - - self.ui.write(',\n "extra": {%s}' % + self.ui.write((',\n "manifest": %s') % jmanifestnode) + + self.ui.write((',\n "extra": {%s}') % ", ".join('"%s": "%s"' % (j(k), j(v)) for k, v in ctx.extra().items())) files = ctx.p1().status(ctx) - self.ui.write(',\n "modified": [%s]' % + self.ui.write((',\n "modified": [%s]') % ", ".join('"%s"' % j(f) for f in files[0])) - self.ui.write(',\n "added": [%s]' % + self.ui.write((',\n "added": [%s]') % ", ".join('"%s"' % j(f) for f in files[1])) - self.ui.write(',\n "removed": [%s]' % + self.ui.write((',\n "removed": [%s]') % ", ".join('"%s"' % j(f) for f in files[2])) elif self.ui.verbose: - self.ui.write(',\n "files": [%s]' % + self.ui.write((',\n "files": [%s]') % ", ".join('"%s"' % j(f) for f in ctx.files())) if copies: - self.ui.write(',\n "copies": {%s}' % + self.ui.write((',\n "copies": {%s}') % ", ".join('"%s": "%s"' % (j(k), j(v)) for k, v in copies)) @@ -1463,12 +1463,13 @@ self.ui.pushbuffer() diffordiffstat(self.ui, self.repo, diffopts, prev, node, match=matchfn, stat=True) - self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer())) + self.ui.write((',\n "diffstat": "%s"') + % j(self.ui.popbuffer())) if diff: self.ui.pushbuffer() diffordiffstat(self.ui, self.repo, diffopts, prev, node, match=matchfn, stat=False) - self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer())) + self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer())) self.ui.write("\n }") diff -r d0ae5b8f80dc -r 844f72885fb9 mercurial/commands.py --- a/mercurial/commands.py Wed Jun 22 21:30:49 2016 +0100 +++ b/mercurial/commands.py Tue Jun 21 00:50:39 2016 +0900 @@ -2095,7 +2095,7 @@ def _debugchangegroup(ui, gen, all=None, indent=0, **opts): indent_string = ' ' * indent if all: - ui.write("%sformat: id, p1, p2, cset, delta base, len(delta)\n" + ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n") % indent_string) def showchunks(named): @@ -2562,12 +2562,12 @@ break if format == 0: - ui.write(" rev offset length " + basehdr + " linkrev" - " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen))) + ui.write((" rev offset length " + basehdr + " linkrev" + " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen))) elif format == 1: - ui.write(" rev flag offset length" + ui.write((" rev flag offset length" " size " + basehdr + " link p1 p2" - " %s\n" % "nodeid".rjust(idlen)) + " %s\n") % "nodeid".rjust(idlen)) for i in r: node = r.node(i) @@ -3030,13 +3030,13 @@ else: locker = 'user %s, process %s, host %s' \ % (user, pid, host) - ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age)) + ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age)) return 1 except OSError as e: if e.errno != errno.ENOENT: raise - ui.write("%-6s free\n" % (name + ":")) + ui.write(("%-6s free\n") % (name + ":")) return 0 held += report(repo.svfs, "lock", repo.lock) @@ -3329,8 +3329,8 @@ if opts.get("dump"): numrevs = len(r) - ui.write("# rev p1rev p2rev start end deltastart base p1 p2" - " rawsize totalsize compression heads chainlen\n") + ui.write(("# rev p1rev p2rev start end deltastart base p1 p2" + " rawsize totalsize compression heads chainlen\n")) ts = 0 heads = set() @@ -3519,18 +3519,19 @@ ui.note(revset.prettyformat(tree), "\n") newtree = revset.expandaliases(ui, tree) if newtree != tree: - ui.note("* expanded:\n", revset.prettyformat(newtree), "\n") + ui.note(("* expanded:\n"), revset.prettyformat(newtree), "\n") tree = newtree newtree = revset.foldconcat(tree) if newtree != tree: - ui.note("* concatenated:\n", revset.prettyformat(newtree), "\n") + ui.note(("* concatenated:\n"), revset.prettyformat(newtree), "\n") if opts["optimize"]: optimizedtree = revset.optimize(newtree) - ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n") + ui.note(("* optimized:\n"), + revset.prettyformat(optimizedtree), "\n") func = revset.match(ui, expr, repo) revs = func(repo) if ui.verbose: - ui.note("* set:\n", revset.prettyformatset(revs), "\n") + ui.note(("* set:\n"), revset.prettyformatset(revs), "\n") for c in revs: ui.write("%s\n" % c) @@ -3685,7 +3686,7 @@ ui.note(templater.prettyformat(tree), '\n') newtree = templater.expandaliases(tree, aliases) if newtree != tree: - ui.note("* expanded:\n", templater.prettyformat(newtree), '\n') + ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n') mapfile = None if revs is None: diff -r d0ae5b8f80dc -r 844f72885fb9 tests/test-contrib-check-code.t --- a/tests/test-contrib-check-code.t Wed Jun 22 21:30:49 2016 +0100 +++ b/tests/test-contrib-check-code.t Tue Jun 21 00:50:39 2016 +0900 @@ -262,6 +262,20 @@ > 'bar foo-' > 'bar') > EOF + +'missing _() in ui message' detection + + $ cat > uigettext.py < ui.status("% 10s %05d % -3.2f %*s %%" + > # this use '\\\\' instead of '\\', because the latter in + > # heredoc on shell becomes just '\' + > '\\\\ \n \t \0' + > """12345 + > """ + > '''.:*+-= + > ''' "%-6d \n 123456 .:*+-= foobar") + > EOF + $ "$check_code" stringjoin.py stringjoin.py:1: > foo = (' foo' @@ -288,3 +302,9 @@ > 'bar foo-' string join across lines with no space [1] + + $ "$check_code" uigettext.py + uigettext.py:1: + > ui.status("% 10s %05d % -3.2f %*s %%" + missing _() in ui message (use () to hide false-positives) + [1]