├── MANIFEST.in
├── dynasty
├── __init__.py
├── dynasty.py
└── utils.py
├── requirements.txt
├── print_demo.png
├── widget_demo.gif
├── .gitattributes
├── setup.py
├── README.md
├── LICENSE
└── example.ipynb
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include requirements.txt
--------------------------------------------------------------------------------
/dynasty/__init__.py:
--------------------------------------------------------------------------------
1 | from .dynasty import *
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pandas
2 | numpy
3 | anytree
4 | ipytree
--------------------------------------------------------------------------------
/print_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/migolan/dynasty/HEAD/print_demo.png
--------------------------------------------------------------------------------
/widget_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/migolan/dynasty/HEAD/widget_demo.gif
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ipynb linguist-detectable=false
2 | *.py linguist-detectable=true
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(
4 | name="dynasty",
5 | packages=['dynasty'],
6 | install_requires=open('requirements.txt').read().splitlines(),
7 | description="A Python class hierarchy analyzer",
8 | long_description=open('README.md').read(),
9 | long_description_content_type='text/markdown',
10 | author="Michal Golan",
11 | author_email="migolan@gmail.com",
12 | url="https://github.com/migolan/dynasty",
13 | version="0.0.3"
14 | )
15 |
--------------------------------------------------------------------------------
/dynasty/dynasty.py:
--------------------------------------------------------------------------------
1 | from .utils import get_pkg_classes, get_anytree, get_ipytree, print_anytree, print_class_hierarchy
2 |
3 |
4 | class Dynasty:
5 | def __init__(self, pkg):
6 | self.pkg = pkg
7 | self.name = pkg.__name__
8 | self.class_table = get_pkg_classes(pkg)
9 | self.anytree = get_anytree(self.class_table)
10 |
11 | def print(self, classpath=False):
12 | print_anytree(self.anytree, classpath=classpath)
13 |
14 | def _print(self):
15 | print_class_hierarchy(self.class_table)
16 |
17 | def widget(self, classpath=False):
18 | return get_ipytree(self.anytree, classpath=classpath)
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dynasty
2 |
3 | Dynasty extracts the class hierachy for your Python package, to track down lost or misplaced classes and help prevent [ravioli code](https://docs.python-guide.org/writing/structure).
4 |
5 | **Installation**
6 | ```buildoutcfg
7 | pip install dynasty
8 | ```
9 |
10 | **Usage**
11 | ```python
12 | import mypackage
13 | from dynasty import Dynasty
14 |
15 | mypackage_dynasty = Dynasty(mypackage)
16 |
17 | # display collapsible class hierarchy (in jupyter notebook)
18 | mypackage_dynasty.widget()
19 |
20 | # print class hierarchy to screen
21 | mypackage_dynasty.print()
22 | ```
23 |
24 |
25 |
26 | **Author**
27 |
28 | Michal Golan <[migolan@gmail.com](mailto:migolan@gmail.com)> [migolan@github](https://github.com/migolan)
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Michal Golan
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.
22 |
--------------------------------------------------------------------------------
/dynasty/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pyclbr
3 | import pandas as pd
4 | import numpy as np
5 | import anytree
6 | import ipytree
7 |
8 |
9 | def get_module_classes(module_name):
10 | """Returns all classes in module."""
11 | module_members = pyclbr.readmodule_ex(module_name, [])
12 | module_members = dict(sorted(module_members.items(), key=lambda a: getattr(a, 'lineno', 0)))
13 | module_members = module_members.values()
14 |
15 | classes = [x for x in module_members if isinstance(x, pyclbr.Class)]
16 | classnames = [x.name for x in classes]
17 | baseclasses = [x.super for x in classes]
18 | baseclasses = [x[0] if len(x) > 0 else "-" for x in baseclasses]
19 | baseclasses = [x.name if isinstance(x, pyclbr.Class) else x for x in baseclasses]
20 | class_table = pd.DataFrame(list(zip(classnames, baseclasses)), columns=['classname', 'baseclass'])
21 | class_table['module'] = module_name
22 | return class_table
23 |
24 |
25 | def get_pkg_modules(pkg_path):
26 | """Returns all modules in package recursively."""
27 | modules = []
28 | pkg_root = os.path.split(pkg_path)[0]
29 | for path, subdirs, files in os.walk(pkg_path):
30 | pyfiles = filter(lambda x: x.endswith(".py"), files)
31 | pyfilepaths = [os.path.join(path, x) for x in pyfiles]
32 | newmodules = [x.replace(".py", "")
33 | .replace(pkg_root+os.path.sep, "")
34 | .replace(os.path.sep, ".")
35 | for x in pyfilepaths]
36 | modules.extend(newmodules)
37 | return modules
38 |
39 |
40 | def get_pkg_classes(pkg):
41 | """Returns all classes in package recursively."""
42 | class_table = pd.DataFrame()
43 | pkg_path = pkg.__path__[0]
44 | for module_name in get_pkg_modules(pkg_path):
45 | class_table = pd.concat([class_table, get_module_classes(module_name)])
46 | class_table.reset_index(drop=True, inplace=True)
47 | class_table.name = pkg.__name__
48 | return class_table
49 |
50 |
51 | def analyze_children(class_table):
52 | """Finds child classes across package recursively."""
53 | class_table['children'] = np.empty((len(class_table), 0)).tolist()
54 | for i, row in class_table.iterrows():
55 | baseclassrows = class_table[class_table['classname'] == row['baseclass']]
56 | if len(baseclassrows) > 0:
57 | baseclassrows['children'].values[0].append(row['classname'])
58 |
59 |
60 | def print_class_hierarchy(class_table, baseclass="-", prefix="|_"):
61 | """Prints class hierarchy in tree structure."""
62 | if baseclass == "-":
63 | print(class_table.name)
64 | base_parented_classes = class_table[class_table['baseclass'] == baseclass].reset_index()
65 | for i, classdata in base_parented_classes.iterrows():
66 | print(prefix + classdata['module'] + "." + classdata['classname'])
67 | next_prefix = prefix.replace("_", " ")
68 | if i == len(base_parented_classes)-1:
69 | k = next_prefix.rfind("|")
70 | next_prefix = next_prefix[:k] + " " + next_prefix[k+1:]
71 | next_prefix = next_prefix + "|_"
72 | print_class_hierarchy(class_table, classdata['classname'], next_prefix)
73 |
74 |
75 | def get_anytree(class_table, baseclass="-"):
76 | if baseclass == "-":
77 | anytree_node = anytree.Node(class_table.name)
78 | anytree_node.classpath = class_table.name
79 | else:
80 | anytree_node = anytree.Node(baseclass)
81 | base_parented_classes = class_table[class_table['baseclass'] == baseclass].reset_index()
82 | for i, classdata in base_parented_classes.iterrows():
83 | child_anytree_node = get_anytree(class_table, classdata['classname'])
84 | child_anytree_node.parent = anytree_node
85 | child_anytree_node.classpath = classdata['module'] + "." + classdata['classname']
86 | return anytree_node
87 |
88 |
89 | def print_anytree(anytree_topnode, classpath=False):
90 | for pre, fill, node in anytree.RenderTree(anytree_topnode):
91 | print("%s%s" % (pre, node.classpath if classpath else node.name))
92 |
93 |
94 | def get_ipytree(anytree_topnode, classpath=False):
95 | tree = ipytree.Tree()
96 | ipytree_topnode = get_ipytree_node(anytree_topnode, classpath)
97 | ipytree_topnode.icon = "archive"
98 | for node in ipytree_topnode.nodes:
99 | node.icon = "angle-right"
100 | tree.add_node(ipytree_topnode)
101 | return tree
102 |
103 |
104 | def get_ipytree_node(anytree_node, classpath=False):
105 | if hasattr(anytree_node, "_NodeMixin__children"):
106 | child_ipytree_nodes = [get_ipytree_node(x, classpath=classpath) for x in anytree_node._NodeMixin__children]
107 | else:
108 | child_ipytree_nodes = []
109 | return ipytree.Node(anytree_node.classpath if classpath else anytree_node.name, child_ipytree_nodes, icon="angle-up")
110 |
--------------------------------------------------------------------------------
/example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "0aff441f-69fa-44b4-9d2d-b4aaf42424c9",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "import pylint\n",
11 | "from dynasty import Dynasty"
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": 2,
17 | "id": "554a2214-f481-4b22-877f-180fd3230b6b",
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "pylint_dynasty = Dynasty(pylint)"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 3,
27 | "id": "1c41ccb8-3342-4831-8601-66fefdf6bcda",
28 | "metadata": {},
29 | "outputs": [
30 | {
31 | "data": {
32 | "application/vnd.jupyter.widget-view+json": {
33 | "model_id": "28c9a3bcadba4d509df0034303d3dc78",
34 | "version_major": 2,
35 | "version_minor": 0
36 | },
37 | "text/plain": [
38 | "Tree(nodes=(Node(icon='archive', name='pylint', nodes=(Node(icon='angle-right', name='OptionsManagerMixIn', no…"
39 | ]
40 | },
41 | "metadata": {},
42 | "output_type": "display_data"
43 | }
44 | ],
45 | "source": [
46 | "pylint_dynasty.widget()"
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": 4,
52 | "id": "f3b6f9ed-25f3-40e9-a6ef-977d387c98b2",
53 | "metadata": {},
54 | "outputs": [
55 | {
56 | "name": "stdout",
57 | "output_type": "stream",
58 | "text": [
59 | "pylint\n",
60 | "├── OptionsManagerMixIn\n",
61 | "│ └── ConfigurationMixIn\n",
62 | "│ └── Run\n",
63 | "├── OptionsProviderMixIn\n",
64 | "│ └── BaseChecker\n",
65 | "│ ├── BaseTokenChecker\n",
66 | "│ │ ├── FormatChecker\n",
67 | "│ │ ├── RawMetricsChecker\n",
68 | "│ │ ├── SpellingChecker\n",
69 | "│ │ ├── StringConstantChecker\n",
70 | "│ │ └── ElseifUsedChecker\n",
71 | "│ ├── ClassChecker\n",
72 | "│ ├── SpecialMethodsChecker\n",
73 | "│ ├── MisdesignChecker\n",
74 | "│ ├── ImportsChecker\n",
75 | "│ ├── ByIdManagedMessagesChecker\n",
76 | "│ ├── EncodingChecker\n",
77 | "│ ├── NewStyleConflictChecker\n",
78 | "│ ├── SimilarChecker\n",
79 | "│ ├── StdlibChecker\n",
80 | "│ ├── StringFormatChecker\n",
81 | "│ ├── TypeChecker\n",
82 | "│ ├── IterableChecker\n",
83 | "│ ├── VariablesChecker\n",
84 | "│ ├── BadBuiltinChecker\n",
85 | "│ ├── DocstringParameterChecker\n",
86 | "│ └── MultipleTypesChecker\n",
87 | "├── WarningScope\n",
88 | "├── DotBackend\n",
89 | "├── Interface\n",
90 | "│ ├── IChecker\n",
91 | "│ │ ├── IRawChecker\n",
92 | "│ │ ├── ITokenChecker\n",
93 | "│ │ └── IAstroidChecker\n",
94 | "│ └── IReporter\n",
95 | "├── UnittestLinter\n",
96 | "├── CheckerTestCase\n",
97 | "├── FunctionalTestFile\n",
98 | "├── LintModuleTest\n",
99 | "├── NamingStyle\n",
100 | "│ ├── SnakeCaseStyle\n",
101 | "│ ├── CamelCaseStyle\n",
102 | "│ ├── PascalCaseStyle\n",
103 | "│ ├── UpperCaseStyle\n",
104 | "│ └── AnyStyle\n",
105 | "├── ScopeAccessMap\n",
106 | "├── BaseVisitor\n",
107 | "│ ├── ExceptionRaiseRefVisitor\n",
108 | "│ └── ExceptionRaiseLeafVisitor\n",
109 | "├── _ContinuedIndent\n",
110 | "├── TokenWrapper\n",
111 | "├── ContinuedLineState\n",
112 | "├── Similar\n",
113 | "├── LineSet\n",
114 | "├── Filter\n",
115 | "│ ├── WordsWithDigigtsFilter\n",
116 | "│ ├── WordsWithUnderscores\n",
117 | "│ ├── CamelCasedWord\n",
118 | "│ └── SphinxDirectives\n",
119 | "├── Chunker\n",
120 | "│ └── ForwardSlashChunkder\n",
121 | "├── NamesConsumer\n",
122 | "├── Docstring\n",
123 | "│ ├── SphinxDocstring\n",
124 | "│ │ └── EpytextDocstring\n",
125 | "│ └── GoogleDocstring\n",
126 | "│ └── NumpyDocstring\n",
127 | "├── Run\n",
128 | "├── MessageDefinition\n",
129 | "├── MessageDefinitionStore\n",
130 | "├── MessagesHandlerMixIn\n",
131 | "├── MessageIdStore\n",
132 | "├── DiaDefGenerator\n",
133 | "│ └── ClassDiadefGenerator\n",
134 | "├── DiadefsHandler\n",
135 | "├── Figure\n",
136 | "│ ├── Relationship\n",
137 | "│ ├── DiagramEntity\n",
138 | "│ └── ClassDiagram\n",
139 | "│ └── PackageDiagram\n",
140 | "├── IdGeneratorMixIn\n",
141 | "│ └── Linker\n",
142 | "├── Project\n",
143 | "├── FilterMixIn\n",
144 | "├── ASTWalker\n",
145 | "│ └── LocalsVisitor\n",
146 | "│ └── DefaultDiadefGenerator\n",
147 | "├── VCGPrinter\n",
148 | "├── DiagramWriter\n",
149 | "│ ├── DotWriter\n",
150 | "│ └── VCGWriter\n",
151 | "├── BaseReporter\n",
152 | "│ ├── TestReporter\n",
153 | "│ ├── MinimalTestReporter\n",
154 | "│ ├── FunctionalTestReporter\n",
155 | "│ ├── CollectingReporter\n",
156 | "│ ├── JSONReporter\n",
157 | "│ └── TextReporter\n",
158 | "│ ├── ParseableTextReporter\n",
159 | "│ │ └── VSTextReporter\n",
160 | "│ └── ColorizedTextReporter\n",
161 | "├── ReportsHandlerMixIn\n",
162 | "├── VNode\n",
163 | "│ ├── BaseLayout\n",
164 | "│ │ ├── Section\n",
165 | "│ │ │ └── EvaluationSection\n",
166 | "│ │ ├── Title\n",
167 | "│ │ ├── Paragraph\n",
168 | "│ │ └── Table\n",
169 | "│ └── Text\n",
170 | "│ └── VerbatimText\n",
171 | "├── BaseWriter\n",
172 | "│ └── TextWriter\n",
173 | "├── ASTWalker\n",
174 | "│ └── LocalsVisitor\n",
175 | "│ └── DefaultDiadefGenerator\n",
176 | "└── FileState\n"
177 | ]
178 | }
179 | ],
180 | "source": [
181 | "pylint_dynasty.print()"
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": 5,
187 | "id": "b5c94d8c-d8fa-4206-8b82-29fa25fa8355",
188 | "metadata": {},
189 | "outputs": [
190 | {
191 | "name": "stdout",
192 | "output_type": "stream",
193 | "text": [
194 | "pylint\n",
195 | "├── pylint.config.OptionsManagerMixIn\n",
196 | "│ └── pylint.config.ConfigurationMixIn\n",
197 | "│ └── pylint.pyreverse.main.Run\n",
198 | "├── pylint.config.OptionsProviderMixIn\n",
199 | "│ └── pylint.checkers.base_checker.BaseChecker\n",
200 | "│ ├── pylint.checkers.base_checker.BaseTokenChecker\n",
201 | "│ │ ├── pylint.checkers.format.FormatChecker\n",
202 | "│ │ ├── pylint.checkers.raw_metrics.RawMetricsChecker\n",
203 | "│ │ ├── pylint.checkers.spelling.SpellingChecker\n",
204 | "│ │ ├── pylint.checkers.strings.StringConstantChecker\n",
205 | "│ │ └── pylint.extensions.check_elif.ElseifUsedChecker\n",
206 | "│ ├── pylint.checkers.classes.ClassChecker\n",
207 | "│ ├── pylint.checkers.classes.SpecialMethodsChecker\n",
208 | "│ ├── pylint.checkers.design_analysis.MisdesignChecker\n",
209 | "│ ├── pylint.checkers.imports.ImportsChecker\n",
210 | "│ ├── pylint.checkers.misc.ByIdManagedMessagesChecker\n",
211 | "│ ├── pylint.checkers.misc.EncodingChecker\n",
212 | "│ ├── pylint.checkers.newstyle.NewStyleConflictChecker\n",
213 | "│ ├── pylint.checkers.similar.SimilarChecker\n",
214 | "│ ├── pylint.checkers.stdlib.StdlibChecker\n",
215 | "│ ├── pylint.checkers.strings.StringFormatChecker\n",
216 | "│ ├── pylint.checkers.typecheck.TypeChecker\n",
217 | "│ ├── pylint.checkers.typecheck.IterableChecker\n",
218 | "│ ├── pylint.checkers.variables.VariablesChecker\n",
219 | "│ ├── pylint.extensions.bad_builtin.BadBuiltinChecker\n",
220 | "│ ├── pylint.extensions.docparams.DocstringParameterChecker\n",
221 | "│ └── pylint.extensions.redefined_variable_type.MultipleTypesChecker\n",
222 | "├── pylint.constants.WarningScope\n",
223 | "├── pylint.graph.DotBackend\n",
224 | "├── pylint.interfaces.Interface\n",
225 | "│ ├── pylint.interfaces.IChecker\n",
226 | "│ │ ├── pylint.interfaces.IRawChecker\n",
227 | "│ │ ├── pylint.interfaces.ITokenChecker\n",
228 | "│ │ └── pylint.interfaces.IAstroidChecker\n",
229 | "│ └── pylint.interfaces.IReporter\n",
230 | "├── pylint.testutils.UnittestLinter\n",
231 | "├── pylint.testutils.CheckerTestCase\n",
232 | "├── pylint.testutils.FunctionalTestFile\n",
233 | "├── pylint.testutils.LintModuleTest\n",
234 | "├── pylint.checkers.base.NamingStyle\n",
235 | "│ ├── pylint.checkers.base.SnakeCaseStyle\n",
236 | "│ ├── pylint.checkers.base.CamelCaseStyle\n",
237 | "│ ├── pylint.checkers.base.PascalCaseStyle\n",
238 | "│ ├── pylint.checkers.base.UpperCaseStyle\n",
239 | "│ └── pylint.checkers.base.AnyStyle\n",
240 | "├── pylint.checkers.classes.ScopeAccessMap\n",
241 | "├── pylint.checkers.exceptions.BaseVisitor\n",
242 | "│ ├── pylint.checkers.exceptions.ExceptionRaiseRefVisitor\n",
243 | "│ └── pylint.checkers.exceptions.ExceptionRaiseLeafVisitor\n",
244 | "├── pylint.checkers.format._ContinuedIndent\n",
245 | "├── pylint.checkers.format.TokenWrapper\n",
246 | "├── pylint.checkers.format.ContinuedLineState\n",
247 | "├── pylint.checkers.similar.Similar\n",
248 | "├── pylint.checkers.similar.LineSet\n",
249 | "├── pylint.checkers.spelling.Filter\n",
250 | "│ ├── pylint.checkers.spelling.WordsWithDigigtsFilter\n",
251 | "│ ├── pylint.checkers.spelling.WordsWithUnderscores\n",
252 | "│ ├── pylint.checkers.spelling.CamelCasedWord\n",
253 | "│ └── pylint.checkers.spelling.SphinxDirectives\n",
254 | "├── pylint.checkers.spelling.Chunker\n",
255 | "│ └── pylint.checkers.spelling.ForwardSlashChunkder\n",
256 | "├── pylint.checkers.variables.NamesConsumer\n",
257 | "├── pylint.extensions._check_docs_utils.Docstring\n",
258 | "│ ├── pylint.extensions._check_docs_utils.SphinxDocstring\n",
259 | "│ │ └── pylint.extensions._check_docs_utils.EpytextDocstring\n",
260 | "│ └── pylint.extensions._check_docs_utils.GoogleDocstring\n",
261 | "│ └── pylint.extensions._check_docs_utils.NumpyDocstring\n",
262 | "├── pylint.lint.run.Run\n",
263 | "├── pylint.message.message_definition.MessageDefinition\n",
264 | "├── pylint.message.message_definition_store.MessageDefinitionStore\n",
265 | "├── pylint.message.message_handler_mix_in.MessagesHandlerMixIn\n",
266 | "├── pylint.message.message_id_store.MessageIdStore\n",
267 | "├── pylint.pyreverse.diadefslib.DiaDefGenerator\n",
268 | "│ └── pylint.pyreverse.diadefslib.ClassDiadefGenerator\n",
269 | "├── pylint.pyreverse.diadefslib.DiadefsHandler\n",
270 | "├── pylint.pyreverse.diagrams.Figure\n",
271 | "│ ├── pylint.pyreverse.diagrams.Relationship\n",
272 | "│ ├── pylint.pyreverse.diagrams.DiagramEntity\n",
273 | "│ └── pylint.pyreverse.diagrams.ClassDiagram\n",
274 | "│ └── pylint.pyreverse.diagrams.PackageDiagram\n",
275 | "├── pylint.pyreverse.inspector.IdGeneratorMixIn\n",
276 | "│ └── pylint.pyreverse.inspector.Linker\n",
277 | "├── pylint.pyreverse.inspector.Project\n",
278 | "├── pylint.pyreverse.utils.FilterMixIn\n",
279 | "├── pylint.pyreverse.utils.ASTWalker\n",
280 | "│ └── pylint.pyreverse.utils.LocalsVisitor\n",
281 | "│ └── pylint.pyreverse.diadefslib.DefaultDiadefGenerator\n",
282 | "├── pylint.pyreverse.vcgutils.VCGPrinter\n",
283 | "├── pylint.pyreverse.writer.DiagramWriter\n",
284 | "│ ├── pylint.pyreverse.writer.DotWriter\n",
285 | "│ └── pylint.pyreverse.writer.VCGWriter\n",
286 | "├── pylint.reporters.base_reporter.BaseReporter\n",
287 | "│ ├── pylint.testutils.TestReporter\n",
288 | "│ ├── pylint.testutils.MinimalTestReporter\n",
289 | "│ ├── pylint.testutils.FunctionalTestReporter\n",
290 | "│ ├── pylint.reporters.collecting_reporter.CollectingReporter\n",
291 | "│ ├── pylint.reporters.json_reporter.JSONReporter\n",
292 | "│ └── pylint.reporters.text.TextReporter\n",
293 | "│ ├── pylint.reporters.text.ParseableTextReporter\n",
294 | "│ │ └── pylint.reporters.text.VSTextReporter\n",
295 | "│ └── pylint.reporters.text.ColorizedTextReporter\n",
296 | "├── pylint.reporters.reports_handler_mix_in.ReportsHandlerMixIn\n",
297 | "├── pylint.reporters.ureports.nodes.VNode\n",
298 | "│ ├── pylint.reporters.ureports.nodes.BaseLayout\n",
299 | "│ │ ├── pylint.reporters.ureports.nodes.Section\n",
300 | "│ │ │ └── pylint.reporters.ureports.nodes.EvaluationSection\n",
301 | "│ │ ├── pylint.reporters.ureports.nodes.Title\n",
302 | "│ │ ├── pylint.reporters.ureports.nodes.Paragraph\n",
303 | "│ │ └── pylint.reporters.ureports.nodes.Table\n",
304 | "│ └── pylint.reporters.ureports.nodes.Text\n",
305 | "│ └── pylint.reporters.ureports.nodes.VerbatimText\n",
306 | "├── pylint.reporters.ureports.__init__.BaseWriter\n",
307 | "│ └── pylint.reporters.ureports.text_writer.TextWriter\n",
308 | "├── pylint.utils.ast_walker.ASTWalker\n",
309 | "│ └── pylint.pyreverse.utils.LocalsVisitor\n",
310 | "│ └── pylint.pyreverse.diadefslib.DefaultDiadefGenerator\n",
311 | "└── pylint.utils.file_state.FileState\n"
312 | ]
313 | }
314 | ],
315 | "source": [
316 | "pylint_dynasty.print(True)"
317 | ]
318 | },
319 | {
320 | "cell_type": "code",
321 | "execution_count": 6,
322 | "id": "69617c61-dbe3-4a4d-ad21-d0ac9bcad277",
323 | "metadata": {},
324 | "outputs": [
325 | {
326 | "data": {
327 | "application/vnd.jupyter.widget-view+json": {
328 | "model_id": "45aac1c32ce94b7d8801328e4ecd6eab",
329 | "version_major": 2,
330 | "version_minor": 0
331 | },
332 | "text/plain": [
333 | "Tree(nodes=(Node(icon='archive', name='pylint', nodes=(Node(icon='angle-right', name='pylint.config.OptionsMan…"
334 | ]
335 | },
336 | "metadata": {},
337 | "output_type": "display_data"
338 | }
339 | ],
340 | "source": [
341 | "pylint_dynasty.widget(True)"
342 | ]
343 | },
344 | {
345 | "cell_type": "code",
346 | "execution_count": null,
347 | "id": "1ee4bbc7-3e38-422d-b682-794aaa7f1bcd",
348 | "metadata": {},
349 | "outputs": [],
350 | "source": []
351 | }
352 | ],
353 | "metadata": {
354 | "kernelspec": {
355 | "display_name": "Python 3",
356 | "language": "python",
357 | "name": "python3"
358 | },
359 | "language_info": {
360 | "codemirror_mode": {
361 | "name": "ipython",
362 | "version": 3
363 | },
364 | "file_extension": ".py",
365 | "mimetype": "text/x-python",
366 | "name": "python",
367 | "nbconvert_exporter": "python",
368 | "pygments_lexer": "ipython3",
369 | "version": "3.8.3"
370 | }
371 | },
372 | "nbformat": 4,
373 | "nbformat_minor": 5
374 | }
375 |
--------------------------------------------------------------------------------