pax_global_header00006660000000000000000000000064134220575440014520gustar00rootroot0000000000000052 comment=5622312f78a29046f76666ba145a25b9ef09e0ad pipdeptree-0.13.2/000077500000000000000000000000001342205754400137445ustar00rootroot00000000000000pipdeptree-0.13.2/.gitignore000066400000000000000000000002101342205754400157250ustar00rootroot00000000000000*.pyc __pycache__ *~ *.egg-info/ build/ dist/ .tox/ tests/virtualenvs/equimapper/ .coverage coverage.xml htmlcov/ .cache/ .pytest_cache/pipdeptree-0.13.2/.travis.yml000066400000000000000000000005221342205754400160540ustar00rootroot00000000000000--- # travis-ci config for pipdeptree language: python python: - "2.7" - "3.3" - "3.4" - "3.5" - "3.6" addons: apt: packages: - graphviz before_install: - pip install -U pip>=8.0.2 - pip install graphviz - make test-env install: pip install . script: py.test -v after_script: make clean clean-env sudo: false pipdeptree-0.13.2/CHANGES.md000066400000000000000000000074161342205754400153460ustar00rootroot00000000000000Changelog ========= 0.13.2 ------ * Fixed call to `FrozenRequirement.to_dist` to handle changes to the internal api of pip version 19.0. The api change is because dependency links support has been removed in pip 19.0 See more: - https://github.com/pypa/pip/pull/6060 - https://github.com/pypa/pip/pull/5881/commits/46ffb13f13f69c509fd253329da49889008f8e23 0.13.1 ------ * Fixed import after changes in pip._internal introduced in pip version 18.1 0.13.0 ------ * Added `--exclude` option to exclude packages specified as CSV * In case of multiple version specs eg. =y, fix the order to ensure consistent output. The sorting is naive - puts the '>' prior to '<', and '!'. * [Developer affecting] Updated dependencies in test environments, thereby fixing the long standing issue of inconsistent test behaviour. 0.12.1 ------ * Fix import of 'FrozenRequirement' for pip 10.0.0 0.12.0 ------ * Changes to make pipdeptree work with pip 10.0.0. This change is backward compatible. 0.11.0 ------ * Added support for nested json output (`--json-tree` flag). Behaviour of `--json` stays the same. * Test environments have been updated to fix the builds. 0.10.1 ------ * Fixed change of behaviour due to support for ``--json`` and ``--packages`` together. PR #65 was reverted for this. 0.10.0 ------ * Dropped support for Python 2.6. * ``--json`` and ``--packages`` options can now be used together. * Fixed binary graphviz output on Python 3 0.9.0 ----- * Support for visualizing dependency tree of packages using Graphviz in various formats. * Support to consider only packages installed in the user directory. * Fix the output to use a better term, "Any" instead of "None" if a dependency doesn't need to be of a specific version. * CLI option to print version. 0.8.0 ----- * Use pip's list of excluded default packages. This means that the ``pipdeptree`` package itself is no longer excluded and will appear in the output tree. * Fix the bug that caused a package to appear in conflicting deps although it's installed version could be guessed. 0.7.0 ----- * Fix for a bug in reverse mode. * Alphabetical sorting of packages in the output. * Fallback to guess installed version of packages "skipped" by pip. 0.6.0 ----- * Better checking for possibly "confusing" dependencies, hence the word "confusing" in the warning message is now replaced with "coflicting" [PR#37] * Fix a bug when rendering dependencies of packages [PR#38] * The ``--nowarn`` flag is now replaced with ``--warn`` with 'silence', 'suppress' and 'fail' as possible values, thus giving more control over what should happen when there are warnings. The default behaviour (ie. when the flag is not specified) remains the same. [PR#39] * Fixes for Python 3.5 support [PR#40] 0.5.0 ----- * Add `--reverse` flag to show the dependency tree upside down. * Add `--packages` flag to show only select packages in output. * Add `--json` flag to output dependency tree as json that may be used by external tools. 0.4.3 ----- * Add python support classifiers to setup.py * Include license and changelog in distribution tar ball * Removed bullets from output of pipdeptree if the `freeze` (-f) flag is set. * Changes related to test setup and travis-ci integration. 0.4.2 ----- * Fix Python 3.x incompatibility (`next()` instead of `.next()`) * Suppress error if a dep is in skipped packages 0.4.1 ----- * Fix: Show warning about cyclic deps only if found 0.4 --- * Python 2.6 compatibility * Fix infinite recursion in case of cyclic dependencies * Show warnings about cyclic dependencies * Travis integration and other improvements 0.3 --- * Add `--freeze` flag * Warn about possible confusing dependencies * Some minor help text and README fixes 0.2 --- * Minor fixes 0.1 --- First version pipdeptree-0.13.2/LICENSE000066400000000000000000000020631342205754400147520ustar00rootroot00000000000000Copyright (c) 2015 Vineet Naik (naikvin@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pipdeptree-0.13.2/MANIFEST.in000066400000000000000000000000321342205754400154750ustar00rootroot00000000000000include LICENSE CHANGES.mdpipdeptree-0.13.2/Makefile000066400000000000000000000006361342205754400154110ustar00rootroot00000000000000 clean: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + find . -name '__pycache__' -exec rmdir {} + clean-env: rm -rf tests/virtualenvs/{testenv,cyclicenv,unsatisfiedenv} rm tests/virtualenvs/*.pickle test-env: pip install tox cd tests/virtualenvs; make testenv cyclicenv unsatisfiedenv test: test-env tox -e py27 test-tox: test-env tox pipdeptree-0.13.2/README.rst000066400000000000000000000273541342205754400154460ustar00rootroot00000000000000pipdeptree ========== .. image:: https://travis-ci.org/naiquevin/pipdeptree.svg?branch=master :target: https://travis-ci.org/naiquevin/pipdeptree ``pipdeptree`` is a command line utility for displaying the installed python packages in form of a dependency tree. It works for packages installed globally on a machine as well as in a virtualenv. Since ``pip freeze`` shows all dependencies as a flat list, finding out which are the top level packages and which packages do they depend on requires some effort. It can also be tedious to resolve conflicting dependencies because ``pip`` doesn't yet have true dependency resolution (more on this later). This utility tries to solve this problem. To some extent, this tool is inspired by ``lein deps :tree`` command of `Leiningen `_. Installation ------------ .. code-block:: bash $ pip install pipdeptree This will install the latest version of ``pipdeptree`` which requires at least Python 2.7. Prior to version ``0.10.0``, Python 2.6 was also supported, so in case you are still stuck with 2.6, please install ``0.9.0``. Usage and examples ------------------ To give you a brief idea, here is the output of ``pipdeptree`` compared with ``pip freeze``: .. code-block:: bash $ pip freeze Flask==0.10.1 Flask-Script==0.6.6 Jinja2==2.7.2 -e git+git@github.com:naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy-master Mako==0.9.1 MarkupSafe==0.18 SQLAlchemy==0.9.1 Werkzeug==0.9.4 alembic==0.6.2 argparse==1.2.1 ipython==2.0.0 itsdangerous==0.23 psycopg2==2.5.2 redis==2.9.1 slugify==0.0.1 wsgiref==0.1.2 And now see what ``pipdeptree`` outputs, .. code-block:: bash $ pipdeptree Warning!!! Possible conflicting dependencies found: * Mako==0.9.1 -> MarkupSafe [required: >=0.9.2, installed: 0.18] Jinja2==2.7.2 -> MarkupSafe [installed: 0.18] ------------------------------------------------------------------------ Lookupy==0.1 wsgiref==0.1.2 argparse==1.2.1 psycopg2==2.5.2 Flask-Script==0.6.6 - Flask [installed: 0.10.1] - Werkzeug [required: >=0.7, installed: 0.9.4] - Jinja2 [required: >=2.4, installed: 2.7.2] - MarkupSafe [installed: 0.18] - itsdangerous [required: >=0.21, installed: 0.23] alembic==0.6.2 - SQLAlchemy [required: >=0.7.3, installed: 0.9.1] - Mako [installed: 0.9.1] - MarkupSafe [required: >=0.9.2, installed: 0.18] ipython==2.0.0 slugify==0.0.1 redis==2.9.1 Is it possible to find out why a particular package is installed? ----------------------------------------------------------------- `New in ver. 0.5.0` Yes, there's a `--reverse` (or simply `-r`) flag for this. To find out what all packages require paricular package(s), it can be combined with `--packages` flag as follows: .. code-block:: bash $ pipdeptree --reverse --packages itsdangerous,gnureadline gnureadline==6.3.3 - ipython==2.0.0 [requires: gnureadline] itsdangerous==0.24 - Flask==0.10.1 [requires: itsdangerous>=0.21] - Flask-Script==0.6.6 [requires: Flask] What's with the warning about conflicting dependencies? ------------------------------------------------------- As seen in the above output, ``pipdeptree`` by default warns about possible conflicting dependencies. Any package that's specified as a dependency of multiple packages with a different version is considered as a possible conflicting dependency. This is helpful because ``pip`` `doesn't have true dependency resolution `_ yet. The warning is printed to stderr instead of stdout and it can be completely silenced by using the ``-w silence`` or ``--warn silence`` flag. On the other hand, it can be made mode strict with ``--warn fail`` in which case the command will not only print the warnings to stderr but also exit with a non-zero status code. This could be useful if you want to fit this tool into your CI pipeline. **Note** The ``--warn`` flag was added in version 0.6.0. If you are using an older version, use ``--nowarn`` flag. Warnings about circular dependencies ------------------------------------ In case any of the packages have circular dependencies (eg. package A depending upon package B and package B depending upon package A), then ``pipdeptree`` will print warnings about that as well. .. code-block:: bash $ pipdeptree Warning!!! Cyclic dependencies found: - CircularDependencyA => CircularDependencyB => CircularDependencyA - CircularDependencyB => CircularDependencyA => CircularDependencyB ------------------------------------------------------------------------ wsgiref==0.1.2 argparse==1.2.1 As with the conflicting dependencies warnings, these are printed to stderr and can be controlled using the ``--warn`` flag. Using pipdeptree to write requirements.txt file ----------------------------------------------- If you wish to track only the top level packages in your ``requirements.txt`` file, it's possible to do so using ``pipdeptree`` by grep-ing only the top-level lines from the output, .. code-block:: bash $ pipdeptree | grep -P '^\w+' Lookupy==0.1 wsgiref==0.1.2 argparse==1.2.1 psycopg2==2.5.2 Flask-Script==0.6.6 alembic==0.6.2 ipython==2.0.0 slugify==0.0.1 redis==2.9.1 There is a problem here though. The output doesn't mention anything about ``Lookupy`` being installed as an editable package (refer to the output of ``pip freeze`` above) and information about its source is lost. To fix this, ``pipdeptree`` must be run with a ``-f`` or ``--freeze`` flag. .. code-block:: bash $ pipdeptree -f --warn silence | grep -P '^[\w0-9\-=.]+' -e git+git@github.com:naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy-master wsgiref==0.1.2 argparse==1.2.1 psycopg2==2.5.2 Flask-Script==0.6.6 alembic==0.6.2 ipython==2.0.0 slugify==0.0.1 redis==2.9.1 $ pipdeptree -f --warn silence | grep -P '^[\w0-9\-=.]+' > requirements.txt The freeze flag will also not output the hyphens for child dependencies, so you could dump the complete output of ``pipdeptree -f`` to the requirements.txt file making the file human-friendly (due to indentations) as well as pip-friendly. (Take care of duplicate dependencies though) Using pipdeptree with external tools ------------------------------------ `New in ver. 0.5.0` It's also possible to have pipdeptree output json representation of the dependency tree so that it may be used as input to other external tools. .. code-block:: bash $ pipdeptree --json Note that ``--json`` will output a flat list of all packages with their immediate dependencies. To obtain nested json, use ``--json-tree`` (added in version ``0.11.0``). .. code-block:: bash $ pipdeptree --json-tree The dependency graph can be layed out as any of the formats supported by `GraphViz `_: .. code-block:: bash $ pipdeptree --graph-output dot > dependencies.dot $ pipdeptree --graph-output pdf > dependencies.pdf $ pipdeptree --graph-output png > dependencies.png $ pipdeptree --graph-output svg > dependencies.svg Note that ``graphviz`` is an optional dependency ie. required only if you want to use ``--graph-output``. Also note that ``--json``, ``--json-tree`` and ``--graph-output`` options always override ``--package`` and ``--reverse``. Usage ----- .. code-block:: bash usage: pipdeptree.py [-h] [-v] [-f] [-a] [-l] [-u] [-w [{silence,suppress,fail}]] [-r] [-p PACKAGES] [-j] [--json-tree] [--graph-output OUTPUT_FORMAT] Dependency tree of the installed python packages optional arguments: -h, --help show this help message and exit -v, --version show program's version number and exit -f, --freeze Print names so as to write freeze files -a, --all list all deps at top level -l, --local-only If in a virtualenv that has global access do not show globally installed packages -u, --user-only Only show installations in the user site dir -w [{silence,suppress,fail}], --warn [{silence,suppress,fail}] Warning control. "suppress" will show warnings but return 0 whether or not they are present. "silence" will not show warnings at all and always return 0. "fail" will show warnings and return 1 if any are present. The default is "suppress". -r, --reverse Shows the dependency tree in the reverse fashion ie. the sub-dependencies are listed with the list of packages that need them under them. -p PACKAGES, --packages PACKAGES Comma separated list of select packages to show in the output. If set, --all will be ignored. -e PACKAGES, --exclude PACKAGES Comma separated list of select packages to exclude from the output. If set, --all will be ignored. -j, --json Display dependency tree as json. This will yield "raw" output that may be used by external tools. This option overrides all other options. --json-tree Display dependency tree as json which is nested the same way as the plain text output printed by default. This option overrides all other options (except --json). --graph-output OUTPUT_FORMAT Print a dependency graph in the specified output format. Available are all formats supported by GraphViz, e.g.: dot, jpeg, pdf, png, svg Known Issues ------------ * To work with packages installed inside a virtualenv, pipdeptree also needs to be installed in the same virtualenv even if it's already installed globally. * One thing you might have noticed already is that ``flask`` is shown as a dependency of ``flask-script``, which although correct, sounds a bit odd. ``flask-script`` is being used here *because* we are using ``flask`` and not the other way around. Same with ``sqlalchemy`` and ``alembic``. I haven't yet thought about a possible solution to this! (May be if libs that are "extensions" could be distinguished from the ones that are "dependencies". Suggestions are welcome.) Runnings Tests (for contributors) --------------------------------- Tests can be run against all version of python using `tox `_ as follows: .. code-block:: bash $ make test-tox This assumes that you have python versions 2.7, 3.3 and 3.4, 3.5, 3.6 installed on your machine. (See more: tox.ini) Or if you don't want to install all the versions of python but want to run tests quickly against Python2.7 only: .. code-block:: bash $ make test Tests require some virtualenvs to be created, so another assumption is that you have ``virtualenv`` installed. Before pushing the code or sending pull requests it's recommended to run ``make test-tox`` once so that tests are run on all environments. (See more: Makefile) Release checklist ----------------- * Make sure that tests pass on TravisCI. * Create a commit with following changes and push it to github - Update the `__version__` in the `pipdeptree.py` file. - Add Changelog in `CHANGES.md` file. - Also update `README.md` if required. * Create an annotated tag on the above commit and push the tag to github * Upload new version to PyPI. License ------- MIT (See `LICENSE <./LICENSE>`_) pipdeptree-0.13.2/pipdeptree.py000066400000000000000000000527321342205754400164700ustar00rootroot00000000000000from __future__ import print_function import os import sys from itertools import chain from collections import defaultdict import argparse from operator import attrgetter import json from importlib import import_module try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict try: from pip._internal.utils.misc import get_installed_distributions from pip._internal.operations.freeze import FrozenRequirement except ImportError: from pip import get_installed_distributions, FrozenRequirement import pkg_resources # inline: # from graphviz import backend, Digraph __version__ = '0.13.2' flatten = chain.from_iterable def build_dist_index(pkgs): """Build an index pkgs by their key as a dict. :param list pkgs: list of pkg_resources.Distribution instances :returns: index of the pkgs by the pkg key :rtype: dict """ return dict((p.key, DistPackage(p)) for p in pkgs) def construct_tree(index): """Construct tree representation of the pkgs from the index. The keys of the dict representing the tree will be objects of type DistPackage and the values will be list of ReqPackage objects. :param dict index: dist index ie. index of pkgs by their keys :returns: tree of pkgs and their dependencies :rtype: dict """ return dict((p, [ReqPackage(r, index.get(r.key)) for r in p.requires()]) for p in index.values()) def sorted_tree(tree): """Sorts the dict representation of the tree The root packages as well as the intermediate packages are sorted in the alphabetical order of the package names. :param dict tree: the pkg dependency tree obtained by calling `construct_tree` function :returns: sorted tree :rtype: collections.OrderedDict """ return OrderedDict(sorted([(k, sorted(v, key=attrgetter('key'))) for k, v in tree.items()], key=lambda kv: kv[0].key)) def find_tree_root(tree, key): """Find a root in a tree by it's key :param dict tree: the pkg dependency tree obtained by calling `construct_tree` function :param str key: key of the root node to find :returns: a root node if found else None :rtype: mixed """ result = [p for p in tree.keys() if p.key == key] assert len(result) in [0, 1] return None if len(result) == 0 else result[0] def reverse_tree(tree): """Reverse the dependency tree. ie. the keys of the resulting dict are objects of type ReqPackage and the values are lists of DistPackage objects. :param dict tree: the pkg dependency tree obtained by calling `construct_tree` function :returns: reversed tree :rtype: dict """ rtree = defaultdict(list) child_keys = set(c.key for c in flatten(tree.values())) for k, vs in tree.items(): for v in vs: node = find_tree_root(rtree, v.key) or v rtree[node].append(k.as_required_by(v)) if k.key not in child_keys: rtree[k.as_requirement()] = [] return rtree def guess_version(pkg_key, default='?'): """Guess the version of a pkg when pip doesn't provide it :param str pkg_key: key of the package :param str default: default version to return if unable to find :returns: version :rtype: string """ try: m = import_module(pkg_key) except ImportError: return default else: return getattr(m, '__version__', default) def frozen_req_from_dist(dist): try: return FrozenRequirement.from_dist(dist) except TypeError: return FrozenRequirement.from_dist(dist, []) class Package(object): """Abstract class for wrappers around objects that pip returns. This class needs to be subclassed with implementations for `render_as_root` and `render_as_branch` methods. """ def __init__(self, obj): self._obj = obj self.project_name = obj.project_name self.key = obj.key def render_as_root(self, frozen): return NotImplementedError def render_as_branch(self, frozen): return NotImplementedError def render(self, parent=None, frozen=False): if not parent: return self.render_as_root(frozen) else: return self.render_as_branch(frozen) @staticmethod def frozen_repr(obj): fr = frozen_req_from_dist(obj) return str(fr).strip() def __getattr__(self, key): return getattr(self._obj, key) def __repr__(self): return '<{0}("{1}")>'.format(self.__class__.__name__, self.key) class DistPackage(Package): """Wrapper class for pkg_resources.Distribution instances :param obj: pkg_resources.Distribution to wrap over :param req: optional ReqPackage object to associate this DistPackage with. This is useful for displaying the tree in reverse """ def __init__(self, obj, req=None): super(DistPackage, self).__init__(obj) self.version_spec = None self.req = req def render_as_root(self, frozen): if not frozen: return '{0}=={1}'.format(self.project_name, self.version) else: return self.__class__.frozen_repr(self._obj) def render_as_branch(self, frozen): assert self.req is not None if not frozen: parent_ver_spec = self.req.version_spec parent_str = self.req.project_name if parent_ver_spec: parent_str += parent_ver_spec return ( '{0}=={1} [requires: {2}]' ).format(self.project_name, self.version, parent_str) else: return self.render_as_root(frozen) def as_requirement(self): """Return a ReqPackage representation of this DistPackage""" return ReqPackage(self._obj.as_requirement(), dist=self) def as_required_by(self, req): """Return a DistPackage instance associated to a requirement This association is necessary for displaying the tree in reverse. :param ReqPackage req: the requirement to associate with :returns: DistPackage instance """ return self.__class__(self._obj, req) def as_dict(self): return {'key': self.key, 'package_name': self.project_name, 'installed_version': self.version} class ReqPackage(Package): """Wrapper class for Requirements instance :param obj: The `Requirements` instance to wrap over :param dist: optional `pkg_resources.Distribution` instance for this requirement """ UNKNOWN_VERSION = '?' def __init__(self, obj, dist=None): super(ReqPackage, self).__init__(obj) self.dist = dist @property def version_spec(self): specs = sorted(self._obj.specs, reverse=True) # `reverse` makes '>' prior to '<' return ','.join([''.join(sp) for sp in specs]) if specs else None @property def installed_version(self): if not self.dist: return guess_version(self.key, self.UNKNOWN_VERSION) return self.dist.version def is_conflicting(self): """If installed version conflicts with required version""" # unknown installed version is also considered conflicting if self.installed_version == self.UNKNOWN_VERSION: return True ver_spec = (self.version_spec if self.version_spec else '') req_version_str = '{0}{1}'.format(self.project_name, ver_spec) req_obj = pkg_resources.Requirement.parse(req_version_str) return self.installed_version not in req_obj def render_as_root(self, frozen): if not frozen: return '{0}=={1}'.format(self.project_name, self.installed_version) elif self.dist: return self.__class__.frozen_repr(self.dist._obj) else: return self.project_name def render_as_branch(self, frozen): if not frozen: req_ver = self.version_spec if self.version_spec else 'Any' return ( '{0} [required: {1}, installed: {2}]' ).format(self.project_name, req_ver, self.installed_version) else: return self.render_as_root(frozen) def as_dict(self): return {'key': self.key, 'package_name': self.project_name, 'installed_version': self.installed_version, 'required_version': self.version_spec} def render_tree(tree, list_all=True, show_only=None, frozen=False, exclude=None): """Convert tree to string representation :param dict tree: the package tree :param bool list_all: whether to list all the pgks at the root level or only those that are the sub-dependencies :param set show_only: set of select packages to be shown in the output. This is optional arg, default: None. :param bool frozen: whether or not show the names of the pkgs in the output that's favourable to pip --freeze :param set exclude: set of select packages to be excluded from the output. This is optional arg, default: None. :returns: string representation of the tree :rtype: str """ tree = sorted_tree(tree) branch_keys = set(r.key for r in flatten(tree.values())) nodes = tree.keys() use_bullets = not frozen key_tree = dict((k.key, v) for k, v in tree.items()) get_children = lambda n: key_tree.get(n.key, []) if show_only: nodes = [p for p in nodes if p.key in show_only or p.project_name in show_only] elif not list_all: nodes = [p for p in nodes if p.key not in branch_keys] def aux(node, parent=None, indent=0, chain=None): if exclude and (node.key in exclude or node.project_name in exclude): return [] if chain is None: chain = [node.project_name] node_str = node.render(parent, frozen) if parent: prefix = ' '*indent + ('- ' if use_bullets else '') node_str = prefix + node_str result = [node_str] children = [aux(c, node, indent=indent+2, chain=chain+[c.project_name]) for c in get_children(node) if c.project_name not in chain] result += list(flatten(children)) return result lines = flatten([aux(p) for p in nodes]) return '\n'.join(lines) def render_json(tree, indent): """Converts the tree into a flat json representation. The json repr will be a list of hashes, each hash having 2 fields: - package - dependencies: list of dependencies :param dict tree: dependency tree :param int indent: no. of spaces to indent json :returns: json representation of the tree :rtype: str """ return json.dumps([{'package': k.as_dict(), 'dependencies': [v.as_dict() for v in vs]} for k, vs in tree.items()], indent=indent) def render_json_tree(tree, indent): """Converts the tree into a nested json representation. The json repr will be a list of hashes, each hash having the following fields: - package_name - key - required_version - installed_version - dependencies: list of dependencies :param dict tree: dependency tree :param int indent: no. of spaces to indent json :returns: json representation of the tree :rtype: str """ tree = sorted_tree(tree) branch_keys = set(r.key for r in flatten(tree.values())) nodes = [p for p in tree.keys() if p.key not in branch_keys] key_tree = dict((k.key, v) for k, v in tree.items()) get_children = lambda n: key_tree.get(n.key, []) def aux(node, parent=None, chain=None): if chain is None: chain = [node.project_name] d = node.as_dict() if parent: d['required_version'] = node.version_spec if node.version_spec else 'Any' else: d['required_version'] = d['installed_version'] d['dependencies'] = [ aux(c, parent=node, chain=chain+[c.project_name]) for c in get_children(node) if c.project_name not in chain ] return d return json.dumps([aux(p) for p in nodes], indent=indent) def dump_graphviz(tree, output_format='dot'): """Output dependency graph as one of the supported GraphViz output formats. :param dict tree: dependency graph :param string output_format: output format :returns: representation of tree in the specified output format :rtype: str or binary representation depending on the output format """ try: from graphviz import backend, Digraph except ImportError: print('graphviz is not available, but necessary for the output ' 'option. Please install it.', file=sys.stderr) sys.exit(1) if output_format not in backend.FORMATS: print('{0} is not a supported output format.'.format(output_format), file=sys.stderr) print('Supported formats are: {0}'.format( ', '.join(sorted(backend.FORMATS))), file=sys.stderr) sys.exit(1) graph = Digraph(format=output_format) for package, deps in tree.items(): project_name = package.project_name label = '{0}\n{1}'.format(project_name, package.version) graph.node(project_name, label=label) for dep in deps: label = dep.version_spec if not label: label = 'any' graph.edge(project_name, dep.project_name, label=label) # Allow output of dot format, even if GraphViz isn't installed. if output_format == 'dot': return graph.source # As it's unknown if the selected output format is binary or not, try to # decode it as UTF8 and only print it out in binary if that's not possible. try: return graph.pipe().decode('utf-8') except UnicodeDecodeError: return graph.pipe() def print_graphviz(dump_output): """Dump the data generated by GraphViz to stdout. :param dump_output: The output from dump_graphviz """ if hasattr(dump_output, 'encode'): print(dump_output) else: with os.fdopen(sys.stdout.fileno(), 'wb') as bytestream: bytestream.write(dump_output) def conflicting_deps(tree): """Returns dependencies which are not present or conflict with the requirements of other packages. e.g. will warn if pkg1 requires pkg2==2.0 and pkg2==1.0 is installed :param tree: the requirements tree (dict) :returns: dict of DistPackage -> list of unsatisfied/unknown ReqPackage :rtype: dict """ conflicting = defaultdict(list) for p, rs in tree.items(): for req in rs: if req.is_conflicting(): conflicting[p].append(req) return conflicting def cyclic_deps(tree): """Return cyclic dependencies as list of tuples :param list pkgs: pkg_resources.Distribution instances :param dict pkg_index: mapping of pkgs with their respective keys :returns: list of tuples representing cyclic dependencies :rtype: generator """ key_tree = dict((k.key, v) for k, v in tree.items()) get_children = lambda n: key_tree.get(n.key, []) cyclic = [] for p, rs in tree.items(): for req in rs: if p.key in map(attrgetter('key'), get_children(req)): cyclic.append((p, req, p)) return cyclic def get_parser(): parser = argparse.ArgumentParser(description=( 'Dependency tree of the installed python packages' )) parser.add_argument('-v', '--version', action='version', version='{0}'.format(__version__)) parser.add_argument('-f', '--freeze', action='store_true', help='Print names so as to write freeze files') parser.add_argument('-a', '--all', action='store_true', help='list all deps at top level') parser.add_argument('-l', '--local-only', action='store_true', help=( 'If in a virtualenv that has global access ' 'do not show globally installed packages' )) parser.add_argument('-u', '--user-only', action='store_true', help=( 'Only show installations in the user site dir' )) parser.add_argument('-w', '--warn', action='store', dest='warn', nargs='?', default='suppress', choices=('silence', 'suppress', 'fail'), help=( 'Warning control. "suppress" will show warnings ' 'but return 0 whether or not they are present. ' '"silence" will not show warnings at all and ' 'always return 0. "fail" will show warnings and ' 'return 1 if any are present. The default is ' '"suppress".' )) parser.add_argument('-r', '--reverse', action='store_true', default=False, help=( 'Shows the dependency tree in the reverse fashion ' 'ie. the sub-dependencies are listed with the ' 'list of packages that need them under them.' )) parser.add_argument('-p', '--packages', help=( 'Comma separated list of select packages to show ' 'in the output. If set, --all will be ignored.' )) parser.add_argument('-e', '--exclude', help=( 'Comma separated list of select packages to exclude ' 'from the output. If set, --all will be ignored.' ), metavar='PACKAGES') parser.add_argument('-j', '--json', action='store_true', default=False, help=( 'Display dependency tree as json. This will yield ' '"raw" output that may be used by external tools. ' 'This option overrides all other options.' )) parser.add_argument('--json-tree', action='store_true', default=False, help=( 'Display dependency tree as json which is nested ' 'the same way as the plain text output printed by default. ' 'This option overrides all other options (except --json).' )) parser.add_argument('--graph-output', dest='output_format', help=( 'Print a dependency graph in the specified output ' 'format. Available are all formats supported by ' 'GraphViz, e.g.: dot, jpeg, pdf, png, svg' )) return parser def _get_args(): parser = get_parser() return parser.parse_args() def main(): args = _get_args() pkgs = get_installed_distributions(local_only=args.local_only, user_only=args.user_only) dist_index = build_dist_index(pkgs) tree = construct_tree(dist_index) if args.json: print(render_json(tree, indent=4)) return 0 elif args.json_tree: print(render_json_tree(tree, indent=4)) return 0 elif args.output_format: output = dump_graphviz(tree, output_format=args.output_format) print_graphviz(output) return 0 return_code = 0 # show warnings about possibly conflicting deps if found and # warnings are enabled if args.warn != 'silence': conflicting = conflicting_deps(tree) if conflicting: print('Warning!!! Possibly conflicting dependencies found:', file=sys.stderr) for p, reqs in conflicting.items(): pkg = p.render_as_root(False) print('* {}'.format(pkg), file=sys.stderr) for req in reqs: req_str = req.render_as_branch(False) print(' - {}'.format(req_str), file=sys.stderr) print('-'*72, file=sys.stderr) cyclic = cyclic_deps(tree) if cyclic: print('Warning!! Cyclic dependencies found:', file=sys.stderr) for a, b, c in cyclic: print('* {0} => {1} => {2}'.format(a.project_name, b.project_name, c.project_name), file=sys.stderr) print('-'*72, file=sys.stderr) if args.warn == 'fail' and (conflicting or cyclic): return_code = 1 show_only = set(args.packages.split(',')) if args.packages else None exclude = set(args.exclude.split(',')) if args.exclude else None if show_only and exclude and (show_only & exclude): print('Conflicting packages found in --packages and --exclude lists.', file=sys.stderr) sys.exit(1) tree = render_tree(tree if not args.reverse else reverse_tree(tree), list_all=args.all, show_only=show_only, frozen=args.freeze, exclude=exclude) print(tree) return return_code if __name__ == '__main__': sys.exit(main()) pipdeptree-0.13.2/pytest.ini000066400000000000000000000010701342205754400157730ustar00rootroot00000000000000[pytest] norecursedirs = build docs/_build *.egg .tox *.venv virtualenvs addopts = # --verbose --tb=short # Turn on --capture to have brief, less noisy output # You will only see output if the test fails # Use --capture no if you want to see it all or have problems debugging # --capture=fd # --capture=no # show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed. -rfEsxX # --junitxml=junit.xml # --cov=pipdeptree --cov-report=xml --cov-report=html --cov-report=term-missing pipdeptree-0.13.2/setup.py000066400000000000000000000026021342205754400154560ustar00rootroot00000000000000import re import ast from setuptools import setup _version_re = re.compile(r'__version__\s+=\s+(.*)') with open('pipdeptree.py', 'rb') as f: version = str(ast.literal_eval(_version_re.search( f.read().decode('utf-8')).group(1))) with open('./README.rst') as f: long_desc = f.read() install_requires = ["pip >= 6.0.0"] setup( name='pipdeptree', version=version, author='Vineet Naik', author_email='naikvin@gmail.com', url='https://github.com/naiquevin/pipdeptree', license='MIT License', description='Command line utility to show dependency tree of packages', long_description=long_desc, install_requires=install_requires, extras_require={'graphviz': ['graphviz']}, python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*', py_modules=['pipdeptree'], entry_points={ 'console_scripts': [ 'pipdeptree = pipdeptree:main' ] }, classifiers=[ 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ] ) pipdeptree-0.13.2/tests/000077500000000000000000000000001342205754400151065ustar00rootroot00000000000000pipdeptree-0.13.2/tests/test_pipdeptree.py000066400000000000000000000300311342205754400206550ustar00rootroot00000000000000import json import os import pickle import sys from contextlib import contextmanager from tempfile import NamedTemporaryFile from operator import attrgetter import pytest from pipdeptree import (build_dist_index, construct_tree, DistPackage, ReqPackage, render_tree, reverse_tree, cyclic_deps, conflicting_deps, get_parser, render_json, render_json_tree, dump_graphviz, print_graphviz, main) def venv_fixture(pickle_file): """Loads required virtualenv pkg data from a pickle file :param pickle_file: path to a .pickle file :returns: a tuple of pkgs, pkg_index, req_map :rtype: tuple """ with open(pickle_file, 'rb') as f: pkgs = pickle.load(f) dist_index = build_dist_index(pkgs) tree = construct_tree(dist_index) return pkgs, dist_index, tree pkgs, dist_index, tree = venv_fixture('tests/virtualenvs/testenv.pickle') def find_dist(key): return dist_index[key] def find_req(key, parent_key): parent = [x for x in tree.keys() if x.key == parent_key][0] return [x for x in tree[parent] if x.key == key][0] def test_build_dist_index(): assert len(dist_index) == len(pkgs) assert all(isinstance(x, str) for x in dist_index.keys()) assert all(isinstance(x, DistPackage) for x in dist_index.values()) def test_tree(): assert len(tree) == len(pkgs) assert all((isinstance(k, DistPackage) and all(isinstance(v, ReqPackage) for v in vs)) for k, vs in tree.items()) def test_reverse_tree(): rtree = reverse_tree(tree) assert all(isinstance(k, ReqPackage) for k, vs in rtree.items()) assert all(all(isinstance(v, DistPackage) for v in vs) for k, vs in rtree.items()) assert all(all(v.req is not None for v in vs) for k, vs in rtree.items()) def test_DistPackage_render_as_root(): alembic = find_dist('alembic') assert alembic.version == '0.9.10' assert alembic.project_name == 'alembic' assert alembic.render_as_root(frozen=False) == 'alembic==0.9.10' def test_DistPackage_render_as_branch(): sqlalchemy = find_req('sqlalchemy', 'alembic') alembic = find_dist('alembic').as_required_by(sqlalchemy) assert alembic.project_name == 'alembic' assert alembic.version == '0.9.10' assert sqlalchemy.project_name == 'SQLAlchemy' assert sqlalchemy.version_spec == '>=0.7.6' assert sqlalchemy.installed_version == '1.2.9' result_1 = alembic.render_as_branch(False) result_2 = alembic.render_as_branch(False) assert result_1 == result_2 == 'alembic==0.9.10 [requires: SQLAlchemy>=0.7.6]' def test_ReqPackage_render_as_root(): flask = find_req('flask', 'flask-script') assert flask.project_name == 'Flask' assert flask.installed_version == '1.0.2' assert flask.render_as_root(frozen=False) == 'Flask==1.0.2' def test_ReqPackage_render_as_branch(): mks1 = find_req('markupsafe', 'jinja2') assert mks1.project_name == 'MarkupSafe' assert mks1.installed_version == '1.0' assert mks1.version_spec == '>=0.23' assert mks1.render_as_branch(False) == 'MarkupSafe [required: >=0.23, installed: 1.0]' assert mks1.render_as_branch(True) == 'MarkupSafe==1.0' mks2 = find_req('markupsafe', 'mako') assert mks2.project_name == 'MarkupSafe' assert mks2.installed_version == '1.0' assert mks2.version_spec == '>=0.9.2' assert mks2.render_as_branch(False) == 'MarkupSafe [required: >=0.9.2, installed: 1.0]' assert mks2.render_as_branch(True) == 'MarkupSafe==1.0' def test_render_tree_only_top(): tree_str = render_tree(tree, list_all=False) lines = set(tree_str.split('\n')) assert 'Flask-Script==2.0.6' in lines assert ' - SQLAlchemy [required: >=0.7.6, installed: 1.2.9]' in lines assert 'Lookupy==0.1' in lines assert 'itsdangerous==0.24' not in lines def test_render_tree_list_all(): tree_str = render_tree(tree, list_all=True) lines = set(tree_str.split('\n')) assert 'Flask-Script==2.0.6' in lines assert ' - SQLAlchemy [required: >=0.7.6, installed: 1.2.9]' in lines assert 'Lookupy==0.1' in lines assert 'itsdangerous==0.24' in lines def test_render_tree_exclude(): tree_str = render_tree(tree, list_all=True, exclude={'itsdangerous', 'SQLAlchemy', 'Flask', 'markupsafe', 'wheel'}) expected = """alembic==0.9.10 - Mako [required: Any, installed: 1.0.7] - python-dateutil [required: Any, installed: 2.7.3] - six [required: >=1.5, installed: 1.11.0] - python-editor [required: >=0.3, installed: 1.0.3] click==6.7 Flask-Script==2.0.6 gnureadline==6.3.8 Jinja2==2.10 Lookupy==0.1 Mako==1.0.7 psycopg2==2.7.5 python-dateutil==2.7.3 - six [required: >=1.5, installed: 1.11.0] python-editor==1.0.3 redis==2.10.6 six==1.11.0 slugify==0.0.1 Werkzeug==0.14.1""" assert expected == tree_str def test_render_tree_exclude_reverse(): rtree = reverse_tree(tree) tree_str = render_tree(rtree, list_all=True, exclude={'itsdangerous', 'SQLAlchemy', 'Flask', 'markupsafe', 'wheel'}) expected = """alembic==0.9.10 click==6.7 Flask-Script==2.0.6 gnureadline==6.3.8 Jinja2==2.10 Lookupy==0.1 Mako==1.0.7 - alembic==0.9.10 [requires: Mako] psycopg2==2.7.5 python-dateutil==2.7.3 - alembic==0.9.10 [requires: python-dateutil] python-editor==1.0.3 - alembic==0.9.10 [requires: python-editor>=0.3] redis==2.10.6 six==1.11.0 - python-dateutil==2.7.3 [requires: six>=1.5] - alembic==0.9.10 [requires: python-dateutil] slugify==0.0.1 Werkzeug==0.14.1""" assert expected == tree_str def test_render_tree_freeze(): tree_str = render_tree(tree, list_all=False, frozen=True) lines = set() for line in tree_str.split('\n'): # Workaround for https://github.com/pypa/pip/issues/1867 # When hash randomization is enabled, pip can return different names # for git editables from run to run line = line.replace('origin/master', 'master') line = line.replace('origin/HEAD', 'master') lines.add(line) assert 'Flask-Script==2.0.6' in lines assert ' SQLAlchemy==1.2.9' in lines # TODO! Fix the following failing test # assert '-e git+https://github.com/naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy-master' in lines assert 'itsdangerous==0.24' not in lines def test_render_json(capsys): output = render_json(tree, indent=4) print_graphviz(output) out, _ = capsys.readouterr() assert out.startswith('[\n {\n "') assert out.strip().endswith('}\n]') data = json.loads(out) assert 'package' in data[0] assert 'dependencies' in data[0] def test_render_json_tree(): output = render_json_tree(tree, indent=4) data = json.loads(output) # @TODO: This test fails on travis because gnureadline doesn't # appear as a dependency of ipython (which it is) # # ignored_pkgs = {'pip', 'pipdeptree', 'setuptools', 'wheel'} # pkg_keys = set([d['key'].lower() for d in data # if d['key'].lower() not in ignored_pkgs]) # expected = {'alembic', 'flask-script', 'ipython', # 'lookupy', 'psycopg2', 'redis', 'slugify'} # assert pkg_keys - expected == set() matching_pkgs = [p for p in data if p['key'] == 'flask-script'] assert matching_pkgs flask_script = matching_pkgs[0] matching_pkgs = [p for p in flask_script['dependencies'] if p['key'] == 'flask'] assert matching_pkgs flask = matching_pkgs[0] matching_pkgs = [p for p in flask['dependencies'] if p['key'] == 'jinja2'] assert matching_pkgs jinja2 = matching_pkgs[0] assert [p for p in jinja2['dependencies'] if p['key'] == 'markupsafe'] def test_render_pdf(): output = dump_graphviz(tree, output_format='pdf') @contextmanager def redirect_stdout(new_target): old_target, sys.stdout = sys.stdout, new_target try: yield new_target finally: sys.stdout = old_target f = NamedTemporaryFile(delete=False) with redirect_stdout(f): print_graphviz(output) with open(f.name, 'rb') as rf: out = rf.read() os.remove(f.name) assert out[:4] == b'%PDF' def test_render_svg(capsys): output = dump_graphviz(tree, output_format='svg') print_graphviz(output) out, _ = capsys.readouterr() assert out.startswith('') def test_parser_default(): parser = get_parser() args = parser.parse_args([]) assert not args.json assert args.output_format is None def test_parser_j(): parser = get_parser() args = parser.parse_args(['-j']) assert args.json assert args.output_format is None def test_parser_json(): parser = get_parser() args = parser.parse_args(['--json']) assert args.json assert args.output_format is None def test_parser_json_tree(): parser = get_parser() args = parser.parse_args(['--json-tree']) assert args.json_tree assert not args.json assert args.output_format is None def test_parser_pdf(): parser = get_parser() args = parser.parse_args(['--graph-output', 'pdf']) assert args.output_format == 'pdf' assert not args.json def test_parser_svg(): parser = get_parser() args = parser.parse_args(['--graph-output', 'svg']) assert args.output_format == 'svg' assert not args.json def test_cyclic_dependencies(): cyclic_pkgs, dist_index, tree = venv_fixture('tests/virtualenvs/cyclicenv.pickle') cyclic = [map(attrgetter('key'), cs) for cs in cyclic_deps(tree)] assert len(cyclic) == 2 a, b, c = cyclic[0] x, y, z = cyclic[1] assert a == c == y assert x == z == b def test_render_tree_cyclic_dependency(): cyclic_pkgs, dist_index, tree = venv_fixture('tests/virtualenvs/cyclicenv.pickle') tree_str = render_tree(tree, list_all=True) lines = set(tree_str.split('\n')) assert 'CircularDependencyA==0.0.0' in lines assert ' - CircularDependencyB [required: Any, installed: 0.0.0]' in lines assert 'CircularDependencyB==0.0.0' in lines assert ' - CircularDependencyA [required: Any, installed: 0.0.0]' in lines def test_render_tree_freeze_cyclic_dependency(): cyclic_pkgs, dist_index, tree = venv_fixture('tests/virtualenvs/cyclicenv.pickle') tree_str = render_tree(tree, list_all=True, frozen=True) lines = set(tree_str.split('\n')) assert 'CircularDependencyA==0.0.0' in lines assert ' CircularDependencyB==0.0.0' in lines assert 'CircularDependencyB==0.0.0' in lines assert ' CircularDependencyA==0.0.0' in lines def test_conflicting_deps(): # the custom environment has a bad jinja version and it's missing simplejson _, _, conflicting_tree = venv_fixture('tests/virtualenvs/unsatisfiedenv.pickle') flask = next((x for x in conflicting_tree.keys() if x.key == 'flask')) jinja = next((x for x in conflicting_tree[flask] if x.key == 'jinja2')) uritemplate = next((x for x in conflicting_tree.keys() if x.key == 'uritemplate')) simplejson = next((x for x in conflicting_tree[uritemplate] if x.key == 'simplejson')) assert jinja assert flask assert uritemplate assert simplejson unsatisfied = conflicting_deps(conflicting_tree) assert unsatisfied == { flask: [jinja], uritemplate: [simplejson], } def test_main_basic(monkeypatch): parser = get_parser() args = parser.parse_args('') def _get_args(): return args monkeypatch.setattr('pipdeptree._get_args', _get_args) assert main() == 0 def test_main_show_only_and_exclude_ok(monkeypatch): parser = get_parser() args = parser.parse_args('--packages Flask --exclude Jinja2'.split()) def _get_args(): return args monkeypatch.setattr('pipdeptree._get_args', _get_args) assert main() == 0 def test_main_show_only_and_exclude_fails(monkeypatch): parser = get_parser() args = parser.parse_args('--packages Flask --exclude Jinja2,Flask'.split()) def _get_args(): return args monkeypatch.setattr('pipdeptree._get_args', _get_args) with pytest.raises(SystemExit): main() pipdeptree-0.13.2/tests/virtualenvs/000077500000000000000000000000001342205754400174705ustar00rootroot00000000000000pipdeptree-0.13.2/tests/virtualenvs/.gitignore000066400000000000000000000000541342205754400214570ustar00rootroot00000000000000*.pickle testenv/ cyclicenv/ unsatisfiedenv/pipdeptree-0.13.2/tests/virtualenvs/Makefile000066400000000000000000000014141342205754400211300ustar00rootroot00000000000000 testenv: virtualenv --python=python2.7 testenv testenv/bin/pip install -U pip setuptools testenv/bin/pip install -r testenv_requirements.txt testenv/bin/python pickle_env.py > testenv.pickle cyclicenv: virtualenv --python=python2.7 cyclicenv cyclicenv/bin/pip install -U pip setuptools cyclicenv/bin/pip install -r cyclicenv_requirements.txt cyclicenv/bin/python pickle_env.py > cyclicenv.pickle unsatisfiedenv: virtualenv --python=python2.7 unsatisfiedenv unsatisfiedenv/bin/pip install -U pip setuptools unsatisfiedenv/bin/pip install -r unsatisfiedenv_requirements.txt # test case where simplejson is required but not installed unsatisfiedenv/bin/pip install uritemplate==0.6 --no-dependencies unsatisfiedenv/bin/python pickle_env.py > unsatisfiedenv.pickle pipdeptree-0.13.2/tests/virtualenvs/cyclicenv_requirements.txt000066400000000000000000000000501342205754400250060ustar00rootroot00000000000000CircularDependencyA CircularDependencyB pipdeptree-0.13.2/tests/virtualenvs/pickle_env.py000077500000000000000000000011051342205754400221610ustar00rootroot00000000000000#!/usr/bin/env python # This is a small tool to create a pickle file for a set of packages for the # purposes of writing tests import pickle import sys try: from pip._internal.utils.misc import get_installed_distributions except ImportError: from pip import get_installed_distributions def main(): default_skip = ['setuptools', 'pip', 'python', 'distribute'] skip = default_skip + ['pipdeptree'] pkgs = get_installed_distributions(local_only=True, skip=skip) pickle.dump(pkgs, sys.stdout) return 0 if __name__ == '__main__': sys.exit(main()) pipdeptree-0.13.2/tests/virtualenvs/testenv_requirements.txt000066400000000000000000000004651342205754400245310ustar00rootroot00000000000000Flask==1.0.2 Flask-Script==2.0.6 Jinja2==2.10 -e git+https://github.com/naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy Mako==1.0.7 MarkupSafe==1.0 SQLAlchemy==1.2.9 Werkzeug==0.14.1 alembic==0.9.10 gnureadline==6.3.8 itsdangerous==0.24 psycopg2==2.7.5 redis==2.10.6 slugify==0.0.1 pipdeptree-0.13.2/tests/virtualenvs/unsatisfiedenv_requirements.txt000066400000000000000000000001511342205754400260600ustar00rootroot00000000000000Flask==0.10.1 itsdangerous==0.24 Jinja2==2.3 # requires >=2.4 MarkupSafe==0.23 Werkzeug==0.11.2 argparse pipdeptree-0.13.2/tox.ini000066400000000000000000000010111342205754400152500ustar00rootroot00000000000000# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py27, py33, py34, py35, py36 [testenv] commands = py.test {posargs:-v --cov pipdeptree --cov-report xml --cov-report html --cov-report term-missing tests/} deps = tox==3.0.0 graphviz pip>=8.0.2 pluggy==0.6.0 pytest==3.5.0 pytest-cov