├── 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 | --------------------------------------------------------------------------------