blob: a62a1c2e7fa9aa558729fb0b348e8640f382d700 [file] [log] [blame]
# Copyright (c) 2008-2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Brett Cannon <brett@python.org>
# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
# Copyright (c) 2015-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
# Copyright (c) 2016 Alexander Pervakov <frost.nzcr4@jagmort.com>
# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
# Copyright (c) 2019, 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2021 Mark Byrne <mbyrnepr2@gmail.com>
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/LICENSE
"""
%prog [options] <packages>
create UML diagrams for classes and modules in <packages>
"""
import os
import subprocess
import sys
from pylint.config import ConfigurationMixIn
from pylint.pyreverse import writer
from pylint.pyreverse.diadefslib import DiadefsHandler
from pylint.pyreverse.inspector import Linker, project_from_files
from pylint.pyreverse.utils import insert_default_options
OPTIONS = (
(
"filter-mode",
dict(
short="f",
default="PUB_ONLY",
dest="mode",
type="string",
action="store",
metavar="<mode>",
help="""filter attributes and functions according to
<mode>. Correct modes are :
'PUB_ONLY' filter all non public attributes
[DEFAULT], equivalent to PRIVATE+SPECIAL_A
'ALL' no filter
'SPECIAL' filter Python special functions
except constructor
'OTHER' filter protected and private
attributes""",
),
),
(
"class",
dict(
short="c",
action="append",
metavar="<class>",
dest="classes",
default=[],
help="create a class diagram with all classes related to <class>;\
this uses by default the options -ASmy",
),
),
(
"show-ancestors",
dict(
short="a",
action="store",
metavar="<ancestor>",
type="int",
help="show <ancestor> generations of ancestor classes not in <projects>",
),
),
(
"all-ancestors",
dict(
short="A",
default=None,
help="show all ancestors off all classes in <projects>",
),
),
(
"show-associated",
dict(
short="s",
action="store",
metavar="<association_level>",
type="int",
help="show <association_level> levels of associated classes not in <projects>",
),
),
(
"all-associated",
dict(
short="S",
default=None,
help="show recursively all associated off all associated classes",
),
),
(
"show-builtin",
dict(
short="b",
action="store_true",
default=False,
help="include builtin objects in representation of classes",
),
),
(
"module-names",
dict(
short="m",
default=None,
type="yn",
metavar="[yn]",
help="include module name in representation of classes",
),
),
(
"only-classnames",
dict(
short="k",
action="store_true",
default=False,
help="don't show attributes and methods in the class boxes; \
this disables -f values",
),
),
(
"output",
dict(
short="o",
dest="output_format",
action="store",
default="dot",
metavar="<format>",
help="create a *.<format> output file if format available.",
),
),
(
"ignore",
{
"type": "csv",
"metavar": "<file[,file...]>",
"dest": "ignore_list",
"default": ("CVS",),
"help": "Files or directories to be skipped. They "
"should be base names, not paths.",
},
),
(
"project",
{
"default": "",
"type": "string",
"short": "p",
"metavar": "<project name>",
"help": "set the project name.",
},
),
(
"output-directory",
{
"default": "",
"type": "string",
"short": "d",
"action": "store",
"metavar": "<output_directory>",
"help": "set the output directory path.",
},
),
)
def _check_graphviz_available(output_format):
"""check if we need graphviz for different output format"""
try:
subprocess.call(["dot", "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError:
print(
"The output format '%s' is currently not available.\n"
"Please install 'Graphviz' to have other output formats "
"than 'dot' or 'vcg'." % output_format
)
sys.exit(32)
class Run(ConfigurationMixIn):
"""base class providing common behaviour for pyreverse commands"""
options = OPTIONS # type: ignore
def __init__(self, args):
ConfigurationMixIn.__init__(self, usage=__doc__)
insert_default_options()
args = self.load_command_line_configuration()
if self.config.output_format not in ("dot", "vcg"):
_check_graphviz_available(self.config.output_format)
sys.exit(self.run(args))
def run(self, args):
"""checking arguments and run project"""
if not args:
print(self.help())
return 1
# insert current working directory to the python path to recognize
# dependencies to local modules even if cwd is not in the PYTHONPATH
sys.path.insert(0, os.getcwd())
try:
project = project_from_files(
args,
project_name=self.config.project,
black_list=self.config.ignore_list,
)
linker = Linker(project, tag=True)
handler = DiadefsHandler(self.config)
diadefs = handler.get_diadefs(project, linker)
finally:
sys.path.pop(0)
if self.config.output_format == "vcg":
writer.VCGWriter(self.config).write(diadefs)
else:
writer.DotWriter(self.config).write(diadefs)
return 0
if __name__ == "__main__":
Run(sys.argv[1:])