def test_serialiser_python(make_dist): make_dist() jdist = JohnnyDist("jdtest") assert jdist.serialise() == [{ "name": "jdtest", "summary": "default text for metadata summary" }]
def test_serialiser(): dist = JohnnyDist('wheel') assert dist.serialise(format=None) == [{ 'name': 'wheel', 'summary': 'A built-package format for Python.' }] assert dist.serialise( format='toml' ) == 'name = "wheel"\nsummary = "A built-package format for Python."\n' assert dist.serialise( format='yaml' ) == '- {name: wheel, summary: A built-package format for Python.}\n' with ShouldRaise(Exception('Unsupported format')): dist.serialise(format='bogus')
def test_serialiser_toml(make_dist): make_dist() jdist = JohnnyDist("jdtest") assert jdist.serialise(format="toml") == dedent("""\ name = "jdtest" summary = "default text for metadata summary" """)
def main(): default_fields = os.environ.get("JOHNNYDEP_FIELDS", "name,summary").split(",") parser = ArgumentParser() parser.add_argument("req", help="The project name or requirement specifier") parser.add_argument("--index-url", "-i") parser.add_argument( "--output-format", "-o", choices=["human", "json", "yaml", "python", "toml", "pinned"], default="human", ) parser.add_argument( "--no-deps", help="Don't recurse the dependency tree", dest="recurse", action="store_false" ) parser.add_argument( "--fields", "-f", nargs="*", default=default_fields, choices=list(FIELDS) + ["ALL"] ) parser.add_argument("--for-python", "-p", dest="env", type=python_interpreter) parser.add_argument("--verbose", "-v", default=1, type=int, choices=range(3)) args = parser.parse_args() if "ALL" in args.fields: args.fields = list(FIELDS) configure_logging(verbosity=args.verbose) dist = JohnnyDist(args.req, index_url=args.index_url, env=args.env) print(dist.serialise(fields=args.fields, format=args.output_format, recurse=args.recurse))
def test_serialiser_json(make_dist): make_dist() jdist = JohnnyDist("jdtest") assert jdist.serialise(format="json") == dedent("""\ [ { "name": "jdtest", "summary": "default text for metadata summary" } ]""")
def test_serialiser_with_deps(): dist = JohnnyDist('fakedist[dev]') assert dist.serialise(fields=['name']) == [ { 'name': 'fakedist' }, { 'name': 'wheel' }, ]
def test_serialiser_custom_fields(make_dist): make_dist(version="0.1") make_dist(version="0.2") jdist = JohnnyDist("jdtest") expected = [{ "versions_available": ["0.1", "0.2"], "version_latest": "0.2" }] assert jdist.serialise(fields=("versions_available", "version_latest")) == expected
def test_non_json_metadata(): # this dist uses old-skool metadata format (plaintext not json) dist = JohnnyDist('testpath==0.3.1') assert dist.serialise(fields=['name', 'summary', 'import_names']) == [{ 'name': 'testpath', 'summary': 'Test utilities for code working with files and commands', 'import_names': ['testpath'], }]
def test_plaintext_whl_metadata(add_to_index): # this dist uses an old-skool metadata version 1.2 sdist_fname = os.path.join(here, "testpath-0.3.1-py2.py3-none-any.whl") fragment = "#md5=12728181294cf6f815421081d620c494" add_to_index(name="testpath", path=sdist_fname, urlfragment=fragment) dist = JohnnyDist("testpath==0.3.1") assert dist.serialise(fields=["name", "summary", "import_names", "homepage"]) == [ { "name": "testpath", "summary": "Test utilities for code working with files and commands", "import_names": ["testpath"], "homepage": "https://github.com/jupyter/testpath", } ]
def test_resolve_unresolvable(): dist = JohnnyDist('distX') data = dist.serialise(recurse=False, fields=['project_name', 'summary', 'requires']) assert data == [{ 'project_name': 'distX', 'summary': 'Dist with unresolvable dependencies', 'requires': ['distC (<=0.1)', 'distC (>0.2)'], # this installation requirement can not be resolved }] gen = flatten_deps(dist) assert next(gen) is dist with ShouldRaise( DistributionNotFound( 'No matching distribution found for distc<=0.1,>0.2')): next(gen)
def test_resolve_unresolvable(make_dist): make_dist(name="dist1", description="unresolvable", install_requires=["dist2<=0.1", "dist2>0.2"]) make_dist(name="dist2", version="0.1") make_dist(name="dist2", version="0.3") dist = JohnnyDist("dist1") data = dist.serialise(recurse=False, fields=["project_name", "summary", "requires"]) assert data == [{ "project_name": "dist1", "summary": "unresolvable", "requires": ["dist2<=0.1", "dist2>0.2"], }] gen = flatten_deps(dist) assert next(gen) is dist with pytest.raises(CalledProcessError) as cm: next(gen) msg = "DistributionNotFound: No matching distribution found for dist2<=0.1,>0.2\n" assert cm.value.output.decode().endswith(msg)
def main(): default_fields = os.environ.get('JOHNNYDEP_FIELDS', 'name,summary').split(',') parser = ArgumentParser() parser.add_argument('req', help='The project name or requirement specifier') parser.add_argument('--verbose', '-v', default=0, action='count') parser.add_argument('--index-url', '-i', default=DEFAULT_INDEX) parser.add_argument( '--output-format', '-o', choices=['json', 'yaml', 'text', 'python', 'toml', 'pinned'], default='text') parser.add_argument('--no-deps', help="Don't recurse the dependency tree", dest='recurse', action='store_false') parser.add_argument('--fields', '-f', nargs='*', default=default_fields, choices=list(FIELDS) + ['ALL']) args = parser.parse_args() if 'ALL' in args.fields: args.fields = list(FIELDS) configure_logging(verbosity=args.verbose) dist = JohnnyDist(args.req, index_url=args.index_url) if args.output_format == 'text': table = gen_table(dist, extra_cols=args.fields) tabulate.PRESERVE_WHITESPACE = True print(tabulate.tabulate(table, headers='keys')) else: print( dist.serialise(fields=args.fields, format=args.output_format, recurse=args.recurse))
def test_diamond_dependency_tree(): dist = JohnnyDist('distA') fields = [ 'name', 'summary', 'specifier', 'requires', 'required_by', 'import_names', 'homepage', 'extras_available', 'extras_requested', 'project_name', 'versions_available', 'version_installed', 'version_latest', 'version_latest_in_spec', 'download_link', 'checksum', ] data = dist.serialise(fields=fields) data = [dict(x) for x in data] assert data == [ { 'checksum': 'md5=c422ba4693b32da3c0721d425aff4eed', 'download_link': 'https://pypi.python.org/simple/dista/distA-0.1-py2.py3-none-any.whl', 'extras_available': [], 'extras_requested': [], 'homepage': None, 'import_names': [], 'name': 'dista', 'project_name': 'distA', 'required_by': [], 'requires': ['distB1', 'distB2'], 'specifier': '', 'summary': 'Top of a diamond dependency tree', 'version_installed': None, 'version_latest': '0.1', 'version_latest_in_spec': '0.1', 'versions_available': ['0.1'], }, { 'checksum': 'md5=a96f048902789a3faf8e8e829a27a5ec', 'download_link': 'https://pypi.python.org/simple/distb1/distB1-0.1-py2.py3-none-any.whl', 'extras_available': [], 'extras_requested': [], 'homepage': None, 'import_names': [], 'name': 'distb1', 'project_name': 'distB1', 'required_by': ['distA'], 'requires': ['distC[x,z] (<0.3)'], 'specifier': '', 'summary': 'Left edge of a diamond dependency tree', 'version_installed': None, 'version_latest': '0.1', 'version_latest_in_spec': '0.1', 'versions_available': ['0.1'], }, { 'checksum': 'md5=bc5761d4232c8e4a95dc0125605f5f65', 'download_link': 'https://pypi.python.org/simple/distb2/distB2-0.1-py2.py3-none-any.whl', 'extras_available': [], 'extras_requested': [], 'homepage': None, 'import_names': [], 'name': 'distb2', 'project_name': 'distB2', 'required_by': ['distA'], 'requires': ['distC[y] (!=0.2)'], 'specifier': '', 'summary': 'Right edge of a diamond dependency tree', 'version_installed': None, 'version_latest': '0.1', 'version_latest_in_spec': '0.1', 'versions_available': ['0.1'], }, { 'checksum': 'md5=e3404449fa48c97b384b6b64c52f5ce2', 'download_link': 'https://pypi.python.org/simple/distc/distC-0.1-py2.py3-none-any.whl', 'extras_available': ['X', 'Y', 'Z'], 'extras_requested': ['x', 'y', 'z'], # Note: extras got merged 'homepage': None, 'import_names': [], 'name': 'distc', 'project_name': 'distC', 'required_by': ['distB1', 'distB2'], 'requires': [], 'specifier': '!=0.2,<0.3', # Note: specifiers got merged 'summary': 'Bottom of a diamond dependency tree', 'version_installed': None, 'version_latest': '0.3', 'version_latest_in_spec': '0.1', # Even though this was not "best" for either dep 'versions_available': ['0.1', '0.2', '0.3'], }, ]
def test_serialiser_with_children_no_recurse(make_dist): make_dist(name="a", install_requires=["b"]) make_dist(name="b", version="2.0") jdist = JohnnyDist("A") assert jdist.serialise(format="pinned", recurse=False) == "a==0.1.2"
def test_serialiser_unsupported(make_dist): make_dist() jdist = JohnnyDist("jdtest") with pytest.raises(Exception) as cm: jdist.serialise(format="bogus") assert cm.value.args == ("Unsupported format", )
def test_serialiser_pinned(make_dist): make_dist() jdist = JohnnyDist("jdtest[a]") assert jdist.serialise(format="pinned") == "jdtest[a]==0.1.2"
def test_serialiser_yaml(make_dist): make_dist() jdist = JohnnyDist("jdtest") yaml_out = "- {name: jdtest, summary: default text for metadata summary}\n" assert jdist.serialise(format="yaml") == yaml_out
def get_dependencies_of(package: str): jd = JohnnyDist(package) dep_raw = jd.serialise(fields=('name', ), format='json') dep = json.loads(dep_raw) return dep
def test_get_caching(make_dist, mocker): # this test is trying to make sure that distribution "c", a node which appears # twice in the dependency tree, is only downloaded from the index once. # i.e. check that the ttl caching on the downloader is working correctly downloader = mocker.patch("johnnydep.lib.pipper.get") _, c, _ = make_dist(name="c", description="leaf node") _, b1, _ = make_dist(name="b1", install_requires=["c"], description="branch one") _, b2, _ = make_dist(name="b2", install_requires=["c"], description="branch two") _, a, _ = make_dist(name="a", install_requires=["b1", "b2"], description="root node") downloader.side_effect = [ { "path": a }, { "path": b1 }, { "path": b2 }, { "path": c }, ] jdist = JohnnyDist("a") txt = jdist.serialise(format="human") assert txt == dedent("""\ name summary --------- ---------- a root node ├── b1 branch one │ └── c leaf node └── b2 branch two └── c leaf node""") assert downloader.call_count == 4 assert downloader.call_args_list == [ mocker.call("a", env=None, extra_index_url=None, index_url=None, tmpdir="."), mocker.call("b1", env=None, extra_index_url=None, index_url=None, tmpdir="."), mocker.call("b2", env=None, extra_index_url=None, index_url=None, tmpdir="."), mocker.call("c", env=None, extra_index_url=None, index_url=None, tmpdir="."), ]