pizza-lib 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pizza_lib-0.1.0/LICENSE +21 -0
- pizza_lib-0.1.0/PKG-INFO +89 -0
- pizza_lib-0.1.0/README.md +64 -0
- pizza_lib-0.1.0/pizza_lib.egg-info/PKG-INFO +89 -0
- pizza_lib-0.1.0/pizza_lib.egg-info/SOURCES.txt +34 -0
- pizza_lib-0.1.0/pizza_lib.egg-info/dependency_links.txt +1 -0
- pizza_lib-0.1.0/pizza_lib.egg-info/requires.txt +9 -0
- pizza_lib-0.1.0/pizza_lib.egg-info/top_level.txt +1 -0
- pizza_lib-0.1.0/pydsa/__init__.py +25 -0
- pizza_lib-0.1.0/pydsa/_treeformat.py +224 -0
- pizza_lib-0.1.0/pydsa/adj.py +166 -0
- pizza_lib-0.1.0/pydsa/arith.py +19 -0
- pizza_lib-0.1.0/pydsa/bst.py +78 -0
- pizza_lib-0.1.0/pydsa/combo.py +37 -0
- pizza_lib-0.1.0/pydsa/counting.py +64 -0
- pizza_lib-0.1.0/pydsa/digits.py +50 -0
- pizza_lib-0.1.0/pydsa/generators.py +21 -0
- pizza_lib-0.1.0/pydsa/graphs.py +441 -0
- pizza_lib-0.1.0/pydsa/grid.py +206 -0
- pizza_lib-0.1.0/pydsa/heaps.py +128 -0
- pizza_lib-0.1.0/pydsa/linked.py +77 -0
- pizza_lib-0.1.0/pydsa/nodes.py +72 -0
- pizza_lib-0.1.0/pydsa/search.py +248 -0
- pizza_lib-0.1.0/pydsa/seq.py +22 -0
- pizza_lib-0.1.0/pydsa/tree.py +299 -0
- pizza_lib-0.1.0/pyproject.toml +48 -0
- pizza_lib-0.1.0/setup.cfg +4 -0
- pizza_lib-0.1.0/tests/test_adj.py +119 -0
- pizza_lib-0.1.0/tests/test_combo.py +35 -0
- pizza_lib-0.1.0/tests/test_counting.py +40 -0
- pizza_lib-0.1.0/tests/test_digits.py +74 -0
- pizza_lib-0.1.0/tests/test_graphs.py +106 -0
- pizza_lib-0.1.0/tests/test_grid.py +183 -0
- pizza_lib-0.1.0/tests/test_search.py +63 -0
- pizza_lib-0.1.0/tests/test_seq.py +26 -0
- pizza_lib-0.1.0/tests/test_tree.py +80 -0
pizza_lib-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shyal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pizza_lib-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pizza-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Data structure and algorithm helpers for LeetCode-style Python: grids, adjacency, binary search bounds, heaps, trees, linked lists.
|
|
5
|
+
Author: Shyal
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shyal/pydsa
|
|
8
|
+
Project-URL: Repository, https://github.com/shyal/pydsa
|
|
9
|
+
Keywords: leetcode,algorithms,data-structures,grid,graph,heap,binary-search
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Classifier: Topic :: Education
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Provides-Extra: draw
|
|
18
|
+
Requires-Dist: rich; extra == "draw"
|
|
19
|
+
Requires-Dist: colorama; extra == "draw"
|
|
20
|
+
Requires-Dist: PrettyPrintTree; extra == "draw"
|
|
21
|
+
Requires-Dist: graphviz; extra == "draw"
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# pydsa
|
|
27
|
+
|
|
28
|
+
Data structure and algorithm helpers for LeetCode-style Python: the
|
|
29
|
+
functions one keeps rewriting at the top of a solution, written once and
|
|
30
|
+
tested.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
pip install pizza-lib # the import name is pydsa
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from pydsa import table, cells, nbrs, first_true, Multiset, maxheappush
|
|
38
|
+
|
|
39
|
+
dp = table(3, 4) # a 3 by 4 grid of zeros
|
|
40
|
+
for i, j in cells(grid): # every cell, row by row
|
|
41
|
+
for ni, nj in nbrs(grid, i, j): # its on-grid neighbours
|
|
42
|
+
...
|
|
43
|
+
first_true(1, 10**9, lambda x: x * x >= n) # the first x in [1, 10^9] with ok(x)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Modules
|
|
47
|
+
|
|
48
|
+
| Module | Names |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `grid` | `table`, `like`, `shape`, `cells`, `nbrs`, `in_bounds`, `is_edge`, `edges`, `put`, `row`, `col`, `set_row`, `set_col`, `grid_bfs`, `CARDINALS`, `DIAGONALS`, `ALL_EIGHT` |
|
|
51
|
+
| `adj` | `adjacency`, `indegrees`, `levels`, `components`, `Graph`, `graph`, `dijkstra` |
|
|
52
|
+
| `search` | `first_true`, `last_true`, `first_false`, `last_false`, `min_chunks`, `viz_binary_search` |
|
|
53
|
+
| `heaps` | `maxheappush`, `maxheappop`, `maxheapify`, `maxheappushpop`, `maxheapreplace`, `maxheappeek`, `maxheapitems`, `heap`, `draw_heap` |
|
|
54
|
+
| `counting` | `Multiset`, `count_pairs` |
|
|
55
|
+
| `combo` | `pairs`, `triples` |
|
|
56
|
+
| `digits` | `to_digits`, `to_int`, `even`, `odd` |
|
|
57
|
+
| `arith` | `ceil_div`, `batched` |
|
|
58
|
+
| `seq` | `lcs` |
|
|
59
|
+
| `generators` | `as_list` |
|
|
60
|
+
| `nodes` | `TreeNode`, `ListNode`, `GraphNode`, `Node` |
|
|
61
|
+
| `tree` | `build_tree`, `get_level_order`, `find_node`, `get_inorder`, `is_balanced`, `build_nary_tree`, `build_general_tree`, `get_general_tree`, `generate_random_tree`, `generate_full_binary_tree`, `draw_tree`, `draw_general_tree` |
|
|
62
|
+
| `bst` | `insert`, `generate_random_bst`, `tree_to_list`, `is_valid_bst`, `generate_and_print_random_bst` |
|
|
63
|
+
| `linked` | `build_linked_list`, `get_list_values`, `print_linked_list`, `linked_list_reverse_k`, `draw_linked_list` |
|
|
64
|
+
| `graphs` | `build_graph`, `get_adj_list`, `build_graph_from_edge_list`, `as_dict_of_dicts`, `is_edge_list`, `draw_graph`, `draw_graphviz`, `draw_ascii_graph` |
|
|
65
|
+
|
|
66
|
+
Every public name is importable from the package itself. The package
|
|
67
|
+
imports only the standard library. The `draw_*` functions, `tabulate`-style
|
|
68
|
+
output and `viz_binary_search` import rich, colorama, PrettyPrintTree and
|
|
69
|
+
graphviz when first called:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
pip install 'pizza-lib[draw]'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Every function has a docstring with its contract; the tests in `tests/`
|
|
76
|
+
are the examples.
|
|
77
|
+
|
|
78
|
+
## Where it comes from
|
|
79
|
+
|
|
80
|
+
These helpers were the harness of the `leet` repo, preloaded into
|
|
81
|
+
`builtins` for every solve there, and the library that the `mu` language
|
|
82
|
+
pastes into the Python it emits. Both still use them from here.
|
|
83
|
+
|
|
84
|
+
## Tests
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
pip install -e '.[test]'
|
|
88
|
+
pytest
|
|
89
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# pydsa
|
|
2
|
+
|
|
3
|
+
Data structure and algorithm helpers for LeetCode-style Python: the
|
|
4
|
+
functions one keeps rewriting at the top of a solution, written once and
|
|
5
|
+
tested.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install pizza-lib # the import name is pydsa
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from pydsa import table, cells, nbrs, first_true, Multiset, maxheappush
|
|
13
|
+
|
|
14
|
+
dp = table(3, 4) # a 3 by 4 grid of zeros
|
|
15
|
+
for i, j in cells(grid): # every cell, row by row
|
|
16
|
+
for ni, nj in nbrs(grid, i, j): # its on-grid neighbours
|
|
17
|
+
...
|
|
18
|
+
first_true(1, 10**9, lambda x: x * x >= n) # the first x in [1, 10^9] with ok(x)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Modules
|
|
22
|
+
|
|
23
|
+
| Module | Names |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `grid` | `table`, `like`, `shape`, `cells`, `nbrs`, `in_bounds`, `is_edge`, `edges`, `put`, `row`, `col`, `set_row`, `set_col`, `grid_bfs`, `CARDINALS`, `DIAGONALS`, `ALL_EIGHT` |
|
|
26
|
+
| `adj` | `adjacency`, `indegrees`, `levels`, `components`, `Graph`, `graph`, `dijkstra` |
|
|
27
|
+
| `search` | `first_true`, `last_true`, `first_false`, `last_false`, `min_chunks`, `viz_binary_search` |
|
|
28
|
+
| `heaps` | `maxheappush`, `maxheappop`, `maxheapify`, `maxheappushpop`, `maxheapreplace`, `maxheappeek`, `maxheapitems`, `heap`, `draw_heap` |
|
|
29
|
+
| `counting` | `Multiset`, `count_pairs` |
|
|
30
|
+
| `combo` | `pairs`, `triples` |
|
|
31
|
+
| `digits` | `to_digits`, `to_int`, `even`, `odd` |
|
|
32
|
+
| `arith` | `ceil_div`, `batched` |
|
|
33
|
+
| `seq` | `lcs` |
|
|
34
|
+
| `generators` | `as_list` |
|
|
35
|
+
| `nodes` | `TreeNode`, `ListNode`, `GraphNode`, `Node` |
|
|
36
|
+
| `tree` | `build_tree`, `get_level_order`, `find_node`, `get_inorder`, `is_balanced`, `build_nary_tree`, `build_general_tree`, `get_general_tree`, `generate_random_tree`, `generate_full_binary_tree`, `draw_tree`, `draw_general_tree` |
|
|
37
|
+
| `bst` | `insert`, `generate_random_bst`, `tree_to_list`, `is_valid_bst`, `generate_and_print_random_bst` |
|
|
38
|
+
| `linked` | `build_linked_list`, `get_list_values`, `print_linked_list`, `linked_list_reverse_k`, `draw_linked_list` |
|
|
39
|
+
| `graphs` | `build_graph`, `get_adj_list`, `build_graph_from_edge_list`, `as_dict_of_dicts`, `is_edge_list`, `draw_graph`, `draw_graphviz`, `draw_ascii_graph` |
|
|
40
|
+
|
|
41
|
+
Every public name is importable from the package itself. The package
|
|
42
|
+
imports only the standard library. The `draw_*` functions, `tabulate`-style
|
|
43
|
+
output and `viz_binary_search` import rich, colorama, PrettyPrintTree and
|
|
44
|
+
graphviz when first called:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
pip install 'pizza-lib[draw]'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Every function has a docstring with its contract; the tests in `tests/`
|
|
51
|
+
are the examples.
|
|
52
|
+
|
|
53
|
+
## Where it comes from
|
|
54
|
+
|
|
55
|
+
These helpers were the harness of the `leet` repo, preloaded into
|
|
56
|
+
`builtins` for every solve there, and the library that the `mu` language
|
|
57
|
+
pastes into the Python it emits. Both still use them from here.
|
|
58
|
+
|
|
59
|
+
## Tests
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
pip install -e '.[test]'
|
|
63
|
+
pytest
|
|
64
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pizza-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Data structure and algorithm helpers for LeetCode-style Python: grids, adjacency, binary search bounds, heaps, trees, linked lists.
|
|
5
|
+
Author: Shyal
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shyal/pydsa
|
|
8
|
+
Project-URL: Repository, https://github.com/shyal/pydsa
|
|
9
|
+
Keywords: leetcode,algorithms,data-structures,grid,graph,heap,binary-search
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Classifier: Topic :: Education
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Provides-Extra: draw
|
|
18
|
+
Requires-Dist: rich; extra == "draw"
|
|
19
|
+
Requires-Dist: colorama; extra == "draw"
|
|
20
|
+
Requires-Dist: PrettyPrintTree; extra == "draw"
|
|
21
|
+
Requires-Dist: graphviz; extra == "draw"
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# pydsa
|
|
27
|
+
|
|
28
|
+
Data structure and algorithm helpers for LeetCode-style Python: the
|
|
29
|
+
functions one keeps rewriting at the top of a solution, written once and
|
|
30
|
+
tested.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
pip install pizza-lib # the import name is pydsa
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from pydsa import table, cells, nbrs, first_true, Multiset, maxheappush
|
|
38
|
+
|
|
39
|
+
dp = table(3, 4) # a 3 by 4 grid of zeros
|
|
40
|
+
for i, j in cells(grid): # every cell, row by row
|
|
41
|
+
for ni, nj in nbrs(grid, i, j): # its on-grid neighbours
|
|
42
|
+
...
|
|
43
|
+
first_true(1, 10**9, lambda x: x * x >= n) # the first x in [1, 10^9] with ok(x)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Modules
|
|
47
|
+
|
|
48
|
+
| Module | Names |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `grid` | `table`, `like`, `shape`, `cells`, `nbrs`, `in_bounds`, `is_edge`, `edges`, `put`, `row`, `col`, `set_row`, `set_col`, `grid_bfs`, `CARDINALS`, `DIAGONALS`, `ALL_EIGHT` |
|
|
51
|
+
| `adj` | `adjacency`, `indegrees`, `levels`, `components`, `Graph`, `graph`, `dijkstra` |
|
|
52
|
+
| `search` | `first_true`, `last_true`, `first_false`, `last_false`, `min_chunks`, `viz_binary_search` |
|
|
53
|
+
| `heaps` | `maxheappush`, `maxheappop`, `maxheapify`, `maxheappushpop`, `maxheapreplace`, `maxheappeek`, `maxheapitems`, `heap`, `draw_heap` |
|
|
54
|
+
| `counting` | `Multiset`, `count_pairs` |
|
|
55
|
+
| `combo` | `pairs`, `triples` |
|
|
56
|
+
| `digits` | `to_digits`, `to_int`, `even`, `odd` |
|
|
57
|
+
| `arith` | `ceil_div`, `batched` |
|
|
58
|
+
| `seq` | `lcs` |
|
|
59
|
+
| `generators` | `as_list` |
|
|
60
|
+
| `nodes` | `TreeNode`, `ListNode`, `GraphNode`, `Node` |
|
|
61
|
+
| `tree` | `build_tree`, `get_level_order`, `find_node`, `get_inorder`, `is_balanced`, `build_nary_tree`, `build_general_tree`, `get_general_tree`, `generate_random_tree`, `generate_full_binary_tree`, `draw_tree`, `draw_general_tree` |
|
|
62
|
+
| `bst` | `insert`, `generate_random_bst`, `tree_to_list`, `is_valid_bst`, `generate_and_print_random_bst` |
|
|
63
|
+
| `linked` | `build_linked_list`, `get_list_values`, `print_linked_list`, `linked_list_reverse_k`, `draw_linked_list` |
|
|
64
|
+
| `graphs` | `build_graph`, `get_adj_list`, `build_graph_from_edge_list`, `as_dict_of_dicts`, `is_edge_list`, `draw_graph`, `draw_graphviz`, `draw_ascii_graph` |
|
|
65
|
+
|
|
66
|
+
Every public name is importable from the package itself. The package
|
|
67
|
+
imports only the standard library. The `draw_*` functions, `tabulate`-style
|
|
68
|
+
output and `viz_binary_search` import rich, colorama, PrettyPrintTree and
|
|
69
|
+
graphviz when first called:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
pip install 'pizza-lib[draw]'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Every function has a docstring with its contract; the tests in `tests/`
|
|
76
|
+
are the examples.
|
|
77
|
+
|
|
78
|
+
## Where it comes from
|
|
79
|
+
|
|
80
|
+
These helpers were the harness of the `leet` repo, preloaded into
|
|
81
|
+
`builtins` for every solve there, and the library that the `mu` language
|
|
82
|
+
pastes into the Python it emits. Both still use them from here.
|
|
83
|
+
|
|
84
|
+
## Tests
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
pip install -e '.[test]'
|
|
88
|
+
pytest
|
|
89
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
pizza_lib.egg-info/PKG-INFO
|
|
5
|
+
pizza_lib.egg-info/SOURCES.txt
|
|
6
|
+
pizza_lib.egg-info/dependency_links.txt
|
|
7
|
+
pizza_lib.egg-info/requires.txt
|
|
8
|
+
pizza_lib.egg-info/top_level.txt
|
|
9
|
+
pydsa/__init__.py
|
|
10
|
+
pydsa/_treeformat.py
|
|
11
|
+
pydsa/adj.py
|
|
12
|
+
pydsa/arith.py
|
|
13
|
+
pydsa/bst.py
|
|
14
|
+
pydsa/combo.py
|
|
15
|
+
pydsa/counting.py
|
|
16
|
+
pydsa/digits.py
|
|
17
|
+
pydsa/generators.py
|
|
18
|
+
pydsa/graphs.py
|
|
19
|
+
pydsa/grid.py
|
|
20
|
+
pydsa/heaps.py
|
|
21
|
+
pydsa/linked.py
|
|
22
|
+
pydsa/nodes.py
|
|
23
|
+
pydsa/search.py
|
|
24
|
+
pydsa/seq.py
|
|
25
|
+
pydsa/tree.py
|
|
26
|
+
tests/test_adj.py
|
|
27
|
+
tests/test_combo.py
|
|
28
|
+
tests/test_counting.py
|
|
29
|
+
tests/test_digits.py
|
|
30
|
+
tests/test_graphs.py
|
|
31
|
+
tests/test_grid.py
|
|
32
|
+
tests/test_search.py
|
|
33
|
+
tests/test_seq.py
|
|
34
|
+
tests/test_tree.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydsa
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""pydsa: data structures and algorithm helpers for LeetCode-style Python.
|
|
2
|
+
|
|
3
|
+
Every public name of every module is importable from the package. The
|
|
4
|
+
package imports only the standard library; the drawing functions import
|
|
5
|
+
rich, colorama, PrettyPrintTree and graphviz when first called
|
|
6
|
+
(`pip install pydsa[draw]`).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .adj import * # noqa: F401,F403
|
|
10
|
+
from .arith import * # noqa: F401,F403
|
|
11
|
+
from .bst import * # noqa: F401,F403
|
|
12
|
+
from .combo import * # noqa: F401,F403
|
|
13
|
+
from .counting import * # noqa: F401,F403
|
|
14
|
+
from .digits import * # noqa: F401,F403
|
|
15
|
+
from .generators import * # noqa: F401,F403
|
|
16
|
+
from .graphs import * # noqa: F401,F403
|
|
17
|
+
from .grid import * # noqa: F401,F403
|
|
18
|
+
from .heaps import * # noqa: F401,F403
|
|
19
|
+
from .linked import * # noqa: F401,F403
|
|
20
|
+
from .nodes import * # noqa: F401,F403
|
|
21
|
+
from .search import * # noqa: F401,F403
|
|
22
|
+
from .seq import * # noqa: F401,F403
|
|
23
|
+
from .tree import * # noqa: F401,F403
|
|
24
|
+
|
|
25
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from typing import Any, Callable, Generic, Iterable, TypeVar
|
|
2
|
+
|
|
3
|
+
from PrettyPrint.PrintTree.HorizontalTree import (
|
|
4
|
+
add_parent as add_parent_left,
|
|
5
|
+
)
|
|
6
|
+
from PrettyPrint.PrintTree.HorizontalTree import (
|
|
7
|
+
join_vertically,
|
|
8
|
+
)
|
|
9
|
+
from PrettyPrint.PrintTree.VerticalTree import (
|
|
10
|
+
add_parent as add_parent_top,
|
|
11
|
+
)
|
|
12
|
+
from PrettyPrint.PrintTree.VerticalTree import (
|
|
13
|
+
join_horizontally,
|
|
14
|
+
)
|
|
15
|
+
from PrettyPrint.Utils.NodeFormatter import NodeFormatter
|
|
16
|
+
from PrettyPrint.Utils.Orientation import Orientation
|
|
17
|
+
from PrettyPrint.Utils.StyleAwareUtils import trim_text
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
# ANSI escape codes for connector foreground colors
|
|
22
|
+
RED = "\033[31m"
|
|
23
|
+
BLUE = "\033[34m"
|
|
24
|
+
RESET = "\033[0m"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TreeFormatter(Generic[T]):
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
get_children: Callable[[T], Iterable[T]],
|
|
31
|
+
get_val: Callable[[T], Any],
|
|
32
|
+
get_label: Callable[[T], Any] | None,
|
|
33
|
+
label_color: str,
|
|
34
|
+
show_newline_literal: bool,
|
|
35
|
+
newline_literal: str,
|
|
36
|
+
trim: int,
|
|
37
|
+
trim_symbol: str,
|
|
38
|
+
start_message: Callable[[T], str] | None,
|
|
39
|
+
color: str,
|
|
40
|
+
border: bool,
|
|
41
|
+
max_depth: int,
|
|
42
|
+
orientation: bool,
|
|
43
|
+
):
|
|
44
|
+
self.get_children = get_children
|
|
45
|
+
self.get_node_val = get_val
|
|
46
|
+
self.get_label = get_label
|
|
47
|
+
self.label_color = label_color
|
|
48
|
+
self.trim = trim
|
|
49
|
+
self.trim_symbol = trim_symbol
|
|
50
|
+
self.show_newline = show_newline_literal
|
|
51
|
+
self.newline_literal = newline_literal
|
|
52
|
+
self.start_message = start_message
|
|
53
|
+
self.color = color
|
|
54
|
+
self.border = border
|
|
55
|
+
self.max_depth = max_depth
|
|
56
|
+
self.orientation = orientation
|
|
57
|
+
|
|
58
|
+
def format(self, node: T) -> str:
|
|
59
|
+
if self.orientation == Orientation.Vertical:
|
|
60
|
+
res = self.tree_vertical_join(node)
|
|
61
|
+
else:
|
|
62
|
+
res = self.tree_horizontal_join(node)
|
|
63
|
+
res = res.to_str().rstrip()
|
|
64
|
+
if self.start_message:
|
|
65
|
+
return f"{ self.start_message(node) }\n{ res }"
|
|
66
|
+
return res
|
|
67
|
+
|
|
68
|
+
def tree_vertical_join(
|
|
69
|
+
self, tree_node: T, depth: int = 0, is_left: bool | None = None
|
|
70
|
+
) -> NodeFormatter:
|
|
71
|
+
"""
|
|
72
|
+
tree_node: actual user tree node (T)
|
|
73
|
+
returns: NodeFormatter for the subtree rooted at tree_node
|
|
74
|
+
is_left: whether tree_node itself is a left child of its parent (used to color the connector to its parent)
|
|
75
|
+
"""
|
|
76
|
+
label = self.get_label(tree_node) if self.get_label else None
|
|
77
|
+
|
|
78
|
+
# Get raw children as provided by get_children (may omit None)
|
|
79
|
+
children_raw = list(self.get_children(tree_node))
|
|
80
|
+
|
|
81
|
+
# Create NodeFormatter for current node (styles/colors/trim etc.)
|
|
82
|
+
node = self.add_styles(tree_node)
|
|
83
|
+
|
|
84
|
+
if children_raw and (self.max_depth == -1 or depth < self.max_depth):
|
|
85
|
+
# Build formatted children and remember whether each child is left/right
|
|
86
|
+
children_with_flags = []
|
|
87
|
+
for i, child in enumerate(children_raw):
|
|
88
|
+
# Determine is_left flag correctly for binary trees:
|
|
89
|
+
# - If two children are present, index 0 is left, index 1 is right.
|
|
90
|
+
# - If only one child is present, try to inspect tree_node.left/right attributes.
|
|
91
|
+
if len(children_raw) == 2:
|
|
92
|
+
child_is_left = i == 0
|
|
93
|
+
elif len(children_raw) == 1:
|
|
94
|
+
left_attr = getattr(tree_node, "left", object())
|
|
95
|
+
right_attr = getattr(tree_node, "right", object())
|
|
96
|
+
if child is left_attr:
|
|
97
|
+
child_is_left = True
|
|
98
|
+
elif child is right_attr:
|
|
99
|
+
child_is_left = False
|
|
100
|
+
else:
|
|
101
|
+
# Fallback (best-effort): treat single child as left if we can't detect
|
|
102
|
+
child_is_left = i == 0
|
|
103
|
+
else:
|
|
104
|
+
# No children (shouldn't reach here because children_raw is truthy)
|
|
105
|
+
child_is_left = None
|
|
106
|
+
|
|
107
|
+
child_fmt = self.tree_vertical_join(
|
|
108
|
+
child, depth + 1, is_left=child_is_left
|
|
109
|
+
)
|
|
110
|
+
children_with_flags.append((child_fmt, child_is_left))
|
|
111
|
+
|
|
112
|
+
if len(children_with_flags) == 1:
|
|
113
|
+
# Single-child case: insert a colored '|' connector above the child
|
|
114
|
+
child_node_fmt, child_is_left = children_with_flags[0]
|
|
115
|
+
if child_is_left is True:
|
|
116
|
+
edge_str = f"{RED}/{RESET}"
|
|
117
|
+
elif child_is_left is False:
|
|
118
|
+
edge_str = f"{BLUE}\\{RESET}"
|
|
119
|
+
else:
|
|
120
|
+
edge_str = "|"
|
|
121
|
+
child_node_fmt.lines.insert(
|
|
122
|
+
0, " " * child_node_fmt.get_middle_width() + edge_str
|
|
123
|
+
)
|
|
124
|
+
children_node = child_node_fmt
|
|
125
|
+
else:
|
|
126
|
+
# Two children: join them horizontally (we rely on join_horizontally for placement)
|
|
127
|
+
children_node = join_horizontally(
|
|
128
|
+
[fmt for fmt, _ in children_with_flags]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
node = add_parent_top(node, children_node)
|
|
132
|
+
|
|
133
|
+
node = self.add_label(label, node, add_parent_top, "|")
|
|
134
|
+
return node
|
|
135
|
+
|
|
136
|
+
def tree_horizontal_join(
|
|
137
|
+
self, tree_node: T, depth: int = 0, is_left: bool | None = None
|
|
138
|
+
) -> NodeFormatter:
|
|
139
|
+
"""
|
|
140
|
+
Horizontal join variant (similar logic to vertical).
|
|
141
|
+
"""
|
|
142
|
+
label = self.get_label(tree_node) if self.get_label else None
|
|
143
|
+
children_raw = list(self.get_children(tree_node))
|
|
144
|
+
node = self.add_styles(tree_node)
|
|
145
|
+
node_padding = " " * node.width
|
|
146
|
+
node.lines = node.lines + [node_padding]
|
|
147
|
+
node.height += 1
|
|
148
|
+
|
|
149
|
+
if children_raw and (self.max_depth == -1 or depth < self.max_depth):
|
|
150
|
+
children_with_flags = []
|
|
151
|
+
for i, child in enumerate(children_raw):
|
|
152
|
+
if len(children_raw) == 2:
|
|
153
|
+
child_is_left = i == 0
|
|
154
|
+
elif len(children_raw) == 1:
|
|
155
|
+
left_attr = getattr(tree_node, "left", object())
|
|
156
|
+
right_attr = getattr(tree_node, "right", object())
|
|
157
|
+
if child is left_attr:
|
|
158
|
+
child_is_left = True
|
|
159
|
+
elif child is right_attr:
|
|
160
|
+
child_is_left = False
|
|
161
|
+
else:
|
|
162
|
+
child_is_left = i == 0
|
|
163
|
+
else:
|
|
164
|
+
child_is_left = None
|
|
165
|
+
|
|
166
|
+
child_fmt = self.tree_horizontal_join(
|
|
167
|
+
child, depth + 1, is_left=child_is_left
|
|
168
|
+
)
|
|
169
|
+
children_with_flags.append((child_fmt, child_is_left))
|
|
170
|
+
|
|
171
|
+
if len(children_with_flags) == 1:
|
|
172
|
+
children_node, child_is_left = children_with_flags[0]
|
|
173
|
+
middle = children_node.get_middle_height()
|
|
174
|
+
new_lines = []
|
|
175
|
+
for r_idx, line in enumerate(children_node.lines):
|
|
176
|
+
if r_idx == middle:
|
|
177
|
+
if child_is_left is True:
|
|
178
|
+
edge_str = f"{RED}─{RESET}"
|
|
179
|
+
elif child_is_left is False:
|
|
180
|
+
edge_str = f"{BLUE}─{RESET}"
|
|
181
|
+
else:
|
|
182
|
+
edge_str = "─"
|
|
183
|
+
new_lines.append(edge_str + line)
|
|
184
|
+
else:
|
|
185
|
+
new_lines.append(" " + line)
|
|
186
|
+
children_node.lines = new_lines
|
|
187
|
+
else:
|
|
188
|
+
children_node = join_vertically([fmt for fmt, _ in children_with_flags])
|
|
189
|
+
|
|
190
|
+
node = add_parent_left(node, children_node)
|
|
191
|
+
|
|
192
|
+
node = self.add_label(label, node, add_parent_left, "─")
|
|
193
|
+
return node
|
|
194
|
+
|
|
195
|
+
def add_label(
|
|
196
|
+
self,
|
|
197
|
+
label: Any,
|
|
198
|
+
node: NodeFormatter,
|
|
199
|
+
parent_adder: Callable[[NodeFormatter, NodeFormatter], NodeFormatter],
|
|
200
|
+
seperator: str,
|
|
201
|
+
) -> NodeFormatter:
|
|
202
|
+
if label:
|
|
203
|
+
label = NodeFormatter.from_string(str(label))
|
|
204
|
+
if self.label_color:
|
|
205
|
+
label.color_bg(self.label_color, True)
|
|
206
|
+
node = parent_adder(NodeFormatter.from_string(seperator), node)
|
|
207
|
+
node = parent_adder(label, node)
|
|
208
|
+
return node
|
|
209
|
+
|
|
210
|
+
def add_styles(self, tree_node: T) -> NodeFormatter:
|
|
211
|
+
"""
|
|
212
|
+
Build and return a NodeFormatter for the given tree node (apply trim, newline literal, background color, border).
|
|
213
|
+
"""
|
|
214
|
+
contents = str(self.get_node_val(tree_node))
|
|
215
|
+
if self.show_newline:
|
|
216
|
+
contents = contents.replace("\n", self.newline_literal)
|
|
217
|
+
if self.trim != -1:
|
|
218
|
+
contents = trim_text(contents, self.trim, self.trim_symbol)
|
|
219
|
+
node = NodeFormatter.from_string(contents)
|
|
220
|
+
if self.border:
|
|
221
|
+
node.add_border()
|
|
222
|
+
if self.color:
|
|
223
|
+
node.color_bg(self.color, add_space=not self.border)
|
|
224
|
+
return node
|