-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpipin.py
More file actions
154 lines (126 loc) · 4.76 KB
/
pipin.py
File metadata and controls
154 lines (126 loc) · 4.76 KB
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import argparse
from collections import defaultdict
import os
import re
import sys
try:
unicode
except NameError: # pragma: no cover
# Python 3
basestring = unicode = str
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
# dirs to exclude from search
EXCLUDE_PATHS = [
'.git', '.hg', '.egg', 'eggs', 'dist', 'bin', 'var', 'sdist',
'lib', 'lib64', 'sass', '.metadata', '.plugins', '.tox', 'migrations',
'projectmigrations', 'static', 'templates', 'templatetags', 'locale',
'client_media', 'docs']
# todo: add ssh, sftp protocols
uri_regex = re.compile(r'^(svn|git|bzr|hg|http|https|file|ftp):(\.+)')
file_uri_regex = re.compile(
r'^(?P<path>[^#]+)#egg=(?P<name>[^&]+)$', re.MULTILINE)
editable_uri_regex = re.compile(r'^((?P<vcs>svn|git|bzr|hg)\+)?'
'(?P<uri>[^#&]+)#egg=(?P<name>[^&]+)$',
re.MULTILINE)
vcs_uri_regex = re.compile(r'^(?P<vcs>svn|git|bzr|hg)\+'
'(?P<uri>[^#&]+)#egg=(?P<name>[^&]+)$',
re.MULTILINE)
def is_uri(uri):
match = re.match(uri_regex, uri.lower())
return match is not None
def is_vcs_uri(uri):
match = re.match(vcs_uri_regex, uri.lower())
return match is not None
def parse(s):
if not isinstance(s, basestring):
s = s.read()
excludes = (
'#', '-r', '--requirement',
'-f', '--find-links',
'-i', '--index-url', '--extra-index-url', '--no-index',
'-Z', '--always-unzip')
for line in s.splitlines():
line = line.strip()
if not line:
continue
if [x for x in excludes if line.startswith(x)]:
continue
if line.startswith('file:'):
match = re.match(file_uri_regex, line)
# TODO: shall it check for editable apps?
elif line.startswith('-e') or line.startswith('--editable') or \
is_uri(line) or is_vcs_uri(line):
if line.startswith('-e'):
tmpstr = line[len('-e'):].strip()
# too short requirement format
if len(tmpstr) < 5:
continue
elif line.startswith('--editable'):
tmpstr = line[len('--editable'):].strip()
else:
tmpstr = line
match = re.match(editable_uri_regex, tmpstr)
else:
try:
yield line
continue
except ValueError:
match = None
if match:
yield match.groupdict()['name']
else:
raise ValueError('Invalid requirement line "%s"' % line)
def _locate(root, filename):
for path, dirs, files in os.walk(os.path.abspath(root)):
if any(x in path for x in EXCLUDE_PATHS):
continue
if filename in files:
yield path, os.path.join(path, filename)
def _out(text, color):
sys.stdout.write("\x1b[1;%dm" % (30 + color) + text + "\x1b[0m\n")
def lets_pipin(_apps, _path, _file=None):
cnt = defaultdict(int)
cnt_projects = 0
filename = _file or 'requirements.txt'
for path, fpath in _locate(root=_path, filename=filename):
cnt_projects += 1
with open(fpath, 'r') as fopen:
items = ' '.join(parse(fopen))
_out(fpath.split('/')[-2].upper() + ' (' + fpath + ')', YELLOW)
reapp = None
for app in _apps:
if '*' in app:
reapp = re.search(
r'\b%s\b([\>\=\<]+)%s' % tuple(app.split('*')), items)
reapp = reapp.group() if reapp else None
if reapp:
_out("%s found" % reapp, CYAN)
cnt['%s_found' % app] += 1
elif app in items:
_out("%s found" % app, CYAN)
cnt['%s_found' % app] += 1
else:
_out("%s not found" % app, RED)
cnt['%s_not_found' % app] += 1
for app in _apps:
_out("\nSearched %s projects for %s:" % (cnt_projects, app), WHITE)
_out(" %s found" % cnt['%s_found' % app], CYAN)
_out(" %s not found" % cnt['%s_not_found' % app], RED)
def run(): # pragma: no cover
parser = argparse.ArgumentParser()
parser.add_argument(
"app",
nargs="+",
help="the app(s) you wish to search for.")
parser.add_argument(
"path",
action="store",
help="a directory to search in (use `.` for current directory).")
parser.add_argument(
"-f", "--file",
action="store",
help="name of the requirements file (default to `requirements.txt`).")
args = parser.parse_args()
lets_pipin(args.app, args.path, args.file)
if __name__ == '__main__': # pragma: no cover
run()