def __assign(self, root, item, args=None, value=None, default=False): """Similar to __dotop, but assigns a value to the given variable instead of simply returning it. The first three parameters are the root item, the item and arguments, as per __dotop, followed by the value to which the variable should be set and an optional 'default' flag. If set true, the variable will only be set if currently false. """ item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self if root is None or item is None: return None elif self.PRIVATE and re.search(self.PRIVATE, item): return None elif isinstance(root, dict) or atroot: if not (default and root.get(item)): root[item] = value return value elif isinstance(root, (list, tuple)) and re.match(r"-?\d+$", str(item)): item = int(item) if not (default and 0 <= item < len(root) and root[item]): root[item] = value return value elif util.is_object(root): if not (default and getattr(root, item)()): args.append(value) return getattr(root, item)(*args) else: raise Error("don't know how to assign to %s.%s" % (root, item)) return None
def __dotop(self, root, item, args=None, lvalue=False): """This is the core 'dot' operation method which evaluates elements of variables against their root. All variables have an implicit root which is the stash object itself. Thus, a non-compound variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is '(stash.)foo.bar'. The first parameter is the current root, initially the stash itself. The second parameter contains the name of the variable element, e.g. 'foo'. The third optional parameter is a list of any parenthesised arguments specified for the variable, which are passed to sub-routines, object methods, etc. The final parameter is an optional flag to indicate if this variable is being evaluated on the left side of an assignment (e.g. foo.bar.baz = 10). When set true, intermediated dictionaries will be created (e.g. bar) if necessary. Returns the result of evaluating the item against the root, having performed any variable "magic". The value returned can then be used as the root of the next __dotop() in a compound sequence. Returns None if the variable is undefined. """ root = util.unscalar(root) item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self result = None # return undef without an error if either side of dot is unviable if root is None or item is None: return None # or if an attempt is made to access a private member, starting _ or . if (self.PRIVATE and isinstance(item, str) and re.search(self.PRIVATE, item)): return None found = True isdict = isinstance(root, dict) if atroot or isdict: # if root is a regular dict or a Template::Stash kinda dict (the # *real* root of everything). We first lookup the named key # in the hash, or create an empty hash in its place if undefined # and the lvalue flag is set. Otherwise, we check the HASH_OPS # pseudo-methods table, calling the code if found, or return None if isdict: # We have to try all these variants because Perl hash keys are # stringified, but Python's aren't. try: value = root[item] except (KeyError, TypeError): try: value = root[str(item)] except (KeyError, TypeError): try: value = root[int(item)] except (KeyError, TypeError, ValueError): value = None else: value = root[item] if value is not None: if callable(value): result = value(*args) else: return value elif lvalue: # we create an intermediate hash if this is an lvalue root[item] = {} return root[item] # ugly hack: only allow import vmeth to be called on root stash else: try: value = self.HASH_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if (value and not atroot) or item == "import": result = value(root, *args) else: try: return _slice(root, item) except TypeError: found = False elif isinstance(root, (list, tuple, util.Sequence)): # if root is a list then we check for a LIST_OPS pseudo-method # or return the numerical index into the list, or None if isinstance(root, util.Sequence): root = root.as_list() try: value = self.LIST_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if value: result = value(root, *args) else: try: value = root[int(item)] except TypeError: sliced = [] try: return _slice(root, item) except TypeError: pass except IndexError: return None else: if callable(value): result = value(*args) else: return value elif util.is_object(root): try: value = getattr(root, item) except (AttributeError, TypeError): # Failed to get object method, so try some fallbacks. try: func = self.HASH_OPS[item] except (KeyError, TypeError): pass else: return func(root.__dict__, *args) else: if callable(value): return value(*args) else: return value elif item in self.SCALAR_OPS and not lvalue: result = self.SCALAR_OPS[item](root, *args) elif item in self.LIST_OPS and not lvalue: result = self.LIST_OPS[item]([root], *args) elif self.__debug: raise Error("don't know how to access [%r].%s" % (root, item)) else: result = [] if not found and self.__debug: raise Error("%s is undefined" % (item,)) elif result is not None: return result elif self.__debug: raise Error("%s is undefined" % (item,)) else: return None
def __dotop(self, root, item, args=None, lvalue=False): """This is the core 'dot' operation method which evaluates elements of variables against their root. All variables have an implicit root which is the stash object itself. Thus, a non-compound variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is '(stash.)foo.bar'. The first parameter is the current root, initially the stash itself. The second parameter contains the name of the variable element, e.g. 'foo'. The third optional parameter is a list of any parenthesised arguments specified for the variable, which are passed to sub-routines, object methods, etc. The final parameter is an optional flag to indicate if this variable is being evaluated on the left side of an assignment (e.g. foo.bar.baz = 10). When set true, intermediated dictionaries will be created (e.g. bar) if necessary. Returns the result of evaluating the item against the root, having performed any variable "magic". The value returned can then be used as the root of the next __dotop() in a compound sequence. Returns None if the variable is undefined. """ root = util.unscalar(root) item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self result = None # return undef without an error if either side of dot is unviable if root is None or item is None: return None # or if an attempt is made to access a private member, starting _ or . if (self.PRIVATE and isinstance(item, str) and re.search(self.PRIVATE, item)): return None found = True isdict = isinstance(root, dict) if atroot or isdict: # if root is a regular dict or a Template::Stash kinda dict (the # *real* root of everything). We first lookup the named key # in the hash, or create an empty hash in its place if undefined # and the lvalue flag is set. Otherwise, we check the HASH_OPS # pseudo-methods table, calling the code if found, or return None if isdict: # We have to try all these variants because Perl hash keys are # stringified, but Python's aren't. try: value = root[item] except (KeyError, TypeError): try: value = root[str(item)] except (KeyError, TypeError): try: value = root[int(item)] except (KeyError, TypeError, ValueError): value = None else: value = root[item] if value is not None: if callable(value): result = value(*args) else: return value elif lvalue: # we create an intermediate hash if this is an lvalue root[item] = {} return root[item] # ugly hack: only allow import vmeth to be called on root stash else: try: value = self.HASH_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if (value and not atroot) or item == "import": result = value(root, *args) else: try: return _slice(root, item) except TypeError: found = False elif isinstance(root, (list, tuple, util.Sequence)): # if root is a list then we check for a LIST_OPS pseudo-method # or return the numerical index into the list, or None if isinstance(root, util.Sequence): root = root.as_list() try: value = self.LIST_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if value: result = value(root, *args) else: try: value = root[int(item)] except TypeError: sliced = [] try: return _slice(root, item) except TypeError: pass except IndexError: return None else: if callable(value): result = value(*args) else: return value elif util.is_object(root): try: value = getattr(root, item) except (AttributeError, TypeError): # Failed to get object method, so try some fallbacks. try: func = self.HASH_OPS[item] except (KeyError, TypeError): pass else: return func(root.__dict__, *args) else: if callable(value): return value(*args) else: return value elif item in self.SCALAR_OPS and not lvalue: result = self.SCALAR_OPS[item](root, *args) elif item in self.LIST_OPS and not lvalue: result = self.LIST_OPS[item]([root], *args) elif self.__debug: raise Error("don't know how to access [%r].%s" % (root, item)) else: result = [] if not found and self.__debug: raise Error("%s is undefined" % (item, )) elif result is not None: return result elif self.__debug: raise Error("%s is undefined" % (item, )) else: return None