def _parse_contents(content, parser): is_attr = False is_node = False res = None for c in content: if ':' in c: outs = c.split(':') if len(outs) != 2: parser.error('incorrect format for content "%s"' % c) if is_attr: parser.error('cannot mix node-type contents ' 'with attribute-type contents') is_node = True if res is None: res = OrderedDict() outs = map(lambda x: x.strip(), outs) key, value = outs if key not in res: res[key] = value else: parser.error('duplicate name "%s" for node-type content' % key) else: if is_node: parser.error('cannot mix node-type contents ' 'with attribute-type contents') is_attr = True if res is None: res = [] res.append(c) if isinstance(res, list): if len(res) == 1: res = res[0] return res
def elems(self): """ Get elements of this node. """ if self._elems is None: this_is_dir = False this_is_file = False self._elems = OrderedDict() if not isinstance(self.content, dict): raise NodeError('invalid content format') for name in self.content: content = self.content[name] elem, this_is_dir, this_is_file = self._make_elem( name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) self._vals = self._elems.values() return self._elems
def _parse_contents(content, parser): is_attr = False is_node = False res = None for c in content: if ':' in c: outs = c.split(':') if len(outs) != 2: parser.error('incorrect format for content "%s"' % c) if is_attr: parser.error( 'cannot mix node-type contents ' 'with attribute-type contents') is_node = True if res is None: res = OrderedDict() outs = map(lambda x: x.strip(), outs) key, value = outs if key not in res: res[key] = value else: parser.error( 'duplicate name "%s" for node-type content' % key) else: if is_node: parser.error( 'cannot mix node-type contents ' 'with attribute-type contents') is_attr = True if res is None: res = [] res.append(c) if isinstance(res, list): if len(res) == 1: res = res[0] return res
def do_add_path(bucket, args, parser): root = bucket.root indexer = Indexer( elem=root, parser=parser, names=args.names, unordered=True, cache_lines=False, no_output=True, ) elem = indexer.get() path = eval_path(args.path) name = args.name or os.path.basename(path) content = OrderedDict([('path', path)]) elem.add(name, content) print('file "%s" added to %s "%s"' % (name, elem.type.lower(), elem))
def get_elems(self, elem_type=None): """ The generic method to get elements. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". """ elems = self.elems if elem_type is not None: items = self.elems.items() if elem_type not in ('file', 'directory', 'node', 'attribute'): raise NodeError('unknown element type') elem_class = getattr(sys.modules[__name__], elem_type.title()) items = filter(lambda x: isinstance(x[1], elem_class), items) elems = OrderedDict(items) return elems
def elems(self): """ Get elements of this node. """ if self._elems is None: this_is_dir = False this_is_file = False self._elems = OrderedDict() if not isinstance(self.content, dict): raise NodeError('invalid content format') for name in self.content: content = self.content[name] elem, this_is_dir, this_is_file = self._make_elem(name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) self._vals = self._elems.values() return self._elems
class Node(Element): """ The Node class. """ def __init__(self, name, bucket, parent, content, *args, **kwargs): super(Node, self).__init__(name, bucket, parent, content, *args, **kwargs) self._elems = None self._vals = None self._i = 0 self._len = None if not self.bucket.settings.lazy_bucket: # self.elems is called here so that the next-level elements are # loaded and the classes of the current elements are updated self._len = len(self.elems) self._action = NodeAction(self) @property def is_root(self): return self.parent is None @property def elems(self): """ Get elements of this node. """ if self._elems is None: this_is_dir = False this_is_file = False self._elems = OrderedDict() if not isinstance(self.content, dict): raise NodeError('invalid content format') for name in self.content: content = self.content[name] elem, this_is_dir, this_is_file = self._make_elem( name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) self._vals = self._elems.values() return self._elems def _update_class(self, this_is_dir, this_is_file): """Update node's class to Directory or File.""" if this_is_dir is True: self.__class__ = Directory elif this_is_file is True: self.__class__ = File def _make_elem(self, name, content): """ Make a proper element based on the content. :return: new child element, whether self should be a file, and whether self should be a directory. """ this_is_dir = self.is_dir this_is_file = self.is_file is_dict = isinstance(content, dict) if not is_dict: this_is_file = True elem = (Attribute(name=name, bucket=self.bucket, parent=self, content=content)) else: this_is_dir = True elem = Node(name=name, bucket=self.bucket, parent=self, content=content) return elem, this_is_dir, this_is_file @property def svals(self): """Shortcut to get sorted element values.""" self.get_sorted_vals() @property def vals(self): """Shortcut to get unordered element values.""" if self._elems is None: self._vals = self.elems.values() return self._vals @property def attrs(self): return self.get_elems(elem_type='attribute') @property def attr_vals(self): return self.get_vals(unordered=True, elem_type='attribute') @property def attr_svals(self): return self.get_vals(elem_type='attribute') @property def nodes(self): return self.get_elems(elem_type='node') @property def node_vals(self): return self.get_vals(unordered=True, elem_type='node') @property def node_svals(self): return self.get_vals(elem_type='node') def __iter__(self): return self def __next__(self): if self._i < self.len: i = self._i self._i += 1 return self.vals[i] else: raise StopIteration next = __next__ @property def len(self): if self._len is None: self._len = len(self.elems) return self._len def get_elem_by_name(self, name): try: return self.elems[name] except KeyError: raise ElemError('element "%s" does not exist in this %s' % (name, self.type.lower())) def get_elem_by_num(self, num, sort_by, unordered, elem_type): vals = self.get_vals(sort_by, unordered, elem_type) try: return vals[num - 1] if vals else None except IndexError: raise ElemError('element index out of range (1-%d)' % len(vals)) def get_elems(self, elem_type=None): """ The generic method to get elements. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". """ elems = self.elems if elem_type is not None: items = self.elems.items() if elem_type not in ('file', 'directory', 'node', 'attribute'): raise NodeError('unknown element type') elem_class = getattr(sys.modules[__name__], elem_type.title()) items = filter(lambda x: isinstance(x[1], elem_class), items) elems = OrderedDict(items) return elems def get_vals(self, sort_by=None, unordered=False, elem_type=None): """ The generic method to get element values. :param sort_by: the name of the sorting key. If it is None and `unordered` is False, then the sorting key is the element name. If it is a name, then the content of the attribute with this name is used as the key. :param unordered: whether to present elements unordered. If it is True, the original order in the document is used, and `sort_by` has no effect. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". """ vals = self.vals if elem_type is not None: if elem_type not in ('file', 'directory', 'node', 'attribute'): raise NodeError('unknown element type') elem_class = getattr(sys.modules[__name__], elem_type.title()) vals = filter(lambda elem: isinstance(elem, elem_class), vals) if unordered: return vals if sort_by is None: return sorted(vals, key=attrgetter('name')) def sort_key(elem): if getattr(elem, 'attrs'): return elem.attrs.get(sort_by) return None return sorted(self.vals, key=sort_key) def add(self, name, content): """ Create an element with name and content and add it to this node. """ if name not in self.elems: self.content[name] = content elem, this_is_dir, this_is_file = self._make_elem(name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) # update vals self._vals = self.elems.values() else: raise NodeError('element "%s" already exists in this %s' % (name, self.type.lower())) def delete(self, name): """Delete an element with name in this node.""" if name in self.elems: this_is_dir, this_is_file = self._delete_elem(name) self._update_class(this_is_dir, this_is_file) # update vals self._vals = self.elems.values() else: raise NodeError('element "%s" does not exist in this %s' % (name, self.type.lower())) def _delete_elem(self, name): this_is_dir = self.is_dir this_is_file = self.is_file del self._elems[name] del self.content[name] if not self.content: # add a placeholder to keep this node as a file self.add(PLACEHOLDER, True) # check remaining elements for elem in self.elems: if self.elems[elem].is_attr: this_is_file = True if self.elems[elem].is_node: this_is_dir = True return this_is_dir, this_is_file
class Node(Element): """ The Node class. """ def __init__(self, name, bucket, parent, content, *args, **kwargs): super(Node, self).__init__(name, bucket, parent, content, *args, **kwargs) self._elems = None self._vals = None self._i = 0 self._len = None if not self.bucket.settings.lazy_bucket: # self.elems is called here so that the next-level elements are # loaded and the classes of the current elements are updated self._len = len(self.elems) self._action = NodeAction(self) @property def is_root(self): return self.parent is None @property def elems(self): """ Get elements of this node. """ if self._elems is None: this_is_dir = False this_is_file = False self._elems = OrderedDict() if not isinstance(self.content, dict): raise NodeError('invalid content format') for name in self.content: content = self.content[name] elem, this_is_dir, this_is_file = self._make_elem(name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) self._vals = self._elems.values() return self._elems def _update_class(self, this_is_dir, this_is_file): """Update node's class to Directory or File.""" if this_is_dir is True: self.__class__ = Directory elif this_is_file is True: self.__class__ = File def _make_elem(self, name, content): """ Make a proper element based on the content. :return: new child element, whether self should be a file, and whether self should be a directory. """ this_is_dir = self.is_dir this_is_file = self.is_file is_dict = isinstance(content, dict) if not is_dict: this_is_file = True elem = ( Attribute(name=name, bucket=self.bucket, parent=self, content=content) ) else: this_is_dir = True elem = Node(name=name, bucket=self.bucket, parent=self, content=content) return elem, this_is_dir, this_is_file @property def svals(self): """Shortcut to get sorted element values.""" self.get_sorted_vals() @property def vals(self): """Shortcut to get unordered element values.""" if self._elems is None: self._vals = self.elems.values() return self._vals @property def attrs(self): return self.get_elems(elem_type='attribute') @property def attr_vals(self): return self.get_vals(unordered=True, elem_type='attribute') @property def attr_svals(self): return self.get_vals(elem_type='attribute') @property def nodes(self): return self.get_elems(elem_type='node') @property def node_vals(self): return self.get_vals(unordered=True, elem_type='node') @property def node_svals(self): return self.get_vals(elem_type='node') def __iter__(self): return self def __next__(self): if self._i < self.len: i = self._i self._i += 1 return self.vals[i] else: raise StopIteration next = __next__ def ls(self, name_or_num=None, show_path=False, sort_by=None, unordered=False, elem_type=None, **kwargs): """ List and print elements of the Node object. If `name_or_num` is not None, then return the element that matches. :param name_or_num: element name or number. The name has higher precedence than number. :param show_path: whether to show path to the element. :param sort_by: the name of the sorting key. If it is None, then the sorting key is the element name. If it is a name, then the content of the attribute with this name is used as the key. :param unordered: whether to present elements unordered. If it is True, the original order in the document is used. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". :return: the element that matches `name_or_num` (if it is not None) """ if show_path and not self.is_root: self._print_path() if name_or_num is None: self._ls_all(show_path, sort_by, unordered, elem_type) else: elem = None try: name_or_num = int(name_or_num) except ValueError: pass if isinstance(name_or_num, int): if str(name_or_num) in self.elems: elem = self.get_elem_by_name(name_or_num) else: elem = self.get_elem_by_num( name_or_num, sort_by, unordered, elem_type) else: elem = self.get_elem_by_name(name_or_num) return elem def _ls_all(self, show_path, sort_by, unordered, elem_type): try: indent = '' if show_path: indent = INDENT_UNIT * self.level vals = self.get_vals(sort_by, unordered, elem_type) width = len(str(len(vals))) fmt = '%s%{}d [%s] %s'.format(width) for num, elem in enumerate(vals, start=1): if not configs.short_output: self.lines.append( fmt % (indent, num, elem.type[0], elem.name)) else: self.lines.append(elem.name) finally: self.flush_lines() @property def len(self): if self._len is None: self._len = len(self.elems) return self._len def get_elem_by_name(self, name): try: return self.elems[name] except KeyError: raise ElemError( 'element "%s" does not exist in this %s' % ( name, self.type.lower())) def get_elem_by_num(self, num, sort_by, unordered, elem_type): vals = self.get_vals(sort_by, unordered, elem_type) try: return vals[num - 1] if vals else None except IndexError: raise ElemError( 'element index out of range (1-%d)' % len(vals)) def _print_path(self): indent = INDENT_UNIT * (self.level - 1) self.lines.append('%s%s' % (indent, self.name)) def get_elems(self, elem_type=None): """ The generic method to get elements. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". """ elems = self.elems if elem_type is not None: items = self.elems.items() if elem_type not in ('file', 'directory', 'node', 'attribute'): raise NodeError('unknown element type') elem_class = getattr(sys.modules[__name__], elem_type.title()) items = filter(lambda x: isinstance(x[1], elem_class), items) elems = OrderedDict(items) return elems def get_vals(self, sort_by=None, unordered=False, elem_type=None): """ The generic method to get element values. :param sort_by: the name of the sorting key. If it is None and `unordered` is False, then the sorting key is the element name. If it is a name, then the content of the attribute with this name is used as the key. :param unordered: whether to present elements unordered. If it is True, the original order in the document is used, and `sort_by` has no effect. :param elem_type: the element type. If None, then all types are included. Otherwise, it is one of "file", "directory", "node", and "attribute". """ vals = self.vals if elem_type is not None: if elem_type not in ('file', 'directory', 'node', 'attribute'): raise NodeError('unknown element type') elem_class = getattr(sys.modules[__name__], elem_type.title()) vals = filter(lambda elem: isinstance(elem, elem_class), vals) if unordered: return vals if sort_by is None: return sorted(vals, key=attrgetter('name')) def sort_key(elem): if getattr(elem, 'attrs'): return elem.attrs.get(sort_by) return None return sorted(self.vals, key=sort_key) def add(self, name, content): """ Create an element with name and content and add it to this node. """ if name not in self.elems: self.content[name] = content elem, this_is_dir, this_is_file = self._make_elem(name, content) self._elems[name] = elem self._update_class(this_is_dir, this_is_file) # update vals self._vals = self.elems.values() else: raise NodeError( 'element "%s" already exists in this %s' % ( name, self.type.lower())) def delete(self, name): """Delete an element with name in this node.""" if name in self.elems: this_is_dir, this_is_file = self._delete_elem(name) self._update_class(this_is_dir, this_is_file) # update vals self._vals = self.elems.values() else: raise NodeError( 'element "%s" does not exist in this %s' % ( name, self.type.lower())) def _delete_elem(self, name): this_is_dir = self.is_dir this_is_file = self.is_file del self._elems[name] del self.content[name] if not self.content: # add a placeholder to keep this node as a file self.add(PLACEHOLDER, True) # check remaining elements for elem in self.elems: if self.elems[elem].is_attr: this_is_file = True if self.elems[elem].is_node: this_is_dir = True return this_is_dir, this_is_file