Mercurial > hgrepos > Python > libs > ConfigMix
view configmix/config.py @ 472:89bbb66aff02 v0.19.2
+++++ v0.19.2
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 16 Dec 2021 09:30:43 +0100 |
| parents | 02b210a2b022 |
| children | fa5b800055e4 |
line wrap: on
line source
# -*- coding: utf-8 -*- # :- # :Copyright: (c) 2015-2021, Franz Glasner. All rights reserved. # :License: BSD-3-Clause. See LICENSE.txt for details. # :- """The unified configuration dictionary with attribute support or variable substitution. """ from __future__ import division, absolute_import, print_function, generators __all__ = ["Configuration"] import warnings try: from collections import OrderedDict as ConfigurationBase except ImportError: try: from ordereddict import OrderedDict as ConfigurationBase except ImportError: ConfigurationBase = dict try: from urllib.parse import urlsplit except ImportError: from urlparse import urlsplit from .variables import lookup_varns, lookup_filter from .compat import u, uchr, n, PY2 from .constants import REF_NAMESPACE, NONE_FILTER, EMPTY_FILTER _MARKER = object() class _AttributeDict(ConfigurationBase): def __getattr__(self, name): try: v = self[name] except KeyError: raise AttributeError("%s has no attribute %r" % (type(self), name)) else: # Wrap a dict into another dict with attribute access support if isinstance(v, dict): return _AttributeDict(v) else: return v class CoercingMethodsMixin(object): """Mixin to provide some common implementations for retrieval methods that convert return values to a fixed type (int, bool, float). Both :class:`~.Configuration` and :class:`~._JailedConfiguration` use this mixin. """ def getintvarl_s(self, *path, **kwds): """Get a (possibly substituted) variable and coerce text to a number. """ s = self.getvarl_s(*path, **kwds) if isinstance(s, Configuration._TEXTTYPE): return int(s, 0) else: return s def getfirstintvarl_s(self, *paths, **kwds): """Get a (possibly substituted) variable and coerce text to a number. """ s = self.getfirstvarl_s(*paths, **kwds) if isinstance(s, Configuration._TEXTTYPE): return int(s, 0) else: return s def getintvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and coerce text to a number. """ s = self.getvar_s(varname, default=default) if isinstance(s, Configuration._TEXTTYPE): return int(s, 0) else: return s def getfirstintvar_s(self, *varnames, **kwds): """A variant of :meth:`~.getintvar_s` that returns the first found variable in the list of given variables in `varnames`. """ s = self.getfirstvar_s(*varnames, **kwds) if isinstance(s, Configuration._TEXTTYPE): return int(s, 0) else: return s def getboolvarl_s(self, *path, **kwds): """Get a (possibly substituted) variable and convert text to a boolean """ s = self.getvarl_s(*path, **kwds) if isinstance(s, Configuration._TEXTTYPE): sl = s.strip().lower() if sl not in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % (s, )) return self._BOOL_CVT[sl] else: return s def getfirstboolvarl_s(self, *paths, **kwds): """Get a (possibly substituted) variable and convert text to a boolean """ s = self.getfirstvarl_s(*paths, **kwds) if isinstance(s, Configuration._TEXTTYPE): sl = s.strip().lower() if sl not in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % (s, )) return self._BOOL_CVT[sl] else: return s def getboolvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and convert text to a boolean """ s = self.getvar_s(varname, default=default) if isinstance(s, Configuration._TEXTTYPE): sl = s.strip().lower() if sl not in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % (s, )) return self._BOOL_CVT[sl] else: return s def getfirstboolvar_s(self, *varnames, **kwds): """A variant of :meth:`~.getboolvar_s` that returns the first found variable in the list of given variables in `varnames`. """ s = self.getfirstvar_s(*varnames, **kwds) if isinstance(s, Configuration._TEXTTYPE): sl = s.strip().lower() if sl not in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % (s, )) return self._BOOL_CVT[sl] else: return s # Conversion of booleans _BOOL_CVT = { u('1'): True, u('yes'): True, u('true'): True, u('on'): True, u('0'): False, u('no'): False, u('false'): False, u('off'): False } def getfloatvarl_s(self, *path, **kwds): """Get a (possibly substituted) variable and convert text to a float """ s = self.getvarl_s(*path, **kwds) if isinstance(s, Configuration._TEXTTYPE): return float(s) else: return s def getfirstfloatvarl_s(self, *path, **kwds): """Get a (possibly substituted) variable and convert text to a float """ s = self.getfirstvarl_s(*path, **kwds) if isinstance(s, Configuration._TEXTTYPE): return float(s) else: return s def getfloatvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and convert text to a float """ s = self.getvar_s(varname, default) if isinstance(s, Configuration._TEXTTYPE): return float(s) else: return s def getfirstfloatvar_s(self, varname, default=_MARKER): """Get a (possibly substituted) variable and convert text to a float """ s = self.getfirstvar_s(varname, default) if isinstance(s, Configuration._TEXTTYPE): return float(s) else: return s class Configuration(CoercingMethodsMixin, _AttributeDict): """The configuration dictionary with attribute support or variable substitution. .. note:: When retrieving by attribute names variables will *not* substituted. """ # Speed _TEXTTYPE = type(u("")) _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'%') _COMMENT = u(b'#') is_jail = False """Flag to show that this is not a jail for another configuration""" def __getitem__(self, key): """Mapping and list interface that forwards to :meth:`~.getvarl_s` """ if isinstance(key, (tuple, list)): return self.getvarl_s(*key) else: return self.getvarl_s(key) def get(self, key, default=None): """Mapping interface that forwards to :meth:`~.getvarl_s` """ if isinstance(key, (tuple, list)): return self.getvarl_s(*key, default=default) else: return self.getvarl_s(key, default=default) def __contains__(self, key): """Containment test""" if isinstance(key, (tuple, list)): # No namespace and quoting support here try: self._lookupvar(*key) except KeyError: return False else: return True else: return super(Configuration, self).__contains__(key) def getitem_ns(self, key): """Just forward to the *original* :meth:`dict.__getitem__`. No variable interpolation and key path access. """ return super(Configuration, self).__getitem__(key) def getvarl(self, *path, **kwds): """Get a variable where the hierarchy is given in `path` as sequence and the namespace is given in the `namespace` keyword argument. No variable interpolation is done and no filters are applied. Quoting of `path` and `namespace` is *not* needed and wrong. """ default = kwds.pop("default", _MARKER) namespace = kwds.pop("namespace", None) try: if not namespace: lookupfn = self._lookupvar else: if namespace == REF_NAMESPACE: lookupfn = self._lookupref else: lookupfn = lookup_varns(namespace) varvalue = lookupfn(*path) except KeyError: if default is _MARKER: raise KeyError("Variable %r not found" % (path,)) else: return default else: return varvalue def getkeysl(self, *path, **kwds): """Yield the keys of a variable value. :rtype: A generator :raise KeyError: .. note:: Dictionary keys are not subject to interpolation. """ if "default" in kwds: raise TypeError("got unexpected keyword argument: default") for k in self.getvarl(*path, **kwds).keys(): yield k def getfirstvarl(self, *paths, **kwds): """A variant of :meth:`~.getvarl` that returns the first found variable in the `paths` list. Every item in `paths` is either a `tuple` or `list` or a `dict`. If the path item is a `dict` then it must have two keys "namespace" and "path". If the path item is a `list` or `tuple` then the namespace is assumed to be `None`. Note that a caller that wants to use variables from a non-default namespace must use a sequence of dicts. No variable interpolation is done and no filters are applied. Quoting of anything in `paths` is *not* needed and wrong. """ default = kwds.pop("default", _MARKER) for path in paths: if isinstance(path, (list, tuple)): try: varvalue = self.getvarl(*path) except KeyError: pass else: return varvalue elif isinstance(path, dict): try: namespace = path["namespace"] p = path["path"] except KeyError: raise TypeError("a paths dict item must have a `path'" " and a `namespace' key") else: try: varvalue = self.getvarl(*p, namespace=namespace) except KeyError: pass else: return varvalue else: raise TypeError("a paths item must be a dict, list or tuple") if default is _MARKER: raise KeyError( "none of the given variables found: %r" % (paths,)) else: return default def getvar(self, varname, default=_MARKER): """Get a variable of the form ``[ns:][[key1.]key2.]name`` - including variables from other namespaces. No variable interpolation is done and no filters are applied. Special characters (e.g. ``:`` and ``.``) must be quoted when using the default namespace. See also :meth:`~.quote`. """ varns, varname = self._split_ns(varname) if not varns: if varname: varnameparts = [ self.unquote(vp) for vp in varname.split(self._HIER_SEPARATOR) ] else: varnameparts = tuple() else: varnameparts = (varname,) return self.getvarl(*varnameparts, namespace=varns, default=default) def getkeys(self, varname): """Yield all the keys of a variable value. :rtype: A generator :raise KeyError: .. note:: Dictionary keys are not subject to interpolation. """ for k in self.getvar(varname).keys(): yield k def getfirstvar(self, *varnames, **kwds): """A variant of :meth:`~.getvar` that returns the first found variable in the list of given variables in `varnames`. """ default = kwds.pop("default", _MARKER) for varname in varnames: try: varvalue = self.getvar(varname) except KeyError: pass else: return varvalue if default is _MARKER: raise KeyError( "none of the given variables found: %r" % (varnames,)) else: return default def getvarl_s(self, *path, **kwds): """Get a variable - including variables from other namespaces. `path` and `namespace` are interpreted as in :meth:`.getvarl`. But variables will be interpolated recursively within the variable values and filters are applied. For more details see chapter :ref:`variable-interpolation`. """ default = kwds.pop("default", _MARKER) namespace = kwds.pop("namespace", None) try: obj = self.getvarl(*path, namespace=namespace) return self.substitute_variables_in_obj(obj) except KeyError: if default is _MARKER: raise else: return default def getfirstvarl_s(self, *paths, **kwds): """A variant of :meth:`~.getfirstvarl` that does variable interpolation. `paths` and `kwds` are interpreted as in :meth:`.getfirstvarl`. But variables will be interpolated recursively within the variable values and filters are applied. For more details see chapter :ref:`variable-interpolation`. """ default = kwds.pop("default", _MARKER) for path in paths: if isinstance(path, (list, tuple)): try: obj = self.getvarl(*path) except KeyError: pass else: return self.substitute_variables_in_obj(obj) elif isinstance(path, dict): try: namespace = path["namespace"] p = path["path"] except KeyError: raise TypeError("a paths dict item must have a `path'" " and a `namespace' key") else: try: obj = self.getvarl(*p, namespace=namespace) except KeyError: pass else: return self.substitute_variables_in_obj(obj) else: raise TypeError("a paths item must be a dict, list or tuple") if default is _MARKER: raise KeyError( "none of the given variables found: %r" % (paths,)) else: return default def getvar_s(self, varname, default=_MARKER): """Get a variable - including variables from other namespaces. `varname` is interpreted as in :meth:`.getvar`. But variables will be interpolated recursively within the variable values and filters are applied. For more details see chapter :ref:`variable-interpolation`. """ try: obj = self.getvar(varname) return self.substitute_variables_in_obj(obj) except KeyError: if default is _MARKER: raise else: return default def getfirstvar_s(self, *varnames, **kwds): """A variant of :meth:`~.getvar_s` that returns the first found variable in the list of given variables in `varnames`. """ default = kwds.pop("default", _MARKER) for varname in varnames: try: obj = self.getvar(varname) except KeyError: pass else: return self.substitute_variables_in_obj(obj) if default is _MARKER: raise KeyError( "none of the given variables found: %r" % (varnames,)) else: return default def _split_ns(self, s): nameparts = s.split(self._NS_SEPARATOR, 1) if len(nameparts) == 1: return (None, s, ) else: return (self.unquote(nameparts[0]), nameparts[1], ) def _split_filters(self, s): nameparts = s.split(self._FILTER_SEPARATOR) if len(nameparts) == 1: return (s, [], ) else: return (nameparts[0].rstrip(), nameparts[1:], ) def _lookupvar(self, *path, **kwds): """Lookup a variable within a hierarchy. If no default is given an unexisting `path` raises a `KeyError` else `default` is returned. """ default = kwds.pop("default", _MARKER) if not path: return self try: v = self.expand_if_reference( super(Configuration, self).__getitem__(path[0])) for p in path[1:]: v = self.expand_if_reference(v[p]) except TypeError: raise KeyError( "Configuration variable %r not found" "(missing intermediate keys?)" % (path,)) except KeyError: if default is _MARKER: raise KeyError( "Configuration variable %r not found" % (path,)) else: return default return v def _lookupref(self, key, default=_MARKER): """ `key` must be a configuration reference URI without any (namespace) prefixes and suffixes """ return self.expand_ref_uri(key, default=default) def expand_if_reference(self, v, default=_MARKER): """Check whether `v` is a configuration reference and -- if true -- then expand it. `v` must match the pattern ``{{ref:<REFERENCE>}}`` All non-matching texttypes and all non-texttypes are returned unchanged. """ if not isinstance(v, self._TEXTTYPE): return v if not v.startswith(self._STARTTOK_REF) \ or not v.endswith(self._ENDTOK_REF): return v return self.expand_ref_uri( v[len(self._STARTTOK_REF):-len(self._ENDTOK_REF)], default=default) def expand_ref_uri(self, uri, default=_MARKER): pu = urlsplit(uri) if pu.scheme or pu.netloc or pu.path or pu.query: raise ValueError("only fragment-only URIs are supported") if not pu.fragment: return self if pu.fragment.startswith(self._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`.""" if isinstance(obj, self._TEXTTYPE): # a string - really replace the value return self.expand_variable(obj) elif isinstance(obj, list): return [self.substitute_variables_in_obj(i) for i in obj] elif isinstance(obj, tuple): tmp = [self.substitute_variables_in_obj(i) for i in obj] return type(obj)(tmp) elif isinstance(obj, dict): newdict = type(obj)() for k in obj: newdict[k] = self.substitute_variables_in_obj(obj[k]) return newdict elif isinstance(obj, set): newset = type(obj)() for i in obj: newset.add(self.substitute_variables_in_obj(i)) else: return obj def expand_variable(self, s): """Expand variables in the single string `s`""" start = s.find(self._STARTTOK, 0) while start != -1: end = s.find(self._ENDTOK, start) if end < 0: return s varname, filters = self._split_filters(s[start+2:end]) try: if NONE_FILTER in filters: varvalue = self._apply_filters( filters, self.getvar_s(varname, default=None)) elif EMPTY_FILTER in filters: varvalue = self._apply_filters( filters, self.getvar_s(varname, default=u(""))) else: varvalue = self._apply_filters( filters, self.getvar_s(varname)) except KeyError: warnings.warn("Cannot expand variable %r in string " "%r" % (varname, s, ), UserWarning, stacklevel=1) raise # # Dont apply and type conversions to the variable value if # the whole `s` is just one expansion # if (start == 0) and (end + 2 == len(s)): return varvalue if varvalue is None: varvalue = u("") replaced = s[:start] + u(str(varvalue)) s = replaced + s[end+2:] # don't re-evaluate because `self.getvar_s()` expands already start = s.find(self._STARTTOK, len(replaced)) return s def _apply_filters(self, filters, value): for name in filters: try: filterfn = lookup_filter(name) except KeyError: # # Convert to NameError because we find a missing filters # a very serious error. # raise NameError("Filter %r not found" % name) else: value = filterfn(self, value) return value @classmethod def quote(klass, s): """Replace important special characters in string `s` by replacing them with ``%xNN`` where `NN` are the two hexadecimal digits of the characters unicode codepoint value. Handled are the important special chars: ``%``, ``.``, ``:``, ``#``; ``'``, ``"``, ``|``, ``{``, ``}``, ``[`` and ``]``. See also the :ref:`quoting` section. """ qc = klass._QUOTE s = s.replace(qc, qc + "x25") s = s.replace(klass._DOT, qc + "x2e") s = s.replace(klass._NS_SEPARATOR, qc + "x3a") s = s.replace(klass._COMMENT, qc + "x23") s = s.replace(klass._FILTER_SEPARATOR, qc + "x7c") s = s.replace('"', qc + "x22") s = s.replace("'", qc + "x27") s = s.replace('{', qc + "x7b") s = s.replace('}', qc + "x7d") s = s.replace('[', qc + "x5b") return s.replace(']', qc + "x5d") @classmethod def unquote(klass, s): """Unquote the content of `s`: handle all patterns ``%xNN``, ``%uNNNN`` or ``%UNNNNNNNN``. This is the inverse of :meth:`~.quote`. """ if klass._QUOTE not in s: return s res = [] parts = s.split(klass._QUOTE) res.append(parts[0]) for p in parts[1:]: if p.startswith(u(b'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 p.startswith(u(b'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 p.startswith(u(b'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 ''.join(res) def jailed(self, rootpath=None, root=None, bind_root=True): """Return a "jailed" configuration of the current configuration. :param rootpath: a sequence of strings that shall emcompass the chroot-like jail of the returned configuration :type rootpath: list or tuple :param str root: a string path expression that shall encompass the chroot-like jail of the returned configuration :param bool bind_root: if you do a :meth:`~.rebind` just after creation of a jailed config you can set `bind_root` to `False`; otherwise use the default :return: a jailed (aka restricted) configuration :rtype: _JailedConfiguration Exactly one of `rootpath` or `root` must be given. """ if rootpath is not None and root is not None: raise ValueError("only one of `rootpath' or `root' can be given") if rootpath is None and root is None: raise ValueError("one of `rootpath' or `root' must be given") if rootpath is not None and not isinstance(rootpath, (list, tuple)): raise TypeError("`rootpath' must be a list or a tuple") if root is not None: # convert to path varns, varname = self._split_ns(root) if varns: raise ValueError( "jailed configurations do not support namespaces") if varname: rootpath = [ self.unquote(p) for p in root.split( self._HIER_SEPARATOR) ] else: rootpath = tuple() jc = _JailedConfiguration(*rootpath) if bind_root: jc.rebind(self) return jc class _JailedConfiguration(CoercingMethodsMixin): """A jailed and restricted variant of :class:`Configuration`. Restriction is two-fold: - The access to configuration variables in `config` is restricted to the configuration sub-tree that is configured in `path`. - Not all access-methods of :class:`Configuration` are implemented yet. .. seealso:: :ref:`jailed-configuration` .. note:: There is no namespace support. .. note:: Do not call the constructor directly. Instantiate a jailed configuration from the parent configuration's :meth:`~.Configuration.jailed` factory method. """ __slots__ = ("_base", "_path", "_pathstr") is_jail = True """Flag to show that this is a jail for another configuration""" def __init__(self, *path): super(_JailedConfiguration, self).__init__() self._path = path if path: self._pathstr = \ Configuration._HIER_SEPARATOR.join( [Configuration.quote(p) for p in path]) \ + Configuration._HIER_SEPARATOR else: self._pathstr = '' @property def base(self): """Ask for the base (aka parent) configuration". This configuration is always unjailed. """ return self._base def rebind(self, new_base): """Bind the jail to a new unjailed configuration `new_base`. The new configuration base also must have an existing path to the root. :param Configuration new_base: the new base """ if new_base.is_jail: raise TypeError("can only bind to an unjailed configuration") self._base = new_base # # Early error out if the chroot does not exist but allow # degenerated case if `self._path` is empty. # if self._path and self._path not in new_base: raise KeyError( "base key path %r not available in the new base" % (self._path, )) def __getattr__(self, name): """Attribute-style access. Result values are interpolated (i.e. forwarded to :meth:`~.getvarl_s`) """ try: v = self._base.getvarl_s(*(self._path + (name, ))) except KeyError: raise AttributeError("%s has no attribute %r" % (type(self), name)) else: # Wrap a dict into another dict with attribute access support if isinstance(v, dict): return _AttributeDict(v) else: return v def __getitem__(self, key): """Mapping and list interface that forwards to :meth:`~.getvarl_s` """ if isinstance(key, tuple): return self._base.getvarl_s(*(self._path + key)) elif isinstance(key, list): return self._base.getvarl_s(*(self._path + tuple(key))) else: return self._base.getvarl_s(*self._path)[key] def get(self, key, default=None): if isinstance(key, tuple): return self._base.get(self._path + key, default=default) elif isinstance(key, list): return self._base.get(self._path + tuple(key), default=default) else: return self._base.get(self._path + (key, ), default=default) def __contains__(self, key): """Containment support for containers""" if isinstance(key, tuple): return (self._path + key) in self._base elif isinstance(key, list): return (self._path + tuple(key)) in self._base else: return (self._path + (key, )) in self._base def getvarl(self, *path, **kwds): return self._base.getvarl(*(self._path + path), **kwds) def getkeysl(self, *path, **kwds): for k in self._base.getkeysl(*(self._path + path), **kwds): yield k def getfirstvarl(self, *paths, **kwds): real_paths = [] for path in paths: if isinstance(path, (list, tuple)): real_paths.append(self._path + path) elif isinstance(path, dict): raise TypeError( "a `dict' is not supported in a jailed configuration") else: raise TypeError("a paths item must be a list or tuple") return self._base.getfirstvarl(*real_paths, **kwds) def getvarl_s(self, *path, **kwds): return self._base.getvarl_s(*(self._path + path), **kwds) def getfirstvarl_s(self, *paths, **kwds): real_paths = [] for path in paths: if isinstance(path, (list, tuple)): real_paths.append(self._path + path) elif isinstance(path, dict): raise TypeError( "a `dict' is not supported in a jailed configuration") else: raise TypeError("a paths item must be a list or tuple") return self._base.getfirstvarl_s(*real_paths, **kwds) def getvar(self, varname, **kwds): return self._base.getvar(self._pathstr + varname, **kwds) def getkeys(self, varname): for k in self._base.getkeys(self._pathstr + varname): yield k def getfirstvar(self, *varnames, **kwds): real_varnames = [self._pathstr + vn for vn in varnames] return self._base.getfirstvar(*real_varnames, **kwds) def getvar_s(self, varname, **kwds): return self._base.getvar_s(self._pathstr + varname, **kwds) def getfirstvar_s(self, *varnames, **kwds): real_varnames = [self._pathstr + vn for vn in varnames] return self._base.getfirstvar_s(*real_varnames, **kwds) def __iter__(self): """Iteration support for containers""" return iter(self._base.getvarl_s(*self._path)) def __len__(self): """Length support for containers""" return len(self._base.getvarl(*self._path)) if PY2: def __nonzero__(self): """Map- and list-style evaluation in boolean context""" return bool(self._base.getvarl_s(*self._path)) else: def __bool__(self): """Map- and list-style evaluation in boolean context""" return bool(self._base.getvarl_s(*self._path)) def jailed(self, rootpath=None, root=None, bind_root=True): """Return a "jailed" configuration that effectively is a subjail of the current jail For a more complete description see :meth:`.Configuration.jailed`. """ if rootpath is not None and root is not None: raise ValueError("only one of `rootpath' or `root' can be given") if rootpath is None and root is None: raise ValueError("one of `rootpath' or `root' must be given") if rootpath is not None and not isinstance(rootpath, (list, tuple)): raise TypeError("`rootpath' must be a list or a tuple") if root is not None: # convert to path varns, varname = self._base._split_ns(root) if varns: raise ValueError( "sub-jails do not support namespaces") if varname: rootpath = [ self._base.unquote(p) for p in varname.split( self._base._HIER_SEPARATOR) ] else: rootpath = tuple() if self._path: new_rootpath = self._path + tuple(rootpath) else: new_rootpath = rootpath sjc = _JailedConfiguration(*new_rootpath) if bind_root: sjc.rebind(self._base) return sjc def __repr__(self): r = "_JailedConfiguration(rootpath=%s)" % n(repr(self._path)) return r
