├── .gitignore ├── MANIFEST.in ├── README.md ├── docs ├── SocketServer_classes.png ├── ast_classes.png ├── logging_classes.png ├── maxjob_classes.png ├── maxjob_packages.png ├── pluggy_classes.png ├── pyplantuml_packages.png ├── re_classes.png ├── setuptools_classes.png ├── tokenize_classes.png ├── tox_classes.png ├── urllib_classes.png └── webbrowser_classes.png ├── experiments ├── inspect_help.txt ├── inspecttest.py ├── inspecttest.tmp ├── notes.txt ├── plantuml.txt ├── py2plantuml.py ├── submit.py ├── templates.py └── test.py ├── pyplantuml ├── __init__.py ├── adapter.py ├── cli.py ├── online.py └── writer.py ├── setup.py └── tests ├── test___init__.py ├── test_adapter.py ├── test_cli.py ├── test_online.py └── test_writer.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | 4 | *.sublime-* 5 | *.egg* 6 | dist 7 | build 8 | 9 | venv -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyplantuml 2 | 3 | ![](docs/pyplantuml_packages.png) 4 | 5 | Creates UML diagrams (package and class structure) from Python source code. 6 | 7 | Build on top of pylint's pyreverse to do the static code analysis and supports most of its original commandline interface. 8 | 9 | It basically outputs plantuml description as text files instead of .dot files. The plantuml code features a simple syntax and is a great base for manual extension, e.g. to sketch out ideas on how to evolve the data model. 10 | 11 | 12 | ## Installation 13 | 14 | From pypi: 15 | 16 | $ pip install pyplantuml 17 | 18 | From source: 19 | 20 | $ python setup.py develop 21 | 22 | After installation it is available from the commandline as two commands, see below. 23 | 24 | 25 | ## Dependencies 26 | 27 | * [pylint](https://www.pylint.org/), for it includes [astroid](https://www.astroid.org/) and [pyreverse](https://www.logilab.org/blogentry/6883). 28 | 29 | * [plantuml](http://de.plantuml.com/), to convert the resulting text files to images. It is enough to download the **plantuml.jar** and place it somewhere in a directory that is on your PATH, pyplantuml will then be able to find it. 30 | 31 | 32 | ## Usage 33 | 34 | $ cd 35 | 36 | $ pyplantuml [pyreverse-options] 37 | 38 | If a plantuml.jar can be found on PATH, it will automatically be 39 | called afterwards to convert the text files to images. 40 | 41 | $ pyplantuml-web [pyreverse-options] 42 | 43 | Will use the online form on www.plantuml.com for conversion and 44 | display the result in your default browser. Do not use for sensitive data! 45 | 46 | 47 | ## Examples 48 | 49 | pyplantuml is a great way to visualize the data structure of an existing package. It can be used on custom packages as well as builtins, as some of the examples below illustrate. 50 | 51 | ### ast 52 | ![](docs/ast_classes.png) 53 | 54 | ### logging 55 | ![](docs/logging_classes.png) 56 | 57 | ### maxjob 58 | ![](docs/maxjob_packages.png) 59 | ![](docs/maxjob_classes.png) 60 | 61 | ### pluggy 62 | ![](docs/pluggy_classes.png) 63 | 64 | ### re 65 | ![](docs/re_classes.png) 66 | 67 | ### setuptools 68 | ![](docs/setuptools_classes.png) 69 | 70 | ### SocketServer 71 | ![](docs/SocketServer_classes.png) 72 | 73 | ### tokenize 74 | ![](docs/tokenize_classes.png) 75 | 76 | ### tox 77 | ![](docs/tox_classes.png) 78 | 79 | ### urllib 80 | ![](docs/urllib_classes.png) 81 | 82 | ### webbrowser 83 | ![](docs/webbrowser_classes.png) -------------------------------------------------------------------------------- /docs/SocketServer_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/SocketServer_classes.png -------------------------------------------------------------------------------- /docs/ast_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/ast_classes.png -------------------------------------------------------------------------------- /docs/logging_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/logging_classes.png -------------------------------------------------------------------------------- /docs/maxjob_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/maxjob_classes.png -------------------------------------------------------------------------------- /docs/maxjob_packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/maxjob_packages.png -------------------------------------------------------------------------------- /docs/pluggy_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/pluggy_classes.png -------------------------------------------------------------------------------- /docs/pyplantuml_packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/pyplantuml_packages.png -------------------------------------------------------------------------------- /docs/re_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/re_classes.png -------------------------------------------------------------------------------- /docs/setuptools_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/setuptools_classes.png -------------------------------------------------------------------------------- /docs/tokenize_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/tokenize_classes.png -------------------------------------------------------------------------------- /docs/tox_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/tox_classes.png -------------------------------------------------------------------------------- /docs/urllib_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/urllib_classes.png -------------------------------------------------------------------------------- /docs/webbrowser_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/docs/webbrowser_classes.png -------------------------------------------------------------------------------- /experiments/inspect_help.txt: -------------------------------------------------------------------------------- 1 | Help on module inspect: 2 | 3 | NAME 4 | inspect - Get useful information from live Python objects. 5 | 6 | DESCRIPTION 7 | This module encapsulates the interface provided by the internal special 8 | attributes (co_*, im_*, tb_*, etc.) in a friendlier fashion. 9 | It also provides some help for examining source code and class layout. 10 | 11 | Here are some of the useful functions provided by this module: 12 | 13 | ismodule(), isclass(), ismethod(), isfunction(), isgeneratorfunction(), 14 | isgenerator(), istraceback(), isframe(), iscode(), isbuiltin(), 15 | isroutine() - check object types 16 | getmembers() - get members of an object that satisfy a given condition 17 | 18 | getfile(), getsourcefile(), getsource() - find an object's source code 19 | getdoc(), getcomments() - get documentation on an object 20 | getmodule() - determine the module that an object came from 21 | getclasstree() - arrange classes so as to represent their hierarchy 22 | 23 | getargspec(), getargvalues(), getcallargs() - get info about function arguments 24 | getfullargspec() - same, with support for Python-3000 features 25 | formatargspec(), formatargvalues() - format an argument spec 26 | getouterframes(), getinnerframes() - get info about frames 27 | currentframe() - get the current stack frame 28 | stack(), trace() - get info about frames on the stack or in a traceback 29 | 30 | signature() - get a Signature object for the callable 31 | 32 | CLASSES 33 | builtins.Exception(builtins.BaseException) 34 | EndOfBlock 35 | builtins.object 36 | BlockFinder 37 | BoundArguments 38 | Parameter 39 | Signature 40 | builtins.tuple(builtins.object) 41 | ArgInfo 42 | ArgSpec 43 | Arguments 44 | Attribute 45 | ClosureVars 46 | FullArgSpec 47 | ModuleInfo 48 | Traceback 49 | 50 | class ArgInfo(builtins.tuple) 51 | | ArgInfo(args, varargs, keywords, locals) 52 | | 53 | | Method resolution order: 54 | | ArgInfo 55 | | builtins.tuple 56 | | builtins.object 57 | | 58 | | Methods defined here: 59 | | 60 | | __getnewargs__(self) 61 | | Return self as a plain tuple. Used by copy and pickle. 62 | | 63 | | __getstate__(self) 64 | | Exclude the OrderedDict from pickling 65 | | 66 | | __repr__(self) 67 | | Return a nicely formatted representation string 68 | | 69 | | _asdict(self) 70 | | Return a new OrderedDict which maps field names to their values. 71 | | 72 | | _replace(_self, **kwds) 73 | | Return a new ArgInfo object replacing specified fields with new values 74 | | 75 | | ---------------------------------------------------------------------- 76 | | Class methods defined here: 77 | | 78 | | _make(iterable, new=, len=) from builtins.type 79 | | Make a new ArgInfo object from a sequence or iterable 80 | | 81 | | ---------------------------------------------------------------------- 82 | | Static methods defined here: 83 | | 84 | | __new__(_cls, args, varargs, keywords, locals) 85 | | Create new instance of ArgInfo(args, varargs, keywords, locals) 86 | | 87 | | ---------------------------------------------------------------------- 88 | | Data descriptors defined here: 89 | | 90 | | __dict__ 91 | | A new OrderedDict mapping field names to their values 92 | | 93 | | args 94 | | Alias for field number 0 95 | | 96 | | keywords 97 | | Alias for field number 2 98 | | 99 | | locals 100 | | Alias for field number 3 101 | | 102 | | varargs 103 | | Alias for field number 1 104 | | 105 | | ---------------------------------------------------------------------- 106 | | Data and other attributes defined here: 107 | | 108 | | _fields = ('args', 'varargs', 'keywords', 'locals') 109 | | 110 | | _source = "from builtins import property as _property, tupl..._itemget... 111 | | 112 | | ---------------------------------------------------------------------- 113 | | Methods inherited from builtins.tuple: 114 | | 115 | | __add__(self, value, /) 116 | | Return self+value. 117 | | 118 | | __contains__(self, key, /) 119 | | Return key in self. 120 | | 121 | | __eq__(self, value, /) 122 | | Return self==value. 123 | | 124 | | __ge__(self, value, /) 125 | | Return self>=value. 126 | | 127 | | __getattribute__(self, name, /) 128 | | Return getattr(self, name). 129 | | 130 | | __getitem__(self, key, /) 131 | | Return self[key]. 132 | | 133 | | __gt__(self, value, /) 134 | | Return self>value. 135 | | 136 | | __hash__(self, /) 137 | | Return hash(self). 138 | | 139 | | __iter__(self, /) 140 | | Implement iter(self). 141 | | 142 | | __le__(self, value, /) 143 | | Return self<=value. 144 | | 145 | | __len__(self, /) 146 | | Return len(self). 147 | | 148 | | __lt__(self, value, /) 149 | | Return self integer -- return number of occurrences of value 165 | | 166 | | index(...) 167 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 168 | | Raises ValueError if the value is not present. 169 | 170 | class ArgSpec(builtins.tuple) 171 | | ArgSpec(args, varargs, keywords, defaults) 172 | | 173 | | Method resolution order: 174 | | ArgSpec 175 | | builtins.tuple 176 | | builtins.object 177 | | 178 | | Methods defined here: 179 | | 180 | | __getnewargs__(self) 181 | | Return self as a plain tuple. Used by copy and pickle. 182 | | 183 | | __getstate__(self) 184 | | Exclude the OrderedDict from pickling 185 | | 186 | | __repr__(self) 187 | | Return a nicely formatted representation string 188 | | 189 | | _asdict(self) 190 | | Return a new OrderedDict which maps field names to their values. 191 | | 192 | | _replace(_self, **kwds) 193 | | Return a new ArgSpec object replacing specified fields with new values 194 | | 195 | | ---------------------------------------------------------------------- 196 | | Class methods defined here: 197 | | 198 | | _make(iterable, new=, len=) from builtins.type 199 | | Make a new ArgSpec object from a sequence or iterable 200 | | 201 | | ---------------------------------------------------------------------- 202 | | Static methods defined here: 203 | | 204 | | __new__(_cls, args, varargs, keywords, defaults) 205 | | Create new instance of ArgSpec(args, varargs, keywords, defaults) 206 | | 207 | | ---------------------------------------------------------------------- 208 | | Data descriptors defined here: 209 | | 210 | | __dict__ 211 | | A new OrderedDict mapping field names to their values 212 | | 213 | | args 214 | | Alias for field number 0 215 | | 216 | | defaults 217 | | Alias for field number 3 218 | | 219 | | keywords 220 | | Alias for field number 2 221 | | 222 | | varargs 223 | | Alias for field number 1 224 | | 225 | | ---------------------------------------------------------------------- 226 | | Data and other attributes defined here: 227 | | 228 | | _fields = ('args', 'varargs', 'keywords', 'defaults') 229 | | 230 | | _source = "from builtins import property as _property, tupl..._itemget... 231 | | 232 | | ---------------------------------------------------------------------- 233 | | Methods inherited from builtins.tuple: 234 | | 235 | | __add__(self, value, /) 236 | | Return self+value. 237 | | 238 | | __contains__(self, key, /) 239 | | Return key in self. 240 | | 241 | | __eq__(self, value, /) 242 | | Return self==value. 243 | | 244 | | __ge__(self, value, /) 245 | | Return self>=value. 246 | | 247 | | __getattribute__(self, name, /) 248 | | Return getattr(self, name). 249 | | 250 | | __getitem__(self, key, /) 251 | | Return self[key]. 252 | | 253 | | __gt__(self, value, /) 254 | | Return self>value. 255 | | 256 | | __hash__(self, /) 257 | | Return hash(self). 258 | | 259 | | __iter__(self, /) 260 | | Implement iter(self). 261 | | 262 | | __le__(self, value, /) 263 | | Return self<=value. 264 | | 265 | | __len__(self, /) 266 | | Return len(self). 267 | | 268 | | __lt__(self, value, /) 269 | | Return self integer -- return number of occurrences of value 285 | | 286 | | index(...) 287 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 288 | | Raises ValueError if the value is not present. 289 | 290 | class Arguments(builtins.tuple) 291 | | Arguments(args, varargs, varkw) 292 | | 293 | | Method resolution order: 294 | | Arguments 295 | | builtins.tuple 296 | | builtins.object 297 | | 298 | | Methods defined here: 299 | | 300 | | __getnewargs__(self) 301 | | Return self as a plain tuple. Used by copy and pickle. 302 | | 303 | | __getstate__(self) 304 | | Exclude the OrderedDict from pickling 305 | | 306 | | __repr__(self) 307 | | Return a nicely formatted representation string 308 | | 309 | | _asdict(self) 310 | | Return a new OrderedDict which maps field names to their values. 311 | | 312 | | _replace(_self, **kwds) 313 | | Return a new Arguments object replacing specified fields with new values 314 | | 315 | | ---------------------------------------------------------------------- 316 | | Class methods defined here: 317 | | 318 | | _make(iterable, new=, len=) from builtins.type 319 | | Make a new Arguments object from a sequence or iterable 320 | | 321 | | ---------------------------------------------------------------------- 322 | | Static methods defined here: 323 | | 324 | | __new__(_cls, args, varargs, varkw) 325 | | Create new instance of Arguments(args, varargs, varkw) 326 | | 327 | | ---------------------------------------------------------------------- 328 | | Data descriptors defined here: 329 | | 330 | | __dict__ 331 | | A new OrderedDict mapping field names to their values 332 | | 333 | | args 334 | | Alias for field number 0 335 | | 336 | | varargs 337 | | Alias for field number 1 338 | | 339 | | varkw 340 | | Alias for field number 2 341 | | 342 | | ---------------------------------------------------------------------- 343 | | Data and other attributes defined here: 344 | | 345 | | _fields = ('args', 'varargs', 'varkw') 346 | | 347 | | _source = "from builtins import property as _property, tupl..._itemget... 348 | | 349 | | ---------------------------------------------------------------------- 350 | | Methods inherited from builtins.tuple: 351 | | 352 | | __add__(self, value, /) 353 | | Return self+value. 354 | | 355 | | __contains__(self, key, /) 356 | | Return key in self. 357 | | 358 | | __eq__(self, value, /) 359 | | Return self==value. 360 | | 361 | | __ge__(self, value, /) 362 | | Return self>=value. 363 | | 364 | | __getattribute__(self, name, /) 365 | | Return getattr(self, name). 366 | | 367 | | __getitem__(self, key, /) 368 | | Return self[key]. 369 | | 370 | | __gt__(self, value, /) 371 | | Return self>value. 372 | | 373 | | __hash__(self, /) 374 | | Return hash(self). 375 | | 376 | | __iter__(self, /) 377 | | Implement iter(self). 378 | | 379 | | __le__(self, value, /) 380 | | Return self<=value. 381 | | 382 | | __len__(self, /) 383 | | Return len(self). 384 | | 385 | | __lt__(self, value, /) 386 | | Return self integer -- return number of occurrences of value 402 | | 403 | | index(...) 404 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 405 | | Raises ValueError if the value is not present. 406 | 407 | class Attribute(builtins.tuple) 408 | | Attribute(name, kind, defining_class, object) 409 | | 410 | | Method resolution order: 411 | | Attribute 412 | | builtins.tuple 413 | | builtins.object 414 | | 415 | | Methods defined here: 416 | | 417 | | __getnewargs__(self) 418 | | Return self as a plain tuple. Used by copy and pickle. 419 | | 420 | | __getstate__(self) 421 | | Exclude the OrderedDict from pickling 422 | | 423 | | __repr__(self) 424 | | Return a nicely formatted representation string 425 | | 426 | | _asdict(self) 427 | | Return a new OrderedDict which maps field names to their values. 428 | | 429 | | _replace(_self, **kwds) 430 | | Return a new Attribute object replacing specified fields with new values 431 | | 432 | | ---------------------------------------------------------------------- 433 | | Class methods defined here: 434 | | 435 | | _make(iterable, new=, len=) from builtins.type 436 | | Make a new Attribute object from a sequence or iterable 437 | | 438 | | ---------------------------------------------------------------------- 439 | | Static methods defined here: 440 | | 441 | | __new__(_cls, name, kind, defining_class, object) 442 | | Create new instance of Attribute(name, kind, defining_class, object) 443 | | 444 | | ---------------------------------------------------------------------- 445 | | Data descriptors defined here: 446 | | 447 | | __dict__ 448 | | A new OrderedDict mapping field names to their values 449 | | 450 | | defining_class 451 | | Alias for field number 2 452 | | 453 | | kind 454 | | Alias for field number 1 455 | | 456 | | name 457 | | Alias for field number 0 458 | | 459 | | object 460 | | Alias for field number 3 461 | | 462 | | ---------------------------------------------------------------------- 463 | | Data and other attributes defined here: 464 | | 465 | | _fields = ('name', 'kind', 'defining_class', 'object') 466 | | 467 | | _source = "from builtins import property as _property, tupl..._itemget... 468 | | 469 | | ---------------------------------------------------------------------- 470 | | Methods inherited from builtins.tuple: 471 | | 472 | | __add__(self, value, /) 473 | | Return self+value. 474 | | 475 | | __contains__(self, key, /) 476 | | Return key in self. 477 | | 478 | | __eq__(self, value, /) 479 | | Return self==value. 480 | | 481 | | __ge__(self, value, /) 482 | | Return self>=value. 483 | | 484 | | __getattribute__(self, name, /) 485 | | Return getattr(self, name). 486 | | 487 | | __getitem__(self, key, /) 488 | | Return self[key]. 489 | | 490 | | __gt__(self, value, /) 491 | | Return self>value. 492 | | 493 | | __hash__(self, /) 494 | | Return hash(self). 495 | | 496 | | __iter__(self, /) 497 | | Implement iter(self). 498 | | 499 | | __le__(self, value, /) 500 | | Return self<=value. 501 | | 502 | | __len__(self, /) 503 | | Return len(self). 504 | | 505 | | __lt__(self, value, /) 506 | | Return self integer -- return number of occurrences of value 522 | | 523 | | index(...) 524 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 525 | | Raises ValueError if the value is not present. 526 | 527 | class BlockFinder(builtins.object) 528 | | Provide a tokeneater() method to detect the end of a code block. 529 | | 530 | | Methods defined here: 531 | | 532 | | __init__(self) 533 | | 534 | | tokeneater(self, type, token, srowcol, erowcol, line) 535 | | 536 | | ---------------------------------------------------------------------- 537 | | Data descriptors defined here: 538 | | 539 | | __dict__ 540 | | dictionary for instance variables (if defined) 541 | | 542 | | __weakref__ 543 | | list of weak references to the object (if defined) 544 | 545 | class BoundArguments(builtins.object) 546 | | Result of `Signature.bind` call. Holds the mapping of arguments 547 | | to the function's parameters. 548 | | 549 | | Has the following public attributes: 550 | | 551 | | * arguments : OrderedDict 552 | | An ordered mutable mapping of parameters' names to arguments' values. 553 | | Does not contain arguments' default values. 554 | | * signature : Signature 555 | | The Signature object that created this instance. 556 | | * args : tuple 557 | | Tuple of positional arguments values. 558 | | * kwargs : dict 559 | | Dict of keyword arguments values. 560 | | 561 | | Methods defined here: 562 | | 563 | | __eq__(self, other) 564 | | 565 | | __init__(self, signature, arguments) 566 | | 567 | | __ne__(self, other) 568 | | 569 | | ---------------------------------------------------------------------- 570 | | Data descriptors defined here: 571 | | 572 | | __dict__ 573 | | dictionary for instance variables (if defined) 574 | | 575 | | __weakref__ 576 | | list of weak references to the object (if defined) 577 | | 578 | | args 579 | | 580 | | kwargs 581 | | 582 | | signature 583 | | 584 | | ---------------------------------------------------------------------- 585 | | Data and other attributes defined here: 586 | | 587 | | __hash__ = None 588 | 589 | class ClosureVars(builtins.tuple) 590 | | ClosureVars(nonlocals, globals, builtins, unbound) 591 | | 592 | | Method resolution order: 593 | | ClosureVars 594 | | builtins.tuple 595 | | builtins.object 596 | | 597 | | Methods defined here: 598 | | 599 | | __getnewargs__(self) 600 | | Return self as a plain tuple. Used by copy and pickle. 601 | | 602 | | __getstate__(self) 603 | | Exclude the OrderedDict from pickling 604 | | 605 | | __repr__(self) 606 | | Return a nicely formatted representation string 607 | | 608 | | _asdict(self) 609 | | Return a new OrderedDict which maps field names to their values. 610 | | 611 | | _replace(_self, **kwds) 612 | | Return a new ClosureVars object replacing specified fields with new values 613 | | 614 | | ---------------------------------------------------------------------- 615 | | Class methods defined here: 616 | | 617 | | _make(iterable, new=, len=) from builtins.type 618 | | Make a new ClosureVars object from a sequence or iterable 619 | | 620 | | ---------------------------------------------------------------------- 621 | | Static methods defined here: 622 | | 623 | | __new__(_cls, nonlocals, globals, builtins, unbound) 624 | | Create new instance of ClosureVars(nonlocals, globals, builtins, unbound) 625 | | 626 | | ---------------------------------------------------------------------- 627 | | Data descriptors defined here: 628 | | 629 | | __dict__ 630 | | A new OrderedDict mapping field names to their values 631 | | 632 | | builtins 633 | | Alias for field number 2 634 | | 635 | | globals 636 | | Alias for field number 1 637 | | 638 | | nonlocals 639 | | Alias for field number 0 640 | | 641 | | unbound 642 | | Alias for field number 3 643 | | 644 | | ---------------------------------------------------------------------- 645 | | Data and other attributes defined here: 646 | | 647 | | _fields = ('nonlocals', 'globals', 'builtins', 'unbound') 648 | | 649 | | _source = "from builtins import property as _property, tupl..._itemget... 650 | | 651 | | ---------------------------------------------------------------------- 652 | | Methods inherited from builtins.tuple: 653 | | 654 | | __add__(self, value, /) 655 | | Return self+value. 656 | | 657 | | __contains__(self, key, /) 658 | | Return key in self. 659 | | 660 | | __eq__(self, value, /) 661 | | Return self==value. 662 | | 663 | | __ge__(self, value, /) 664 | | Return self>=value. 665 | | 666 | | __getattribute__(self, name, /) 667 | | Return getattr(self, name). 668 | | 669 | | __getitem__(self, key, /) 670 | | Return self[key]. 671 | | 672 | | __gt__(self, value, /) 673 | | Return self>value. 674 | | 675 | | __hash__(self, /) 676 | | Return hash(self). 677 | | 678 | | __iter__(self, /) 679 | | Implement iter(self). 680 | | 681 | | __le__(self, value, /) 682 | | Return self<=value. 683 | | 684 | | __len__(self, /) 685 | | Return len(self). 686 | | 687 | | __lt__(self, value, /) 688 | | Return self integer -- return number of occurrences of value 704 | | 705 | | index(...) 706 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 707 | | Raises ValueError if the value is not present. 708 | 709 | class EndOfBlock(builtins.Exception) 710 | | Method resolution order: 711 | | EndOfBlock 712 | | builtins.Exception 713 | | builtins.BaseException 714 | | builtins.object 715 | | 716 | | Data descriptors defined here: 717 | | 718 | | __weakref__ 719 | | list of weak references to the object (if defined) 720 | | 721 | | ---------------------------------------------------------------------- 722 | | Methods inherited from builtins.Exception: 723 | | 724 | | __init__(self, /, *args, **kwargs) 725 | | Initialize self. See help(type(self)) for accurate signature. 726 | | 727 | | __new__(*args, **kwargs) from builtins.type 728 | | Create and return a new object. See help(type) for accurate signature. 729 | | 730 | | ---------------------------------------------------------------------- 731 | | Methods inherited from builtins.BaseException: 732 | | 733 | | __delattr__(self, name, /) 734 | | Implement delattr(self, name). 735 | | 736 | | __getattribute__(self, name, /) 737 | | Return getattr(self, name). 738 | | 739 | | __reduce__(...) 740 | | 741 | | __repr__(self, /) 742 | | Return repr(self). 743 | | 744 | | __setattr__(self, name, value, /) 745 | | Implement setattr(self, name, value). 746 | | 747 | | __setstate__(...) 748 | | 749 | | __str__(self, /) 750 | | Return str(self). 751 | | 752 | | with_traceback(...) 753 | | Exception.with_traceback(tb) -- 754 | | set self.__traceback__ to tb and return self. 755 | | 756 | | ---------------------------------------------------------------------- 757 | | Data descriptors inherited from builtins.BaseException: 758 | | 759 | | __cause__ 760 | | exception cause 761 | | 762 | | __context__ 763 | | exception context 764 | | 765 | | __dict__ 766 | | 767 | | __suppress_context__ 768 | | 769 | | __traceback__ 770 | | 771 | | args 772 | 773 | class FullArgSpec(builtins.tuple) 774 | | FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations) 775 | | 776 | | Method resolution order: 777 | | FullArgSpec 778 | | builtins.tuple 779 | | builtins.object 780 | | 781 | | Methods defined here: 782 | | 783 | | __getnewargs__(self) 784 | | Return self as a plain tuple. Used by copy and pickle. 785 | | 786 | | __getstate__(self) 787 | | Exclude the OrderedDict from pickling 788 | | 789 | | __repr__(self) 790 | | Return a nicely formatted representation string 791 | | 792 | | _asdict(self) 793 | | Return a new OrderedDict which maps field names to their values. 794 | | 795 | | _replace(_self, **kwds) 796 | | Return a new FullArgSpec object replacing specified fields with new values 797 | | 798 | | ---------------------------------------------------------------------- 799 | | Class methods defined here: 800 | | 801 | | _make(iterable, new=, len=) from builtins.type 802 | | Make a new FullArgSpec object from a sequence or iterable 803 | | 804 | | ---------------------------------------------------------------------- 805 | | Static methods defined here: 806 | | 807 | | __new__(_cls, args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations) 808 | | Create new instance of FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations) 809 | | 810 | | ---------------------------------------------------------------------- 811 | | Data descriptors defined here: 812 | | 813 | | __dict__ 814 | | A new OrderedDict mapping field names to their values 815 | | 816 | | annotations 817 | | Alias for field number 6 818 | | 819 | | args 820 | | Alias for field number 0 821 | | 822 | | defaults 823 | | Alias for field number 3 824 | | 825 | | kwonlyargs 826 | | Alias for field number 4 827 | | 828 | | kwonlydefaults 829 | | Alias for field number 5 830 | | 831 | | varargs 832 | | Alias for field number 1 833 | | 834 | | varkw 835 | | Alias for field number 2 836 | | 837 | | ---------------------------------------------------------------------- 838 | | Data and other attributes defined here: 839 | | 840 | | _fields = ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 'kwon... 841 | | 842 | | _source = "from builtins import property as _property, tupl..._itemget... 843 | | 844 | | ---------------------------------------------------------------------- 845 | | Methods inherited from builtins.tuple: 846 | | 847 | | __add__(self, value, /) 848 | | Return self+value. 849 | | 850 | | __contains__(self, key, /) 851 | | Return key in self. 852 | | 853 | | __eq__(self, value, /) 854 | | Return self==value. 855 | | 856 | | __ge__(self, value, /) 857 | | Return self>=value. 858 | | 859 | | __getattribute__(self, name, /) 860 | | Return getattr(self, name). 861 | | 862 | | __getitem__(self, key, /) 863 | | Return self[key]. 864 | | 865 | | __gt__(self, value, /) 866 | | Return self>value. 867 | | 868 | | __hash__(self, /) 869 | | Return hash(self). 870 | | 871 | | __iter__(self, /) 872 | | Implement iter(self). 873 | | 874 | | __le__(self, value, /) 875 | | Return self<=value. 876 | | 877 | | __len__(self, /) 878 | | Return len(self). 879 | | 880 | | __lt__(self, value, /) 881 | | Return self integer -- return number of occurrences of value 897 | | 898 | | index(...) 899 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 900 | | Raises ValueError if the value is not present. 901 | 902 | class ModuleInfo(builtins.tuple) 903 | | ModuleInfo(name, suffix, mode, module_type) 904 | | 905 | | Method resolution order: 906 | | ModuleInfo 907 | | builtins.tuple 908 | | builtins.object 909 | | 910 | | Methods defined here: 911 | | 912 | | __getnewargs__(self) 913 | | Return self as a plain tuple. Used by copy and pickle. 914 | | 915 | | __getstate__(self) 916 | | Exclude the OrderedDict from pickling 917 | | 918 | | __repr__(self) 919 | | Return a nicely formatted representation string 920 | | 921 | | _asdict(self) 922 | | Return a new OrderedDict which maps field names to their values. 923 | | 924 | | _replace(_self, **kwds) 925 | | Return a new ModuleInfo object replacing specified fields with new values 926 | | 927 | | ---------------------------------------------------------------------- 928 | | Class methods defined here: 929 | | 930 | | _make(iterable, new=, len=) from builtins.type 931 | | Make a new ModuleInfo object from a sequence or iterable 932 | | 933 | | ---------------------------------------------------------------------- 934 | | Static methods defined here: 935 | | 936 | | __new__(_cls, name, suffix, mode, module_type) 937 | | Create new instance of ModuleInfo(name, suffix, mode, module_type) 938 | | 939 | | ---------------------------------------------------------------------- 940 | | Data descriptors defined here: 941 | | 942 | | __dict__ 943 | | A new OrderedDict mapping field names to their values 944 | | 945 | | mode 946 | | Alias for field number 2 947 | | 948 | | module_type 949 | | Alias for field number 3 950 | | 951 | | name 952 | | Alias for field number 0 953 | | 954 | | suffix 955 | | Alias for field number 1 956 | | 957 | | ---------------------------------------------------------------------- 958 | | Data and other attributes defined here: 959 | | 960 | | _fields = ('name', 'suffix', 'mode', 'module_type') 961 | | 962 | | _source = "from builtins import property as _property, tupl..._itemget... 963 | | 964 | | ---------------------------------------------------------------------- 965 | | Methods inherited from builtins.tuple: 966 | | 967 | | __add__(self, value, /) 968 | | Return self+value. 969 | | 970 | | __contains__(self, key, /) 971 | | Return key in self. 972 | | 973 | | __eq__(self, value, /) 974 | | Return self==value. 975 | | 976 | | __ge__(self, value, /) 977 | | Return self>=value. 978 | | 979 | | __getattribute__(self, name, /) 980 | | Return getattr(self, name). 981 | | 982 | | __getitem__(self, key, /) 983 | | Return self[key]. 984 | | 985 | | __gt__(self, value, /) 986 | | Return self>value. 987 | | 988 | | __hash__(self, /) 989 | | Return hash(self). 990 | | 991 | | __iter__(self, /) 992 | | Implement iter(self). 993 | | 994 | | __le__(self, value, /) 995 | | Return self<=value. 996 | | 997 | | __len__(self, /) 998 | | Return len(self). 999 | | 1000 | | __lt__(self, value, /) 1001 | | Return self integer -- return number of occurrences of value 1017 | | 1018 | | index(...) 1019 | | T.index(value, [start, [stop]]) -> integer -- return first index of value. 1020 | | Raises ValueError if the value is not present. 1021 | 1022 | class Parameter(builtins.object) 1023 | | Represents a parameter in a function signature. 1024 | | 1025 | | Has the following public attributes: 1026 | | 1027 | | * name : str 1028 | | The name of the parameter as a string. 1029 | | * default : object 1030 | | The default value for the parameter if specified. If the 1031 | | parameter has no default value, this attribute is set to 1032 | | `Parameter.empty`. 1033 | | * annotation 1034 | | The annotation for the parameter if specified. If the 1035 | | parameter has no annotation, this attribute is set to 1036 | | `Parameter.empty`. 1037 | | * kind : str 1038 | | Describes how argument values are bound to the parameter. 1039 | | Possible values: `Parameter.POSITIONAL_ONLY`, 1040 | | `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`, 1041 | | `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`. 1042 | | 1043 | | Methods defined here: 1044 | | 1045 | | __eq__(self, other) 1046 | | 1047 | | __init__(self, name, kind, *, default, annotation) 1048 | | 1049 | | __ne__(self, other) 1050 | | 1051 | | __repr__(self) 1052 | | 1053 | | __str__(self) 1054 | | 1055 | | replace(self, *, name=, kind=, annotation=, default=) 1056 | | Creates a customized copy of the Parameter. 1057 | | 1058 | | ---------------------------------------------------------------------- 1059 | | Data descriptors defined here: 1060 | | 1061 | | annotation 1062 | | 1063 | | default 1064 | | 1065 | | kind 1066 | | 1067 | | name 1068 | | 1069 | | ---------------------------------------------------------------------- 1070 | | Data and other attributes defined here: 1071 | | 1072 | | KEYWORD_ONLY = <_ParameterKind: 'KEYWORD_ONLY'> 1073 | | 1074 | | POSITIONAL_ONLY = <_ParameterKind: 'POSITIONAL_ONLY'> 1075 | | 1076 | | POSITIONAL_OR_KEYWORD = <_ParameterKind: 'POSITIONAL_OR_KEYWORD'> 1077 | | 1078 | | VAR_KEYWORD = <_ParameterKind: 'VAR_KEYWORD'> 1079 | | 1080 | | VAR_POSITIONAL = <_ParameterKind: 'VAR_POSITIONAL'> 1081 | | 1082 | | __hash__ = None 1083 | | 1084 | | empty = 1085 | 1086 | class Signature(builtins.object) 1087 | | A Signature object represents the overall signature of a function. 1088 | | It stores a Parameter object for each parameter accepted by the 1089 | | function, as well as information specific to the function itself. 1090 | | 1091 | | A Signature object has the following public attributes and methods: 1092 | | 1093 | | * parameters : OrderedDict 1094 | | An ordered mapping of parameters' names to the corresponding 1095 | | Parameter objects (keyword-only arguments are in the same order 1096 | | as listed in `code.co_varnames`). 1097 | | * return_annotation : object 1098 | | The annotation for the return type of the function if specified. 1099 | | If the function has no annotation for its return type, this 1100 | | attribute is set to `Signature.empty`. 1101 | | * bind(*args, **kwargs) -> BoundArguments 1102 | | Creates a mapping from positional and keyword arguments to 1103 | | parameters. 1104 | | * bind_partial(*args, **kwargs) -> BoundArguments 1105 | | Creates a partial mapping from positional and keyword arguments 1106 | | to parameters (simulating 'functools.partial' behavior.) 1107 | | 1108 | | Methods defined here: 1109 | | 1110 | | __eq__(self, other) 1111 | | 1112 | | __init__(self, parameters=None, *, return_annotation, __validate_parameters__=True) 1113 | | Constructs Signature from the given list of Parameter 1114 | | objects and 'return_annotation'. All arguments are optional. 1115 | | 1116 | | __ne__(self, other) 1117 | | 1118 | | __str__(self) 1119 | | 1120 | | bind(*args, **kwargs) 1121 | | Get a BoundArguments object, that maps the passed `args` 1122 | | and `kwargs` to the function's signature. Raises `TypeError` 1123 | | if the passed arguments can not be bound. 1124 | | 1125 | | bind_partial(*args, **kwargs) 1126 | | Get a BoundArguments object, that partially maps the 1127 | | passed `args` and `kwargs` to the function's signature. 1128 | | Raises `TypeError` if the passed arguments can not be bound. 1129 | | 1130 | | replace(self, *, parameters=, return_annotation=) 1131 | | Creates a customized copy of the Signature. 1132 | | Pass 'parameters' and/or 'return_annotation' arguments 1133 | | to override them in the new copy. 1134 | | 1135 | | ---------------------------------------------------------------------- 1136 | | Class methods defined here: 1137 | | 1138 | | from_builtin(func) from builtins.type 1139 | | 1140 | | from_function(func) from builtins.type 1141 | | Constructs Signature for the given python function 1142 | | 1143 | | ---------------------------------------------------------------------- 1144 | | Data descriptors defined here: 1145 | | 1146 | | parameters 1147 | | 1148 | | return_annotation 1149 | | 1150 | | ---------------------------------------------------------------------- 1151 | | Data and other attributes defined here: 1152 | | 1153 | | __hash__ = None 1154 | | 1155 | | empty = 1156 | 1157 | class Traceback(builtins.tuple) 1158 | | Traceback(filename, lineno, function, code_context, index) 1159 | | 1160 | | Method resolution order: 1161 | | Traceback 1162 | | builtins.tuple 1163 | | builtins.object 1164 | | 1165 | | Methods defined here: 1166 | | 1167 | | __getnewargs__(self) 1168 | | Return self as a plain tuple. Used by copy and pickle. 1169 | | 1170 | | __getstate__(self) 1171 | | Exclude the OrderedDict from pickling 1172 | | 1173 | | __repr__(self) 1174 | | Return a nicely formatted representation string 1175 | | 1176 | | _asdict(self) 1177 | | Return a new OrderedDict which maps field names to their values. 1178 | | 1179 | | _replace(_self, **kwds) 1180 | | Return a new Traceback object replacing specified fields with new values 1181 | | 1182 | | ---------------------------------------------------------------------- 1183 | | Class methods defined here: 1184 | | 1185 | | _make(iterable, new=, len=) from builtins.type 1186 | | Make a new Traceback object from a sequence or iterable 1187 | | 1188 | | ---------------------------------------------------------------------- 1189 | | Static methods defined here: 1190 | | 1191 | | __new__(_cls, filename, lineno, function, code_context, index) 1192 | | Create new instance of Traceback(filename, lineno, function, code_context, index) 1193 | | 1194 | | ---------------------------------------------------------------------- 1195 | | Data descriptors defined here: 1196 | | 1197 | | __dict__ 1198 | | A new OrderedDict mapping field names to their values 1199 | | 1200 | | code_context 1201 | | Alias for field number 3 1202 | | 1203 | | filename 1204 | | Alias for field number 0 1205 | | 1206 | | function 1207 | | Alias for field number 2 1208 | | 1209 | | index 1210 | | Alias for field number 4 1211 | | 1212 | | lineno 1213 | | Alias for field number 1 1214 | | 1215 | | ---------------------------------------------------------------------- 1216 | | Data and other attributes defined here: 1217 | | 1218 | | _fields = ('filename', 'lineno', 'function', 'code_context', 'index') 1219 | | 1220 | | _source = "from builtins import property as _property, tupl..._itemget... 1221 | | 1222 | | ---------------------------------------------------------------------- 1223 | | Methods inherited from builtins.tuple: 1224 | | 1225 | | __add__(self, value, /) 1226 | | Return self+value. 1227 | | 1228 | | __contains__(self, key, /) 1229 | | Return key in self. 1230 | | 1231 | | __eq__(self, value, /) 1232 | | Return self==value. 1233 | | 1234 | | __ge__(self, value, /) 1235 | | Return self>=value. 1236 | | 1237 | | __getattribute__(self, name, /) 1238 | | Return getattr(self, name). 1239 | | 1240 | | __getitem__(self, key, /) 1241 | | Return self[key]. 1242 | | 1243 | | __gt__(self, value, /) 1244 | | Return self>value. 1245 | | 1246 | | __hash__(self, /) 1247 | | Return hash(self). 1248 | | 1249 | | __iter__(self, /) 1250 | | Implement iter(self). 1251 | | 1252 | | __le__(self, value, /) 1253 | | Return self<=value. 1254 | | 1255 | | __len__(self, /) 1256 | | Return len(self). 1257 | | 1258 | | __lt__(self, value, /) 1259 | | Return self integer -- return number of occurrences of value 1275 | 1276 | FUNCTIONS 1277 | classify_class_attrs(cls) 1278 | Return list of attribute-descriptor tuples. 1279 | 1280 | For each name in dir(cls), the return list contains a 4-tuple 1281 | with these elements: 1282 | 1283 | 0. The name (a string). 1284 | 1285 | 1. The kind of attribute this is, one of these strings: 1286 | 'class method' created via classmethod() 1287 | 'static method' created via staticmethod() 1288 | 'property' created via property() 1289 | 'method' any other flavor of method or descriptor 1290 | 'data' not a method 1291 | 1292 | 2. The class which defined this attribute (a class). 1293 | 1294 | 3. The object as obtained by calling getattr; if this fails, or if the 1295 | resulting object does not live anywhere in the class' mro (including 1296 | metaclasses) then the object is looked up in the defining class's 1297 | dict (found by walking the mro). 1298 | 1299 | If one of the items in dir(cls) is stored in the metaclass it will now 1300 | be discovered and not have None be listed as the class in which it was 1301 | defined. Any items whose home class cannot be discovered are skipped. 1302 | 1303 | cleandoc(doc) 1304 | Clean up indentation from docstrings. 1305 | 1306 | Any whitespace that can be uniformly removed from the second line 1307 | onwards is removed. 1308 | 1309 | currentframe() 1310 | Return the frame of the caller or None if this is not possible. 1311 | 1312 | findsource(object) 1313 | Return the entire source file and starting line number for an object. 1314 | 1315 | The argument may be a module, class, method, function, traceback, frame, 1316 | or code object. The source code is returned as a list of all the lines 1317 | in the file and the line number indexes a line in that list. An OSError 1318 | is raised if the source code cannot be retrieved. 1319 | 1320 | formatannotation(annotation, base_module=None) 1321 | 1322 | formatannotationrelativeto(object) 1323 | 1324 | formatargspec(args, varargs=None, varkw=None, defaults=None, kwonlyargs=(), kwonlydefaults={}, annotations={}, formatarg=, formatvarargs= at 0x025A6DF8>, formatvarkw= at 0x025A6E40>, formatvalue= at 0x025A6E88>, formatreturns= at 0x025A6ED0>, formatannotation=) 1325 | Format an argument spec from the values returned by getargspec 1326 | or getfullargspec. 1327 | 1328 | The first seven arguments are (args, varargs, varkw, defaults, 1329 | kwonlyargs, kwonlydefaults, annotations). The other five arguments 1330 | are the corresponding optional formatting functions that are called to 1331 | turn names and values into strings. The last argument is an optional 1332 | function to format the sequence of arguments. 1333 | 1334 | formatargvalues(args, varargs, varkw, locals, formatarg=, formatvarargs= at 0x025A6F60>, formatvarkw= at 0x025A6FA8>, formatvalue= at 0x025A1030>) 1335 | Format an argument spec from the 4 values returned by getargvalues. 1336 | 1337 | The first four arguments are (args, varargs, varkw, locals). The 1338 | next four arguments are the corresponding optional formatting functions 1339 | that are called to turn names and values into strings. The ninth 1340 | argument is an optional function to format the sequence of arguments. 1341 | 1342 | getabsfile(object, _filename=None) 1343 | Return an absolute path to the source or compiled file for an object. 1344 | 1345 | The idea is for each object to have a unique origin, so this routine 1346 | normalizes the result as much as possible. 1347 | 1348 | getargs(co) 1349 | Get information about the arguments accepted by a code object. 1350 | 1351 | Three things are returned: (args, varargs, varkw), where 1352 | 'args' is the list of argument names. Keyword-only arguments are 1353 | appended. 'varargs' and 'varkw' are the names of the * and ** 1354 | arguments or None. 1355 | 1356 | getargspec(func) 1357 | Get the names and default values of a function's arguments. 1358 | 1359 | A tuple of four things is returned: (args, varargs, varkw, defaults). 1360 | 'args' is a list of the argument names. 1361 | 'args' will include keyword-only argument names. 1362 | 'varargs' and 'varkw' are the names of the * and ** arguments or None. 1363 | 'defaults' is an n-tuple of the default values of the last n arguments. 1364 | 1365 | Use the getfullargspec() API for Python-3000 code, as annotations 1366 | and keyword arguments are supported. getargspec() will raise ValueError 1367 | if the func has either annotations or keyword arguments. 1368 | 1369 | getargvalues(frame) 1370 | Get information about arguments passed into a particular frame. 1371 | 1372 | A tuple of four things is returned: (args, varargs, varkw, locals). 1373 | 'args' is a list of the argument names. 1374 | 'varargs' and 'varkw' are the names of the * and ** arguments or None. 1375 | 'locals' is the locals dictionary of the given frame. 1376 | 1377 | getattr_static(obj, attr, default=) 1378 | Retrieve attributes without triggering dynamic lookup via the 1379 | descriptor protocol, __getattr__ or __getattribute__. 1380 | 1381 | Note: this function may not be able to retrieve all attributes 1382 | that getattr can fetch (like dynamically created attributes) 1383 | and may find attributes that getattr can't (like descriptors 1384 | that raise AttributeError). It can also return descriptor objects 1385 | instead of instance members in some cases. See the 1386 | documentation for details. 1387 | 1388 | getblock(lines) 1389 | Extract the block of code at the top of the given list of lines. 1390 | 1391 | getcallargs(*func_and_positional, **named) 1392 | Get the mapping of arguments to values. 1393 | 1394 | A dict is returned, with keys the function argument names (including the 1395 | names of the * and ** arguments, if any), and values the respective bound 1396 | values from 'positional' and 'named'. 1397 | 1398 | getclasstree(classes, unique=False) 1399 | Arrange the given list of classes into a hierarchy of nested lists. 1400 | 1401 | Where a nested list appears, it contains classes derived from the class 1402 | whose entry immediately precedes the list. Each entry is a 2-tuple 1403 | containing a class and a tuple of its base classes. If the 'unique' 1404 | argument is true, exactly one entry appears in the returned structure 1405 | for each class in the given list. Otherwise, classes using multiple 1406 | inheritance and their descendants will appear multiple times. 1407 | 1408 | getclosurevars(func) 1409 | Get the mapping of free variables to their current values. 1410 | 1411 | Returns a named tuple of dicts mapping the current nonlocal, global 1412 | and builtin references as seen by the body of the function. A final 1413 | set of unbound names that could not be resolved is also provided. 1414 | 1415 | getcomments(object) 1416 | Get lines of comments immediately preceding an object's source code. 1417 | 1418 | Returns None when source can't be found. 1419 | 1420 | getdoc(object) 1421 | Get the documentation string for an object. 1422 | 1423 | All tabs are expanded to spaces. To clean up docstrings that are 1424 | indented to line up with blocks of code, any whitespace than can be 1425 | uniformly removed from the second line onwards is removed. 1426 | 1427 | getfile(object) 1428 | Work out which source or compiled file an object was defined in. 1429 | 1430 | getframeinfo(frame, context=1) 1431 | Get information about a frame or traceback object. 1432 | 1433 | A tuple of five things is returned: the filename, the line number of 1434 | the current line, the function name, a list of lines of context from 1435 | the source code, and the index of the current line within that list. 1436 | The optional second argument specifies the number of lines of context 1437 | to return, which are centered around the current line. 1438 | 1439 | getfullargspec(func) 1440 | Get the names and default values of a callable object's arguments. 1441 | 1442 | A tuple of seven things is returned: 1443 | (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults annotations). 1444 | 'args' is a list of the argument names. 1445 | 'varargs' and 'varkw' are the names of the * and ** arguments or None. 1446 | 'defaults' is an n-tuple of the default values of the last n arguments. 1447 | 'kwonlyargs' is a list of keyword-only argument names. 1448 | 'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults. 1449 | 'annotations' is a dictionary mapping argument names to annotations. 1450 | 1451 | The first four items in the tuple correspond to getargspec(). 1452 | 1453 | getgeneratorlocals(generator) 1454 | Get the mapping of generator local variables to their current values. 1455 | 1456 | A dict is returned, with the keys the local variable names and values the 1457 | bound values. 1458 | 1459 | getgeneratorstate(generator) 1460 | Get current state of a generator-iterator. 1461 | 1462 | Possible states are: 1463 | GEN_CREATED: Waiting to start execution. 1464 | GEN_RUNNING: Currently being executed by the interpreter. 1465 | GEN_SUSPENDED: Currently suspended at a yield expression. 1466 | GEN_CLOSED: Execution has completed. 1467 | 1468 | getinnerframes(tb, context=1) 1469 | Get a list of records for a traceback's frame and all lower frames. 1470 | 1471 | Each record contains a frame object, filename, line number, function 1472 | name, a list of lines of context, and index within the context. 1473 | 1474 | getlineno(frame) 1475 | Get the line number from a frame object, allowing for optimization. 1476 | 1477 | getmembers(object, predicate=None) 1478 | Return all members of an object as (name, value) pairs sorted by name. 1479 | Optionally, only return members that satisfy a given predicate. 1480 | 1481 | getmodule(object, _filename=None) 1482 | Return the module an object was defined in, or None if not found. 1483 | 1484 | getmoduleinfo(path) 1485 | Get the module name, suffix, mode, and module type for a given file. 1486 | 1487 | getmodulename(path) 1488 | Return the module name for a given file, or None. 1489 | 1490 | getmro(cls) 1491 | Return tuple of base classes (including cls) in method resolution order. 1492 | 1493 | getouterframes(frame, context=1) 1494 | Get a list of records for a frame and all higher (calling) frames. 1495 | 1496 | Each record contains a frame object, filename, line number, function 1497 | name, a list of lines of context, and index within the context. 1498 | 1499 | getsource(object) 1500 | Return the text of the source code for an object. 1501 | 1502 | The argument may be a module, class, method, function, traceback, frame, 1503 | or code object. The source code is returned as a single string. An 1504 | OSError is raised if the source code cannot be retrieved. 1505 | 1506 | getsourcefile(object) 1507 | Return the filename that can be used to locate an object's source. 1508 | Return None if no way can be identified to get the source. 1509 | 1510 | getsourcelines(object) 1511 | Return a list of source lines and starting line number for an object. 1512 | 1513 | The argument may be a module, class, method, function, traceback, frame, 1514 | or code object. The source code is returned as a list of the lines 1515 | corresponding to the object and the line number indicates where in the 1516 | original source file the first line of code was found. An OSError is 1517 | raised if the source code cannot be retrieved. 1518 | 1519 | indentsize(line) 1520 | Return the indent size, in spaces, at the start of a line of text. 1521 | 1522 | isabstract(object) 1523 | Return true if the object is an abstract base class (ABC). 1524 | 1525 | isbuiltin(object) 1526 | Return true if the object is a built-in function or method. 1527 | 1528 | Built-in functions and methods provide these attributes: 1529 | __doc__ documentation string 1530 | __name__ original name of this function or method 1531 | __self__ instance to which a method is bound, or None 1532 | 1533 | isclass(object) 1534 | Return true if the object is a class. 1535 | 1536 | Class objects provide these attributes: 1537 | __doc__ documentation string 1538 | __module__ name of module in which this class was defined 1539 | 1540 | iscode(object) 1541 | Return true if the object is a code object. 1542 | 1543 | Code objects provide these attributes: 1544 | co_argcount number of arguments (not including * or ** args) 1545 | co_code string of raw compiled bytecode 1546 | co_consts tuple of constants used in the bytecode 1547 | co_filename name of file in which this code object was created 1548 | co_firstlineno number of first line in Python source code 1549 | co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg 1550 | co_lnotab encoded mapping of line numbers to bytecode indices 1551 | co_name name with which this code object was defined 1552 | co_names tuple of names of local variables 1553 | co_nlocals number of local variables 1554 | co_stacksize virtual machine stack space required 1555 | co_varnames tuple of names of arguments and local variables 1556 | 1557 | isdatadescriptor(object) 1558 | Return true if the object is a data descriptor. 1559 | 1560 | Data descriptors have both a __get__ and a __set__ attribute. Examples are 1561 | properties (defined in Python) and getsets and members (defined in C). 1562 | Typically, data descriptors will also have __name__ and __doc__ attributes 1563 | (properties, getsets, and members have both of these attributes), but this 1564 | is not guaranteed. 1565 | 1566 | isframe(object) 1567 | Return true if the object is a frame object. 1568 | 1569 | Frame objects provide these attributes: 1570 | f_back next outer frame object (this frame's caller) 1571 | f_builtins built-in namespace seen by this frame 1572 | f_code code object being executed in this frame 1573 | f_globals global namespace seen by this frame 1574 | f_lasti index of last attempted instruction in bytecode 1575 | f_lineno current line number in Python source code 1576 | f_locals local namespace seen by this frame 1577 | f_trace tracing function for this frame, or None 1578 | 1579 | isfunction(object) 1580 | Return true if the object is a user-defined function. 1581 | 1582 | Function objects provide these attributes: 1583 | __doc__ documentation string 1584 | __name__ name with which this function was defined 1585 | __code__ code object containing compiled function bytecode 1586 | __defaults__ tuple of any default values for arguments 1587 | __globals__ global namespace in which this function was defined 1588 | __annotations__ dict of parameter annotations 1589 | __kwdefaults__ dict of keyword only parameters with defaults 1590 | 1591 | isgenerator(object) 1592 | Return true if the object is a generator. 1593 | 1594 | Generator objects provide these attributes: 1595 | __iter__ defined to support iteration over container 1596 | close raises a new GeneratorExit exception inside the 1597 | generator to terminate the iteration 1598 | gi_code code object 1599 | gi_frame frame object or possibly None once the generator has 1600 | been exhausted 1601 | gi_running set to 1 when generator is executing, 0 otherwise 1602 | next return the next item from the container 1603 | send resumes the generator and "sends" a value that becomes 1604 | the result of the current yield-expression 1605 | throw used to raise an exception inside the generator 1606 | 1607 | isgeneratorfunction(object) 1608 | Return true if the object is a user-defined generator function. 1609 | 1610 | Generator function objects provides same attributes as functions. 1611 | 1612 | See help(isfunction) for attributes listing. 1613 | 1614 | isgetsetdescriptor(object) 1615 | Return true if the object is a getset descriptor. 1616 | 1617 | getset descriptors are specialized descriptors defined in extension 1618 | modules. 1619 | 1620 | ismemberdescriptor(object) 1621 | Return true if the object is a member descriptor. 1622 | 1623 | Member descriptors are specialized descriptors defined in extension 1624 | modules. 1625 | 1626 | ismethod(object) 1627 | Return true if the object is an instance method. 1628 | 1629 | Instance method objects provide these attributes: 1630 | __doc__ documentation string 1631 | __name__ name with which this method was defined 1632 | __func__ function object containing implementation of method 1633 | __self__ instance to which this method is bound 1634 | 1635 | ismethoddescriptor(object) 1636 | Return true if the object is a method descriptor. 1637 | 1638 | But not if ismethod() or isclass() or isfunction() are true. 1639 | 1640 | This is new in Python 2.2, and, for example, is true of int.__add__. 1641 | An object passing this test has a __get__ attribute but not a __set__ 1642 | attribute, but beyond that the set of attributes varies. __name__ is 1643 | usually sensible, and __doc__ often is. 1644 | 1645 | Methods implemented via descriptors that also pass one of the other 1646 | tests return false from the ismethoddescriptor() test, simply because 1647 | the other tests promise more -- you can, e.g., count on having the 1648 | __func__ attribute (etc) when an object passes ismethod(). 1649 | 1650 | ismodule(object) 1651 | Return true if the object is a module. 1652 | 1653 | Module objects provide these attributes: 1654 | __cached__ pathname to byte compiled file 1655 | __doc__ documentation string 1656 | __file__ filename (missing for built-in modules) 1657 | 1658 | isroutine(object) 1659 | Return true if the object is any kind of function or method. 1660 | 1661 | istraceback(object) 1662 | Return true if the object is a traceback. 1663 | 1664 | Traceback objects provide these attributes: 1665 | tb_frame frame object at this level 1666 | tb_lasti index of last attempted instruction in bytecode 1667 | tb_lineno current line number in Python source code 1668 | tb_next next inner traceback object (called by this level) 1669 | 1670 | signature(obj) 1671 | Get a signature object for the passed callable. 1672 | 1673 | stack(context=1) 1674 | Return a list of records for the stack above the caller's frame. 1675 | 1676 | trace(context=1) 1677 | Return a list of records for the stack below the current exception. 1678 | 1679 | unwrap(func, *, stop=None) 1680 | Get the object wrapped by *func*. 1681 | 1682 | Follows the chain of :attr:`__wrapped__` attributes returning the last 1683 | object in the chain. 1684 | 1685 | *stop* is an optional callback accepting an object in the wrapper chain 1686 | as its sole argument that allows the unwrapping to be terminated early if 1687 | the callback returns a true value. If the callback never returns a true 1688 | value, the last object in the chain is returned as usual. For example, 1689 | :func:`signature` uses this to stop unwrapping if any object in the 1690 | chain has a ``__signature__`` attribute defined. 1691 | 1692 | :exc:`ValueError` is raised if a cycle is encountered. 1693 | 1694 | walktree(classes, children, parent) 1695 | Recursive helper function for getclasstree(). 1696 | 1697 | DATA 1698 | CO_GENERATOR = 32 1699 | CO_NESTED = 16 1700 | CO_NEWLOCALS = 2 1701 | CO_NOFREE = 64 1702 | CO_OPTIMIZED = 1 1703 | CO_VARARGS = 4 1704 | CO_VARKEYWORDS = 8 1705 | GEN_CLOSED = 'GEN_CLOSED' 1706 | GEN_CREATED = 'GEN_CREATED' 1707 | GEN_RUNNING = 'GEN_RUNNING' 1708 | GEN_SUSPENDED = 'GEN_SUSPENDED' 1709 | TPFLAGS_IS_ABSTRACT = 1048576 1710 | k = 64 1711 | mod_dict = {'ArgInfo': , 'ArgSpec': ', 'Yury Selivanov ') 1717 | 1718 | FILE 1719 | c:\python34\lib\inspect.py 1720 | 1721 | 1722 | -------------------------------------------------------------------------------- /experiments/inspecttest.py: -------------------------------------------------------------------------------- 1 | """TODO 2 | 3 | we have support for scanning a folder for modules now and analyzing 4 | them in a single model: how to add support for any modules and packages? 5 | 6 | why can some modules not be read by pclbr? 7 | 8 | how to get the fields of a class and global variables of a module? 9 | 10 | module namespaces (optional)? 11 | 12 | add commandline interface (target, output, style, verbosity) 13 | 14 | make it a pypi package 15 | 16 | """ 17 | 18 | import os 19 | import sys 20 | import pydoc 21 | import pyclbr 22 | import inspect 23 | import pkgutil 24 | 25 | from collections import defaultdict 26 | 27 | import templates 28 | # from py2plantuml import templates 29 | 30 | 31 | def getClasses(moduleName): 32 | classes = pyclbr.readmodule(moduleName) 33 | return classes 34 | 35 | 36 | def getFunctions(module): 37 | members = inspect.getmembers(module) 38 | functions = [m for m in members if inspect.isfunction(m)] 39 | return functions 40 | 41 | 42 | def getModuleNamesFromFolder(folder, pkgpath='', done=None): 43 | """Write out HTML documentation for all modules in a directory tree.""" 44 | modules = [] 45 | if done is None: done = {} 46 | for importer, modname, ispkg in pkgutil.walk_packages([folder], pkgpath): 47 | modules.append(modname) 48 | return modules 49 | 50 | 51 | def resolveModule(moduleName, forceload=0): 52 | obj = pydoc.locate(moduleName, forceload) 53 | if not obj: 54 | print ("Could not resolve module ", moduleName) 55 | return obj 56 | 57 | 58 | class PlantUmlWriter(object): 59 | 60 | def __init__(self, model): 61 | self.model = model 62 | 63 | self.stream = "" 64 | 65 | def write(self, outputFile=None, title=None): 66 | self.stream += templates.STARTUML 67 | 68 | if title: 69 | titleLine = templates.TITLE.format(name=title) 70 | self.stream += titleLine 71 | 72 | styleLine = templates.STYLE 73 | self.stream += styleLine 74 | 75 | for moduleName, classes in self.model["moduleToClasses"].items(): 76 | 77 | for className, classDescriptor in classes.items(): 78 | self.stream += templates.EMPTY 79 | 80 | methods = sorted(classDescriptor.methods.keys()) 81 | methodLines = "" 82 | for method in methods: 83 | methodLines += templates.METHOD.format( 84 | returnValue="", name=method 85 | ) 86 | # self.stream.write( 87 | # templates.CLASS.format( 88 | # name=className, methods=methodLines 89 | # ) 90 | # ) 91 | self.stream += "class " + className + "{\n" 92 | for methodLine in methodLines: 93 | self.stream += methodLine 94 | self.stream += "}\n" 95 | 96 | supers = classDescriptor.super 97 | for super_ in supers: 98 | if isinstance(super_, pyclbr.Class): 99 | superName = super_.name 100 | else: 101 | superName = super_ 102 | if "mixin" in superName.lower(): 103 | relation = templates.COMPOSITION.format( 104 | child=className, parent=superName 105 | ) 106 | else: 107 | relation = templates.INHERITANCE.format( 108 | child=className, parent=superName 109 | ) 110 | self.stream += relation 111 | 112 | self.stream += templates.ENDUML 113 | 114 | output = self.stream 115 | print (self.stream) 116 | if outputFile: 117 | with open(outputFile, "w") as f: 118 | f.write(output) 119 | print("Written to " + outputFile) 120 | os.system("plantuml " + outputFile) 121 | os.system(outputFile.replace(".txt", ".png")) 122 | else: 123 | print(output) 124 | 125 | 126 | if __name__ == "__main__": 127 | 128 | moduleToClasses = defaultdict(dict) 129 | moduleToFunctions = defaultdict(list) 130 | 131 | args = sys.argv[1:] 132 | root = args[0] 133 | 134 | if "/" in root or "\\" in root or root in (".", ".."): 135 | # It's a directory path. 136 | sys.path.insert(0, root) 137 | else: 138 | # It's probably a module or package. 139 | print ("not supported atm") 140 | 141 | moduleNames = getModuleNamesFromFolder(root) 142 | 143 | outputFile = "plantuml.txt" 144 | 145 | for moduleName in moduleNames: 146 | classes = getClasses(moduleName) 147 | module = resolveModule(moduleName) 148 | functions = getFunctions(module) 149 | 150 | moduleToFunctions[moduleName].append(functions) 151 | moduleToClasses[moduleName].update(classes) 152 | 153 | model = { 154 | "moduleToFunctions": dict(moduleToFunctions), 155 | "moduleToClasses": dict(moduleToClasses), 156 | } 157 | 158 | PlantUmlWriter(model).write(outputFile) 159 | 160 | 161 | # python inspecttest.py "c:\python34\lib\logging" -------------------------------------------------------------------------------- /experiments/inspecttest.tmp: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | skinparam class { 4 | BackgroundColor White 5 | ArrowColor Grey 6 | BorderColor Black 7 | } 8 | @enduml 9 | -------------------------------------------------------------------------------- /experiments/notes.txt: -------------------------------------------------------------------------------- 1 | use pylint's pyreverse to do the analysis of module/package. 2 | 3 | pyreverse then has a writer module that can output the result as either vcg or dot. 4 | dot is then visualized as a png image. 5 | 6 | how could we hook into this? 7 | 8 | we could write a custom writer class that has the same interface as the pyreverse.writer.DiagramWriter 9 | this is a template class that needs its successor to implement some methods and it also needs 10 | a printer instance to work. 11 | 12 | template methods are: 13 | 14 | set_printer 15 | get_title 16 | get_values 17 | close_graph 18 | 19 | the printer class instance must provide the following methods: 20 | 21 | open_graph 22 | emit_node 23 | emit_edge 24 | close_graph 25 | 26 | the nodes would be classes and maybe global data/functions. 27 | the edges would be relationsships between nodes like class inheritance, composition or aggregation. 28 | inside the pyreverse module they are called: specialization, implements, association 29 | 30 | 31 | so to sum it up, we need a custom writer that takes a diagram and uses 32 | a custom printer to convert this to a plantuml representation of the 33 | diagram. 34 | 35 | 36 | 37 | maybe it does not have to be that complex, it may be enough use the diagrams that are passed to 38 | the writer in pyreverse.main as a base. -------------------------------------------------------------------------------- /experiments/plantuml.txt: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | skinparam class { 4 | BackgroundColor White 5 | ArrowColor Grey 6 | BorderColor Black 7 | } 8 | @enduml 9 | -------------------------------------------------------------------------------- /experiments/py2plantuml.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import pyclbr 4 | import pkgutil 5 | import setuptools 6 | 7 | STARTUML = "@startuml\n" 8 | ENDUML = "@enduml\n" 9 | TITLE = "title {package}\n" 10 | STYLE = """ 11 | skinparam class { 12 | BackgroundColor White 13 | ArrowColor Grey 14 | BorderColor Black 15 | } 16 | 17 | """ 18 | INHERITANCE = "{parent} <|-- {child}\n" 19 | COMPOSITION = "{parent} *-- {child}\n" 20 | CLASSMETHOD = "{child} : {method}\n" 21 | 22 | 23 | def getCodeFromFile(filePath): 24 | with open(filePath) as f: 25 | code = f.read() 26 | return code 27 | 28 | 29 | def getCodeLinesFromFile(filePath): 30 | with open(filePath) as f: 31 | lines = f.readlines() 32 | return lines 33 | 34 | 35 | def yieldModules(package): 36 | data = pkgutil.walk_packages(path=package, onerror=lambda m: None) 37 | for importer, module, isPackage in data: 38 | yield module 39 | 40 | 41 | def getPackages(directory): 42 | packages = setuptools.find_packages(directory) 43 | return packages 44 | 45 | 46 | def getRelationLine(parent, child): 47 | if "mixin" in parent.lower(): 48 | return COMPOSITION.format(**locals()) 49 | else: 50 | return INHERITANCE.format(**locals()) 51 | 52 | 53 | def getMethodLine(child, method): 54 | return CLASSMETHOD.format(**locals()) 55 | 56 | 57 | def toPlantUML(module, outputFile): 58 | if os.path.isfile(module): 59 | module = os.path.splitext(os.path.basename(module))[0] 60 | 61 | with open(outputFile, "w") as f: 62 | f.write(STARTUML) 63 | f.write(STYLE) 64 | title = TITLE.format(package=module) 65 | f.write(title) 66 | 67 | classDescriptors = pyclbr.readmodule(module) 68 | for className, classData in classDescriptors.items(): 69 | child = className 70 | methods = sorted([m + "()" for m in classData.methods]) 71 | parents = [p.name if hasattr(p, "name") else str(p) for p in classData.super] 72 | 73 | for parent in parents: 74 | relationLine = getRelationLine(parent, child) 75 | f.write(relationLine) 76 | 77 | for method in methods: 78 | methodLine = getMethodLine(child, method) 79 | f.write(methodLine) 80 | f.write(ENDUML) 81 | 82 | os.system("notepad " + outputFile) 83 | 84 | 85 | if __name__ == "__main__": 86 | args = sys.argv[1:] 87 | module, outputFile = args 88 | toPlantUML(module, outputFile) -------------------------------------------------------------------------------- /experiments/submit.py: -------------------------------------------------------------------------------- 1 | try: # 3.x 2 | import urllib.parse as urllib 3 | import urllib.request as urllib2 4 | except ImportError: # 2.x 5 | import urllib 6 | import urllib2 7 | 8 | import webbrowser 9 | 10 | uml = """ 11 | @startuml 12 | class foo <-- class bar : extends 13 | @enduml 14 | """ 15 | 16 | url = "http://www.plantuml.com/plantuml/form" 17 | data = urllib.urlencode({"text" : uml}) 18 | binary = data.encode("utf-8") 19 | results = urllib2.urlopen(url, binary) 20 | with open("results.html", "wb") as f: 21 | f.write(results.read()) 22 | 23 | webbrowser.open("results.html") -------------------------------------------------------------------------------- /experiments/templates.py: -------------------------------------------------------------------------------- 1 | EMPTY = "\n" 2 | 3 | STARTUML = "@startuml\n" 4 | 5 | ENDUML = "@enduml\n" 6 | 7 | TITLE = "title {name}\n" 8 | 9 | STYLE = """ 10 | skinparam class { 11 | BackgroundColor White 12 | ArrowColor Grey 13 | BorderColor Black 14 | } 15 | """ 16 | 17 | INHERITANCE = "{parent} <|-- {child}\n" 18 | 19 | COMPOSITION = "{parent} *-- {child}\n" 20 | 21 | CLASSMETHOD = "{child} : {method}\n" 22 | 23 | CLASS = """ 24 | class {0} { 25 | {1} 26 | } 27 | """ 28 | 29 | METHOD = "{returnValue} {name}()\n" -------------------------------------------------------------------------------- /experiments/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from astroid.inspector import Linker 5 | 6 | from pylint.pyreverse.diadefslib import DiadefsHandler 7 | from pylint.pyreverse.utils import is_interface 8 | from pylint.pyreverse.main import Run 9 | 10 | 11 | EMPTY = "\n" 12 | STARTUML = "@startuml\n" 13 | ENDUML = "@enduml\n" 14 | TITLE = "title {title}\n" 15 | STYLECLASS = """ 16 | skinparam class { 17 | BackgroundColor White 18 | ArrowColor Grey 19 | BorderColor Black 20 | } 21 | 22 | """ 23 | STYLEPACKAGE = """ 24 | skinparam package { 25 | BackgroundColor White 26 | ArrowColor Grey 27 | BorderColor Black 28 | } 29 | 30 | """ 31 | OPEN = "{\n" 32 | CLOSE = "}\n" 33 | CLASS = "class {name} \n" 34 | CLASSOPEN = "class {name} {{\n" 35 | INTERFACE = "interface {name} \n" 36 | INTERFACEOPEN = "interface {name} {{\n" 37 | PACKAGE = "package {name} \n" 38 | DEPENDS = "{parent} +-- {child}\n" 39 | EXTENSION = "{parent} <|-- {child}\n" 40 | COMPOSITION = "{parent} *-- {child}\n" 41 | AGGREGATION = "{parent} o-- {child}\n" 42 | CLASSMETHOD = " {name}({args})\n" 43 | CLASSATTR = " {name}\n" 44 | 45 | relationship2plantuml = { 46 | "specialization" : EXTENSION, 47 | "association" : AGGREGATION, 48 | "implements" : COMPOSITION 49 | } 50 | attr2type = { 51 | "public" : "+", 52 | "protected" : "#", 53 | "private" : "-" 54 | } 55 | 56 | 57 | def getPublicAttrs(attributes): 58 | return filter(lambda a: not a.startswith("_"), attributes) 59 | 60 | 61 | def getPrivateAttrs(attributes): 62 | return filter(lambda a: a.startswith("__"), attributes) 63 | 64 | 65 | def getProtectedAttrs(attributes): 66 | return filter(lambda a: a.startswith("_") and not a.startswith("__"), attributes) 67 | 68 | 69 | def getAttrBase(attr): 70 | """E.g. 'Filesystem : str' -> 'Filesystem' """ 71 | return attr.split(":")[0].strip() 72 | 73 | 74 | def getFieldTypePrefix(attr): 75 | if attr.startswith("__"): return attr2type["private"] 76 | if attr.startswith("_"): return attr2type["protected"] 77 | return attr2type["public"] 78 | 79 | 80 | def getAttrDesc(attr): 81 | base = getAttrBase(attr) 82 | desc = getFieldTypePrefix(attr) + base 83 | return desc 84 | 85 | 86 | def writePackageDiagram(diagram): 87 | stream = STARTUML 88 | stream += STYLEPACKAGE 89 | 90 | stream += TITLE.format(title=diagram.title) 91 | 92 | for module in diagram.modules(): 93 | stream += PACKAGE.format(name=module.title) 94 | 95 | for relation_type, relationsships in diagram.relationships.items(): 96 | for rel in relationsships: 97 | stream += DEPENDS.format( 98 | parent=rel.to_object.title, child=rel.from_object.title 99 | ) 100 | stream += "\n" + ENDUML 101 | 102 | with open("packages.txt", "w") as f: 103 | f.write(stream) 104 | 105 | 106 | def writeClassDiagram(diagram): 107 | stream = STARTUML 108 | stream += STYLECLASS 109 | 110 | stream += TITLE.format(title=diagram.title) 111 | 112 | # modules = [] 113 | 114 | for obj in diagram.objects: 115 | # objModule = obj.node.root() 116 | # if objModule not in modules: 117 | # modules.append(objModule) 118 | # pprint (objModule.locals) 119 | 120 | 121 | attributes = diagram.get_attrs(obj.node) 122 | methods = diagram.get_methods(obj.node) 123 | if attributes or methods: 124 | template = INTERFACEOPEN if is_interface(obj.node) else CLASSOPEN 125 | stream += template.format(name=obj.title) 126 | 127 | # magics = getMagicAttrs() 128 | publics = getPublicAttrs(attributes) 129 | protecteds = getProtectedAttrs(attributes) 130 | privates = getPrivateAttrs(attributes) 131 | 132 | # if publics: 133 | # stream += ".. public ..\n" 134 | for attr in sorted(publics): 135 | stream += getAttrDesc(attr) + "\n" 136 | 137 | # if protecteds: 138 | # stream += ".. protected ..\n" 139 | for attr in sorted(protecteds): 140 | stream += getAttrDesc(attr) + "\n" 141 | 142 | # if privates: 143 | # stream += ".. private ..\n" 144 | for attr in sorted(privates): 145 | stream += getAttrDesc(attr) + "\n" 146 | 147 | # for attr in sorted(attributes): 148 | # shortAttr = attr.split(":")[0].strip() 149 | # prefixedAttr = getFieldTypePrefix(shortAttr) + shortAttr 150 | # stream += CLASSATTR.format(name=prefixedAttr) 151 | 152 | # stream += "==\n" 153 | 154 | for method in sorted(methods, key=lambda m: m.name): 155 | prefixedMethod = getFieldTypePrefix(method.name) + method.name 156 | stream += CLASSMETHOD.format( 157 | name=prefixedMethod, args=method.args.format_args() 158 | ) 159 | 160 | stream += CLOSE 161 | else: 162 | template = INTERFACE if is_interface(obj.node) else CLASS 163 | stream += template.format(name=obj.title) 164 | 165 | stream += EMPTY 166 | 167 | for relation_type, relationsships in diagram.relationships.items(): 168 | for rel in relationsships: 169 | stream += relationship2plantuml[rel.type].format( 170 | parent=rel.to_object.title, child=rel.from_object.title 171 | ) 172 | stream += "\n" + ENDUML 173 | 174 | with open("classes.txt", "w") as f: 175 | f.write(stream) 176 | 177 | 178 | class CustomRun(Run): 179 | 180 | def run(self, args): 181 | """checking arguments and run project""" 182 | if not args: 183 | print(self.help()) 184 | return 1 185 | # insert current working directory to the python path to recognize 186 | # dependencies to local modules even if cwd is not in the PYTHONPATH 187 | sys.path.insert(0, os.getcwd()) 188 | try: 189 | project = self.manager.project_from_files(args) 190 | linker = Linker(project, tag=True) 191 | handler = DiadefsHandler(self.config) 192 | diadefs = handler.get_diadefs(project, linker) 193 | finally: 194 | sys.path.pop(0) 195 | 196 | # For some reason the diagram titles are 197 | #not set correctly, so fix that here if needed. 198 | title = args[-1] 199 | for diagram in diadefs: 200 | if "No Name" in diagram.title: 201 | diagram.title = title 202 | 203 | try: 204 | packageDiagram, classDiagram = diadefs 205 | writePackageDiagram(packageDiagram) 206 | except ValueError: 207 | classDiagram = diadefs[0] 208 | writeClassDiagram(classDiagram) 209 | 210 | 211 | def main(): 212 | args = sys.argv[1:] 213 | CustomRun(args) 214 | 215 | 216 | if __name__ == "__main__": 217 | main() -------------------------------------------------------------------------------- /pyplantuml/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cb109/pyplantuml/3318ee832456eca3b8a6390b30515e182383e36f/pyplantuml/__init__.py -------------------------------------------------------------------------------- /pyplantuml/adapter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from logilab.common.configuration import ConfigurationMixIn 5 | 6 | from astroid.manager import AstroidManager 7 | from astroid.inspector import Linker 8 | 9 | from pylint.pyreverse.main import Run 10 | from pylint.pyreverse.diadefslib import DiadefsHandler 11 | from pylint.pyreverse.utils import insert_default_options 12 | 13 | 14 | # TODO: Get package arg and add to PATH as well. 15 | 16 | class PyreverseAdapter(Run): 17 | """Integrate with pyreverse by overriding its CLI Run class to 18 | create diagram definitions that can be passed to a writer.""" 19 | 20 | def __init__(self, args): 21 | ConfigurationMixIn.__init__(self, usage=__doc__) 22 | insert_default_options() 23 | self.manager = AstroidManager() 24 | self.register_options_provider(self.manager) 25 | self.args = self.load_command_line_configuration() 26 | 27 | def run(self): 28 | if not self.args: 29 | print(self.help()) 30 | return 31 | # Insert current working directory to the python path to recognize 32 | # dependencies to local modules even if cwd is not in the PYTHONPATH. 33 | sys.path.insert(0, os.getcwd()) 34 | try: 35 | project = self.manager.project_from_files(self.args) 36 | linker = Linker(project, tag=True) 37 | handler = DiadefsHandler(self.config) 38 | diadefs = handler.get_diadefs(project, linker) 39 | finally: 40 | sys.path.pop(0) 41 | 42 | return diadefs 43 | 44 | 45 | def getDiagramDefinitions(args): 46 | """Entry point from outside.""" 47 | return PyreverseAdapter(args).run() 48 | -------------------------------------------------------------------------------- /pyplantuml/cli.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from pyplantuml import adapter 5 | from pyplantuml import writer 6 | from pyplantuml import online 7 | 8 | 9 | def parseArgs(args): 10 | return args 11 | 12 | 13 | def getTargetPackage(args): 14 | """Get package name from path.""" 15 | target = args[-1] 16 | package = target 17 | if "/" in target or "\\" in target or target in (".", ".."): 18 | package = os.path.basename(target) 19 | return package 20 | 21 | 22 | def fixDiagramTitle(diadefs, title): 23 | for i, diagram in enumerate(diadefs): 24 | if "No Name" in diagram.title: 25 | diadefs[i].title = title 26 | return diadefs 27 | 28 | 29 | def main(convert_online=False): 30 | args = sys.argv[1:] 31 | if not args: 32 | print (""" 33 | pyplantuml 34 | ---------- 35 | 36 | Usage: 37 | 38 | pyplantuml [pyreverse-options] 39 | 40 | If a plantuml.jar can be found on PATH, it will automatically be 41 | called afterwards to convert the text files to images. 42 | 43 | pyplantuml-web [pyreverse-options] 44 | 45 | Will use the online form on www.plantuml.com for conversion and 46 | display the result in your default browser. 47 | 48 | Do NOT use for sensitive data! 49 | 50 | Example: 51 | 52 | pyplantumtl -f ALL urllib 53 | """) 54 | return False 55 | 56 | target = getTargetPackage(args) 57 | 58 | diadefs = adapter.getDiagramDefinitions(args) 59 | diadefs = fixDiagramTitle(diadefs, target) 60 | 61 | umls = writer.toPlantUml(diadefs, []) 62 | for uml in umls: 63 | umlpath = os.path.abspath(uml) 64 | print("Created: {0}".format(umlpath)) 65 | 66 | # Conversion using the online form. 67 | if convert_online: 68 | online.displayOnline(umlpath) 69 | continue 70 | 71 | images = writer.visualizeLocally(umls) 72 | for image in images: 73 | imagepath = os.path.abspath(image) 74 | print("Created: {0}".format(imagepath)) 75 | 76 | 77 | def convert_local(): 78 | main() 79 | 80 | 81 | def convert_online(): 82 | main(convert_online=True) 83 | -------------------------------------------------------------------------------- /pyplantuml/online.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import webbrowser 4 | 5 | try: # 3.x 6 | import urllib.parse as urllib 7 | import urllib.request as urllib2 8 | except ImportError: # 2.x 9 | import urllib 10 | import urllib2 11 | 12 | ENC = "utf-8" 13 | URL_FORM = "http://www.plantuml.com/plantuml/form" 14 | IMAGE_URL_PATTERN = (r"(?Phttp://www\.plantuml\.com:80/" 15 | "plantuml/png/[a-zA-Z0-9_\-]+)") 16 | 17 | 18 | def _getResultFromPlantUmlServer(uml): 19 | """Takes either uml text or a file as input.""" 20 | if os.path.isfile(uml): 21 | with open(uml) as f: 22 | uml = f.read() 23 | 24 | data = urllib.urlencode({"text" : uml}) 25 | binaryData = data.encode(ENC) 26 | results = urllib2.urlopen(URL_FORM, binaryData) 27 | binaryHtml = results.read() 28 | html = binaryHtml.decode(ENC) 29 | return html 30 | 31 | 32 | def _displayInBrowser(html): 33 | """Extracts the png url from the 34 | html and opens it in the browser.""" 35 | pattern = re.compile(IMAGE_URL_PATTERN) 36 | match = pattern.search(html) 37 | if match: 38 | url = match.group("png") 39 | webbrowser.open(url) 40 | return url 41 | 42 | 43 | def displayOnline(uml): 44 | html = _getResultFromPlantUmlServer(uml) 45 | url = _displayInBrowser(html) 46 | return url 47 | -------------------------------------------------------------------------------- /pyplantuml/writer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from pylint.pyreverse.utils import is_interface 4 | 5 | 6 | EMPTY = "\n" 7 | STARTUML = "@startuml\n" 8 | ENDUML = "@enduml\n" 9 | TITLE = "title {title}\n" 10 | STYLECLASS = """ 11 | skinparam class { 12 | BackgroundColor White 13 | ArrowColor Grey 14 | BorderColor Black 15 | } 16 | """ 17 | STYLEPACKAGE = """ 18 | skinparam package { 19 | BackgroundColor White 20 | ArrowColor Grey 21 | BorderColor Black 22 | } 23 | skinparam packageStyle frame 24 | """ 25 | OPEN = "{\n" 26 | CLOSE = "}\n" 27 | CLASS = "class {name} \n" 28 | CLASSOPEN = "class {name} {{\n" 29 | INTERFACE = "interface {name} \n" 30 | INTERFACEOPEN = "interface {name} {{\n" 31 | PACKAGE = "package {name} {{\n}}\n" 32 | DEPENDS = "{parent} +-- {child}\n" 33 | EXTENSION = "{parent} <|-- {child}\n" 34 | COMPOSITION = "{parent} *-- {child}\n" 35 | AGGREGATION = "{parent} o-- {child}\n" 36 | CLASSMETHOD = " {name}({args})\n" 37 | CLASSATTR = " {name}\n" 38 | 39 | invokeplantuml = 'java -jar "{jar}" "{input}" -o "{output}"' 40 | classes = "{package}_classes.txt" 41 | packages = "{package}_packages.txt" 42 | 43 | relationship2plantuml = { 44 | "specialization" : EXTENSION, 45 | "association" : AGGREGATION, 46 | "implements" : COMPOSITION 47 | } 48 | attr2type = { 49 | "public" : "+", 50 | "protected" : "#", 51 | "private" : "-" 52 | } 53 | 54 | 55 | def getAttrBase(attr): 56 | """E.g. 'Filesystem : str' -> 'Filesystem' """ 57 | return attr.split(":")[0].strip() 58 | 59 | 60 | def getFieldTypePrefix(attr): 61 | """Magic fields are public.""" 62 | if attr.startswith("__") and not attr.endswith("__"): 63 | return attr2type["private"] 64 | if attr.startswith("_") and not attr.endswith("__"): 65 | return attr2type["protected"] 66 | return attr2type["public"] 67 | 68 | 69 | def getAttrDesc(attr): 70 | base = getAttrBase(attr) 71 | desc = getFieldTypePrefix(attr) + base 72 | return desc 73 | 74 | 75 | def writePackageDiagram(diagram): 76 | stream = STARTUML 77 | stream += STYLEPACKAGE 78 | stream += TITLE.format(title=diagram.title) 79 | 80 | for module in diagram.modules(): 81 | stream += PACKAGE.format(name=module.title) 82 | 83 | for relation_type, relationsships in diagram.relationships.items(): 84 | for rel in relationsships: 85 | stream += DEPENDS.format( 86 | parent=rel.to_object.title, child=rel.from_object.title 87 | ) 88 | stream += "\n" + ENDUML 89 | 90 | packagesFile = packages.format(package=diagram.title) 91 | with open(packagesFile, "w") as f: 92 | f.write(stream) 93 | return packagesFile 94 | 95 | 96 | def writeClassDiagram(diagram): 97 | stream = STARTUML 98 | stream += STYLECLASS 99 | stream += TITLE.format(title=diagram.title) 100 | 101 | for obj in diagram.objects: 102 | attributes = diagram.get_attrs(obj.node) 103 | methods = diagram.get_methods(obj.node) 104 | 105 | if attributes or methods: 106 | template = INTERFACEOPEN if is_interface(obj.node) else CLASSOPEN 107 | stream += template.format(name=obj.title) 108 | 109 | for attr in sorted(attributes): 110 | attrDesc = getAttrDesc(attr) 111 | stream += CLASSATTR.format(name=attrDesc) 112 | 113 | for method in sorted(methods, key=lambda m: m.name): 114 | methodDesc = getAttrDesc(method.name) 115 | stream += CLASSMETHOD.format( 116 | name=methodDesc, args=method.args.format_args() 117 | ) 118 | 119 | stream += CLOSE 120 | else: 121 | template = INTERFACE if is_interface(obj.node) else CLASS 122 | stream += template.format(name=obj.title) 123 | 124 | stream += EMPTY 125 | 126 | for relation_type, relationsships in diagram.relationships.items(): 127 | for rel in relationsships: 128 | stream += relationship2plantuml[rel.type].format( 129 | parent=rel.to_object.title, child=rel.from_object.title 130 | ) 131 | 132 | stream += "\n" + ENDUML 133 | 134 | classesFile = classes.format(package=diagram.title) 135 | with open(classesFile, "w") as f: 136 | f.write(stream) 137 | return classesFile 138 | 139 | 140 | def getLocalPlantUmlPath(): 141 | """Returns the full path to plantuml.jar, 142 | if found on PATH, or None.""" 143 | plantuml = "plantuml.jar" 144 | searchPaths = os.environ["PATH"].split(os.pathsep) 145 | for searchPath in searchPaths: 146 | plantumlPath = os.path.join(searchPath, plantuml) 147 | if os.path.isfile(plantumlPath): 148 | return os.path.abspath(plantumlPath) 149 | return None 150 | 151 | 152 | def displayLocalImage(uml, jar): 153 | png = os.path.splitext(uml)[0] + ".png" 154 | cmd = invokeplantuml.format(jar=jar, input=uml, output=os.getcwd()) 155 | os.system(cmd) 156 | os.system(png) 157 | return png 158 | 159 | 160 | def toPlantUml(diadefs, plantumlArgs): 161 | umls = [] 162 | try: 163 | packageDiagram, classDiagram = diadefs 164 | umls.append(writePackageDiagram(packageDiagram)) 165 | except ValueError: 166 | classDiagram = diadefs[0] 167 | umls.append(writeClassDiagram(classDiagram)) 168 | return umls 169 | 170 | 171 | def visualizeLocally(umls): 172 | jar = getLocalPlantUmlPath() 173 | if not jar: 174 | print("Could not find a plantuml.jar on PATH.") 175 | return [] 176 | 177 | images = [] 178 | for uml in umls: 179 | images.append( 180 | displayLocalImage(uml, jar) 181 | ) 182 | return images -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # :coding: utf-8 2 | 3 | import setuptools 4 | 5 | 6 | setuptools.setup( 7 | name="pyplantuml", 8 | version="0.1.51", 9 | description="Creates UML diagrams from python source code.", 10 | long_description=open("README.md").read(), 11 | keywords="uml, code analysis, overview, quickstart, diagram, OOP", 12 | url="https://github.com/cb109/pyplantuml.git", 13 | author="Christoph Buelter", 14 | author_email="buelter.christoph@gmail.com", 15 | packages=setuptools.find_packages(), 16 | install_requires=[ 17 | "astroid==1.3.2", 18 | "pylint==1.4" 19 | ], 20 | entry_points={ 21 | "console_scripts": [ 22 | "pyplantuml=pyplantuml.cli:convert_local", 23 | "pyplantuml-web=pyplantuml.cli:convert_online", 24 | ], 25 | } 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /tests/test___init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/test_adapter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | def test_PyreverseAdapter_run(): 8 | raise(NotImplementedError) 9 | 10 | 11 | def test_getDiagramDefinitions(): 12 | raise(NotImplementedError) 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | def test_parseArgs(): 8 | raise(NotImplementedError) 9 | 10 | 11 | def test_getTargetPackage(): 12 | raise(NotImplementedError) 13 | 14 | 15 | def test_fixDiagramTitle(): 16 | raise(NotImplementedError) 17 | 18 | 19 | def test_main(): 20 | raise(NotImplementedError) 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/test_online.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | def test_getResultFromPlantUmlServer(): 8 | raise(NotImplementedError) 9 | 10 | 11 | def test_displayInBrowser(): 12 | raise(NotImplementedError) 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test_writer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | 7 | def test_getAttrBase(): 8 | raise(NotImplementedError) 9 | 10 | 11 | def test_getFieldTypePrefix(): 12 | raise(NotImplementedError) 13 | 14 | 15 | def test_getAttrDesc(): 16 | raise(NotImplementedError) 17 | 18 | 19 | def test_writePackageDiagram(): 20 | raise(NotImplementedError) 21 | 22 | 23 | def test_writeClassDiagram(): 24 | raise(NotImplementedError) 25 | 26 | 27 | def test_getLocalPlantUmlPath(): 28 | raise(NotImplementedError) 29 | 30 | 31 | def test_displayLocalImage(): 32 | raise(NotImplementedError) 33 | 34 | 35 | def test_displayOnline(): 36 | raise(NotImplementedError) 37 | 38 | 39 | def test_toPlantUml(): 40 | raise(NotImplementedError) 41 | 42 | 43 | def test_visualize(): 44 | raise(NotImplementedError) 45 | 46 | 47 | --------------------------------------------------------------------------------