Python API: use cached translation tables
authorCampbell Barton <ideasman42@gmail.com>
Sat, 13 Jun 2015 09:45:53 +0000 (19:45 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 13 Jun 2015 09:47:32 +0000 (19:47 +1000)
bpy.path.clean_name() and AddPresetBase.as_filename() were doing
inefficient search-replace of individual characters.

Use cached replacement table instead.

release/scripts/modules/bpy/path.py
release/scripts/startup/bl_operators/presets.py

index be38fefbd2a84a00fe443cfac43a7242d174851b..d5b64933165d8789ea1992e1d60afe66651cc584 100644 (file)
@@ -132,23 +132,47 @@ def clean_name(name, replace="_"):
     or the replace argument if defined.
     """
 
-    bad_chars = ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
-                 "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d"
-                 "\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c"
-                 "\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b"
-                 "\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
-                 "\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99"
-                 "\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
-                 "\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
-                 "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"
-                 "\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5"
-                 "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4"
-                 "\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3"
-                 "\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe")
-
-    for ch in bad_chars:
-        name = name.replace(ch, replace)
-    return name
+    if replace != "_":
+        if len(replace) != 1 or ord(replace) > 255:
+            raise ValueError("Value must be a single ascii character")
+
+    def maketrans_init():
+        trans_cache = clean_name._trans_cache
+        trans = trans_cache.get(replace)
+        if trans is None:
+            bad_chars = (
+                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+                0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+                0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2e, 0x2f, 0x3a,
+                0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c,
+                0x5d, 0x5e, 0x60, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+                0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+                0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+                0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+                0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+                0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+                0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+                0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+                0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+                0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+                0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+                0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+                0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+                0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+                0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+                0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+                0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+                )
+            trans = str.maketrans({char: replace for char in bad_chars})
+            trans_cache[replace] = trans
+        return trans
+
+    trans = maketrans_init()
+    return name.translate(trans)
+clean_name._trans_cache = {}
 
 
 def _clean_utf8(name):
index a5270f63f2784437c086faf8819c703b2c57e941..63c1945d2d2cd6d569cae0347337c0720fc3bdb2 100644 (file)
@@ -53,9 +53,20 @@ class AddPresetBase:
 
     @staticmethod
     def as_filename(name):  # could reuse for other presets
-        for char in " !@#$%^&*(){}:\";'[]<>,.\\/?":
-            name = name.replace(char, '_')
-        return name.lower().strip()
+
+        # lazy init maketrans
+        def maketrans_init():
+            cls = AddPresetBase
+            attr = "_as_filename_trans"
+
+            trans = getattr(cls, attr, None)
+            if trans is None:
+                trans = str.maketrans({char: "_" for char in " !@#$%^&*(){}:\";'[]<>,.\\/?"})
+                setattr(cls, attr, trans)
+            return trans
+
+        trans = maketrans_init()
+        return name.lower().strip().translate(trans)
 
     def execute(self, context):
         import os