Added version reporting console command (#82)
* Add a script `report.py` for generating reports to the pyct repo
* Add a console command `report` for generating reports
* Adding tests for version reporting
kbowen authored 3 years ago
GitHub committed 3 years ago
141 | 141 | setup.py is run. The way this works is likely to change in the near |
142 | 142 | future, but is provided here as the first step towards |
143 | 143 | unifying/simplifying the maintenance of a number of pyviz projects. |
144 | ||
145 | ## pyct report | |
146 | ||
147 | Provides a way to check the package versions in the current environment using: | |
148 | 1. A console script (entry point): `pyct report [packages]`, or | |
149 | 2. A python function: `import pyct; pyct.report(packages)` | |
150 | ||
151 | The python function can be particularly useful for e.g. jupyter notebook users, since it is the packages in the current kernel that we usually care about (not those in the environment from which jupyter notebook server/lab was launched). | |
152 | ||
153 | Note that `packages` above can include the name of any Python package (returning the `__version__`), along with the special cases `python` or `conda` (returning the version of the command-line tool) or `system` (returning the OS version). |
29 | 29 | {% for dep in sdata['extras_require']['tests'] %} |
30 | 30 | - "{{ dep }}" |
31 | 31 | {% endfor %} |
32 | commands: | |
33 | - pytest pyct | |
34 | - pyct --help | |
35 | - pyct --version | |
36 | - pyct report --help | |
37 | - pyct report pyct python | |
32 | 38 | |
33 | 39 | about: |
34 | 40 | home: {{ sdata['url'] }} |
2 | 2 | # -*- coding: utf-8 -*- |
3 | 3 | |
4 | 4 | from __future__ import print_function, absolute_import, division |
5 | from . import __version__ | |
6 | from .report import report | |
5 | 7 | |
6 | 8 | import os |
7 | 9 | import importlib |
76 | 78 | import zipfile |
77 | 79 | |
78 | 80 | import yaml |
81 | ||
79 | 82 | try: |
80 | 83 | import requests |
81 | 84 | except ImportError: |
450 | 453 | args = parser.parse_args() |
451 | 454 | args.func(args) if hasattr(args,'func') else parser.error("must supply command to run") |
452 | 455 | |
456 | def main(): | |
457 | parser = argparse.ArgumentParser(description="Commands relating to versioning") | |
458 | parser.add_argument('--version', action='version', version='%(prog)s '+__version__) | |
459 | ||
460 | subparsers = parser.add_subparsers(title='available commands') | |
461 | ||
462 | report_parser = subparsers.add_parser('report', help=inspect.getdoc(report)) | |
463 | report_parser.set_defaults(func=report) | |
464 | report_parser.add_argument('packages',metavar='package',type=str,nargs='+', | |
465 | help='name of package') | |
466 | ||
467 | args = parser.parse_args() | |
468 | ||
469 | if hasattr(args,'func'): | |
470 | args.func(*args.packages) | |
471 | else: | |
472 | parser.error("must supply command to run") |
0 | #!/usr/bin/env python | |
1 | # Import and print the library version and filesystem location for each Python package or shell command specified | |
2 | # | |
3 | # bash usage: $ report numpy pandas python conda | |
4 | # python usage: >>> from report import report; report("numpy","pandas","python","conda") | |
5 | ||
6 | from __future__ import print_function | |
7 | import os.path, importlib, subprocess, platform, sys | |
8 | ||
9 | def report(*packages): | |
10 | """Import and print location and version information for specified Python packages""" | |
11 | accepted_commands = ['python','conda'] | |
12 | for package in packages: | |
13 | loc = "not installed in this environment" | |
14 | ver = "unknown" | |
15 | ||
16 | try: | |
17 | module = importlib.import_module(package) | |
18 | loc = os.path.dirname(module.__file__) | |
19 | ||
20 | try: | |
21 | ver = str(module.__version__) | |
22 | except Exception: | |
23 | pass | |
24 | ||
25 | except (ImportError, ModuleNotFoundError): | |
26 | if package in accepted_commands: | |
27 | try: | |
28 | # See if there is a command by that name and check its --version if so | |
29 | try: | |
30 | loc = subprocess.check_output(['command','-v', package]).decode().splitlines()[0].strip() | |
31 | except: | |
32 | # .exe in case powershell (otherwise wouldn't need it) | |
33 | loc = subprocess.check_output(['where.exe', package]).decode().splitlines()[0].strip() | |
34 | out = "" | |
35 | try: | |
36 | out = subprocess.check_output([package, '--version'], stderr=subprocess.STDOUT) | |
37 | except subprocess.CalledProcessError as e: | |
38 | out = e.output | |
39 | ||
40 | # Assume first word in output with a period and digits is the version | |
41 | for s in out.decode().split(): | |
42 | if '.' in s and str.isdigit(s[0]) and sum(str.isdigit(c) for c in s)>=2: | |
43 | ver=s.strip() | |
44 | break | |
45 | except: | |
46 | pass | |
47 | elif package == 'system': | |
48 | try: | |
49 | ver = platform.platform(terse=True) | |
50 | loc = "OS: " + platform.platform() | |
51 | except Exception: | |
52 | pass | |
53 | else: | |
54 | pass | |
55 | ||
56 | print("{0:30} # {1}".format(package + "=" + ver,loc)) | |
57 | ||
58 | ||
59 | def main(): | |
60 | report(*(sys.argv[1:])) | |
61 | ||
62 | if __name__ == "__main__": | |
63 | main() | |
64 |
0 | from unittest.mock import patch | |
1 | from pyct.report import report | |
2 | ||
3 | class TestModule: | |
4 | __version__ = "1.9.3" | |
5 | __file__ = "/mock/opt/anaconda3/envs/pyct/lib/python3.7/site-packages/param/__init__.py" | |
6 | ||
7 | @patch("builtins.print") | |
8 | @patch("importlib.import_module") | |
9 | def test_report_gives_package_version(mock_import_module, mock_print): | |
10 | module = TestModule() | |
11 | mock_import_module.return_value = module | |
12 | ||
13 | report("param") | |
14 | ||
15 | mock_print.assert_called_with('param=1.9.3 # /mock/opt/anaconda3/envs/pyct/lib/python3.7/site-packages/param') | |
16 | ||
17 | @patch("builtins.print") | |
18 | @patch("subprocess.check_output") | |
19 | def test_report_gives_conda_version(mock_check_output, mock_print): | |
20 | mock_check_output.side_effect = [b'/mock/opt/anaconda3/condabin/conda\n', b'conda 4.8.3\n'] | |
21 | ||
22 | report("conda") | |
23 | ||
24 | mock_print.assert_called_with("conda=4.8.3 # /mock/opt/anaconda3/condabin/conda") | |
25 | ||
26 | ||
27 | @patch("builtins.print") | |
28 | @patch("subprocess.check_output") | |
29 | def test_report_gives_python_version(mock_check_output, mock_print): | |
30 | mock_check_output.side_effect = [b'/mock/opt/anaconda3/envs/pyct/bin/python\n', b'Python 3.7.7\n'] | |
31 | ||
32 | report("python") | |
33 | ||
34 | mock_print.assert_called_with("python=3.7.7 # /mock/opt/anaconda3/envs/pyct/bin/python") | |
35 | ||
36 | @patch("builtins.print") | |
37 | @patch("platform.platform") | |
38 | def test_report_gives_system_version(mock_platform, mock_print): | |
39 | mock_platform.side_effect = ["Darwin-19.2.0", "Darwin-19.2.0-x86_64-i386-64bit"] | |
40 | ||
41 | report("system") | |
42 | ||
43 | mock_print.assert_called_with("system=Darwin-19.2.0 # OS: Darwin-19.2.0-x86_64-i386-64bit") | |
44 | ||
45 | @patch("builtins.print") | |
46 | def test_unknown_package_output(mock_print): | |
47 | report("fake_package") | |
48 | ||
49 | mock_print.assert_called_with("fake_package=unknown # not installed in this environment")⏎ |
78 | 78 | "setuptools", |
79 | 79 | "param >=1.7.0", |
80 | 80 | ] |
81 | }, | |
82 | entry_points = { | |
83 | 'console_scripts': [ | |
84 | 'pyct=pyct.cmd:main', | |
85 | ], | |
81 | 86 | } |
82 | 87 | ) |
83 | 88 |
0 | 0 | [tox] |
1 | envlist = {py27,py37}-{flakes,cmd_examples,build_examples,all}-{default}-{dev,pkg} | |
1 | envlist = {py27,py37}-{flakes,unit,cmd_examples,build_examples,all}-{default}-{dev,pkg} | |
2 | 2 | build = wheel |
3 | 3 | |
4 | 4 | [_flakes] |
16 | 16 | |
17 | 17 | [_all] |
18 | 18 | commands = {[_flakes]commands} |
19 | {[_unit]commands} | |
19 | 20 | {[_cmd_examples]commands} |
20 | 21 | {[_build_examples]commands} |
21 | 22 | deps = .[examples, tests] |
22 | 23 | |
24 | [_unit] | |
25 | description = Run unit tests | |
26 | deps = .[tests] | |
27 | commands = pytest pyct | |
28 | pyct --help | |
29 | pyct --version | |
30 | pyct report --help | |
31 | pyct report pyct python | |
32 | ||
23 | 33 | [testenv] |
24 | 34 | changedir = {envtmpdir} |
25 | 35 | |
26 | commands = cmd_examples: {[_cmd_examples]commands} | |
36 | commands = unit: {[_unit]commands} | |
37 | cmd_examples: {[_cmd_examples]commands} | |
27 | 38 | build_examples: {[_build_examples]commands} |
28 | 39 | flakes: {[_flakes]commands} |
29 | 40 | all: {[_all]commands} |
30 | 41 | |
31 | deps = cmd_examples: {[_cmd_examples]deps} | |
42 | deps = unit: {[_unit]deps} | |
43 | cmd_examples: {[_cmd_examples]deps} | |
32 | 44 | build_examples: {[_build_examples]deps} |
33 | 45 | flakes: {[_flakes]deps} |
34 | 46 | all: {[_all]deps} |
41 | 53 | ignore = E,W |
42 | 54 | include = *.py |
43 | 55 | exclude = .git,__pycache__,.tox,.eggs,*.egg,doc,dist,build,_build,.ipynb_checkpoints,run_test.py |
44 | ||
45 |