diff --git a/lighttree/implementations/json_tree.py b/lighttree/implementations/json_tree.py index c8ac704..acf1ea2 100644 --- a/lighttree/implementations/json_tree.py +++ b/lighttree/implementations/json_tree.py @@ -1,10 +1,12 @@ from typing import Dict, Optional, Any, Union, List from lighttree import Node, Tree, Key +from lighttree.node import NodeId +from lighttree.interactive import TreeBasedObj class JsonTree(Tree): def __init__( - self, d: Dict = None, strict: bool = True, path_separator: str = "." + self, d: Optional[Dict] = None, strict: bool = True, path_separator: str = "." ) -> None: """ :param d: @@ -23,53 +25,63 @@ def _concat(a: Any, b: Any) -> str: return str(b) return ".".join([str(a), str(b)]) - def _fill( - self, data: Any, key: Key, strict: bool, path: Optional[str] = None - ) -> None: + def _fill(self, data: Any, key: Key, strict: bool, path: str = "") -> None: + pid: Optional[NodeId] + if self.is_empty(): + pid = None + else: + pid = self.get_node_id_by_path(path=path) if isinstance(data, list) or not strict and isinstance(data, tuple): - k = self.insert_node( - Node(keyed=False), parent_id=path, key=key, by_path=True - ) + k = self.insert_node(Node(keyed=False), parent_id=pid, key=key) path = self._concat(path, k) for el in data: self._fill(el, strict=strict, path=path, key=None) return if isinstance(data, dict): - k = self.insert_node( - Node(keyed=True), key=key, parent_id=path, by_path=True - ) + k = self.insert_node(Node(keyed=True), key=key, parent_id=pid) path = self._concat(path, k) for sk, el in data.items(): self._fill(el, strict=strict, path=path, key=sk) return if isinstance(data, (str, int, float)): self.insert_node( - Node(accept_children=False, repr_=data, data=data), - parent_id=path, + Node(accept_children=False, repr_=str(data), data=data), + parent_id=pid, key=key, - by_path=True, ) return if data is None: - self.insert_node(Node(accept_children=False), parent_id=path, by_path=True) + self.insert_node(Node(accept_children=False), parent_id=pid) return raise TypeError("Unsupported type %s" % type(data)) - def to_dict(self) -> Union[Dict, List, None]: + def to_json(self) -> Union[Dict, List, None]: if self.root is None: return None - return self._to_dict(self.root) + return self._to_json(self.root) - def _to_dict(self, nid: str) -> Any: + def _to_json(self, nid: NodeId) -> Any: _, n = self.get(nid) if not n.accept_children: return n.data if n.keyed: d = {} for sk, sn in self.children(n.identifier): - d[sk] = self._to_dict(sn.identifier) + d[sk] = self._to_json(sn.identifier) return d l_ = [] for _, sn in self.children(n.identifier): - l_.append(self._to_dict(sn.identifier)) + l_.append(self._to_json(sn.identifier)) return l_ + + +class InteractiveJson(TreeBasedObj): + + _tree: JsonTree + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + return self._tree.to_json() + + +def as_interactive_json(d: Any, strict: bool = False) -> InteractiveJson: + return InteractiveJson(tree=JsonTree(d, strict=strict)) diff --git a/lighttree/interactive.py b/lighttree/interactive.py index 094b6c4..a7b629d 100644 --- a/lighttree/interactive.py +++ b/lighttree/interactive.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import re import unicodedata @@ -32,7 +29,7 @@ def _coerce_attr(attr: Any) -> Union[str, None]: return None -class Obj(object): +class Obj: """Object class that allows to get items both by attribute `__getattribute__` access: `obj.attribute` or by dict `__getitem__` access: >>> obj = Obj(key='value') diff --git a/lighttree/node.py b/lighttree/node.py index a2d516e..c93dbb0 100644 --- a/lighttree/node.py +++ b/lighttree/node.py @@ -1,18 +1,17 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import uuid -from typing import Optional, Any, Tuple, Union +from typing import Optional, Any, Tuple + +NodeId = str class Node(object): def __init__( self, - identifier: Optional[str] = None, + identifier: Optional[NodeId] = None, auto_uuid: bool = True, keyed: bool = True, accept_children: bool = True, - repr_: Optional[Union[str, float]] = None, + repr_: Optional[str] = None, data: Any = None, ) -> None: """ @@ -25,7 +24,7 @@ def __init__( self.identifier = identifier self.keyed = keyed self.accept_children = accept_children - self.repr = str(repr_) if repr_ is not None else None + self.repr = repr_ self.data = data def line_repr(self, depth: int, **kwargs: Any) -> Tuple[str, str]: diff --git a/lighttree/tree.py b/lighttree/tree.py index a7238ee..ebde452 100644 --- a/lighttree/tree.py +++ b/lighttree/tree.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - import copy from typing import ( @@ -18,14 +15,16 @@ from collections import defaultdict from operator import itemgetter -from lighttree.node import Node +from lighttree.node import Node, NodeId from .exceptions import MultipleRootError, NotFoundNodeError, DuplicatedNodeError from .utils import STYLES +# root has no key (None), keyed node has children with str keys, unkeyed node has children with int keys Key = Union[None, str, int] KeyedNode = Tuple[Key, Node] KeyedTree = Tuple[Key, "Tree"] +Path = str class Tree(object): @@ -48,34 +47,29 @@ def __init__(self, path_separator: str = ".") -> None: self.path_separator = path_separator # nodes references and hierarchy in tree - self.root: Optional[str] = None + self.root: Optional[NodeId] = None # node identifier -> node - self._nodes_map: Dict[str, Node] = {} + self._nodes_map: Dict[NodeId, Node] = {} # node identifier -> parent node identifier - self._nodes_parent: Dict[str, Optional[str]] = defaultdict(lambda: None) + self._nodes_parent: Dict[NodeId, Optional[NodeId]] = defaultdict(lambda: None) # "map" node identifier -> map of children nodes identifier -> key - self._nodes_children_map: Dict[str, Dict[str, Union[int, str]]] = defaultdict( - dict - ) + self._nodes_children_map: Dict[NodeId, Dict[NodeId, Key]] = defaultdict(dict) # "list" node identifier -> children nodes identifiers - self._nodes_children_list: Dict[str, List[str]] = defaultdict(list) + self._nodes_children_list: Dict[NodeId, List[NodeId]] = defaultdict(list) - def __contains__(self, identifier: str) -> bool: + def __contains__(self, identifier: NodeId) -> bool: return identifier in self._nodes_map - def get(self, nid: str, by_path: bool = False) -> KeyedNode: + def get(self, nid: NodeId) -> KeyedNode: """Get a node by its id. :param nid: str, identifier of node to fetch - :param by_path: bool, if True nid is the path to the node :rtype: lighttree.node.Node """ - if by_path: - nid = self.get_node_id_by_path(nid) self._ensure_present(nid) return self.get_key(nid), self._nodes_map[nid] - def child_id(self, nid: str, key: Union[str, int], by_path: bool = False) -> str: - _, node = self.get(nid, by_path=by_path) + def child_id(self, nid: NodeId, key: Key) -> NodeId: + _, node = self.get(nid) if node.keyed: child_id = next( (cid for cid, k in self._nodes_children_map[nid].items() if k == key), @@ -84,16 +78,14 @@ def child_id(self, nid: str, key: Union[str, int], by_path: bool = False) -> str if child_id is None: raise ValueError("No child of key %s below %s" % (key, nid)) return child_id - try: - int(key) - except (ValueError, TypeError): - raise ValueError("Expected integer key, got %s" % key) + if not isinstance(key, (str, int)): + raise ValueError("Expected integer 'castable' key, got %s" % key) return self._nodes_children_list[nid][int(key)] - def child(self, nid: str, key: Union[int, str], by_path: bool = False) -> KeyedNode: - return self.get(self.child_id(nid, key, by_path=by_path)) + def child(self, nid: NodeId, key: Key) -> KeyedNode: + return self.get(self.child_id(nid, key)) - def get_node_id_by_path(self, path: str) -> str: + def get_node_id_by_path(self, path: Path) -> NodeId: nid = self.root if nid is None: raise ValueError("Empty tree") @@ -106,7 +98,7 @@ def get_node_id_by_path(self, path: str) -> str: raise ValueError("Empty tree") return nid - def get_path(self, nid: str) -> str: + def get_path(self, nid: NodeId) -> Path: return self.path_separator.join( [ str(k) @@ -116,7 +108,7 @@ def get_path(self, nid: str) -> str: ] ) - def get_key(self, nid: str) -> Key: + def get_key(self, nid: NodeId) -> Key: """Get a node's key. :param nid: str, identifier of node @@ -134,7 +126,7 @@ def get_key(self, nid: str) -> Key: def list( self, - id_in: Optional[Sequence[str]] = None, + id_in: Optional[Sequence[NodeId]] = None, depth_in: Optional[Sequence[int]] = None, filter_: Optional[Callable[[Node], bool]] = None, ) -> List[KeyedNode]: @@ -160,10 +152,11 @@ def is_empty(self) -> bool: def _ensure_present( self, - nid: Optional[str], + nid: Optional[NodeId], defaults_to_root: bool = False, allow_empty: bool = False, - ) -> Union[None, str]: + ) -> Optional[NodeId]: + # TODO - split method based on intent if nid is None: if not self.is_empty() and defaults_to_root: return self.root @@ -211,7 +204,10 @@ def _clone_init(self, deep: bool) -> "Tree": return self.__class__() def clone( - self, with_nodes: bool = True, deep: bool = False, new_root: str = None + self, + with_nodes: bool = True, + deep: bool = False, + new_root: Optional[NodeId] = None, ) -> "Tree": """Clone current instance, with or without nodes. :rtype: :class:`lighttree.tree.Tree` @@ -236,7 +232,7 @@ def clone( new_tree.insert_node(node, parent_id=pid, key=key) return new_tree - def parent(self, nid: str) -> KeyedNode: + def parent(self, nid: NodeId) -> KeyedNode: """Return parent node. Return None if given node id is root. """ @@ -245,11 +241,9 @@ def parent(self, nid: str) -> KeyedNode: raise NotFoundNodeError("Node <%s> has no parent" % nid) return self.get(pid) - def parent_id(self, nid: str, by_path: bool = False) -> str: + def parent_id(self, nid: NodeId) -> NodeId: if nid == self.root: raise NotFoundNodeError("Root node has not parent") - if by_path: - nid = self.get_node_id_by_path(nid) self._ensure_present(nid) parent_id = self._nodes_parent[nid] if parent_id is None: @@ -257,22 +251,20 @@ def parent_id(self, nid: str, by_path: bool = False) -> str: raise NotFoundNodeError() return parent_id - def children(self, nid: str, by_path: bool = False) -> List[KeyedNode]: + def children(self, nid: NodeId) -> List[KeyedNode]: """Return set of given node children node ids.""" - return [self.get(id_) for id_ in self.children_ids(nid, by_path=by_path)] + return [self.get(id_) for id_ in self.children_ids(nid)] - def children_ids(self, nid: str, by_path: bool = False) -> List[str]: - if self.get(nid, by_path=by_path)[1].keyed: + def children_ids(self, nid: NodeId) -> List[NodeId]: + if self.get(nid)[1].keyed: return list(self._nodes_children_map[nid].keys()) return list(self._nodes_children_list[nid]) - def siblings(self, nid: str, by_path: bool = False) -> List[KeyedNode]: + def siblings(self, nid: NodeId) -> List[KeyedNode]: """Return set of ids of nodes that share the provided node's parent.""" - return [self.get(id_) for id_ in self.siblings_ids(nid, by_path=by_path)] + return [self.get(id_) for id_ in self.siblings_ids(nid)] - def siblings_ids(self, nid: str, by_path: bool = False) -> List[str]: - if by_path: - nid = self.get_node_id_by_path(nid) + def siblings_ids(self, nid: NodeId) -> List[NodeId]: self._ensure_present(nid) if nid == self.root: return [] @@ -281,44 +273,36 @@ def siblings_ids(self, nid: str, by_path: bool = False) -> List[str]: return [] return list(set(self.children_ids(parent_id)).difference({nid})) - def is_leaf(self, nid: str, by_path: bool = False) -> bool: + def is_leaf(self, nid: NodeId) -> bool: """Return is node is a leaf in this tree.""" - return len(self.children_ids(nid, by_path=by_path)) == 0 + return len(self.children_ids(nid)) == 0 - def depth(self, nid: str, by_path: bool = False) -> int: + def depth(self, nid: NodeId) -> int: """Return node depth, 0 means root.""" - return len(self.ancestors_ids(nid, by_path=by_path)) + return len(self.ancestors_ids(nid)) def ancestors( self, - nid: str, + nid: NodeId, from_root: bool = False, include_current: bool = False, - by_path: bool = False, ) -> List[KeyedNode]: """From element to root. :param nid: :param from_root: :param include_current: - :param by_path: :return: """ return [ - self.get(id_) - for id_ in self.ancestors_ids( - nid, from_root, include_current, by_path=by_path - ) + self.get(id_) for id_ in self.ancestors_ids(nid, from_root, include_current) ] def ancestors_ids( self, - nid: str, + nid: NodeId, from_root: bool = False, include_current: bool = False, - by_path: bool = False, - ) -> List[str]: - if by_path: - nid = self.get_node_id_by_path(nid) + ) -> List[NodeId]: self._ensure_present(nid) ancestor_ids = [nid] if include_current else [] if nid == self.root: @@ -330,32 +314,27 @@ def ancestors_ids( ancestor_ids = list(reversed(ancestor_ids)) return ancestor_ids - def subtree(self, nid: str, deep: bool = False, by_path: bool = False) -> KeyedTree: - if by_path: - nid = self.get_node_id_by_path(nid) + def subtree(self, nid: NodeId, deep: bool = False) -> KeyedTree: t = self.clone(with_nodes=True, new_root=nid, deep=deep) if t.root is None: return None, t return self.get_key(t.root), t - def leaves( - self, nid: Optional[str] = None, by_path: bool = False - ) -> List[KeyedNode]: + def leaves(self, nid: Optional[NodeId] = None) -> List[KeyedNode]: """Return leaves under a node subtree.""" - return [self.get(id_) for id_ in self.leaves_ids(nid, by_path=by_path)] + return [self.get(id_) for id_ in self.leaves_ids(nid)] - def leaves_ids(self, nid: Optional[str] = None, by_path: bool = False) -> List[str]: - tree = self if nid is None else self.subtree(nid, by_path=by_path)[1] + def leaves_ids(self, nid: Optional[NodeId] = None) -> List[NodeId]: + tree = self if nid is None else self.subtree(nid)[1] return [id_ for id_ in tree._nodes_map.keys() if tree.is_leaf(id_)] def insert( self, item: Union[Node, "Tree"], - parent_id: Optional[str] = None, - child_id: Optional[str] = None, - child_id_below: Optional[str] = None, - key: Optional[Union[int, str]] = None, - by_path: bool = False, + parent_id: Optional[NodeId] = None, + child_id: Optional[NodeId] = None, + child_id_below: Optional[NodeId] = None, + key: Key = None, ) -> "Tree": if isinstance(item, Node): if child_id_below is not None: @@ -367,7 +346,6 @@ def insert( parent_id=parent_id, child_id=child_id, key=key, - by_path=by_path, ) return self if isinstance(item, Tree): @@ -377,7 +355,6 @@ def insert( child_id=child_id, child_id_below=child_id_below, key=key, - by_path=by_path, ) return self raise ValueError( @@ -387,10 +364,9 @@ def insert( def insert_node( self, node: Node, - parent_id: Optional[str] = None, - child_id: Optional[str] = None, - key: Optional[Union[int, str]] = None, - by_path: bool = False, + parent_id: Optional[NodeId] = None, + child_id: Optional[NodeId] = None, + key: Key = None, ) -> Key: """Insert node, return key :param node: @@ -403,17 +379,16 @@ def insert_node( if parent_id is not None and child_id is not None: raise ValueError('Can declare at most "parent_id" or "child_id"') if child_id is not None: - self._insert_node_above(node, child_id=child_id, key=key, by_path=by_path) + self._insert_node_above(node, child_id=child_id, key=key) return None - self._insert_node_below(node, parent_id=parent_id, key=key, by_path=by_path) + self._insert_node_below(node, parent_id=parent_id, key=key) return self.get_key(node.identifier) def _insert_node_below( self, node: Node, - parent_id: Optional[str], - key: Union[None, int, str], - by_path: bool, + parent_id: Optional[NodeId], + key: Key, ) -> None: # insertion at root if parent_id is None: @@ -425,8 +400,6 @@ def _insert_node_below( self._nodes_map[node.identifier] = node return - if by_path: - parent_id = self.get_node_id_by_path(path=parent_id) self._ensure_present(parent_id) node_id = node.identifier @@ -460,11 +433,7 @@ def _insert_node_below( self._nodes_map[node_id] = node self._nodes_parent[node_id] = parent_id - def _insert_node_above( - self, node: Node, child_id: str, key: Union[None, int, str], by_path: bool - ) -> None: - if by_path: - child_id = self.get_node_id_by_path(path=child_id) + def _insert_node_above(self, node: Node, child_id: NodeId, key: Key) -> None: self._ensure_present(child_id) # get parent_id before dropping subtree try: @@ -475,25 +444,20 @@ def _insert_node_above( has_parent = False subtree_key, child_subtree = self.drop_subtree(nid=child_id) if has_parent: - self._insert_node_below( - node=node, parent_id=parent_id, key=subtree_key, by_path=False - ) + self._insert_node_below(node=node, parent_id=parent_id, key=subtree_key) else: - self._insert_node_below( - node=node, parent_id=None, key=subtree_key, by_path=False - ) + self._insert_node_below(node=node, parent_id=None, key=subtree_key) self._insert_tree_below( - new_tree=child_subtree, parent_id=node.identifier, key=key, by_path=False + new_tree=child_subtree, parent_id=node.identifier, key=key ) def insert_tree( self, new_tree: "Tree", - parent_id: Optional[str] = None, - child_id: Optional[str] = None, - child_id_below: Optional[str] = None, - key: Optional[Union[str, int]] = None, - by_path: bool = False, + parent_id: Optional[NodeId] = None, + child_id: Optional[NodeId] = None, + child_id_below: Optional[NodeId] = None, + key: Key = None, ) -> Key: """Return new key""" self._validate_tree_insertion(new_tree) @@ -507,12 +471,9 @@ def insert_tree( child_id=child_id, child_id_below=child_id_below, key=key, - by_path=by_path, ) else: - self._insert_tree_below( - new_tree, parent_id=parent_id, key=key, by_path=by_path - ) + self._insert_tree_below(new_tree, parent_id=parent_id, key=key) if new_tree.root is None: # not possible, but for typing raise ValueError("Empty inserted tree") @@ -521,17 +482,14 @@ def insert_tree( def _insert_tree_below( self, new_tree: "Tree", - parent_id: Optional[str], - key: Union[None, str, int], - by_path: bool, + parent_id: Optional[NodeId], + key: Key, ) -> "Tree": if parent_id is None: # insertion at root requires tree to be empty if not self.is_empty(): raise MultipleRootError("A tree takes one root merely.") else: - if by_path: - parent_id = self.get_node_id_by_path(parent_id) self._ensure_present(parent_id) self._validate_tree_insertion(new_tree) @@ -549,18 +507,13 @@ def _insert_tree_below( def _insert_tree_above( self, new_tree: "Tree", - child_id: str, - child_id_below: Optional[str], - key: Union[None, str, int], - by_path: bool, + child_id: NodeId, + child_id_below: Optional[NodeId], + key: Key, ) -> None: # make all checks before modifying tree - if by_path: - child_id = self.get_node_id_by_path(child_id) self._ensure_present(child_id) if child_id_below is not None: - if by_path: - child_id_below = self.get_node_id_by_path(child_id_below) new_tree._ensure_present(child_id_below) else: new_tree_leaves = new_tree.leaves_ids() @@ -573,10 +526,10 @@ def _insert_tree_above( child_id_below = new_tree_leaves.pop() parent_id = self.parent_id(child_id) subtree_key, child_subtree = self.drop_subtree(child_id) - self._insert_tree_below(new_tree, parent_id, key=subtree_key, by_path=False) - self._insert_tree_below(child_subtree, child_id_below, key=key, by_path=False) + self._insert_tree_below(new_tree, parent_id, key=subtree_key) + self._insert_tree_below(child_subtree, child_id_below, key=key) - def _drop_node(self, nid: str) -> KeyedNode: + def _drop_node(self, nid: NodeId) -> KeyedNode: """Return key, node""" if self.children_ids(nid): raise ValueError("Cannot drop node having children.") @@ -604,17 +557,14 @@ def _drop_node(self, nid: str) -> KeyedNode: def drop_node( self, - nid: str, + nid: NodeId, with_children: bool = True, - by_path: bool = False, ) -> KeyedNode: """If with_children is False, children of this node will take as new parent the dropped node parent. Possible only if node type is same as parent node type. Return key, node. """ - if by_path: - nid = self.get_node_id_by_path(nid) self._ensure_present(nid) children_ids = self.children_ids(nid) @@ -638,12 +588,10 @@ def drop_node( self.drop_node(nid, with_children=True) for cid in children_ids: k, st = removed_subtree.subtree(cid) - self._insert_tree_below(new_tree=st, parent_id=pid, key=k, by_path=False) + self._insert_tree_below(new_tree=st, parent_id=pid, key=k) return removed_key, node - def drop_subtree(self, nid: str, by_path: bool = False) -> KeyedTree: - if by_path: - nid = self.get_node_id_by_path(nid) + def drop_subtree(self, nid: NodeId) -> KeyedTree: self._ensure_present(nid) key, removed_subtree = self.subtree(nid) self.drop_node(nid=nid, with_children=True) @@ -651,8 +599,7 @@ def drop_subtree(self, nid: str, by_path: bool = False) -> KeyedTree: def expand_tree( self, - nid: Optional[str] = None, - by_path: bool = False, + nid: Optional[NodeId] = None, mode: str = "depth", filter_: Optional[Callable[[Union[None, str, int], Node], bool]] = None, filter_through: bool = False, @@ -672,8 +619,6 @@ def expand_tree( """ if mode not in ("depth", "width"): raise NotImplementedError("Traversal mode '%s' is not supported" % mode) - if nid is not None and by_path: - nid = self.get_node_id_by_path(nid) nid = self._ensure_present(nid, defaults_to_root=True, allow_empty=True) if nid is not None: key, node = self.get(nid) @@ -710,8 +655,7 @@ def expand_tree( def show( self, - nid: Optional[str] = None, - by_path: bool = False, + nid: Optional[NodeId] = None, filter_: Optional[Callable[[Node], bool]] = None, display_key: bool = True, reverse: bool = False, @@ -736,8 +680,6 @@ def show( """ output = "" - if nid is not None and by_path: - nid = self.get_node_id_by_path(nid) for is_last_list, key, node in self._iter_nodes_with_location( nid, filter_, reverse @@ -771,7 +713,7 @@ def show( def _iter_nodes_with_location( self, - nid: Optional[str], + nid: Optional[NodeId], filter_: Optional[Callable[[Node], bool]], reverse: bool, is_last_list: Optional[List[bool]] = None, @@ -842,9 +784,7 @@ def _line_prefix_repr(line_type: str, is_last_list: Tuple[bool, ...]) -> str: lasting: str = dt_line_corner if is_last_list[-1] else dt_line_box return leading + lasting - def merge( - self, new_tree: "Tree", nid: Optional[str] = None, by_path: bool = False - ) -> "Tree": + def merge(self, new_tree: "Tree", nid: Optional[NodeId] = None) -> "Tree": """Merge "new_tree" on current tree by pasting its root children on current tree "nid" node. Consider the following trees: @@ -874,9 +814,6 @@ def merge( other cases new_tree root is not pasted. """ - if nid is not None and by_path: - nid = self.get_node_id_by_path(nid) - if not isinstance(new_tree, self.__class__): raise ValueError( 'Wrong type of "new_tree", expected <%s>, got <%s>' @@ -884,7 +821,7 @@ def merge( ) if self.is_empty(): - return self.insert(new_tree, parent_id=None, by_path=False) + return self.insert(new_tree, parent_id=None) nid = self._ensure_present(nid, defaults_to_root=True) diff --git a/setup.cfg b/setup.cfg index abfb47b..5c702c9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,4 +2,7 @@ description-file = README.md [flake8] -ignore=E501,W503,W605 \ No newline at end of file +ignore=E501,W503,W605 + +[mypy] +disallow_untyped_defs = True diff --git a/setup.py b/setup.py index cfd7886..d5be2aa 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,6 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - from setuptools import setup -__version__ = "1.1.4" +__version__ = "1.2.0" develop_requires = [ "pre-commit", diff --git a/tests/test_implementation_json.py b/tests/test_implementation_json.py new file mode 100644 index 0000000..06bf956 --- /dev/null +++ b/tests/test_implementation_json.py @@ -0,0 +1,47 @@ +from unittest import TestCase + +from lighttree.implementations.json_tree import ( + JsonTree, + InteractiveJson, + as_interactive_json, +) + + +class JSONTestCase(TestCase): + def test_json_tree(self): + j = JsonTree({"a": [{}, {"b": 12}, [1, 2, 3]]}) + self.assertEqual( + j.__str__(), + """{} +└── a: [] + ├── {} + ├── {} + │ └── b: 12 + └── [] + ├── 1 + ├── 2 + └── 3 +""", + ) + + def test_as_interactive_json_tree(self): + j = as_interactive_json({"a": [{}, {"b": 12}, [1, 2, 3]]}) + self.assertEqual( + j.__str__(), + """ +{} +└── a: [] + ├── {} + ├── {} + │ └── b: 12 + └── [] + ├── 1 + ├── 2 + └── 3 +""", + ) + self.assertIn("a", dir(j)) + a = j.a + self.assertIsInstance(a, InteractiveJson) + as_json = a() + self.assertEqual(as_json, [{}, {"b": 12}, [1, 2, 3]]) diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 5842514..1cec4c1 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals - import sys from unittest import TestCase diff --git a/tests/test_tree.py b/tests/test_tree.py index f5c54e1..dda3b09 100644 --- a/tests/test_tree.py +++ b/tests/test_tree.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals - from collections import defaultdict from operator import itemgetter from unittest import TestCase @@ -896,17 +891,6 @@ def test_get_node_id_by_path(self): def test_subtree(self): t = get_sample_tree() - # by path - k, st = t.subtree(nid="a.a", by_path=True) - self.assertEqual(k, "a") - self.assertEqual( - st.show(), - """[] -├── AA0 -└── AA1 -""", - ) - # by id nid = t.get_node_id_by_path("a.a") k, st = t.subtree(nid=nid)