aboutsummaryrefslogtreecommitdiff
path: root/versionscanner.py
blob: 404c14a4f0681ba4c0e1cbb51841b499a3dc8891 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
from __future__ import print_function
import json
import os
import pkgutil
import sys


STDERR, STDOUT = sys.stderr, sys.stdout


class VersionScanner(object):
    def __init__(self, package, exclusions=[], recursive=False, packages_only=False):
        self.package = package
        self.prefix = self.package.__name__ + '.'
        self.exclusions = exclusions
        self.versions = dict()
        self.recursive = recursive
        self.packages_only = packages_only
        self.scan()

    def __iter__(self):
        for k, v in self.versions.items():
            yield k, v

    def scan(self):
        try:
            module = self.package
            modname = module.__name__

            try:
                self.versions[modname] = module.__version__ or module.version or module._version
            except AttributeError:
                self.versions[modname] = None
        except ImportError as e:
            print('ImportError({0}): {1}'.format(modname, e), file=sys.stderr)

        if self.recursive:
            try:
                for importer, modname, ispkg in pkgutil.iter_modules(self.package.__path__, self.prefix):
                    excluded = False
                    for ex in self.exclusions:
                        ex = self.prefix + ex
                        if modname == ex:
                            excluded = True
                            break

                    if excluded:
                        continue

                    if self.packages_only and not ispkg:
                        continue

                    try:
                        module = None
                        with open(os.devnull, 'w') as devnull:
                            sys.stdout = devnull
                            module = importer.find_module(modname).load_module(modname)

                        sys.stdout = STDOUT

                        try:
                            self.versions[modname] = module.__version__ or module.version or module._version
                        except AttributeError:
                            self.versions[modname] = None

                    except ImportError as e:
                        print('ImportError({0}): {1}'.format(modname, e), file=sys.stderr)

            except AttributeError:
                # has no sub-packages or sub-modules, so ignore
                pass
            except TypeError:
                # has strange requirements at import-time, so ignore
                pass

    def as_json(self):
        return json.dumps(self.versions, sort_keys=True)


    def as_zip(self):
        return zip(self.versions.keys(), self.versions.values())


if __name__ == '__main__':
    import argparse
    import importlib


    parser = argparse.ArgumentParser()
    parser.add_argument('parent_package')
    parser.add_argument('-e', '--exclude',
        action='append',
        default=[],
        help='Ignore sub-[package|module] by name.')
    parser.add_argument('-v', '--verbose',
        action='store_true',
        help='Show packages without version data.')
    parser.add_argument('-j', '--json',
        action='store_true',
        help='Emit JSON to stdout')
    parser.add_argument('-p', '--packages-only',
        action='store_true',
        help='Ignore non-packages (i.e modules)')
    parser.add_argument('-r', '--recursive',
        action='store_true',
        help='Descend into package looking for additional version data.')

    args = parser.parse_args()

    try:
        with open(os.devnull, 'w') as devnull:
            sys.stdout = devnull
            parent_package = importlib.import_module(args.parent_package)

        sys.stdout = STDOUT
    except ImportError as e:
        print(e, file=sys.stderr)
        exit(1)

    scanner = VersionScanner(parent_package, args.exclude, args.recursive, args.packages_only)


    if args.json:
        print(scanner.as_json())
    else:
        for pkg, version in sorted(scanner):
            if not args.verbose and version is None:
                continue

            print('{0}={1}'.format(pkg, version))