[PATCH 3 of 4 RFC] i18n: add hook point to make tokenizing process as encoding safe

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Thu May 24 12:04:27 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1337873761 -32400
# Branch stable
# Node ID 1d5a60c7f44f106af3c0d56139a1b6d0fd3d0b3c
# Parent  a85e6240d0ab23191d158390095dd48852dcdc39
i18n: add hook point to make tokenizing process as encoding safe

added hook poit is "tokenize()" in "mercurial/encoding.py".

when win32mbcs is enabled, "tokenize()" is replaced with specific
implementation to do below:

    1. convert from specified string to unicode object
    2. invoke the tokenizer with unicode to get generator from it
    3. "pos" of returned value is one in unicode, so recalculate one
       for in byte sequence
    4. convert "token" and "value" from unicode to byte sequence, and
       return them

step (3) of above is required, because the last "pos" value should be
equal to the length of the specified byte sequence, and otherwise it
causes exception raising.

this affects to invocations of below:

    - mercurial.fileset.tokenize()
    - mercurial.revset.tokenize()
    - mercurial.templater.tokenize()

diff -r a85e6240d0ab -r 1d5a60c7f44f hgext/win32mbcs.py
--- a/hgext/win32mbcs.py	Fri May 25 00:27:13 2012 +0900
+++ b/hgext/win32mbcs.py	Fri May 25 00:36:01 2012 +0900
@@ -164,6 +164,23 @@
         raise util.Abort(_("[win32mbcs] conversion in filtering failed with"
                          " %s encoding\n") % (_encoding))
 
+def _tokenize(tokenizer, s):
+    try:
+        us = decode(s)
+        for token, value, pos in tokenizer(us):
+            # re-calculate position in MBCS string
+            pos = len(encode(us[:pos]))
+            yield encode((token, value, pos))
+    except UnicodeError:
+        raise util.Abort(_("[win32mbcs] conversion in tokenizing failed with"
+                         " %s encoding\n") % (_encoding))
+
+def safetokenize(tokenizer, s):
+    if isinstance(s, unicode):
+        return tokenizer(s)
+    else:
+        return _tokenize(tokenizer, s)
+
 def replacename(name, replacement):
     module, name = name.rsplit('.', 1)
     module = sys.modules[module]
@@ -211,6 +228,7 @@
         wrapname("mercurial.osutil.listdir", wrapperforlistdir)
         replacename("mercurial.encoding.escape", safeescape)
         replacename("mercurial.encoding.filter", safefilter)
+        replacename("mercurial.encoding.tokenize", safetokenize)
         # Check sys.args manually instead of using ui.debug() because
         # command line options is not yet applied when
         # extensions.loadall() is called.
diff -r a85e6240d0ab -r 1d5a60c7f44f mercurial/encoding.py
--- a/mercurial/encoding.py	Fri May 25 00:27:13 2012 +0900
+++ b/mercurial/encoding.py	Fri May 25 00:36:01 2012 +0900
@@ -223,6 +223,12 @@
     """
     return filter(s)
 
+def tokenize(tokenizer, s):
+    """Hook point to ensure that the tokenizer parses specified string safely
+    in current encoding.
+    """
+    return tokenizer(s)
+
 def toutf8b(s):
     '''convert a local, possibly-binary string into UTF-8b
 
diff -r a85e6240d0ab -r 1d5a60c7f44f mercurial/parser.py
--- a/mercurial/parser.py	Fri May 25 00:27:13 2012 +0900
+++ b/mercurial/parser.py	Fri May 25 00:36:01 2012 +0900
@@ -15,7 +15,7 @@
 # an action is a tree node name, a tree label, and an optional match
 # __call__(program) parses program into a labelled tree
 
-import error
+import error, encoding
 from i18n import _
 
 class parser(object):
@@ -77,7 +77,7 @@
         return expr
     def parse(self, message):
         'generate a parse tree from a message'
-        self._iter = self._tokenizer(message)
+        self._iter = encoding.tokenize(self._tokenizer, message)
         self._advance()
         res = self._parse()
         token, value, pos = self.current


More information about the Mercurial-devel mailing list