Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/config.py @ 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 | 6a0f761ff35b |
| children | 8e516f17cf95 |
comparison
equal
deleted
inserted
replaced
| 494:60683361ebed | 495:3f0c932588fc |
|---|---|
| 65 """Get a (possibly substituted) variable and coerce text to a | 65 """Get a (possibly substituted) variable and coerce text to a |
| 66 number. | 66 number. |
| 67 | 67 |
| 68 """ | 68 """ |
| 69 s = self.getvarl_s(*path, **kwds) | 69 s = self.getvarl_s(*path, **kwds) |
| 70 if isinstance(s, Configuration._TEXTTYPE): | 70 if isinstance(s, _TEXTTYPE): |
| 71 return int(s, 0) | 71 return int(s, 0) |
| 72 else: | 72 else: |
| 73 return s | 73 return s |
| 74 | 74 |
| 75 def getfirstintvarl_s(self, *paths, **kwds): | 75 def getfirstintvarl_s(self, *paths, **kwds): |
| 76 """Get a (possibly substituted) variable and coerce text to a | 76 """Get a (possibly substituted) variable and coerce text to a |
| 77 number. | 77 number. |
| 78 | 78 |
| 79 """ | 79 """ |
| 80 s = self.getfirstvarl_s(*paths, **kwds) | 80 s = self.getfirstvarl_s(*paths, **kwds) |
| 81 if isinstance(s, Configuration._TEXTTYPE): | 81 if isinstance(s, _TEXTTYPE): |
| 82 return int(s, 0) | 82 return int(s, 0) |
| 83 else: | 83 else: |
| 84 return s | 84 return s |
| 85 | 85 |
| 86 def getintvar_s(self, varname, default=_MARKER): | 86 def getintvar_s(self, varname, default=_MARKER): |
| 87 """Get a (possibly substituted) variable and coerce text to a | 87 """Get a (possibly substituted) variable and coerce text to a |
| 88 number. | 88 number. |
| 89 | 89 |
| 90 """ | 90 """ |
| 91 s = self.getvar_s(varname, default=default) | 91 s = self.getvar_s(varname, default=default) |
| 92 if isinstance(s, Configuration._TEXTTYPE): | 92 if isinstance(s, _TEXTTYPE): |
| 93 return int(s, 0) | 93 return int(s, 0) |
| 94 else: | 94 else: |
| 95 return s | 95 return s |
| 96 | 96 |
| 97 def getfirstintvar_s(self, *varnames, **kwds): | 97 def getfirstintvar_s(self, *varnames, **kwds): |
| 98 """A variant of :meth:`~.getintvar_s` that returns the first found | 98 """A variant of :meth:`~.getintvar_s` that returns the first found |
| 99 variable in the list of given variables in `varnames`. | 99 variable in the list of given variables in `varnames`. |
| 100 | 100 |
| 101 """ | 101 """ |
| 102 s = self.getfirstvar_s(*varnames, **kwds) | 102 s = self.getfirstvar_s(*varnames, **kwds) |
| 103 if isinstance(s, Configuration._TEXTTYPE): | 103 if isinstance(s, _TEXTTYPE): |
| 104 return int(s, 0) | 104 return int(s, 0) |
| 105 else: | 105 else: |
| 106 return s | 106 return s |
| 107 | 107 |
| 108 def getboolvarl_s(self, *path, **kwds): | 108 def getboolvarl_s(self, *path, **kwds): |
| 109 """Get a (possibly substituted) variable and convert text to a | 109 """Get a (possibly substituted) variable and convert text to a |
| 110 boolean | 110 boolean |
| 111 | 111 |
| 112 """ | 112 """ |
| 113 s = self.getvarl_s(*path, **kwds) | 113 s = self.getvarl_s(*path, **kwds) |
| 114 if isinstance(s, Configuration._TEXTTYPE): | 114 if isinstance(s, _TEXTTYPE): |
| 115 sl = s.strip().lower() | 115 sl = s.strip().lower() |
| 116 if sl not in self._BOOL_CVT: | 116 if sl not in self._BOOL_CVT: |
| 117 raise ValueError("Not a boolean: %r" % (s, )) | 117 raise ValueError("Not a boolean: %r" % (s, )) |
| 118 return self._BOOL_CVT[sl] | 118 return self._BOOL_CVT[sl] |
| 119 else: | 119 else: |
| 123 """Get a (possibly substituted) variable and convert text to a | 123 """Get a (possibly substituted) variable and convert text to a |
| 124 boolean | 124 boolean |
| 125 | 125 |
| 126 """ | 126 """ |
| 127 s = self.getfirstvarl_s(*paths, **kwds) | 127 s = self.getfirstvarl_s(*paths, **kwds) |
| 128 if isinstance(s, Configuration._TEXTTYPE): | 128 if isinstance(s, _TEXTTYPE): |
| 129 sl = s.strip().lower() | 129 sl = s.strip().lower() |
| 130 if sl not in self._BOOL_CVT: | 130 if sl not in self._BOOL_CVT: |
| 131 raise ValueError("Not a boolean: %r" % (s, )) | 131 raise ValueError("Not a boolean: %r" % (s, )) |
| 132 return self._BOOL_CVT[sl] | 132 return self._BOOL_CVT[sl] |
| 133 else: | 133 else: |
| 137 """Get a (possibly substituted) variable and convert text to a | 137 """Get a (possibly substituted) variable and convert text to a |
| 138 boolean | 138 boolean |
| 139 | 139 |
| 140 """ | 140 """ |
| 141 s = self.getvar_s(varname, default=default) | 141 s = self.getvar_s(varname, default=default) |
| 142 if isinstance(s, Configuration._TEXTTYPE): | 142 if isinstance(s, _TEXTTYPE): |
| 143 sl = s.strip().lower() | 143 sl = s.strip().lower() |
| 144 if sl not in self._BOOL_CVT: | 144 if sl not in self._BOOL_CVT: |
| 145 raise ValueError("Not a boolean: %r" % (s, )) | 145 raise ValueError("Not a boolean: %r" % (s, )) |
| 146 return self._BOOL_CVT[sl] | 146 return self._BOOL_CVT[sl] |
| 147 else: | 147 else: |
| 151 """A variant of :meth:`~.getboolvar_s` that returns the first found | 151 """A variant of :meth:`~.getboolvar_s` that returns the first found |
| 152 variable in the list of given variables in `varnames`. | 152 variable in the list of given variables in `varnames`. |
| 153 | 153 |
| 154 """ | 154 """ |
| 155 s = self.getfirstvar_s(*varnames, **kwds) | 155 s = self.getfirstvar_s(*varnames, **kwds) |
| 156 if isinstance(s, Configuration._TEXTTYPE): | 156 if isinstance(s, _TEXTTYPE): |
| 157 sl = s.strip().lower() | 157 sl = s.strip().lower() |
| 158 if sl not in self._BOOL_CVT: | 158 if sl not in self._BOOL_CVT: |
| 159 raise ValueError("Not a boolean: %r" % (s, )) | 159 raise ValueError("Not a boolean: %r" % (s, )) |
| 160 return self._BOOL_CVT[sl] | 160 return self._BOOL_CVT[sl] |
| 161 else: | 161 else: |
| 171 """Get a (possibly substituted) variable and convert text to a | 171 """Get a (possibly substituted) variable and convert text to a |
| 172 float | 172 float |
| 173 | 173 |
| 174 """ | 174 """ |
| 175 s = self.getvarl_s(*path, **kwds) | 175 s = self.getvarl_s(*path, **kwds) |
| 176 if isinstance(s, Configuration._TEXTTYPE): | 176 if isinstance(s, _TEXTTYPE): |
| 177 return float(s) | 177 return float(s) |
| 178 else: | 178 else: |
| 179 return s | 179 return s |
| 180 | 180 |
| 181 def getfirstfloatvarl_s(self, *path, **kwds): | 181 def getfirstfloatvarl_s(self, *path, **kwds): |
| 182 """Get a (possibly substituted) variable and convert text to a | 182 """Get a (possibly substituted) variable and convert text to a |
| 183 float | 183 float |
| 184 | 184 |
| 185 """ | 185 """ |
| 186 s = self.getfirstvarl_s(*path, **kwds) | 186 s = self.getfirstvarl_s(*path, **kwds) |
| 187 if isinstance(s, Configuration._TEXTTYPE): | 187 if isinstance(s, _TEXTTYPE): |
| 188 return float(s) | 188 return float(s) |
| 189 else: | 189 else: |
| 190 return s | 190 return s |
| 191 | 191 |
| 192 def getfloatvar_s(self, varname, default=_MARKER): | 192 def getfloatvar_s(self, varname, default=_MARKER): |
| 193 """Get a (possibly substituted) variable and convert text to a | 193 """Get a (possibly substituted) variable and convert text to a |
| 194 float | 194 float |
| 195 | 195 |
| 196 """ | 196 """ |
| 197 s = self.getvar_s(varname, default) | 197 s = self.getvar_s(varname, default) |
| 198 if isinstance(s, Configuration._TEXTTYPE): | 198 if isinstance(s, _TEXTTYPE): |
| 199 return float(s) | 199 return float(s) |
| 200 else: | 200 else: |
| 201 return s | 201 return s |
| 202 | 202 |
| 203 def getfirstfloatvar_s(self, varname, default=_MARKER): | 203 def getfirstfloatvar_s(self, varname, default=_MARKER): |
| 204 """Get a (possibly substituted) variable and convert text to a | 204 """Get a (possibly substituted) variable and convert text to a |
| 205 float | 205 float |
| 206 | 206 |
| 207 """ | 207 """ |
| 208 s = self.getfirstvar_s(varname, default) | 208 s = self.getfirstvar_s(varname, default) |
| 209 if isinstance(s, Configuration._TEXTTYPE): | 209 if isinstance(s, _TEXTTYPE): |
| 210 return float(s) | 210 return float(s) |
| 211 else: | 211 else: |
| 212 return s | 212 return s |
| 213 | |
| 214 | |
| 215 # Speed | |
| 216 _EMPTY_STR = u("") | |
| 217 _TEXTTYPE = type(_EMPTY_STR) | |
| 218 _STARTTOK = u(b"{{") | |
| 219 _ENDTOK = u(b"}}") | |
| 220 _HIER_SEPARATOR = u(b'.') | |
| 221 _NS_SEPARATOR = u(b':') | |
| 222 _FILTER_SEPARATOR = u(b'|') | |
| 223 _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR | |
| 224 _ENDTOK_REF = _ENDTOK | |
| 225 _DOT = u(b'.') | |
| 226 _QUOTE = u(b'%') | |
| 227 _QUOTE_x = u(b'x') | |
| 228 _QUOTE_u = u(b'u') | |
| 229 _QUOTE_U = u(b'U') | |
| 230 _COMMENT = u(b'#') | |
| 231 _QUOTE_MAP = { | |
| 232 0x25: u(b'%x25'), # _QUOTE | |
| 233 0x2e: u(b'%x2e'), # _DOT | |
| 234 0x3a: u(b'%x3a'), # _NS_SEPARATOR | |
| 235 0x23: u(b'%x23'), # _COMMENT / anchor | |
| 236 0x7c: u(b'%x7c'), # _FILTER_SEPARATOR | |
| 237 0x22: u(b'%x22'), | |
| 238 0x27: u(b'%x27'), | |
| 239 0x7b: u(b'%x7b'), | |
| 240 0x7d: u(b'%x7d'), | |
| 241 0x5b: u(b'%x5b'), | |
| 242 0x5d: u(b'%x5d'), | |
| 243 } | |
| 244 _QUOTE_SAFE = u(b'abcdefghijklmnopqrstuvwxyz' | |
| 245 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
| 246 b'0123456789' | |
| 247 b'-_@!$&/\\()=?*+~;,<>^') | |
| 248 """Mostly used configuration key characters that do not need any quoting | |
| 249 | |
| 250 """ | |
| 213 | 251 |
| 214 | 252 |
| 215 class Configuration(CoercingMethodsMixin, _AttributeDict): | 253 class Configuration(CoercingMethodsMixin, _AttributeDict): |
| 216 | 254 |
| 217 """The configuration dictionary with attribute support or | 255 """The configuration dictionary with attribute support or |
| 218 variable substitution. | 256 variable substitution. |
| 219 | 257 |
| 220 .. note:: When retrieving by attribute names variables will *not* | 258 .. note:: When retrieving by attribute names variables will *not* |
| 221 substituted. | 259 substituted. |
| 222 | |
| 223 """ | |
| 224 | |
| 225 # Speed | |
| 226 _EMPTY_STR = u("") | |
| 227 _TEXTTYPE = type(_EMPTY_STR) | |
| 228 _STARTTOK = u(b"{{") | |
| 229 _ENDTOK = u(b"}}") | |
| 230 _HIER_SEPARATOR = u(b'.') | |
| 231 _NS_SEPARATOR = u(b':') | |
| 232 _FILTER_SEPARATOR = u(b'|') | |
| 233 _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR | |
| 234 _ENDTOK_REF = _ENDTOK | |
| 235 _DOT = u(b'.') | |
| 236 _QUOTE = u(b'%') | |
| 237 _QUOTE_x = u(b'x') | |
| 238 _QUOTE_u = u(b'u') | |
| 239 _QUOTE_U = u(b'U') | |
| 240 _COMMENT = u(b'#') | |
| 241 _QUOTE_MAP = { | |
| 242 0x25: u(b'%x25'), # _QUOTE | |
| 243 0x2e: u(b'%x2e'), # _DOT | |
| 244 0x3a: u(b'%x3a'), # _NS_SEPARATOR | |
| 245 0x23: u(b'%x23'), # _COMMENT / anchor | |
| 246 0x7c: u(b'%x7c'), # _FILTER_SEPARATOR | |
| 247 0x22: u(b'%x22'), | |
| 248 0x27: u(b'%x27'), | |
| 249 0x7b: u(b'%x7b'), | |
| 250 0x7d: u(b'%x7d'), | |
| 251 0x5b: u(b'%x5b'), | |
| 252 0x5d: u(b'%x5d'), | |
| 253 } | |
| 254 _QUOTE_SAFE = u(b'abcdefghijklmnopqrstuvwxyz' | |
| 255 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
| 256 b'0123456789' | |
| 257 b'-_@!$&/\\()=?*+~;,<>^') | |
| 258 """Mostly used configuration key characters that do not need any quoting | |
| 259 | 260 |
| 260 """ | 261 """ |
| 261 | 262 |
| 262 is_jail = False | 263 is_jail = False |
| 263 """Flag to show that this is not a jail for another configuration""" | 264 """Flag to show that this is not a jail for another configuration""" |
| 407 varns, varname = self._split_ns(varname) | 408 varns, varname = self._split_ns(varname) |
| 408 if not varns: | 409 if not varns: |
| 409 if varname: | 410 if varname: |
| 410 varnameparts = [ | 411 varnameparts = [ |
| 411 self.unquote(vp) | 412 self.unquote(vp) |
| 412 for vp in varname.split(self._HIER_SEPARATOR) | 413 for vp in varname.split(_HIER_SEPARATOR) |
| 413 ] | 414 ] |
| 414 else: | 415 else: |
| 415 varnameparts = tuple() | 416 varnameparts = tuple() |
| 416 else: | 417 else: |
| 417 varnameparts = (varname,) | 418 varnameparts = (varname,) |
| 551 "none of the given variables found: %r" % (varnames,)) | 552 "none of the given variables found: %r" % (varnames,)) |
| 552 else: | 553 else: |
| 553 return default | 554 return default |
| 554 | 555 |
| 555 def _split_ns(self, s): | 556 def _split_ns(self, s): |
| 556 ns, sep, rest = s.partition(self._NS_SEPARATOR) | 557 ns, sep, rest = s.partition(_NS_SEPARATOR) |
| 557 if sep: | 558 if sep: |
| 558 return (self.unquote(ns), rest) | 559 return (self.unquote(ns), rest) |
| 559 else: | 560 else: |
| 560 return (None, ns) | 561 return (None, ns) |
| 561 | 562 |
| 562 def _split_filters(self, s): | 563 def _split_filters(self, s): |
| 563 name, sep, filters = s.partition(self._FILTER_SEPARATOR) | 564 name, sep, filters = s.partition(_FILTER_SEPARATOR) |
| 564 if sep: | 565 if sep: |
| 565 filters = filters.strip() | 566 filters = filters.strip() |
| 566 if filters: | 567 if filters: |
| 567 return (name.rstrip(), | 568 return (name.rstrip(), |
| 568 filters.split(self._FILTER_SEPARATOR)) | 569 filters.split(_FILTER_SEPARATOR)) |
| 569 else: | 570 else: |
| 570 return (name.rstrip(), []) | 571 return (name.rstrip(), []) |
| 571 else: | 572 else: |
| 572 return (name, []) | 573 return (name, []) |
| 573 | 574 |
| 609 unchanged. | 610 unchanged. |
| 610 | 611 |
| 611 :raise KeyError: If the reverence cannot found | 612 :raise KeyError: If the reverence cannot found |
| 612 | 613 |
| 613 """ | 614 """ |
| 614 if not isinstance(v, self._TEXTTYPE): | 615 if not isinstance(v, _TEXTTYPE): |
| 615 return v | 616 return v |
| 616 if v.startswith(self._STARTTOK_REF) and v.endswith(self._ENDTOK_REF): | 617 if v.startswith(_STARTTOK_REF) and v.endswith(_ENDTOK_REF): |
| 617 return self.expand_ref_uri( | 618 return self.expand_ref_uri( |
| 618 v[len(self._STARTTOK_REF):-len(self._ENDTOK_REF)]) | 619 v[len(_STARTTOK_REF):-len(_ENDTOK_REF)]) |
| 619 else: | 620 else: |
| 620 return v | 621 return v |
| 621 | 622 |
| 622 def expand_ref_uri(self, uri, default=_MARKER): | 623 def expand_ref_uri(self, uri, default=_MARKER): |
| 623 pu = urlsplit(uri) | 624 pu = urlsplit(uri) |
| 624 if pu.scheme or pu.netloc or pu.path or pu.query: | 625 if pu.scheme or pu.netloc or pu.path or pu.query: |
| 625 raise ValueError("only fragment-only URIs are supported") | 626 raise ValueError("only fragment-only URIs are supported") |
| 626 if not pu.fragment: | 627 if not pu.fragment: |
| 627 return self | 628 return self |
| 628 if pu.fragment.startswith(self._DOT): | 629 if pu.fragment.startswith(_DOT): |
| 629 raise ValueError("relative refs not supported") | 630 raise ValueError("relative refs not supported") |
| 630 return self.getvar(pu.fragment, default=default) | 631 return self.getvar(pu.fragment, default=default) |
| 631 | 632 |
| 632 def substitute_variables_in_obj(self, obj): | 633 def substitute_variables_in_obj(self, obj): |
| 633 """Recursively expand variables in the object tree `obj`.""" | 634 """Recursively expand variables in the object tree `obj`.""" |
| 634 ty = type(obj) | 635 ty = type(obj) |
| 635 if issubclass(ty, self._TEXTTYPE): | 636 if issubclass(ty, _TEXTTYPE): |
| 636 # a string - really replace the value | 637 # a string - really replace the value |
| 637 return self.expand_variable(obj) | 638 return self.expand_variable(obj) |
| 638 elif issubclass(ty, dict): | 639 elif issubclass(ty, dict): |
| 639 newdict = ty() | 640 newdict = ty() |
| 640 for k in obj: | 641 for k in obj: |
| 652 else: | 653 else: |
| 653 return obj | 654 return obj |
| 654 | 655 |
| 655 def expand_variable(self, s): | 656 def expand_variable(self, s): |
| 656 """Expand variables in the single string `s`""" | 657 """Expand variables in the single string `s`""" |
| 657 start = s.find(self._STARTTOK, 0) | 658 start = s.find(_STARTTOK, 0) |
| 658 if start < 0: | 659 if start < 0: |
| 659 return s | 660 return s |
| 660 res = [] | 661 res = [] |
| 661 res_append = res.append | 662 res_append = res.append |
| 662 rest = 0 | 663 rest = 0 |
| 663 while start != -1: | 664 while start != -1: |
| 664 res_append(s[rest:start]) | 665 res_append(s[rest:start]) |
| 665 end = s.find(self._ENDTOK, start) | 666 end = s.find(_ENDTOK, start) |
| 666 if end < 0: | 667 if end < 0: |
| 667 rest = start | 668 rest = start |
| 668 break | 669 break |
| 669 varname, filters = self._split_filters(s[start+2:end]) | 670 varname, filters = self._split_filters(s[start+2:end]) |
| 670 try: | 671 try: |
| 672 varvalue = self._apply_filters( | 673 varvalue = self._apply_filters( |
| 673 filters, self.getvar_s(varname, default=None)) | 674 filters, self.getvar_s(varname, default=None)) |
| 674 elif EMPTY_FILTER in filters: | 675 elif EMPTY_FILTER in filters: |
| 675 varvalue = self._apply_filters( | 676 varvalue = self._apply_filters( |
| 676 filters, self.getvar_s(varname, | 677 filters, self.getvar_s(varname, |
| 677 default=self._EMPTY_STR)) | 678 default=_EMPTY_STR)) |
| 678 else: | 679 else: |
| 679 varvalue = self._apply_filters( | 680 varvalue = self._apply_filters( |
| 680 filters, self.getvar_s(varname)) | 681 filters, self.getvar_s(varname)) |
| 681 except KeyError: | 682 except KeyError: |
| 682 warnings.warn("Cannot expand variable %r in string " | 683 warnings.warn("Cannot expand variable %r in string " |
| 695 pass | 696 pass |
| 696 else: | 697 else: |
| 697 res_append(str_and_u(varvalue)) | 698 res_append(str_and_u(varvalue)) |
| 698 # don't re-evaluate because `self.getvar_s()` expands already | 699 # don't re-evaluate because `self.getvar_s()` expands already |
| 699 rest = end + 2 | 700 rest = end + 2 |
| 700 start = s.find(self._STARTTOK, rest) | 701 start = s.find(_STARTTOK, rest) |
| 701 res_append(s[rest:]) | 702 res_append(s[rest:]) |
| 702 return self._EMPTY_STR.join(res) | 703 return _EMPTY_STR.join(res) |
| 703 | 704 |
| 704 def _apply_filters(self, filters, value): | 705 def _apply_filters(self, filters, value): |
| 705 for name in filters: | 706 for name in filters: |
| 706 try: | 707 try: |
| 707 filterfn = lookup_filter(name) | 708 filterfn = lookup_filter(name) |
| 726 | 727 |
| 727 See also the :ref:`quoting` section. | 728 See also the :ref:`quoting` section. |
| 728 | 729 |
| 729 """ | 730 """ |
| 730 # Quick check whether all of the chars are in _QUOTE_SAFE | 731 # Quick check whether all of the chars are in _QUOTE_SAFE |
| 731 if not s.rstrip(klass._QUOTE_SAFE): | 732 if not s.rstrip(_QUOTE_SAFE): |
| 732 return s | 733 return s |
| 733 | 734 |
| 734 # Slow path | 735 # Slow path |
| 735 re_encode = False | 736 re_encode = False |
| 736 if PY2: | 737 if PY2: |
| 737 # Use the Unicode translation variant in PY2 | 738 # Use the Unicode translation variant in PY2 |
| 738 if isinstance(s, str): | 739 if isinstance(s, str): |
| 739 s = s.decode("latin1") | 740 s = s.decode("latin1") |
| 740 re_encode = True | 741 re_encode = True |
| 741 s = s.translate(klass._QUOTE_MAP) | 742 s = s.translate(_QUOTE_MAP) |
| 742 if re_encode: | 743 if re_encode: |
| 743 return s.encode("latin1") | 744 return s.encode("latin1") |
| 744 else: | 745 else: |
| 745 return s | 746 return s |
| 746 | 747 |
| 750 ``%uNNNN`` or ``%UNNNNNNNN``. | 751 ``%uNNNN`` or ``%UNNNNNNNN``. |
| 751 | 752 |
| 752 This is the inverse of :meth:`~.quote`. | 753 This is the inverse of :meth:`~.quote`. |
| 753 | 754 |
| 754 """ | 755 """ |
| 755 if klass._QUOTE not in s: | 756 if _QUOTE not in s: |
| 756 return s | 757 return s |
| 757 parts = s.split(klass._QUOTE) | 758 parts = s.split(_QUOTE) |
| 758 res = [parts[0]] | 759 res = [parts[0]] |
| 759 res_append = res.append | 760 res_append = res.append |
| 760 for p in parts[1:]: | 761 for p in parts[1:]: |
| 761 try: | 762 try: |
| 762 qc = p[0] | 763 qc = p[0] |
| 763 except IndexError: | 764 except IndexError: |
| 764 raise ValueError("unknown quote syntax string: {}".format(s)) | 765 raise ValueError("unknown quote syntax string: {}".format(s)) |
| 765 if qc == klass._QUOTE_x: | 766 if qc == _QUOTE_x: |
| 766 if len(p) < 3: | 767 if len(p) < 3: |
| 767 raise ValueError("quote syntax: length too small") | 768 raise ValueError("quote syntax: length too small") |
| 768 res_append(uchr(int(p[1:3], 16))) | 769 res_append(uchr(int(p[1:3], 16))) |
| 769 res_append(p[3:]) | 770 res_append(p[3:]) |
| 770 elif qc == klass._QUOTE_u: | 771 elif qc == _QUOTE_u: |
| 771 if len(p) < 5: | 772 if len(p) < 5: |
| 772 raise ValueError("quote syntax: length too small") | 773 raise ValueError("quote syntax: length too small") |
| 773 res_append(uchr(int(p[1:5], 16))) | 774 res_append(uchr(int(p[1:5], 16))) |
| 774 res_append(p[5:]) | 775 res_append(p[5:]) |
| 775 elif qc == klass._QUOTE_U: | 776 elif qc == _QUOTE_U: |
| 776 if len(p) < 9: | 777 if len(p) < 9: |
| 777 raise ValueError("quote syntax: length too small") | 778 raise ValueError("quote syntax: length too small") |
| 778 res_append(uchr(int(p[1:9], 16))) | 779 res_append(uchr(int(p[1:9], 16))) |
| 779 res_append(p[9:]) | 780 res_append(p[9:]) |
| 780 else: | 781 else: |
| 781 raise ValueError("unknown quote syntax string: {}".format(s)) | 782 raise ValueError("unknown quote syntax string: {}".format(s)) |
| 782 return klass._EMPTY_STR.join(res) | 783 return _EMPTY_STR.join(res) |
| 783 | 784 |
| 784 def jailed(self, rootpath=None, root=None, bind_root=True): | 785 def jailed(self, rootpath=None, root=None, bind_root=True): |
| 785 """Return a "jailed" configuration of the current configuration. | 786 """Return a "jailed" configuration of the current configuration. |
| 786 | 787 |
| 787 :param rootpath: a sequence of strings that shall emcompass | 788 :param rootpath: a sequence of strings that shall emcompass |
| 813 raise ValueError( | 814 raise ValueError( |
| 814 "jailed configurations do not support namespaces") | 815 "jailed configurations do not support namespaces") |
| 815 if varname: | 816 if varname: |
| 816 rootpath = [ | 817 rootpath = [ |
| 817 self.unquote(p) for p in root.split( | 818 self.unquote(p) for p in root.split( |
| 818 self._HIER_SEPARATOR) | 819 _HIER_SEPARATOR) |
| 819 ] | 820 ] |
| 820 else: | 821 else: |
| 821 rootpath = tuple() | 822 rootpath = tuple() |
| 822 jc = _JailedConfiguration(*rootpath) | 823 jc = _JailedConfiguration(*rootpath) |
| 823 if bind_root: | 824 if bind_root: |
| 855 def __init__(self, *path): | 856 def __init__(self, *path): |
| 856 super(_JailedConfiguration, self).__init__() | 857 super(_JailedConfiguration, self).__init__() |
| 857 self._path = path | 858 self._path = path |
| 858 if path: | 859 if path: |
| 859 self._pathstr = \ | 860 self._pathstr = \ |
| 860 Configuration._HIER_SEPARATOR.join( | 861 _HIER_SEPARATOR.join( |
| 861 [Configuration.quote(p) for p in path]) \ | 862 [Configuration.quote(p) for p in path]) \ |
| 862 + Configuration._HIER_SEPARATOR | 863 + _HIER_SEPARATOR |
| 863 else: | 864 else: |
| 864 self._pathstr = Configuration._EMPTY_STR | 865 self._pathstr = _EMPTY_STR |
| 865 | 866 |
| 866 @property | 867 @property |
| 867 def base(self): | 868 def base(self): |
| 868 """Ask for the base (aka parent) configuration". | 869 """Ask for the base (aka parent) configuration". |
| 869 | 870 |
| 1031 raise ValueError( | 1032 raise ValueError( |
| 1032 "sub-jails do not support namespaces") | 1033 "sub-jails do not support namespaces") |
| 1033 if varname: | 1034 if varname: |
| 1034 rootpath = [ | 1035 rootpath = [ |
| 1035 self._base.unquote(p) for p in varname.split( | 1036 self._base.unquote(p) for p in varname.split( |
| 1036 self._base._HIER_SEPARATOR) | 1037 _HIER_SEPARATOR) |
| 1037 ] | 1038 ] |
| 1038 else: | 1039 else: |
| 1039 rootpath = tuple() | 1040 rootpath = tuple() |
| 1040 if self._path: | 1041 if self._path: |
| 1041 new_rootpath = self._path + tuple(rootpath) | 1042 new_rootpath = self._path + tuple(rootpath) |
