1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00
Files
certbot/tools/merge_requirements.py
Mads Jensen c02b2d30f2 Removed Python legacy __future__ imports (#8697)
There are still some left, but the `modification_check` test fails. Some are still in `tools`, and they can probably be removed as well. `with_statement` was introduced officially in Python 2.5, so there's really old stuff in the code base.
2021-03-05 16:53:20 -08:00

93 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python
"""Merges multiple Python requirements files into one file.
Requirements files specified later take precedence over earlier ones.
Only the simple formats SomeProject==1.2.3 or SomeProject<=1.2.3 are
currently supported.
"""
import sys
def process_entries(entries):
"""
Ignore empty lines, comments and editable requirements
:param list entries: List of entries
:returns: mapping from a project to its version specifier
:rtype: dict
"""
data = {}
for e in entries:
e = e.strip()
if e and not e.startswith('#') and not e.startswith('-e'):
# Support for <= was added as part of
# https://github.com/certbot/certbot/pull/8460 because we weren't
# able to pin a package to an exact version. Normally, this
# functionality shouldn't be needed so we could remove it in the
# future. If you do so, make sure to update other places in this
# file related to this behavior such as this file's docstring.
for comparison in ('==', '<=',):
parts = e.split(comparison)
if len(parts) == 2:
project_name = parts[0]
version = parts[1]
data[project_name] = comparison + version
break
else:
raise ValueError("Unexpected syntax '{0}'".format(e))
return data
def read_file(file_path):
"""Reads in a Python requirements file.
:param str file_path: path to requirements file
:returns: list of entries in the file
:rtype: list
"""
with open(file_path) as file_h:
return file_h.readlines()
def output_requirements(requirements):
"""Prepare print requirements to stdout.
:param dict requirements: mapping from a project to its version
specifier
"""
return '\n'.join('{0}{1}'.format(key, value)
for key, value in sorted(requirements.items()))
def main(*paths):
"""Merges multiple requirements files together and prints the result.
Requirement files specified later in the list take precedence over earlier
files. Files are read from file paths passed from the command line arguments.
If no command line arguments are defined, data is read from stdin instead.
:param tuple paths: paths to requirements files provided on command line
"""
data = {}
if paths:
for path in paths:
data.update(process_entries(read_file(path)))
else:
# Need to check if interactive to avoid blocking if nothing is piped
if not sys.stdin.isatty():
stdin_data = []
for line in sys.stdin:
stdin_data.append(line)
data.update(process_entries(stdin_data))
return output_requirements(data)
if __name__ == '__main__':
print(main(*sys.argv[1:])) # pylint: disable=star-args