From ac1bd55d07fd1bad2f4a92dc0809607c407d9140 Mon Sep 17 00:00:00 2001
From: "Maciej A. Czyzewski" <maciejanthonyczyzewski@gmail.com>
Date: Wed, 9 Jun 2021 15:29:18 +0200
Subject: [PATCH] feature: new params for graphviz + solves #70

- solve abs path bug #70
- new params for graphviz (ranksep; layout)
- tested layout `dot`; `fdp` (square graph)
- updated `.gitignore` (files gen. after `visualize_pyan_architecture.sh`)
---
 .gitignore                     |  5 ++++
 README.md                      |  6 ++--
 pyan/main.py                   | 55 +++++++++++++++++++++++++++++++---
 visualize_pyan_architecture.sh |  5 ++++
 4 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
index 990fdc0c..93313aaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -162,3 +162,8 @@ htmlcov
 .idea/
 .history/
 .vscode/
+
+# our vis. of architecture
+architecture.dot
+architecture.html
+architecture.svg
diff --git a/README.md b/README.md
index d1f19dcf..9e6919a3 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ See `pyan3 --help`.
 
 Example:
 
-`pyan *.py --uses --no-defines --colored --grouped --annotated --dot >myuses.dot`
+`pyan3 *.py --uses --no-defines --colored --grouped --annotated --dot >myuses.dot`
 
 Then render using your favorite GraphViz filter, mainly `dot` or `fdp`:
 
@@ -56,11 +56,11 @@ Then render using your favorite GraphViz filter, mainly `dot` or `fdp`:
 
 Or use directly
 
-`pyan *.py --uses --no-defines --colored --grouped --annotated --svg >myuses.svg`
+`pyan3 *.py --uses --no-defines --colored --grouped --annotated --svg >myuses.svg`
 
 You can also export as an interactive HTML
 
-`pyan *.py --uses --no-defines --colored --grouped --annotated --html > myuses.html`
+`pyan3 *.py --uses --no-defines --colored --grouped --annotated --html > myuses.html`
 
 Alternatively, you can call `pyan` from a script
 
diff --git a/pyan/main.py b/pyan/main.py
index 5d079714..b1a16f63 100644
--- a/pyan/main.py
+++ b/pyan/main.py
@@ -141,6 +141,31 @@ def main(cli_args=None):
         ),
     )
 
+    parser.add_argument(
+        "--dot-ranksep",
+        default="0.5",
+        dest="ranksep",
+        help=(
+            "specifies the dot graph 'ranksep' property for "
+            "controlling desired rank separation, in inches. "
+            "Allowed values: [0.02 .. 1000.0]. "
+            "[dot only]"
+        ),
+    )
+
+    parser.add_argument(
+        "--graphviz-layout",
+        default="dot",
+        dest="layout",
+        help=(
+            "specifies the graphviz 'layout' property for "
+            "the name of the layout algorithm to use. "
+            "Allowed values: ['dot', 'neato', 'fdp', 'sfdp', 'twopi', 'circo']. "
+            "Recommended values: ['dot', 'fdp']. "
+            "[graphviz only]"
+        ),
+    )
+
     parser.add_argument(
         "-a",
         "--annotated",
@@ -159,7 +184,12 @@ def main(cli_args=None):
 
     known_args, unknown_args = parser.parse_known_args(cli_args)
 
-    filenames = [fn2 for fn in unknown_args for fn2 in glob(fn, recursive=True)]
+
+    filenames = []
+    for fn in unknown_args:
+        for fn2 in glob(fn, recursive=True):
+            abs_fn2 = os.path.abspath(fn2)
+            filenames.append(abs_fn2)
 
     # determine root
     if known_args.root is not None:
@@ -203,6 +233,11 @@ def main(cli_args=None):
         handler = logging.FileHandler(known_args.logname)
         logger.addHandler(handler)
 
+    logger.debug(f"[files] {unknown_args}")
+
+    if root:
+        root = os.path.abspath(root)
+
     v = CallGraphVisitor(filenames, logger=logger, root=root)
 
     if known_args.function or known_args.namespace:
@@ -222,13 +257,25 @@ def main(cli_args=None):
     writer = None
 
     if known_args.dot:
-        writer = DotWriter(graph, options=["rankdir=" + known_args.rankdir], output=known_args.filename, logger=logger)
+        writer = DotWriter(graph, options=[
+            "rankdir=" + known_args.rankdir,
+            "ranksep=" + known_args.ranksep,
+            "layout=" + known_args.layout,
+        ], output=known_args.filename, logger=logger)
 
     if known_args.html:
-        writer = HTMLWriter(graph, options=["rankdir=" + known_args.rankdir], output=known_args.filename, logger=logger)
+        writer = HTMLWriter(graph, options=[
+            "rankdir=" + known_args.rankdir,
+            "ranksep=" + known_args.ranksep,
+            "layout=" + known_args.layout,
+        ], output=known_args.filename, logger=logger)
 
     if known_args.svg:
-        writer = SVGWriter(graph, options=["rankdir=" + known_args.rankdir], output=known_args.filename, logger=logger)
+        writer = SVGWriter(graph, options=[
+            "rankdir=" + known_args.rankdir,
+            "ranksep=" + known_args.ranksep,
+            "layout=" + known_args.layout,
+        ], output=known_args.filename, logger=logger)
 
     if known_args.tgf:
         writer = TgfWriter(graph, output=known_args.filename, logger=logger)
diff --git a/visualize_pyan_architecture.sh b/visualize_pyan_architecture.sh
index 22c63342..81b6ca24 100755
--- a/visualize_pyan_architecture.sh
+++ b/visualize_pyan_architecture.sh
@@ -2,3 +2,8 @@
 echo -ne "Pyan architecture: generating architecture.{dot,svg}\n"
 python3 -m pyan pyan/*.py --no-defines --uses --colored --annotate --dot -V >architecture.dot 2>architecture.log
 dot -Tsvg architecture.dot >architecture.svg
+echo -ne "Pyan architecture: generating architecture.{html,graphviz=fdp}\n"
+python3 -m pyan pyan/*.py --no-defines --uses \
+	--grouped --nested-groups \
+	--graphviz-layout fdp \
+	--colored --html > architecture.html
