From 0391d056604458068132313730b9d36be055087b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 06:58:50 +0200 Subject: Adding upstream version 1.7.0. Signed-off-by: Daniel Baumann --- .github/workflows/python-package.yml | 25 +------- README.md | 2 - setup.py | 8 +-- tests/test_plugins.py | 113 ----------------------------------- tests/test_tree.py | 34 +++++++++++ treelib/plugins.py | 35 ----------- treelib/tree.py | 41 ++++++++++++- 7 files changed, 78 insertions(+), 180 deletions(-) delete mode 100644 tests/test_plugins.py delete mode 100644 treelib/plugins.py 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 -- cgit v1.2.3