Skip to content

Commit

Permalink
Merge pull request #29 from leonardbinet/mypy
Browse files Browse the repository at this point in the history
v1.3.0 - stronger typing
  • Loading branch information
leonardbinet authored Jul 28, 2021
2 parents fed7c54 + 7f79e4b commit 025cc29
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 153 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Mypy check
run: |
pip install mypy
mypy lighttree
mypy --install-types --non-interactive lighttree
- name: Test with pytest
run: |
pytest
13 changes: 11 additions & 2 deletions lighttree/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from .interactive import TreeBasedObj
from .tree import Tree, Node, Key, KeyedNode, KeyedTree
from .tree import Tree, Key, KeyedNode, KeyedTree
from .node import Node, AutoIdNode

__all__ = ["Tree", "Node", "TreeBasedObj", "Key", "KeyedNode", "KeyedTree"]
__all__ = [
"Tree",
"Node",
"AutoIdNode",
"TreeBasedObj",
"Key",
"KeyedNode",
"KeyedTree",
]
39 changes: 15 additions & 24 deletions lighttree/implementations/json_tree.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,48 @@
from typing import Dict, Optional, Any, Union, List
from lighttree import Node, Tree, Key
from lighttree import Tree, Key, AutoIdNode
from lighttree.node import NodeId
from lighttree.interactive import TreeBasedObj


class JsonTree(Tree):
def __init__(
self, d: Optional[Dict] = None, strict: bool = True, path_separator: str = "."
) -> None:
def __init__(self, d: Optional[Dict] = None, strict: bool = True) -> None:
"""
:param d:
:param strict: if False, will convert tuples into arrays, else raise error
:param path_separator: separator used to build path
"""
super(JsonTree, self).__init__(path_separator=path_separator)
super(JsonTree, self).__init__()
if d is not None:
self._fill(d, strict=strict, key=None)

@staticmethod
def _concat(a: Any, b: Any) -> str:
if not a and not b:
return ""
if not a:
return str(b)
return ".".join([str(a), str(b)])

def _fill(self, data: Any, key: Key, strict: bool, path: str = "") -> None:
def _fill(
self, data: Any, key: Optional[Key], strict: bool, path: Optional[List] = None
) -> None:
pid: Optional[NodeId]
path_: List = path or []
if self.is_empty():
pid = None
else:
pid = self.get_node_id_by_path(path=path)
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=pid, key=key)
path = self._concat(path, k)
k = self.insert_node(AutoIdNode(keyed=False), parent_id=pid, key=key)
for el in data:
self._fill(el, strict=strict, path=path, key=None)
self._fill(el, strict=strict, path=path_ + ([k] if k else []), key=None)
return
if isinstance(data, dict):
k = self.insert_node(Node(keyed=True), key=key, parent_id=pid)
path = self._concat(path, k)
k = self.insert_node(AutoIdNode(keyed=True), key=key, parent_id=pid)
for sk, el in data.items():
self._fill(el, strict=strict, path=path, key=sk)
self._fill(el, strict=strict, path=path_ + ([k] if k else []), key=sk)
return
if isinstance(data, (str, int, float)):
self.insert_node(
Node(accept_children=False, repr_=str(data), data=data),
AutoIdNode(accept_children=False, repr_=str(data), data=data),
parent_id=pid,
key=key,
)
return
if data is None:
self.insert_node(Node(accept_children=False), parent_id=pid)
self.insert_node(AutoIdNode(accept_children=False), parent_id=pid)
return
raise TypeError("Unsupported type %s" % type(data))

Expand Down
83 changes: 46 additions & 37 deletions lighttree/node.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,63 @@
import uuid
from typing import Optional, Any, Tuple
from dataclasses import dataclass

NodeId = str


class Node(object):
def __init__(
self,
identifier: Optional[NodeId] = None,
auto_uuid: bool = True,
keyed: bool = True,
accept_children: bool = True,
repr_: Optional[str] = None,
data: Any = None,
) -> None:
"""
:param identifier: node identifier, must be unique per tree
"""
if identifier is None:
if not auto_uuid:
raise ValueError("Required identifier")
identifier = str(uuid.uuid4())
self.identifier = identifier
self.keyed = keyed
self.accept_children = accept_children
self.repr = repr_
self.data = data
@dataclass
class Node:

identifier: NodeId
keyed: bool = True
accept_children: bool = True
repr_: Optional[str] = None
data: Any = None

def line_repr(self, depth: int, **kwargs: Any) -> Tuple[str, str]:
"""Control how node is displayed in tree representation.
_
├── one end
│ └── two myEnd
└── three
First returned string is how node is represented on left, second string is how node is represented on right.
MyTree
├── one OneEnd
│ └── two twoEnd
└── three threeEnd
"""
if self.repr is not None:
return self.repr, ""
if self.repr_ is not None:
return self.repr_, ""
if not self.accept_children:
return str(self.data), ""
if hasattr(self.data, "__str__"):
return str(self.data), ""
return "", ""
if self.keyed:
return "{}", ""
return "[]", ""

def __eq__(self, other: Any) -> bool:
if not isinstance(other, self.__class__):
return False
return self.identifier == other.identifier

def __str__(self) -> str:
return "%s, id=%s" % (self.__class__.__name__, self.identifier)
class AutoIdNode(Node):
def __init__(
self,
identifier: Optional[NodeId] = None,
keyed: bool = True,
accept_children: bool = True,
repr_: Optional[str] = None,
data: Any = None,
):

self._auto_generated_id: bool
identifier_: NodeId

if identifier is None:
identifier_ = str(uuid.uuid4())
self._auto_generated_id = True
else:
identifier_ = identifier
self._auto_generated_id = False

def __repr__(self) -> str:
return self.__str__()
super(AutoIdNode, self).__init__(
identifier=identifier_,
keyed=keyed,
accept_children=accept_children,
repr_=repr_,
data=data,
)
Loading

0 comments on commit 025cc29

Please sign in to comment.