[Python-3000] find -> index patch

Jack Diederich jack at psynchronous.com
Thu Aug 24 02:39:48 CEST 2006


On Wed, Aug 23, 2006 at 02:18:59PM -0700, Guido van Rossum wrote:
> Here's the patch (by Hasan Diwan, BTW) for people's perusal. It just
> gets rid of all *uses* of find/rfind from Lib; it doesn't actually
> modify stringobject.c or unicodeobject.c. It doesn't use
> [r]partition()'; someone could look for opportunities to use that
> separately.

I make a go at doing an idiomatic convertion of the first few modules 
tagged by 'grep find( *.py' in Lib, patch attached.

WOW, I love partition.  In all the instances that weren't a simple "in"
test I ended up using [r]partition.  In some cases one of the returned
strings gets thrown away but in those cases it is guaranteed to be small.
The new code is usually smaller than the old and generally clearer.
ex/ cgi.py
-        i = p.find('=')
-        if i >= 0:
-            name = p[:i].strip().lower()
-            value = p[i+1:].strip()
+        (name, sep_found, value) = p.partition('=')
+        if (sep_found):
+            name = name.strip().lower()
+            value = value.strip()

If folks like the way this partial set looks I'll convert the rest.

-Jack
-------------- next part --------------
Index: Lib/CGIHTTPServer.py
===================================================================
--- Lib/CGIHTTPServer.py	(revision 51530)
+++ Lib/CGIHTTPServer.py	(working copy)
@@ -106,16 +106,9 @@
     def run_cgi(self):
         """Execute a CGI script."""
         dir, rest = self.cgi_info
-        i = rest.rfind('?')
-        if i >= 0:
-            rest, query = rest[:i], rest[i+1:]
-        else:
-            query = ''
-        i = rest.find('/')
-        if i >= 0:
-            script, rest = rest[:i], rest[i:]
-        else:
-            script, rest = rest, ''
+        (rest, sep, query) = rest.rpartition('?')
+        (rest, sep, script) = rest.partition('/')
+        rest = sep + rest # keep the slash
         scriptname = dir + '/' + script
         scriptfile = self.translate_path(scriptname)
         if not os.path.exists(scriptfile):
Index: Lib/asynchat.py
===================================================================
--- Lib/asynchat.py	(revision 51530)
+++ Lib/asynchat.py	(working copy)
@@ -125,14 +125,13 @@
                 #    collect data to the prefix
                 # 3) end of buffer does not match any prefix:
                 #    collect data
-                terminator_len = len(terminator)
-                index = self.ac_in_buffer.find(terminator)
-                if index != -1:
+                (data, term_found, more_data) = self.ac_in_buffer.partition(terminator)
+                if term_found:
                     # we found the terminator
-                    if index > 0:
+                    if data:
                         # don't bother reporting the empty string (source of subtle bugs)
-                        self.collect_incoming_data (self.ac_in_buffer[:index])
-                    self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
+                        self.collect_incoming_data(data)
+                    self.ac_in_buffer = more_data
                     # This does the Right Thing if the terminator is changed here.
                     self.found_terminator()
                 else:
Index: Lib/cookielib.py
===================================================================
--- Lib/cookielib.py	(revision 51530)
+++ Lib/cookielib.py	(working copy)
@@ -531,8 +531,10 @@
         return True
     if not is_HDN(A):
         return False
-    i = A.rfind(B)
-    if i == -1 or i == 0:
+    if (not B):
+        return False
+    (before_B, sep, after_B) = A.rpartition(B)
+    if not sep or not before_B:
         # A does not have form NB, or N is the empty string
         return False
     if not B.startswith("."):
@@ -595,7 +597,7 @@
 
     """
     erhn = req_host = request_host(request)
-    if req_host.find(".") == -1 and not IPV4_RE.search(req_host):
+    if "." not in req_host and not IPV4_RE.search(req_host):
         erhn = req_host + ".local"
     return req_host, erhn
 
@@ -616,16 +618,12 @@
 
 def request_port(request):
     host = request.get_host()
-    i = host.find(':')
-    if i >= 0:
-        port = host[i+1:]
-        try:
-            int(port)
-        except ValueError:
-            _debug("nonnumeric port: '%s'", port)
-            return None
-    else:
-        port = DEFAULT_HTTP_PORT
+    port = host.partition(':')[-1] or DEFAULT_HTTP_PORT
+    try:
+        int(port)
+    except ValueError:
+        _debug("nonnumeric port: '%s'", port)
+        return None
     return port
 
 # Characters in addition to A-Z, a-z, 0-9, '_', '.', and '-' that don't
@@ -676,13 +674,9 @@
     '.local'
 
     """
-    i = h.find(".")
-    if i >= 0:
-        #a = h[:i]  # this line is only here to show what a is
-        b = h[i+1:]
-        i = b.find(".")
-        if is_HDN(h) and (i >= 0 or b == "local"):
-            return "."+b
+    (a, sep, b) = h.partition(".")
+    if sep and is_HDN(h) and ("." in b or b == "local"):
+        return "."+b
     return h
 
 def is_third_party(request):
@@ -986,11 +980,9 @@
                 # XXX This should probably be compared with the Konqueror
                 # (kcookiejar.cpp) and Mozilla implementations, but it's a
                 # losing battle.
-                i = domain.rfind(".")
-                j = domain.rfind(".", 0, i)
-                if j == 0:  # domain like .foo.bar
-                    tld = domain[i+1:]
-                    sld = domain[j+1:i]
+                (extra, dot, tld) = domain.rpartition(".")
+                (extra, dot, sld) = extra.rpartition(".")
+                if not extra:  # domain like .foo.bar
                     if sld.lower() in ("co", "ac", "com", "edu", "org", "net",
                        "gov", "mil", "int", "aero", "biz", "cat", "coop",
                        "info", "jobs", "mobi", "museum", "name", "pro",
@@ -1002,7 +994,7 @@
                 undotted_domain = domain[1:]
             else:
                 undotted_domain = domain
-            embedded_dots = (undotted_domain.find(".") >= 0)
+            embedded_dots = ("." in undotted_domain)
             if not embedded_dots and domain != ".local":
                 _debug("   non-local domain %s contains no embedded dot",
                        domain)
@@ -1024,8 +1016,7 @@
             if (cookie.version > 0 or
                 (self.strict_ns_domain & self.DomainStrictNoDots)):
                 host_prefix = req_host[:-len(domain)]
-                if (host_prefix.find(".") >= 0 and
-                    not IPV4_RE.search(req_host)):
+                if ("." in host_prefix and not IPV4_RE.search(req_host)):
                     _debug("   host prefix %s for domain %s contains a dot",
                            host_prefix, domain)
                     return False
@@ -1462,13 +1453,13 @@
         else:
             path_specified = False
             path = request_path(request)
-            i = path.rfind("/")
-            if i != -1:
+            (path, sep, dummy) = path.rpartition("/")
+            if sep:
                 if version == 0:
                     # Netscape spec parts company from reality here
-                    path = path[:i]
+                    pass
                 else:
-                    path = path[:i+1]
+                    path = path + sep
             if len(path) == 0: path = "/"
 
         # set default domain
Index: Lib/cgi.py
===================================================================
--- Lib/cgi.py	(revision 51530)
+++ Lib/cgi.py	(working copy)
@@ -340,10 +340,10 @@
     key = plist.pop(0).lower()
     pdict = {}
     for p in plist:
-        i = p.find('=')
-        if i >= 0:
-            name = p[:i].strip().lower()
-            value = p[i+1:].strip()
+        (name, sep_found, value) = p.partition('=')
+        if (sep_found):
+            name = name.strip().lower()
+            value = value.strip()
             if len(value) >= 2 and value[0] == value[-1] == '"':
                 value = value[1:-1]
                 value = value.replace('\\\\', '\\').replace('\\"', '"')
Index: Lib/ConfigParser.py
===================================================================
--- Lib/ConfigParser.py	(revision 51530)
+++ Lib/ConfigParser.py	(working copy)
@@ -468,9 +468,9 @@
                         if vi in ('=', ':') and ';' in optval:
                             # ';' is a comment delimiter only if it follows
                             # a spacing character
-                            pos = optval.find(';')
-                            if pos != -1 and optval[pos-1].isspace():
-                                optval = optval[:pos]
+                            (new_optval, sep, comment) = optval.partition(';')
+                            if (sep and new_optval[-1:].isspace()):
+                                optval = new_optval
                         optval = optval.strip()
                         # allow empty values
                         if optval == '""':
@@ -599,14 +599,13 @@
         if depth > MAX_INTERPOLATION_DEPTH:
             raise InterpolationDepthError(option, section, rest)
         while rest:
-            p = rest.find("%")
-            if p < 0:
+            (before, sep, after) = rest.partition('%')
+            if (not sep):
                 accum.append(rest)
                 return
-            if p > 0:
-                accum.append(rest[:p])
-                rest = rest[p:]
-            # p is no longer used
+            elif (after):
+                accum.append(before)
+                rest = sep + after
             c = rest[1:2]
             if c == "%":
                 accum.append("%")


More information about the Python-3000 mailing list