[PATCH 1 of 2] scmutil: make filename portability checking extensible

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Tue Jul 10 04:56:43 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1341911825 -32400
# Node ID 6bfbd181f893ff8a52008f5987d2339d77c70646
# Parent  2e13c1bd34dc6afda8fc7cfa22a8cd658276724f
scmutil: make filename portability checking extensible

Before this patch, logic of filename portability checking is fixed and
can't be extended without directly patching up or replacing
"checkportable()".

This patch allows users to add any custom check logic for filename
portability checking by adding an entry to "portabilitycheckers"
table.

Entries of "portabilitycheckers" should be a tuple of two elements:

  - the function to check portability of specified filename: this
    takes one argument referring to target filename to be checked.

  - the function to check whether execution should be aborted forcibly
    or not, if filename is not portable: this takes "ui" argument.

    this checking, known as "os.name == 'nt'" before this patch, is
    separated from "checkportabilityalert()", because custom check
    logic may want to abort forcibly regardless of
    "ui.portablefilenames" configuration or working on Windows.

In this patch, "_checkfilenamealert()" is defined as a function,
because it can be re-used in another patch making "filename collision
auditing" extensible, too.

diff -r 2e13c1bd34dc -r 6bfbd181f893 mercurial/scmutil.py
--- a/mercurial/scmutil.py	Wed Jul 04 17:29:49 2012 +0200
+++ b/mercurial/scmutil.py	Tue Jul 10 18:17:05 2012 +0900
@@ -23,30 +23,43 @@
     if '\r' in f or '\n' in f:
         raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
 
+portabilitycheckers = [
+    (util.checkwinfilename, lambda ui: os.name == 'nt'),
+]
+
 def checkportable(ui, f):
     '''Check if filename f is portable and warn or abort depending on config'''
     checkfilename(f)
-    abort, warn = checkportabilityalert(ui)
-    if abort or warn:
-        msg = util.checkwinfilename(f)
+    confabort, forceabort, warn = _checkfilenamealert(ui, portabilitycheckers)
+    if not confabort and not forceabort and not warn:
+        return
+    for check, forceabort in portabilitycheckers:
+        msg = check(f)
         if msg:
             msg = "%s: %r" % (msg, f)
-            if abort:
+            if confabort or forceabort(ui):
                 raise util.Abort(msg)
             ui.warn(_("warning: %s\n") % msg)
 
 def checkportabilityalert(ui):
     '''check if the user's config requests nothing, a warning, or abort for
     non-portable filenames'''
+    confabort, forceabort, warn = _checkfilenamealert(ui, portabilitycheckers)
+    return confabort or forceabort, warn
+
+def _checkfilenamealert(ui, checkers):
     val = ui.config('ui', 'portablefilenames', 'warn')
     lval = val.lower()
     bval = util.parsebool(val)
-    abort = os.name == 'nt' or lval == 'abort'
+    confabort = lval == 'abort'
     warn = bval or lval == 'warn'
-    if bval is None and not (warn or abort or lval == 'ignore'):
+    if bval is None and not (warn or confabort or lval == 'ignore'):
         raise error.ConfigError(
             _("ui.portablefilenames value is invalid ('%s')") % val)
-    return abort, warn
+    for check, forceabort in checkers:
+        if forceabort(ui):
+            return confabort, True, warn
+    return confabort, False, warn
 
 class casecollisionauditor(object):
     def __init__(self, ui, abort, existingiter):
diff -r 2e13c1bd34dc -r 6bfbd181f893 tests/test-add.t
--- a/tests/test-add.t	Wed Jul 04 17:29:49 2012 +0200
+++ b/tests/test-add.t	Tue Jul 10 18:17:05 2012 +0900
@@ -138,3 +138,41 @@
   ? a.orig
 
   $ cd ..
+
+Test for extensibility of "checkportable".
+
+  $ hg init extensible-checkportable
+  $ cd extensible-checkportable
+
+  $ cat > z-as-invalid.py <<EOF
+  > from mercurial import scmutil
+  > def check(path):
+  >     if 'z' in path:
+  >         return "filename contains 'z'"
+  > def forceabort(ui):
+  >     # abort regardless of ui.portablefilenames, if forceabort=True
+  >     return ui.configbool('z-as-invalid', 'forceabort')
+  > def extsetup(ui):
+  >     scmutil.portabilitycheckers.append((check, forceabort))
+  > EOF
+  $ cat > .hg/hgrc <<EOF
+  > [extensions]
+  > z-as-invalid = ./z-as-invalid.py
+  > EOF
+  $ echo abcdefg > abcdefg
+  $ hg add abcdefg
+  $ echo vwxyz1 > vwxyz1
+  $ hg add vwxyz1
+  warning: filename contains 'z': 'vwxyz1'
+  $ echo vwxyz2 > vwxyz2
+  $ hg --config ui.portablefilenames=ignore add vwxyz2
+  $ echo vwxyz3 > vwxyz3
+  $ hg --config ui.portablefilenames=abort add vwxyz3
+  abort: filename contains 'z': 'vwxyz3'
+  [255]
+  $ echo vwxyz4 > vwxyz4
+  $ hg --config z-as-invalid.forceabort=true add vwxyz4
+  abort: filename contains 'z': 'vwxyz4'
+  [255]
+
+  $ cd ..
diff -r 2e13c1bd34dc -r 6bfbd181f893 tests/test-copy.t
--- a/tests/test-copy.t	Wed Jul 04 17:29:49 2012 +0200
+++ b/tests/test-copy.t	Tue Jul 10 18:17:05 2012 +0900
@@ -214,3 +214,39 @@
     bar
 
   $ cd ..
+
+Test for extensibility of "checkportable".
+
+  $ hg init extensible-checkportable
+  $ cd extensible-checkportable
+
+  $ cat > z-as-invalid.py <<EOF
+  > from mercurial import scmutil
+  > def check(path):
+  >     if 'z' in path:
+  >         return "filename contains 'z'"
+  > def forceabort(ui):
+  >     # abort regardless of ui.portablefilenames, if forceabort=True
+  >     return ui.configbool('z-as-invalid', 'forceabort')
+  > def extsetup(ui):
+  >     scmutil.portabilitycheckers.append((check, forceabort))
+  > EOF
+  $ cat > .hg/hgrc <<EOF
+  > [extensions]
+  > z-as-invalid = ./z-as-invalid.py
+  > EOF
+  $ echo abcdefg > abcdefg
+  $ hg add abcdefg
+  $ hg commit -m '#0'
+  $ hg copy abcdefg hijklmn
+  $ hg copy abcdefg vwxyz1
+  warning: filename contains 'z': 'vwxyz1'
+  $ hg --config ui.portablefilenames=ignore copy abcdefg vwxyz2
+  $ hg --config ui.portablefilenames=abort copy abcdefg vwxyz3
+  abort: filename contains 'z': 'vwxyz3'
+  [255]
+  $ hg --config z-as-invalid.forceabort=true copy abcdefg vwxyz4
+  abort: filename contains 'z': 'vwxyz4'
+  [255]
+
+  $ cd ..
diff -r 2e13c1bd34dc -r 6bfbd181f893 tests/test-rename.t
--- a/tests/test-rename.t	Wed Jul 04 17:29:49 2012 +0200
+++ b/tests/test-rename.t	Tue Jul 10 18:17:05 2012 +0900
@@ -635,3 +635,42 @@
   [255]
   $ hg status -C
 
+Test for extensibility of "checkportable".
+
+  $ hg init extensible-checkportable
+  $ cd extensible-checkportable
+
+  $ cat > z-as-invalid.py <<EOF
+  > from mercurial import scmutil
+  > def check(path):
+  >     if 'z' in path:
+  >         return "filename contains 'z'"
+  > def forceabort(ui):
+  >     # abort regardless of ui.portablefilenames, if forceabort=True
+  >     return ui.configbool('z-as-invalid', 'forceabort')
+  > def extsetup(ui):
+  >     scmutil.portabilitycheckers.append((check, forceabort))
+  > EOF
+  $ cat > .hg/hgrc <<EOF
+  > [extensions]
+  > z-as-invalid = ./z-as-invalid.py
+  > EOF
+  $ echo abcdefg0 > abcdefg0
+  $ echo abcdefg1 > abcdefg1
+  $ echo abcdefg2 > abcdefg2
+  $ echo abcdefg3 > abcdefg3
+  $ echo abcdefg4 > abcdefg4
+  $ hg add abcdefg0 abcdefg1 abcdefg2 abcdefg3 abcdefg4
+  $ hg commit -m '#0'
+  $ hg rename abcdefg0 hijklmn
+  $ hg rename abcdefg1 vwxyz1
+  warning: filename contains 'z': 'vwxyz1'
+  $ hg --config ui.portablefilenames=ignore rename abcdefg2 vwxyz2
+  $ hg --config ui.portablefilenames=abort rename abcdefg3 vwxyz3
+  abort: filename contains 'z': 'vwxyz3'
+  [255]
+  $ hg --config z-as-invalid.forceabort=true copy abcdefg4 vwxyz4
+  abort: filename contains 'z': 'vwxyz4'
+  [255]
+
+  $ cd ..


More information about the Mercurial-devel mailing list