-
Notifications
You must be signed in to change notification settings - Fork 661
/
Copy pathupdate_requirements.py
136 lines (110 loc) · 4.47 KB
/
update_requirements.py
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
# Copyright 2024 The Flax Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utility script for updating examples' "requirements.txt".
WARNING: Updates examples/*/requirements.txt in place, so you probably want to
run this in a clean working directory...
This script is useful to automatically update the requirements of the individual
examples. It's desirable to have these dependencies fully specified and
guaranteed to be complete in order to download an example directly from Github
and run it on a virtual machine without any additional manual package
installation (note though that jax/jaxlib must already be installed).
To update all "requirements.txt" the program is run with a `--versions` argument
that specifies the version of every package that is required by any of the
examples. If any of the versions is not specified, the program will fail with an
error.
Example invocations (from the Flax repo root directory):
python dev --versions='absl-py-0.12.0 clu-0.0.6 flax-0.3.6'
The actual pypi-version list can be read copied from the "Install Dependencies"
step from the latest Github build action:
https://github.com/google/flax/actions/workflows/build.yml
Alternatively, the list can also be provided from the local environment with:
python dev --versions="$(pip freeze | sed s/==/-/g) flax-0.3.6"
This will use the currently installed versions of all packages.
"""
import pathlib
import re
from absl import app, flags
FLAGS = flags.FLAGS
flags.DEFINE_string(
'versions',
None,
'space-separated list of pkg-name-1.0.0 pairs. can be obtained from local '
'environment with '
'`--version="$(pip freeze | sed s/==/-/g) flax-0.3.6"` '
'(note the flax version "override") '
'or from the "install dependencies" step in the github build action '
'https://github.com/google/flax/actions/workflows/build.yml',
)
flags.mark_flag_as_required('versions')
flags.DEFINE_bool('verbose', False, 'enables verbose output.')
flags.DEFINE_list('ignore', ['jax'], 'packages not to add to requirements.')
import_re = re.compile(r'(?:from|import)\s+(\w+)')
# maps `import cv2` to `pip install opencv-python`
pkg_map = {
'absl': 'absl-py',
'atari_py': 'atari-py',
'cv2': 'opencv-python',
'ml_collections': 'ml-collections',
'PIL': 'Pillow',
'tensorflow_datasets': 'tensorflow-datasets',
'tensorflow_text': 'tensorflow-text',
}
standard_libs = set(
'codecs collections dataclasses datetime enum functools math'
' multiprocessing itertools os pathlib random re sys tempfile time typing'
' unicodedata warnings'.split(' ')
)
def main(argv):
del argv
versions = {
pkg_version[: pkg_version.rindex('-')]: pkg_version[
pkg_version.rindex('-') + 1 :
]
for pkg_version in FLAGS.versions.replace('\n', ' ').split(' ')
if '-' in pkg_version
}
if FLAGS.verbose:
print('parsed versions:', sorted(versions.items()))
ignore = set(FLAGS.ignore)
examples_root = pathlib.Path(__file__).absolute().parents[1] / 'examples'
for example_dir in examples_root.glob('*'):
if not example_dir.is_dir():
continue
requirements = example_dir / 'requirements.txt'
if not requirements.exists():
print(f'Skipping directory "{example_dir}"')
continue
pkgs = set()
local_pkgs_and_modules = set()
for path in example_dir.glob('*'):
if path.is_dir() and any(path.glob('*.py')):
local_pkgs_and_modules.add(path.parts[-1]) # local package
for py in example_dir.glob('*.py'):
local_pkgs_and_modules.add(py.parts[-1][:-3]) # local module
for line in open(py):
m = import_re.match(line)
if m:
pkgs.add(m.group(1))
pkgs -= local_pkgs_and_modules | standard_libs
print(f'{requirements} -', end=' ')
with requirements.open('w') as f:
for pkg in sorted(pkgs, key=str.casefold):
if pkg in ignore:
continue
pkg = pkg_map.get(pkg, pkg)
print(f'{pkg}-{versions[pkg]}', end=' ')
f.write(f'{pkg}=={versions[pkg]}\n')
print()
if __name__ == '__main__':
app.run(main)