pydeps

Documentation Status https://travis-ci.org/thebjorn/pydeps.svg https://coveralls.io/repos/thebjorn/pydeps/badge.png

Python module dependency visualization. This package installs the pydeps command, and normal usage will be to use it from the command line.

New in version 1.3.4: --externals will now include modules that haven’t been installed (what modulefinder calls badmodules).

New in version 1.2.8: A shortcut for finding the direct external dependencies of a package was added:

pydeps --externals mypackage

which will print a json formatted list of module names to the screen, e.g.:

(dev) go|c:\srv\lib\dk-tasklib> pydeps --externals dktasklib
[
    "dkfileutils"
]

which meaans that the dktasklib package only depends on the dkfileutils package.

This functionality is also available programatically:

import os
from pydeps.pydeps import externals
# the directory that contains setup.py (one level up from actual package):
os.chdir('package-directory')
print externals('mypackage')

New in version 1.2.5: The defaults are now sensible, such that:

pydeps mypackage

will likely do what you want. It is the same as pydeps --show --max-bacon=2 mypackage which means display the dependency graph in your browser, but limit it to two hops (which includes only the modules that your module imports – not continuing down the import chain). The old default behavior is available with pydeps --noshow --max-bacon=0 mypackage.

To install:

pip install pydeps

To create graphs you need to install Graphviz (make sure the dot command is on your path).

Note

to display the resulting .svg files, pydeps by default calls firefox foo.svg. This is can be overridden with the --display PROGRAM option, where PROGRAM is an executable that can display the image file of the graph.

Note

Please report bugs and feature requests on GitHub at https://github.com/thebjorn/pydeps/issues

This is the result of running pydeps on itself (pydeps pydeps):

https://dl.dropboxusercontent.com/u/94882440/pydeps.svg

pydeps also contains an Erdős-like scoring function (a.k.a. Bacon number, from Six degrees of Kevin Bacon (http://en.wikipedia.org/wiki/Six_Degrees_of_Kevin_Bacon) that lets you filter out modules that are more than a given number of ‘hops’ away from the module you’re interested in. This is useful for finding the interface a module has to the rest of the world.

To find pydeps’ interface to the Python stdlib (less some very common modules).

pydeps pydeps --show --max-bacon 2 --pylib -x os re types _* enum
https://dl.dropboxusercontent.com/u/94882440/pydeps-pylib.svg

--max-bacon 2 (the default) gives the modules that are at most 2 hops away, and modules that belong together have similar colors. Compare that to the output with the --max-bacon=0 (infinite) filter:

https://dl.dropboxusercontent.com/u/94882440/pydeps-pylib-all.svg

All options can also be set in a .pydeps file using .ini file syntax (parsable by ConfigParser). Command line options override options in the .pydeps file in the current directory, which again overrides options in the user’s home directory (%USERPROFILE%\.pydeps on Windows and ${HOME}/.pydeps otherwise).

pydeps can detect and display cycles with the --show-cycles parameter. This will _only_ display the cycles, and for big libraries it is not a particularly fast operation. Given a folder with the following contents (this uses yaml to define a directory structure, like in the tests):

relimp:
    - __init__.py
    - a.py: |
        from . import b
    - b.py: |
        from . import a

pydeps relimp --show-cycles displays:

https://dl.dropboxusercontent.com/u/94882440/pydeps-cycle.svg

An attempt has been made to keep the intermediate formats readable, eg. the output from pydeps --show-deps .. looks like this:

...
"pydeps.mf27": {
    "imported_by": [
        "__main__",
        "pydeps.py2depgraph"
    ],
    "kind": "imp.PY_SOURCE",
    "name": "pydeps.mf27",
    "path": "pydeps\\mf27.py"
},
"pydeps.py2depgraph": {
    "imported_by": [
        "__main__",
        "pydeps.pydeps"
    ],
    "imports": [
        "pydeps.depgraph",
        "pydeps.mf27"
    ],
    "kind": "imp.PY_SOURCE",
    "name": "pydeps.py2depgraph",
    "path": "pydeps\\py2depgraph.py"
}, ...

Usage:

usage: pydeps-script.py [-h] [--config FILE] [--no-config] [-v] [-o file]
                        [-T FORMAT] [--display PROGRAM] [--noshow]
                        [--show-deps] [--show-raw-deps] [--show-dot]
                        [--show-cycles] [--debug] [--noise-level INT]
                        [--max-bacon INT] [--pylib] [--pylib-all]
                        [-x FNAME [FNAME ...]]
                        fname

positional arguments:
  fname                 filename

optional arguments:
  -h, --help            show this help message and exit
  --config FILE         specify config file
  --no-config           disable processing of config files
  -v, --verbose         be more verbose (-vv, -vvv for more verbosity)
  -o file               write output to 'file'
  -T FORMAT             output format (svg|png)
  --display PROGRAM     program used to display the graph (png or svg file
                        depending on the T parameter)
  --noshow              don't call external program to display graph
  --show-deps           show output of dependency analysis
  --show-raw-deps       show output of dependency analysis before removing
                        skips
  --show-dot            show output of dot conversion
  --show-cycles         show only import cycles
  --debug               turn on all the show and verbose options
  --noise-level INT     exclude sources or sinks with degree greater than
                        noise-level
  --max-bacon INT       exclude nodes that are more than n hops away
                        (default=2, 0 -> infinite)
  --pylib               include python std lib modules
  --pylib-all           include all std lib modules (incl. C modules)
  -x FNAME [FNAME ...], --exclude FNAME [FNAME ...]
                        input files to skip

You can of course import pydeps from Python (look in the tests/test_relative_imports.py file for examples.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am ‘Add some feature’)
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

API documentation

pydeps.colors

Color calculations.

class pydeps.colors.ColorSpace(nodes)[source]
add_to_tree(parts, tree)[source]
color(src)[source]
pydeps.colors.brightness(r, g, b)[source]

From w3c (range 0..255).

pydeps.colors.brightnessdiff(a, b)[source]

greater than 125 is good.

pydeps.colors.colordiff()[source]

From w3c (greater than 500 is good). (range [0..765])

pydeps.colors.distinct_hues(count)[source]

Return count hues, equidistantly spaced.

pydeps.colors.foreground(background, *options)[source]

Find the best foreground color from options based on background color.

pydeps.colors.frange(start, end, step)[source]

Like range(), but with floats.

pydeps.colors.name2rgb(hue)[source]

Originally used to calculate color based on module name.

pydeps.colors.rgb2css()[source]

Convert rgb to hex.

pydeps.colors.rgb2eightbit()[source]

Convert floats in [0..1] to integers in [0..256)

pydeps.depgraph

class pydeps.depgraph.DepGraph(depgraf, types, **args)[source]
add_source(src)[source]
calculate_bacon()[source]
connect_generations()[source]

Traverse depth-first adding imported_by.

dissimilarity_metric(a, b)[source]

Return non-zero if references to this module are strange, and should be drawn extra-long. The value defines the length, in rank. This is also good for putting some vertical space between seperate subsystems.

Returns an int between 1 (default) and 4 (highly unrelated).

exclude_bacon(limit)[source]

Exclude models that are more than limit hops away from __main__.

exclude_noise()[source]
find_import_cycles()[source]
get_colors(src, colorspace=None)[source]
levelcounts()[source]
proximity_metric(a, b)[source]

Return the weight of the dependency from a to b. Higher weights usually have shorter straighter edges. Return 1 if it has normal weight. A value of 4 is usually good for ensuring that a related pair of modules are drawn next to each other.

Returns an int between 1 (unknown, default), and 4 (very related).

remove_excluded()[source]

Remove all sources marked as excluded.

skip_modules = ['os', 'sys', 'qt', 'time', '__future__', 'types', 're', 'string', 'bdb', 'pdb', '__main__', 'south']
verbose(n, *args)[source]
class pydeps.depgraph.Source(name, kind=<imp.UNKNOWN: 0>, path=None, imports=(), exclude=False, args=None)[source]
basename
degree
in_degree

Number of incoming arrows.

is_noise()[source]

Is this module just noise? (too common either at top or bottom of the graph).

label

Convert a module name to a formatted node label. This is a default policy - please override.

name_parts
out_degree

Number of outgoing arrows.

path_parts
class pydeps.depgraph.imp[source]
C_BUILTIN = <imp.C_BUILTIN: 6>
C_EXTENSION = <imp.C_EXTENSION: 3>
IMP_HOOK = <imp.IMP_HOOK: 9>
PKG_DIRECTORY = <imp.PKG_DIRECTORY: 5>
PY_CODERESOURCE = <imp.PY_CODERESOURCE: 8>
PY_COMPILED = <imp.PY_COMPILED: 2>
PY_FROZEN = <imp.PY_FROZEN: 7>
PY_RESOURCE = <imp.PY_RESOURCE: 4>
PY_SOURCE = <imp.PY_SOURCE: 1>
UNKNOWN = <imp.UNKNOWN: 0>

pydeps.depgraph2dot

class pydeps.depgraph2dot.CycleGraphDot(colored=True)[source]
render(depgraph, ctx)[source]
class pydeps.depgraph2dot.PyDepGraphDot(colored=True)[source]
render(depgraph, ctx)[source]
pydeps.depgraph2dot.cycles2dot(depgraph)[source]
pydeps.depgraph2dot.dep2dot(depgraph, color=True)[source]

pydeps.dot

Graphviz interface.

pydeps.dot.cmd2args(cmd)[source]

Prepare a command line for execution by Popen.

pydeps.dot.dot(src, **kw)[source]

Execute the dot command to create an svg output.

pydeps.dot.pipe(cmd, txt)[source]

Pipe txt into the command cmd and return the output.

pydeps.mf27

Find modules used by a script, using introspection.

pydeps.mf27.AddPackagePath(packagename, path)[source]
class pydeps.mf27.Module(name, file=None, path=None)[source]
class pydeps.mf27.ModuleFinder(path=None, debug=0, excludes=[], replace_paths=[])[source]
add_module(fqname)[source]
any_missing()[source]

Return a list of modules that appear to be missing. Use any_missing_maybe() if you want to know which modules are certain to be missing, and which may be missing.

any_missing_maybe()[source]

Return two lists, one with modules that are certainly missing and one with modules that may be missing. The latter names could either be submodules or just global names in the package.

The reason it can’t always be determined is that it’s impossible to tell which names are imported when from module import * is done with an extension module, short of actually importing it.

determine_parent(caller, level=-1)[source]
ensure_fromlist(module, fromlist, recursive=0)[source]
find_all_submodules(m)[source]
find_head_package(parent, name)[source]
find_module(name, path, parent=None)[source]
import_hook(name, caller=None, fromlist=None, level=-1)[source]
import_module(partname, fqname, parent)[source]
load_file(pathname)[source]
load_module(fqname, fp, pathname, file_info)[source]
load_package(fqname, pathname)[source]
load_tail(q, tail)[source]
msg(level, msgtxt, *args)[source]
msgin(*args)[source]
msgout(*args)[source]
replace_paths_in_code(co)[source]
report()[source]

Print a report to stdout, listing the found modules with their paths, as well as modules that are missing, or seem to be missing.

run_script(pathname)[source]
scan_code(co, module)[source]
scan_opcodes(co, unpack=<built-in function unpack>)[source]
scan_opcodes_25(co, unpack=<built-in function unpack>)[source]
pydeps.mf27.ReplacePackage(oldname, newname)[source]
pydeps.mf27.test()[source]

pydeps.py2depgraph

class pydeps.py2depgraph.Module(name, file=None, path=None)[source]
shortname
class pydeps.py2depgraph.MyModuleFinder(fname, *args, **kwargs)[source]
add_module(fqname)[source]
ensure_fromlist(module, fromlist, recursive=0)[source]
import_hook(name, caller=None, fromlist=None, level=-1)[source]
import_module(partnam, fqname, parent)[source]
load_module()[source]
class pydeps.py2depgraph.RawDependencies(fname, **kw)[source]
pydeps.py2depgraph.fname2modname(fname, package, prefix='')[source]
class pydeps.py2depgraph.imp[source]
C_BUILTIN = <imp.C_BUILTIN: 6>
C_EXTENSION = <imp.C_EXTENSION: 3>
IMP_HOOK = <imp.IMP_HOOK: 9>
PKG_DIRECTORY = <imp.PKG_DIRECTORY: 5>
PY_CODERESOURCE = <imp.PY_CODERESOURCE: 8>
PY_COMPILED = <imp.PY_COMPILED: 2>
PY_FROZEN = <imp.PY_FROZEN: 7>
PY_RESOURCE = <imp.PY_RESOURCE: 4>
PY_SOURCE = <imp.PY_SOURCE: 1>
pydeps.py2depgraph.is_module(directory)[source]
pydeps.py2depgraph.py2dep(pattern, **kw)[source]
pydeps.py2depgraph.py2depgraph()[source]
pydeps.py2depgraph.pysource(fname)[source]

pydeps.pydeps

cli entrypoints.

pydeps.pydeps.externals(pkgname, **kwargs)[source]

Return a list of direct external dependencies of pkgname.

pydeps.pydeps.parse_args(argv=())[source]

Parse command line arguments, and return a dict.

pydeps.pydeps.pydeps()[source]

Entry point for the pydeps command.

pydeps.render_context

class pydeps.render_context.RenderContext(out=None)[source]
dedent(txt)[source]

Write txt dedented.

graph(*args, **kwds)[source]

Set up a graphviz graph context.

rule(*args, **kwds)[source]

Write indented rule.

text()[source]

Get value of output stream (StringIO).

write(txt)[source]

Write txt to file and output stream (StringIO).

write_attributes(attrs)[source]

Write comma separated attribute values (if exists).

write_node(a, **attrs)[source]

a [a1=x,a2=y];

write_rule(a, b, **attrs)[source]

a -> b [a1=x,a2=y];

writeln(txt)[source]

Write txt and add newline.

Indices and tables