* feature: fnmatch based includes and excludes This is to support wildcards. * PR Feedback Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net> --------- Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net> Co-authored-by: Bernát Gábor <bgabor8@bloomberg.net>
This commit is contained in:
parent
c6b7837a7f
commit
3d3c805777
10
README.md
10
README.md
|
@ -8,7 +8,7 @@ works for packages installed globally on a machine as well as in a virtualenv. S
|
||||||
as a flat list, finding out which are the top level packages and which packages do they depend on requires some effort.
|
as a flat list, finding out which are the top level packages and which packages do they depend on requires some effort.
|
||||||
It\'s also tedious to resolve conflicting dependencies that could have been installed because older version of `pip`
|
It\'s also tedious to resolve conflicting dependencies that could have been installed because older version of `pip`
|
||||||
didn\'t have true dependency resolution[^1]. `pipdeptree` can help here by identifying conflicting dependencies
|
didn\'t have true dependency resolution[^1]. `pipdeptree` can help here by identifying conflicting dependencies
|
||||||
installed in the environment.R
|
installed in the environment.
|
||||||
|
|
||||||
To some extent, `pipdeptree` is inspired by the `lein deps :tree` command of [Leiningen](http://leiningen.org/).
|
To some extent, `pipdeptree` is inspired by the `lein deps :tree` command of [Leiningen](http://leiningen.org/).
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ pipdeptree has been tested with Python versions `3.7`, `3.8`, `3.9` and `3.10`.
|
||||||
If you want to run pipdeptree in the context of a particular virtualenv, you can specify the `--python` option. Note
|
If you want to run pipdeptree in the context of a particular virtualenv, you can specify the `--python` option. Note
|
||||||
that this capability has been recently added in version `2.0.0`.
|
that this capability has been recently added in version `2.0.0`.
|
||||||
|
|
||||||
Alternately, you may also install pipdeptree inside the virtualenv and then run it from there.
|
Alternatively, you may also install pipdeptree inside the virtualenv and then run it from there.
|
||||||
|
|
||||||
## Usage and examples
|
## Usage and examples
|
||||||
|
|
||||||
|
@ -250,10 +250,12 @@ optional arguments:
|
||||||
packages that need them under them.
|
packages that need them under them.
|
||||||
-p PACKAGES, --packages PACKAGES
|
-p PACKAGES, --packages PACKAGES
|
||||||
Comma separated list of select packages to show in the
|
Comma separated list of select packages to show in the
|
||||||
output. If set, --all will be ignored.
|
output. Wildcards are supported, like 'somepackage.*'.
|
||||||
|
If set, --all will be ignored.
|
||||||
-e PACKAGES, --exclude PACKAGES
|
-e PACKAGES, --exclude PACKAGES
|
||||||
Comma separated list of select packages to exclude
|
Comma separated list of select packages to exclude
|
||||||
from the output. If set, --all will be ignored.
|
from the output. Wildcards are supported, like
|
||||||
|
'somepackage.*'. If set, --all will be ignored.
|
||||||
-j, --json Display dependency tree as json. This will yield "raw"
|
-j, --json Display dependency tree as json. This will yield "raw"
|
||||||
output that may be used by external tools. This option
|
output that may be used by external tools. This option
|
||||||
overrides all other options.
|
overrides all other options.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import argparse
|
import argparse
|
||||||
|
import fnmatch
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
@ -350,14 +351,14 @@ class PackageDAG(Mapping):
|
||||||
m = {}
|
m = {}
|
||||||
seen = set()
|
seen = set()
|
||||||
for node in self._obj.keys():
|
for node in self._obj.keys():
|
||||||
if node.key in exclude:
|
if any(fnmatch.fnmatch(node.key, e) for e in exclude):
|
||||||
continue
|
continue
|
||||||
if include is None or node.key in include:
|
if include is None or any(fnmatch.fnmatch(node.key, i) for i in include):
|
||||||
stack.append(node)
|
stack.append(node)
|
||||||
while True:
|
while True:
|
||||||
if len(stack) > 0:
|
if len(stack) > 0:
|
||||||
n = stack.pop()
|
n = stack.pop()
|
||||||
cldn = [c for c in self._obj[n] if c.key not in exclude]
|
cldn = [c for c in self._obj[n] if not any(fnmatch.fnmatch(c.key, e) for e in exclude)]
|
||||||
m[n] = cldn
|
m[n] = cldn
|
||||||
seen.add(n.key)
|
seen.add(n.key)
|
||||||
for c in cldn:
|
for c in cldn:
|
||||||
|
@ -844,12 +845,20 @@ def get_parser():
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p",
|
"-p",
|
||||||
"--packages",
|
"--packages",
|
||||||
help="Comma separated list of select packages to show " "in the output. If set, --all will be ignored.",
|
help=(
|
||||||
|
"Comma separated list of select packages to show in the output. "
|
||||||
|
"Wildcards are supported, like 'somepackage.*'. "
|
||||||
|
"If set, --all will be ignored."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
"-e",
|
||||||
"--exclude",
|
"--exclude",
|
||||||
help="Comma separated list of select packages to exclude " "from the output. If set, --all will be ignored.",
|
help=(
|
||||||
|
"Comma separated list of select packages to exclude from the output. "
|
||||||
|
"Wildcards are supported, like 'somepackage.*'. "
|
||||||
|
"If set, --all will be ignored."
|
||||||
|
),
|
||||||
metavar="PACKAGES",
|
metavar="PACKAGES",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
|
@ -7,6 +7,7 @@ from itertools import chain
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from textwrap import dedent, indent
|
from textwrap import dedent, indent
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
@ -94,6 +95,43 @@ def test_package_dag_filter():
|
||||||
dag_to_dict(t.filter({"d"}, {"D", "e"}))
|
dag_to_dict(t.filter({"d"}, {"D", "e"}))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def t_fnmatch() -> Any:
|
||||||
|
return mock_package_dag(
|
||||||
|
{
|
||||||
|
("a.a", "1"): [("a.b", []), ("a.c", [])],
|
||||||
|
("a.b", "1"): [("a.c", [])],
|
||||||
|
("b.a", "1"): [("b.b", [])],
|
||||||
|
("b.b", "1"): [("a.b", [])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_dag_filter_fnmatch_include_a(t_fnmatch: Any) -> None:
|
||||||
|
# test include for a.*in the result we got only a.* nodes
|
||||||
|
graph = dag_to_dict(t_fnmatch.filter({"a.*"}, None))
|
||||||
|
assert graph == {"a.a": ["a.b", "a.c"], "a.b": ["a.c"]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_dag_filter_fnmatch_include_b(t_fnmatch: Any) -> None:
|
||||||
|
# test include for b.*, which has a.b and a.c in tree, but not a.a
|
||||||
|
# in the result we got the b.* nodes plus the a.b node as child in the tree
|
||||||
|
graph = dag_to_dict(t_fnmatch.filter({"b.*"}, None))
|
||||||
|
assert graph == {"b.a": ["b.b"], "b.b": ["a.b"], "a.b": ["a.c"]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_dag_filter_fnmatch_exclude_c(t_fnmatch: Any) -> None:
|
||||||
|
# test exclude for b.* in the result we got only a.* nodes
|
||||||
|
graph = dag_to_dict(t_fnmatch.filter(None, {"b.*"}))
|
||||||
|
assert graph == {"a.a": ["a.b", "a.c"], "a.b": ["a.c"]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_dag_filter_fnmatch_exclude_a(t_fnmatch: Any) -> None:
|
||||||
|
# test exclude for a.* in the result we got only b.* nodes
|
||||||
|
graph = dag_to_dict(t_fnmatch.filter(None, {"a.*"}))
|
||||||
|
assert graph == {"b.a": ["b.b"], "b.b": []}
|
||||||
|
|
||||||
|
|
||||||
def test_package_dag_reverse():
|
def test_package_dag_reverse():
|
||||||
t1 = t.reverse()
|
t1 = t.reverse()
|
||||||
expected = {"a": [], "b": ["a", "f"], "c": ["a"], "d": ["b", "c"], "e": ["c", "d", "g"], "f": ["g"], "g": []}
|
expected = {"a": [], "b": ["a", "f"], "c": ["a"], "d": ["b", "c"], "e": ["c", "d", "g"], "f": ["g"], "g": []}
|
||||||
|
|
Loading…
Reference in New Issue