
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
phart
Advanced tools
Python Hierarchical ASCII Representation Tool - Pure Python graph visualization in ASCII, no external dependencies* (*except NetworkX)
PHART: The Python Hierarchical ASCII Representation Tool - A Pure Python graph visualization in ASCII, no external dependencies*
(* except NetworkX, which we should probably mention prominently. We just mean no dependencies of the Perl or PHP/webserver types.)
=================================
I was doing some last-minute testing and came across this, from the networkx gallery:
The code there creates a graph from some data pulled from a database of Chess masters tournaments and such at this site:
https://chessproblem.my-free-games.com/chess/games/Download-PGN.php
And plots it with matplotlib. It looked pretty complex so I thought as a lark I would see how difficult it would be to get phart to render the graph. The matplot can be seen here:

So, I added the following to the code at the networkx gallery page linked above:
from phart import ASCIIRenderer, NodeStyle
.. existing code remains here ...
... then directly below the existing lines to create the nx graph:
# make new undirected graph H without multi-edges
H = nx.Graph(G)
... I added this:
renderer=ASCIIRenderer(H)
renderer.write_to_file("wcc.txt")
and ran the code. Immediately this was written to wcc.txt:
---------------------------------[Botvinnik, Mikhail M]---------------------------------
| | | | |
v | | v | | | v
[Bronstein, David I]----[Euwe, Max]----[Keres, Paul]----[Petrosian, Tigran V]----[Reshevsky, Samuel H]----[Smyslov, Vassily V]----[Tal, Mikhail N]
^ | |
| | v
[Alekhine, Alexander A]----[Spassky, Boris V]
| | |
v | | | v
[Bogoljubow, Efim D]----[Capablanca, Jose Raul] ---[Fischer, Robert J]
|^
|
[Lasker, Emanuel]--------------
| |
v v v | v
[Janowski, Dawid M]----[Marshall, Frank J]----[Schlechter, Carl]----[Steinitz, Wilhelm]----[Tarrasch, Siegbert]
| |
v v | |
[Chigorin, Mikhail I]----[Gunsberg, Isidor A]----[Zukertort, Johannes H]
[Karpov, Anatoly]----[Kasparov, Gary]----[Korchnoi, Viktor L]
No fuss. No muss. Just phart.
[main.py]
|
v | v
[config.py]----[utils.py]
| |
v | v
[constants.py]----[helpers.py]
[CEO]
|
v v v
[CFO]----[COO]----[CTO]
| | |
v v | v | | v v
[Controller]----[Dev Lead]----[Marketing Dir]----[Research Lead]----[Sales Dir]
[Router1]
|
v | v
[Switch1]----[Switch2]
| |
v v | v
[Server1]----[Server2] [Server3]----[Server4]
[Start]
|
v
[Input]
|
|v
[Validate]
|
v|
--[Process]
| ^
| v
| [Check]
| |
| | v
[Error]----[Success]
|
v |
[Output]--
|
v|
[End]
[A]
|
v | v
[B]----[D]
| |
| v |
--[C]---
|
v
[E]
Different node styles for the same graph:
0
|
v | v
1----2
| |
v v | v
3----4 5----6
[0]
|
v | v
[1]----[2]
| |
v v | v
[3]----[4] [5]----[6]
(0)
|
v | v
(1)----(2)
| |
v v | v
(3)----(4) (5)----(6)
<0>
|
v | v
<1>----<2>
| |
v v | v
<3>----<4> <5>----<6>
Because it is necessary? OK, sorry... Actually it had a few other names early on, but when it came time to upload to PyPi, we discovered the early names we chose were already taken so we had to choose a new name. We wanted to mash up the relevant terms ("graph", "ascii", "art", "chart", and such) and bonus if the new name is a fitting acronym.
In the case of PHART, the acronym made from the first letters of the obvious first words to come to mind was discovered to spell PHART after the non abbreviated words were suggested. Fortuitous; so it had to be.
You may pronounce it the obvious monosyllabic way, or as "eff art", or perhaps "pee heart", or any way that you like, so long as the audience you are speaking it to knows it is PHART you are referring to.
The mention of not being Perl or a PHP webapp may appear to be throwing shade at the existing solutions, but it is meant in a good-hearted way. Wrapping the OG Graph::Easy is a straightforeard way to go about it, and a web interface to the same is a project I might create as well, but Perl being installed is not the sure ubiquitous thing it omce was, and spinning up a Docker container in order to add ascii art graph output to a python tool seemed a bit excessive.
Additionally, I'm not sure how I didn't find pydot2ascii - which is native python - when I first looked for a solution, but even if I had seen it I may not have realized that I could have exported my NX DAG to DOT, and then used pydot2ascii to go from DOT to ascii art.
So now we have PHART, and the ability to render a NX digraph in ASCII/Unicode, read a DOT file, read GraphML, and a few other things in a well-tested Python module published to PyPi. I hope you find it useful.
requires Python >= 3.10 and NetworkX >= 3.3
pip install phart
import networkx as nx
from phart import ASCIIRenderer
# Create a simple graph
G = nx.DiGraph()
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D")])
# Render it in ASCII
renderer = ASCIIRenderer(G)
print(renderer.render())
[A]
│
v │ v
[B]────[C]
│
│ v
──[D]
The renderer shows edge direction using arrows:
These directional indicators are particularly useful for:
PHART supports multiple character sets for rendering:
--charset unicode (default): Uses Unicode box drawing characters and arrows for
cleaner visualization--charset ascii: Uses only 7-bit ASCII characters, ensuring maximum compatibility
with all terminalsExample:
# Using Unicode (default)
phart graph.dot
# ┌─A─┐
# │ │
# └─B─┘
# Using ASCII only
phart --charset ascii graph.dot
# +-A-+
# | |
# +-B-+
pip install phart[extras]
or using requirements file
pip install -r requirements\extra.txt
>>> dot = '''
... digraph {
... A -> B
... B -> C
... }
... '''
>>> renderer = ASCIIRenderer.from_dot(dot)
>>> print(renderer.render())
A
|
B
|
C
>>>
PHART uses pydot for DOT format support. When processing DOT strings containing multiple graph definitions, only the first graph will be rendered. For more complex DOT processing needs, you can convert your graphs using NetworkX's various graph reading utilities before passing them to PHART.
PHART supports reading GraphML files:
renderer = ASCIIRenderer.from_graphml("graph.graphml")
print(renderer.render())
PHART can directly execute Python files that create and render graphs. When given a Python file, PHART will:
--function is provided)main() function if one existsif __name__ == "__main__": blockExample Python file:
import networkx as nx
from phart import ASCIIRenderer
def demonstrate_graph():
# Create a simple directed graph
G = nx.DiGraph()
G.add_edges_from([("A", "B"), ("B", "C")])
# Create renderer and display the graph
renderer = ASCIIRenderer(G)
print(renderer.render())
if __name__ == "__main__":
demonstrate_graph()
You can execute this file in several ways:
# Execute main() or __main__ block (default behavior)
phart graph.py
# Execute a specific function
phart graph.py --function demonstrate_graph
# Use specific rendering options
phart graph.py --charset ascii --style round
When executing Python files, PHART intelligently merges command-line options with any options specified in your code:
This means you can set specific options in your code while still using command-line options to adjust general rendering settings.
MIT License
FAQs
Python Hierarchical ASCII Representation Tool - Pure Python graph visualization in ASCII, no external dependencies* (*except NetworkX)
We found that phart demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.