Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/config.py @ 502:4f90e1eb7af8
Make quote() and unquote() module globals and also export from configmix
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 18 Dec 2021 01:26:41 +0100 |
| parents | 34cd4f111134 |
| children | a56f1d97a3f3 |
comparison
equal
deleted
inserted
replaced
| 501:1c83389fb8dd | 502:4f90e1eb7af8 |
|---|---|
| 249 """Mostly used configuration key characters that do not need any quoting | 249 """Mostly used configuration key characters that do not need any quoting |
| 250 | 250 |
| 251 """ | 251 """ |
| 252 | 252 |
| 253 | 253 |
| 254 def quote(s): | |
| 255 """Replace important special characters in string `s` by replacing | |
| 256 them with ``%xNN`` where `NN` are the two hexadecimal digits of the | |
| 257 characters unicode codepoint value. | |
| 258 | |
| 259 Handled are the important special chars: ``%``, ``.``, ``:``, | |
| 260 ``#``; ``'``, ``"``, ``|``, ``{``, ``}``, ``[`` and ``]``. | |
| 261 | |
| 262 See also the :ref:`quoting` section. | |
| 263 | |
| 264 """ | |
| 265 # Quick check whether all of the chars are in _QUOTE_SAFE | |
| 266 if not s.rstrip(_QUOTE_SAFE): | |
| 267 return s | |
| 268 | |
| 269 # Slow path | |
| 270 re_encode = False | |
| 271 if PY2: | |
| 272 # Use the Unicode translation variant in PY2 | |
| 273 if isinstance(s, str): | |
| 274 s = s.decode("latin1") | |
| 275 re_encode = True | |
| 276 s = s.translate(_QUOTE_MAP) | |
| 277 if re_encode: | |
| 278 return s.encode("latin1") | |
| 279 else: | |
| 280 return s | |
| 281 | |
| 282 | |
| 283 def unquote(s): | |
| 284 """Unquote the content of `s`: handle all patterns ``%xNN``, | |
| 285 ``%uNNNN`` or ``%UNNNNNNNN``. | |
| 286 | |
| 287 This is the inverse of :func:`.quote`. | |
| 288 | |
| 289 """ | |
| 290 if _QUOTE not in s: | |
| 291 return s | |
| 292 parts = s.split(_QUOTE) | |
| 293 res = [parts[0]] | |
| 294 res_append = res.append | |
| 295 for p in parts[1:]: | |
| 296 try: | |
| 297 qc = p[0] | |
| 298 except IndexError: | |
| 299 raise ValueError("unknown quote syntax string: {}".format(s)) | |
| 300 if qc == _QUOTE_x: | |
| 301 if len(p) < 3: | |
| 302 raise ValueError("quote syntax: length too small") | |
| 303 res_append(uchr(int(p[1:3], 16))) | |
| 304 res_append(p[3:]) | |
| 305 elif qc == _QUOTE_u: | |
| 306 if len(p) < 5: | |
| 307 raise ValueError("quote syntax: length too small") | |
| 308 res_append(uchr(int(p[1:5], 16))) | |
| 309 res_append(p[5:]) | |
| 310 elif qc == _QUOTE_U: | |
| 311 if len(p) < 9: | |
| 312 raise ValueError("quote syntax: length too small") | |
| 313 res_append(uchr(int(p[1:9], 16))) | |
| 314 res_append(p[9:]) | |
| 315 else: | |
| 316 raise ValueError("unknown quote syntax string: {}".format(s)) | |
| 317 return _EMPTY_STR.join(res) | |
| 318 | |
| 319 | |
| 254 class Configuration(CoercingMethodsMixin, _AttributeDict): | 320 class Configuration(CoercingMethodsMixin, _AttributeDict): |
| 255 | 321 |
| 256 """The configuration dictionary with attribute support or | 322 """The configuration dictionary with attribute support or |
| 257 variable substitution. | 323 variable substitution. |
| 258 | 324 |
| 408 No variable interpolation is done and no filters are applied. | 474 No variable interpolation is done and no filters are applied. |
| 409 | 475 |
| 410 Special characters (e.g. ``:`` and ``.``) must be quoted when using | 476 Special characters (e.g. ``:`` and ``.``) must be quoted when using |
| 411 the default namespace. | 477 the default namespace. |
| 412 | 478 |
| 413 See also :meth:`~.quote`. | 479 See also :func:`.quote`. |
| 414 | 480 |
| 415 """ | 481 """ |
| 416 varns, varname = self._split_ns(varname) | 482 varns, varname = self._split_ns(varname) |
| 417 if not varns: | 483 if not varns: |
| 418 if varname: | 484 if varname: |
| 419 varnameparts = [ | 485 varnameparts = [ |
| 420 self.unquote(vp) | 486 unquote(vp) |
| 421 for vp in varname.split(_HIER_SEPARATOR) | 487 for vp in varname.split(_HIER_SEPARATOR) |
| 422 ] | 488 ] |
| 423 else: | 489 else: |
| 424 varnameparts = tuple() | 490 varnameparts = tuple() |
| 425 else: | 491 else: |
| 562 return default | 628 return default |
| 563 | 629 |
| 564 def _split_ns(self, s): | 630 def _split_ns(self, s): |
| 565 ns, sep, rest = s.partition(_NS_SEPARATOR) | 631 ns, sep, rest = s.partition(_NS_SEPARATOR) |
| 566 if sep: | 632 if sep: |
| 567 return (self.unquote(ns), rest) | 633 return (unquote(ns), rest) |
| 568 else: | 634 else: |
| 569 return (None, ns) | 635 return (None, ns) |
| 570 | 636 |
| 571 def _split_filters(self, s): | 637 def _split_filters(self, s): |
| 572 name, sep, filters = s.partition(_FILTER_SEPARATOR) | 638 name, sep, filters = s.partition(_FILTER_SEPARATOR) |
| 735 raise NameError("Filter %r not found" % name) | 801 raise NameError("Filter %r not found" % name) |
| 736 else: | 802 else: |
| 737 value = filterfn(self, value) | 803 value = filterfn(self, value) |
| 738 return value | 804 return value |
| 739 | 805 |
| 740 @staticmethod | |
| 741 def quote(s): | |
| 742 """Replace important special characters in string `s` by replacing | |
| 743 them with ``%xNN`` where `NN` are the two hexadecimal digits of the | |
| 744 characters unicode codepoint value. | |
| 745 | |
| 746 Handled are the important special chars: ``%``, ``.``, ``:``, | |
| 747 ``#``; ``'``, ``"``, ``|``, ``{``, ``}``, ``[`` and ``]``. | |
| 748 | |
| 749 See also the :ref:`quoting` section. | |
| 750 | |
| 751 """ | |
| 752 # Quick check whether all of the chars are in _QUOTE_SAFE | |
| 753 if not s.rstrip(_QUOTE_SAFE): | |
| 754 return s | |
| 755 | |
| 756 # Slow path | |
| 757 re_encode = False | |
| 758 if PY2: | |
| 759 # Use the Unicode translation variant in PY2 | |
| 760 if isinstance(s, str): | |
| 761 s = s.decode("latin1") | |
| 762 re_encode = True | |
| 763 s = s.translate(_QUOTE_MAP) | |
| 764 if re_encode: | |
| 765 return s.encode("latin1") | |
| 766 else: | |
| 767 return s | |
| 768 | |
| 769 @staticmethod | |
| 770 def unquote(s): | |
| 771 """Unquote the content of `s`: handle all patterns ``%xNN``, | |
| 772 ``%uNNNN`` or ``%UNNNNNNNN``. | |
| 773 | |
| 774 This is the inverse of :meth:`~.quote`. | |
| 775 | |
| 776 """ | |
| 777 if _QUOTE not in s: | |
| 778 return s | |
| 779 parts = s.split(_QUOTE) | |
| 780 res = [parts[0]] | |
| 781 res_append = res.append | |
| 782 for p in parts[1:]: | |
| 783 try: | |
| 784 qc = p[0] | |
| 785 except IndexError: | |
| 786 raise ValueError("unknown quote syntax string: {}".format(s)) | |
| 787 if qc == _QUOTE_x: | |
| 788 if len(p) < 3: | |
| 789 raise ValueError("quote syntax: length too small") | |
| 790 res_append(uchr(int(p[1:3], 16))) | |
| 791 res_append(p[3:]) | |
| 792 elif qc == _QUOTE_u: | |
| 793 if len(p) < 5: | |
| 794 raise ValueError("quote syntax: length too small") | |
| 795 res_append(uchr(int(p[1:5], 16))) | |
| 796 res_append(p[5:]) | |
| 797 elif qc == _QUOTE_U: | |
| 798 if len(p) < 9: | |
| 799 raise ValueError("quote syntax: length too small") | |
| 800 res_append(uchr(int(p[1:9], 16))) | |
| 801 res_append(p[9:]) | |
| 802 else: | |
| 803 raise ValueError("unknown quote syntax string: {}".format(s)) | |
| 804 return _EMPTY_STR.join(res) | |
| 805 | |
| 806 def jailed(self, rootpath=None, root=None, bind_root=True): | 806 def jailed(self, rootpath=None, root=None, bind_root=True): |
| 807 """Return a "jailed" configuration of the current configuration. | 807 """Return a "jailed" configuration of the current configuration. |
| 808 | 808 |
| 809 :param rootpath: a sequence of strings that shall emcompass | 809 :param rootpath: a sequence of strings that shall emcompass |
| 810 the chroot-like jail of the returned | 810 the chroot-like jail of the returned |
| 834 if varns: | 834 if varns: |
| 835 raise ValueError( | 835 raise ValueError( |
| 836 "jailed configurations do not support namespaces") | 836 "jailed configurations do not support namespaces") |
| 837 if varname: | 837 if varname: |
| 838 rootpath = [ | 838 rootpath = [ |
| 839 self.unquote(p) for p in root.split( | 839 unquote(p) for p in root.split(_HIER_SEPARATOR) |
| 840 _HIER_SEPARATOR) | |
| 841 ] | 840 ] |
| 842 else: | 841 else: |
| 843 rootpath = tuple() | 842 rootpath = tuple() |
| 844 jc = _JailedConfiguration(*rootpath) | 843 jc = _JailedConfiguration(*rootpath) |
| 845 if bind_root: | 844 if bind_root: |
| 878 super(_JailedConfiguration, self).__init__() | 877 super(_JailedConfiguration, self).__init__() |
| 879 self._path = path | 878 self._path = path |
| 880 if path: | 879 if path: |
| 881 self._pathstr = \ | 880 self._pathstr = \ |
| 882 _HIER_SEPARATOR.join( | 881 _HIER_SEPARATOR.join( |
| 883 [Configuration.quote(p) for p in path]) \ | 882 [quote(p) for p in path]) \ |
| 884 + _HIER_SEPARATOR | 883 + _HIER_SEPARATOR |
| 885 else: | 884 else: |
| 886 self._pathstr = _EMPTY_STR | 885 self._pathstr = _EMPTY_STR |
| 887 | 886 |
| 888 @property | 887 @property |
| 1052 if varns: | 1051 if varns: |
| 1053 raise ValueError( | 1052 raise ValueError( |
| 1054 "sub-jails do not support namespaces") | 1053 "sub-jails do not support namespaces") |
| 1055 if varname: | 1054 if varname: |
| 1056 rootpath = [ | 1055 rootpath = [ |
| 1057 self._base.unquote(p) for p in varname.split( | 1056 unquote(p) for p in varname.split(_HIER_SEPARATOR) |
| 1058 _HIER_SEPARATOR) | |
| 1059 ] | 1057 ] |
| 1060 else: | 1058 else: |
| 1061 rootpath = tuple() | 1059 rootpath = tuple() |
| 1062 if self._path: | 1060 if self._path: |
| 1063 new_rootpath = self._path + tuple(rootpath) | 1061 new_rootpath = self._path + tuple(rootpath) |
