changeset 495:3f0c932588fc

Performance: module-level variable lookup is much faster (similar to local) than class-level (either via CLASS.VARIABLE or self.VARIABLE). See tests/_perf_lookups.py
author Franz Glasner <f.glasner@feldmann-mg.com>
date Fri, 17 Dec 2021 19:34:38 +0100
parents 60683361ebed
children 36ab39e3de53
files configmix/config.py
diffstat 1 files changed, 77 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/configmix/config.py	Fri Dec 17 19:24:54 2021 +0100
+++ b/configmix/config.py	Fri Dec 17 19:34:38 2021 +0100
@@ -67,7 +67,7 @@
 
         """
         s = self.getvarl_s(*path, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return int(s, 0)
         else:
             return s
@@ -78,7 +78,7 @@
 
         """
         s = self.getfirstvarl_s(*paths, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return int(s, 0)
         else:
             return s
@@ -89,7 +89,7 @@
 
         """
         s = self.getvar_s(varname, default=default)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return int(s, 0)
         else:
             return s
@@ -100,7 +100,7 @@
 
         """
         s = self.getfirstvar_s(*varnames, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return int(s, 0)
         else:
             return s
@@ -111,7 +111,7 @@
 
         """
         s = self.getvarl_s(*path, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             sl = s.strip().lower()
             if sl not in self._BOOL_CVT:
                 raise ValueError("Not a boolean: %r" % (s, ))
@@ -125,7 +125,7 @@
 
         """
         s = self.getfirstvarl_s(*paths, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             sl = s.strip().lower()
             if sl not in self._BOOL_CVT:
                 raise ValueError("Not a boolean: %r" % (s, ))
@@ -139,7 +139,7 @@
 
         """
         s = self.getvar_s(varname, default=default)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             sl = s.strip().lower()
             if sl not in self._BOOL_CVT:
                 raise ValueError("Not a boolean: %r" % (s, ))
@@ -153,7 +153,7 @@
 
         """
         s = self.getfirstvar_s(*varnames, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             sl = s.strip().lower()
             if sl not in self._BOOL_CVT:
                 raise ValueError("Not a boolean: %r" % (s, ))
@@ -173,7 +173,7 @@
 
         """
         s = self.getvarl_s(*path, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return float(s)
         else:
             return s
@@ -184,7 +184,7 @@
 
         """
         s = self.getfirstvarl_s(*path, **kwds)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return float(s)
         else:
             return s
@@ -195,7 +195,7 @@
 
         """
         s = self.getvar_s(varname, default)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return float(s)
         else:
             return s
@@ -206,12 +206,50 @@
 
         """
         s = self.getfirstvar_s(varname, default)
-        if isinstance(s, Configuration._TEXTTYPE):
+        if isinstance(s, _TEXTTYPE):
             return float(s)
         else:
             return s
 
 
+# Speed
+_EMPTY_STR = u("")
+_TEXTTYPE = type(_EMPTY_STR)
+_STARTTOK = u(b"{{")
+_ENDTOK = u(b"}}")
+_HIER_SEPARATOR = u(b'.')
+_NS_SEPARATOR = u(b':')
+_FILTER_SEPARATOR = u(b'|')
+_STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR
+_ENDTOK_REF = _ENDTOK
+_DOT = u(b'.')
+_QUOTE = u(b'%')
+_QUOTE_x = u(b'x')
+_QUOTE_u = u(b'u')
+_QUOTE_U = u(b'U')
+_COMMENT = u(b'#')
+_QUOTE_MAP = {
+    0x25: u(b'%x25'),    # _QUOTE
+    0x2e: u(b'%x2e'),    # _DOT
+    0x3a: u(b'%x3a'),    # _NS_SEPARATOR
+    0x23: u(b'%x23'),    # _COMMENT / anchor
+    0x7c: u(b'%x7c'),    # _FILTER_SEPARATOR
+    0x22: u(b'%x22'),
+    0x27: u(b'%x27'),
+    0x7b: u(b'%x7b'),
+    0x7d: u(b'%x7d'),
+    0x5b: u(b'%x5b'),
+    0x5d: u(b'%x5d'),
+}
+_QUOTE_SAFE = u(b'abcdefghijklmnopqrstuvwxyz'
+                b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+                b'0123456789'
+                b'-_@!$&/\\()=?*+~;,<>^')
+"""Mostly used configuration key characters that do not need any quoting
+
+"""
+
+
 class Configuration(CoercingMethodsMixin, _AttributeDict):
 
     """The configuration dictionary with attribute support or
@@ -222,43 +260,6 @@
 
     """
 
-    # Speed
-    _EMPTY_STR = u("")
-    _TEXTTYPE = type(_EMPTY_STR)
-    _STARTTOK = u(b"{{")
-    _ENDTOK = u(b"}}")
-    _HIER_SEPARATOR = u(b'.')
-    _NS_SEPARATOR = u(b':')
-    _FILTER_SEPARATOR = u(b'|')
-    _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR
-    _ENDTOK_REF = _ENDTOK
-    _DOT = u(b'.')
-    _QUOTE = u(b'%')
-    _QUOTE_x = u(b'x')
-    _QUOTE_u = u(b'u')
-    _QUOTE_U = u(b'U')
-    _COMMENT = u(b'#')
-    _QUOTE_MAP = {
-        0x25: u(b'%x25'),    # _QUOTE
-        0x2e: u(b'%x2e'),    # _DOT
-        0x3a: u(b'%x3a'),    # _NS_SEPARATOR
-        0x23: u(b'%x23'),    # _COMMENT / anchor
-        0x7c: u(b'%x7c'),    # _FILTER_SEPARATOR
-        0x22: u(b'%x22'),
-        0x27: u(b'%x27'),
-        0x7b: u(b'%x7b'),
-        0x7d: u(b'%x7d'),
-        0x5b: u(b'%x5b'),
-        0x5d: u(b'%x5d'),
-    }
-    _QUOTE_SAFE = u(b'abcdefghijklmnopqrstuvwxyz'
-                    b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-                    b'0123456789'
-                    b'-_@!$&/\\()=?*+~;,<>^')
-    """Mostly used configuration key characters that do not need any quoting
-
-    """
-
     is_jail = False
     """Flag to show that this is not a jail for another configuration"""
 
@@ -409,7 +410,7 @@
             if varname:
                 varnameparts = [
                     self.unquote(vp)
-                    for vp in varname.split(self._HIER_SEPARATOR)
+                    for vp in varname.split(_HIER_SEPARATOR)
                 ]
             else:
                 varnameparts = tuple()
@@ -553,19 +554,19 @@
             return default
 
     def _split_ns(self, s):
-        ns, sep, rest = s.partition(self._NS_SEPARATOR)
+        ns, sep, rest = s.partition(_NS_SEPARATOR)
         if sep:
             return (self.unquote(ns), rest)
         else:
             return (None, ns)
 
     def _split_filters(self, s):
-        name, sep, filters = s.partition(self._FILTER_SEPARATOR)
+        name, sep, filters = s.partition(_FILTER_SEPARATOR)
         if sep:
             filters = filters.strip()
             if filters:
                 return (name.rstrip(),
-                        filters.split(self._FILTER_SEPARATOR))
+                        filters.split(_FILTER_SEPARATOR))
             else:
                 return (name.rstrip(), [])
         else:
@@ -611,11 +612,11 @@
         :raise KeyError: If the reverence cannot found
 
         """
-        if not isinstance(v, self._TEXTTYPE):
+        if not isinstance(v, _TEXTTYPE):
             return v
-        if v.startswith(self._STARTTOK_REF) and v.endswith(self._ENDTOK_REF):
+        if v.startswith(_STARTTOK_REF) and v.endswith(_ENDTOK_REF):
             return self.expand_ref_uri(
-                v[len(self._STARTTOK_REF):-len(self._ENDTOK_REF)])
+                v[len(_STARTTOK_REF):-len(_ENDTOK_REF)])
         else:
             return v
 
@@ -625,14 +626,14 @@
             raise ValueError("only fragment-only URIs are supported")
         if not pu.fragment:
             return self
-        if pu.fragment.startswith(self._DOT):
+        if pu.fragment.startswith(_DOT):
             raise ValueError("relative refs not supported")
         return self.getvar(pu.fragment, default=default)
 
     def substitute_variables_in_obj(self, obj):
         """Recursively expand variables in the object tree `obj`."""
         ty = type(obj)
-        if issubclass(ty, self._TEXTTYPE):
+        if issubclass(ty, _TEXTTYPE):
             # a string - really replace the value
             return self.expand_variable(obj)
         elif issubclass(ty, dict):
@@ -654,7 +655,7 @@
 
     def expand_variable(self, s):
         """Expand variables in the single string `s`"""
-        start = s.find(self._STARTTOK, 0)
+        start = s.find(_STARTTOK, 0)
         if start < 0:
             return s
         res = []
@@ -662,7 +663,7 @@
         rest = 0
         while start != -1:
             res_append(s[rest:start])
-            end = s.find(self._ENDTOK, start)
+            end = s.find(_ENDTOK, start)
             if end < 0:
                 rest = start
                 break
@@ -674,7 +675,7 @@
                 elif EMPTY_FILTER in filters:
                     varvalue = self._apply_filters(
                         filters, self.getvar_s(varname,
-                                               default=self._EMPTY_STR))
+                                               default=_EMPTY_STR))
                 else:
                     varvalue = self._apply_filters(
                         filters, self.getvar_s(varname))
@@ -697,9 +698,9 @@
                 res_append(str_and_u(varvalue))
             # don't re-evaluate because `self.getvar_s()` expands already
             rest = end + 2
-            start = s.find(self._STARTTOK, rest)
+            start = s.find(_STARTTOK, rest)
         res_append(s[rest:])
-        return self._EMPTY_STR.join(res)
+        return _EMPTY_STR.join(res)
 
     def _apply_filters(self, filters, value):
         for name in filters:
@@ -728,7 +729,7 @@
 
         """
         # Quick check whether all of the chars are in _QUOTE_SAFE
-        if not s.rstrip(klass._QUOTE_SAFE):
+        if not s.rstrip(_QUOTE_SAFE):
             return s
 
         # Slow path
@@ -738,7 +739,7 @@
             if isinstance(s, str):
                 s = s.decode("latin1")
                 re_encode = True
-        s = s.translate(klass._QUOTE_MAP)
+        s = s.translate(_QUOTE_MAP)
         if re_encode:
             return s.encode("latin1")
         else:
@@ -752,9 +753,9 @@
         This is the inverse of :meth:`~.quote`.
 
         """
-        if klass._QUOTE not in s:
+        if _QUOTE not in s:
             return s
-        parts = s.split(klass._QUOTE)
+        parts = s.split(_QUOTE)
         res = [parts[0]]
         res_append = res.append
         for p in parts[1:]:
@@ -762,24 +763,24 @@
                 qc = p[0]
             except IndexError:
                 raise ValueError("unknown quote syntax string: {}".format(s))
-            if qc == klass._QUOTE_x:
+            if qc == _QUOTE_x:
                 if len(p) < 3:
                     raise ValueError("quote syntax: length too small")
                 res_append(uchr(int(p[1:3], 16)))
                 res_append(p[3:])
-            elif qc == klass._QUOTE_u:
+            elif qc == _QUOTE_u:
                 if len(p) < 5:
                     raise ValueError("quote syntax: length too small")
                 res_append(uchr(int(p[1:5], 16)))
                 res_append(p[5:])
-            elif qc == klass._QUOTE_U:
+            elif qc == _QUOTE_U:
                 if len(p) < 9:
                     raise ValueError("quote syntax: length too small")
                 res_append(uchr(int(p[1:9], 16)))
                 res_append(p[9:])
             else:
                 raise ValueError("unknown quote syntax string: {}".format(s))
-        return klass._EMPTY_STR.join(res)
+        return _EMPTY_STR.join(res)
 
     def jailed(self, rootpath=None, root=None, bind_root=True):
         """Return a "jailed" configuration of the current configuration.
@@ -815,7 +816,7 @@
             if varname:
                 rootpath = [
                     self.unquote(p) for p in root.split(
-                        self._HIER_SEPARATOR)
+                        _HIER_SEPARATOR)
                 ]
             else:
                 rootpath = tuple()
@@ -857,11 +858,11 @@
         self._path = path
         if path:
             self._pathstr = \
-                Configuration._HIER_SEPARATOR.join(
+                _HIER_SEPARATOR.join(
                     [Configuration.quote(p) for p in path]) \
-                + Configuration._HIER_SEPARATOR
+                + _HIER_SEPARATOR
         else:
-            self._pathstr = Configuration._EMPTY_STR
+            self._pathstr = _EMPTY_STR
 
     @property
     def base(self):
@@ -1033,7 +1034,7 @@
             if varname:
                 rootpath = [
                     self._base.unquote(p) for p in varname.split(
-                        self._base._HIER_SEPARATOR)
+                        _HIER_SEPARATOR)
                 ]
             else:
                 rootpath = tuple()