Source code for pydeps.pydeps

# -*- coding: utf-8 -*-
"""cli entrypoints.
"""
from __future__ import print_function
import json
import os
import sys

from pydeps.configs import Config
from . import py2depgraph, cli, dot, target
from .depgraph2dot import dep2dot, cycles2dot
import logging
from . import colors
log = logging.getLogger(__name__)


def _pydeps(trgt, **kw):
    # Pass args as a **kw dict since we need to pass it down to functions
    # called, but extract locally relevant parameters first to make the
    # code prettier (and more fault tolerant).
    # print("KW:", kw, '\n', os.getcwd())
    # print('abspath:', os.path.abspath(kw.get('deps_out')))
    # print('target', trgt.workdir)
    # print('target', trgt)
    colors.START_COLOR = kw.get('start_color')
    # show_cycles = kw.get('show_cycles')
    nodot = kw.get('no_dot')
    no_output = kw.get('no_output')
    output = kw.get('output')
    fmt = kw['format']
    show_svg = kw.get('show')
    deps_out = kw.get('deps_out')
    dot_out = kw.get('dot_out')
    # reverse = kw.get('reverse')
    if os.getcwd() != trgt.workdir:
        # the tests are calling _pydeps directoy
        os.chdir(trgt.workdir)

    dep_graph = py2depgraph.py2dep(trgt, **kw)

    if kw.get('show_deps'):
        cli.verbose("DEPS:")
        if deps_out:
            # make sure output files are written to sensible directories
            directory, _fname = os.path.split(deps_out)
            if not directory:
                deps_out = os.path.join(trgt.calling_dir, deps_out)
            with open(deps_out, 'w') as fp:
                fp.write(dep_graph.__json__())
        else:
            print(dep_graph.__json__())

    dotsrc = depgraph_to_dotsrc(trgt, dep_graph, **kw)

    if not nodot:
        if kw.get('show_dot'):
            cli.verbose("DOTSRC:")
            if dot_out:
                # make sure output files are written to sensible directories
                directory, _fname = os.path.split(dot_out)
                if not directory:
                    dot_out = os.path.join(trgt.calling_dir, dot_out)
                with open(dot_out, 'w') as fp:
                    fp.write(dotsrc)
            else:
                print(dotsrc)

        if not no_output:
            try:
                svg = dot.call_graphviz_dot(dotsrc, fmt)
            except OSError as cause:
                raise RuntimeError("While rendering {!r}: {}".format(output, cause))
            if fmt == 'svg':
                svg = svg.replace(b'</title>', b'</title><style>.edge>path:hover{stroke-width:8}</style>')

            try:
                with open(output, 'wb') as fp:
                    cli.verbose("Writing output to:", output)
                    fp.write(svg)
            except OSError as cause:
                raise RuntimeError("While writing {!r}: {}".format(output, cause))

            if show_svg:
                try:
                    dot.display_svg(kw, output)
                except OSError as cause:
                    helpful = ""
                    if cause.errno == 2:
                        helpful = " (can be caused by not finding the program to open this file)"
                    raise RuntimeError("While opening {!r}: {}{}".format(output, cause, helpful))


[docs] def depgraph_to_dotsrc(target, dep_graph, **kw): """Convert the dependency graph (DepGraph class) to dot source code. """ if kw.get('show_cycles'): dotsrc = cycles2dot(target, dep_graph, **kw) elif not kw.get('no_dot'): dotsrc = dep2dot(target, dep_graph, **kw) else: dotsrc = None return dotsrc
[docs] def externals(trgt, **kwargs): """Return a list of direct external dependencies of ``pkgname``. Called for the ``pydeps --externals`` command. """ kw = dict( T='svg', config=None, debug=False, display=None, exclude=[], exclude_exact=[], externals=True, format='svg', max_bacon=2**65, no_config=True, nodot=False, noise_level=2**65, no_show=True, output=None, pylib=True, pylib_all=True, show=False, show_cycles=False, show_deps=False, show_dot=False, show_raw_deps=False, verbose=0, include_missing=True, start_color=0 ) kw.update(kwargs) depgraph = py2depgraph.py2dep(trgt, **kw) pkgname = trgt.fname log.info("DEPGRAPH: %s", depgraph) pkgname = os.path.splitext(pkgname)[0] res = {} ext = set() for k, src in list(depgraph.sources.items()): if k.startswith('_'): continue if not k.startswith(pkgname): continue if src.imports: imps = [imp for imp in src.imports if not imp.startswith(pkgname)] if imps: for imp in imps: ext.add(imp.split('.')[0]) res[k] = imps # return res # debug return list(sorted(ext))
[docs] def pydeps(**args): """Entry point for the ``pydeps`` command. This function should do all the initial parameter and environment munging before calling ``_pydeps`` (so that function has a clean execution path). """ sys.setrecursionlimit(10000) _args = dict(iter(Config(**args))) if args else cli.parse_args(sys.argv[1:]) _args['curdir'] = os.getcwd() inp = target.Target(_args['fname']) log.debug("Target: %r", inp) if _args.get('output'): _args['output'] = os.path.abspath(_args['output']) else: _args['output'] = os.path.join( inp.calling_dir, inp.modpath.replace('.', '_') + '.' + _args.get('format', 'svg') ) with inp.chdir_work(): # log.debug("Current directory: %s", os.getcwd()) _args['fname'] = inp.fname _args['isdir'] = inp.is_dir if _args.get('externals'): del _args['fname'] exts = externals(inp, **_args) print(json.dumps(exts, indent=4)) # return exts # so the tests can assert else: # this is the call you're looking for :-) try: return _pydeps(inp, **_args) except (OSError, RuntimeError) as cause: if log.isEnabledFor(logging.DEBUG): # we only want to log the exception if we're in debug mode log.exception("While running pydeps:") cli.error(str(cause))
[docs] def call_pydeps(file_or_dir, **kwargs): """Programatic entry point for pydeps. See :class:`pydeps.configs.Config` class for the available options. """ sys.setrecursionlimit(10000) inp = target.Target(file_or_dir) log.debug("Target: %r", inp) config = Config(**kwargs) if config.output: config.output = os.path.abspath(config.output) else: config.output = os.path.join( inp.calling_dir, inp.modpath.replace('.', '_') + '.' + config.format ) ctx = dict(iter(config)) with inp.chdir_work(): ctx['fname'] = inp.fname ctx['isdir'] = inp.is_dir if config.externals: del ctx['fname'] return externals(inp, **ctx) return _pydeps(inp, **ctx)
if __name__ == '__main__': # pragma: nocover pydeps()