summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 04:58:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 04:58:50 +0000
commit0391d056604458068132313730b9d36be055087b (patch)
tree04b24c25a8009dd1b45b83eaa8a8b86c8112163b
parentAdding upstream version 1.6.4. (diff)
downloadtreelib-upstream.tar.xz
treelib-upstream.zip
Adding upstream version 1.7.0.upstream/1.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.github/workflows/python-package.yml25
-rw-r--r--README.md2
-rw-r--r--setup.py8
-rw-r--r--tests/test_plugins.py113
-rw-r--r--tests/test_tree.py34
-rw-r--r--treelib/plugins.py35
-rw-r--r--treelib/tree.py41
7 files changed, 78 insertions, 180 deletions
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index 5ffa075..fba950c 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -9,36 +9,13 @@ on:
branches: [ "master" ]
jobs:
- build-pre37:
-
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- python-version: ["2.7"]
-
- steps:
- - uses: actions/checkout@v3
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v3
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- if [ -f requirements-t-pre37.txt ]; then pip install -r requirements-t-pre37.txt; fi
- - name: Test with nosetests
- run: |
- ./scripts/testing.sh
-
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12.0-rc.1"]
steps:
- uses: actions/checkout@v3
diff --git a/README.md b/README.md
index c1ee089..fb818a2 100644
--- a/README.md
+++ b/README.md
@@ -34,5 +34,3 @@ with `scripts/flake8.sh`.
Thank you all,
[committers](https://github.com/caesar0301/treelib/graphs/contributors).
-
-[![ForTheBadge built-with-love](http://ForTheBadge.com/images/badges/built-with-love.svg)](https://GitHub.com/Naereen/)
diff --git a/setup.py b/setup.py
index 910e467..9331bec 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
from setuptools import setup
-__version__ = "1.6.4"
+__version__ = "1.7.0"
setup(
@@ -9,21 +9,19 @@ setup(
url="https://github.com/caesar0301/treelib",
author="Xiaming Chen",
author_email="chenxm35@gmail.com",
- description="A Python 2/3 implementation of tree structure.",
+ description="A Python implementation of tree structure.",
long_description="""This is a simple tree data structure implementation in python.""",
license="Apache License, Version 2.0",
packages=["treelib"],
keywords=["data structure", "tree", "tools"],
install_requires=["six"],
classifiers=[
- "Development Status :: 4 - Beta",
+ "Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
deleted file mode 100644
index 0f04fbf..0000000
--- a/tests/test_plugins.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import codecs
-import os
-import unittest
-
-from treelib import Tree
-from treelib.plugins import export_to_dot
-
-
-class DotExportCase(unittest.TestCase):
- """Test class for the export to dot format function"""
-
- def setUp(self):
- tree = Tree()
- tree.create_node("Hárry", "hárry")
- tree.create_node("Jane", "jane", parent="hárry")
- tree.create_node("Bill", "bill", parent="hárry")
- tree.create_node("Diane", "diane", parent="jane")
- tree.create_node("George", "george", parent="bill")
- self.tree = tree
-
- def read_generated_output(self, filename):
- output = codecs.open(filename, "r", "utf-8")
- generated = output.read()
- output.close()
-
- return generated
-
- def test_export_to_dot(self):
- export_to_dot(self.tree, "tree.dot")
- expected = """\
-digraph tree {
-\t"hárry" [label="Hárry", shape=circle]
-\t"bill" [label="Bill", shape=circle]
-\t"jane" [label="Jane", shape=circle]
-\t"george" [label="George", shape=circle]
-\t"diane" [label="Diane", shape=circle]
-
-\t"hárry" -> "jane"
-\t"hárry" -> "bill"
-\t"bill" -> "george"
-\t"jane" -> "diane"
-}"""
-
- self.assertTrue(
- os.path.isfile("tree.dot"), "The file tree.dot could not be found."
- )
- generated = self.read_generated_output("tree.dot")
-
- self.assertEqual(
- generated, expected, "Generated dot tree is not the expected one"
- )
- os.remove("tree.dot")
-
- def test_export_to_dot_empty_tree(self):
- empty_tree = Tree()
- export_to_dot(empty_tree, "tree.dot")
-
- expected = """\
-digraph tree {
-}"""
- self.assertTrue(
- os.path.isfile("tree.dot"), "The file tree.dot could not be found."
- )
- generated = self.read_generated_output("tree.dot")
-
- self.assertEqual(
- expected, generated, "The generated output for an empty tree is not empty"
- )
- os.remove("tree.dot")
-
- def test_unicode_filename(self):
- tree = Tree()
- tree.create_node("Node 1", "node_1")
- export_to_dot(tree, "ŕʩϢ.dot")
-
- expected = """\
-digraph tree {
-\t"node_1" [label="Node 1", shape=circle]
-}"""
- self.assertTrue(
- os.path.isfile("ŕʩϢ.dot"), "The file ŕʩϢ.dot could not be found."
- )
- generated = self.read_generated_output("ŕʩϢ.dot")
- self.assertEqual(
- expected, generated, "The generated file content is not the expected one"
- )
- os.remove("ŕʩϢ.dot")
-
- def test_export_with_minus_in_filename(self):
- tree = Tree()
- tree.create_node("Example Node", "example-node")
- expected = """\
-digraph tree {
-\t"example-node" [label="Example Node", shape=circle]
-}"""
-
- export_to_dot(tree, "id_with_minus.dot")
- self.assertTrue(
- os.path.isfile("id_with_minus.dot"),
- "The file id_with_minus.dot could not be found.",
- )
- generated = self.read_generated_output("id_with_minus.dot")
- self.assertEqual(
- expected, generated, "The generated file content is not the expected one"
- )
- os.remove("id_with_minus.dot")
-
- def tearDown(self):
- self.tree = None
diff --git a/tests/test_tree.py b/tests/test_tree.py
index 287b734..6fd6a64 100644
--- a/tests/test_tree.py
+++ b/tests/test_tree.py
@@ -35,6 +35,14 @@ class TreeCase(unittest.TestCase):
# |-- George
self.tree = tree
self.copytree = Tree(self.tree, deep=True)
+ self.input_dict = {
+ "Bill": "Harry",
+ "Jane": "Harry",
+ "Harry": None,
+ "Diane": "Jane",
+ "Mark": "Jane",
+ "Mary": "Harry",
+ }
@staticmethod
def get_t1():
@@ -734,3 +742,29 @@ Hárry
t.create_node(identifier="root-B")
self.assertEqual(len(t.nodes.keys()), 1)
self.assertEqual(t.root, "root-B")
+
+ def test_from_map(self):
+ tree = Tree.from_map(self.input_dict)
+ self.assertTrue(tree.size() == 6)
+ self.assertTrue(
+ tree.root == [k for k, v in self.input_dict.items() if v is None][0]
+ )
+ tree = Tree.from_map(self.input_dict, id_func=lambda x: x.upper())
+ self.assertTrue(tree.size() == 6)
+
+ def data_func(x):
+ return x.upper()
+
+ tree = Tree.from_map(self.input_dict, data_func=data_func)
+ self.assertTrue(tree.size() == 6)
+ self.assertTrue(
+ tree.get_node(tree.root).data
+ == data_func([k for k, v in self.input_dict.items() if v is None][0])
+ )
+ with self.assertRaises(ValueError):
+ # invalid input payload without a root
+ tree = Tree.from_map({"a": "b"})
+
+ with self.assertRaises(ValueError):
+ # invalid input payload without more than 1 root
+ tree = Tree.from_map({"a": None, "b": None})
diff --git a/treelib/plugins.py b/treelib/plugins.py
deleted file mode 100644
index 0736c73..0000000
--- a/treelib/plugins.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (C) 2011
-# Brett Alistair Kromkamp - brettkromkamp@gmail.com
-# Copyright (C) 2012-2017
-# Xiaming Chen - chenxm35@gmail.com
-# and other contributors.
-# All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-This is a public location to maintain contributed
-utilities to extend the basic Tree class.
-
-Deprecated! We prefer a unified processing of Tree object.
-"""
-from __future__ import unicode_literals
-
-from .misc import deprecated
-
-
-@deprecated(alias="tree.to_graphviz()")
-def export_to_dot(tree, filename=None, shape="circle", graph="digraph"):
- """Exports the tree in the dot format of the graphviz software"""
- tree.to_graphviz(filename=filename, shape=shape, graph=graph)
diff --git a/treelib/tree.py b/treelib/tree.py
index 7d92297..1cc9ac8 100644
--- a/treelib/tree.py
+++ b/treelib/tree.py
@@ -929,7 +929,7 @@ class Tree(object):
print("Tree is empty")
if stdout:
- print(self._reader)
+ print(self._reader.encode("utf-8"))
else:
return self._reader
@@ -1128,3 +1128,42 @@ class Tree(object):
print(f.getvalue())
f.close()
+
+ @classmethod
+ def from_map(cls, child_parent_dict, id_func=None, data_func=None):
+ """
+ takes a dict with child:parent, and form a tree
+ """
+ tree = Tree()
+ if tree is None or tree.size() > 0:
+ raise ValueError("need to pass in an empty tree")
+ id_func = id_func if id_func else lambda x: x
+ data_func = data_func if data_func else lambda x: None
+ parent_child_dict = {}
+ root_node = None
+ for k, v in child_parent_dict.items():
+ if v is None and root_node is None:
+ root_node = k
+ elif v is None and root_node is not None:
+ raise ValueError("invalid input, more than 1 child has no parent")
+ else:
+ if v in parent_child_dict:
+ parent_child_dict[v].append(k)
+ else:
+ parent_child_dict[v] = [k]
+ if root_node is None:
+ raise ValueError("cannot find root")
+
+ tree.create_node(root_node, id_func(root_node), data=data_func(root_node))
+ queue = [root_node]
+ while len(queue) > 0:
+ parent_node = queue.pop()
+ for child in parent_child_dict.get(parent_node, []):
+ tree.create_node(
+ child,
+ id_func(child),
+ parent=id_func(parent_node),
+ data=data_func(child),
+ )
+ queue.append(child)
+ return tree