|
#!/usr/bin/env python |
|
# -*- coding: utf-8 -*- |
|
|
|
""" |
|
gen-req |
|
reads a 'pyproject.toml' (Poetry) file and exports the list of dependencies |
|
to a 'requirements.txt' file, in a more easily understood, simple and |
|
traditional way. |
|
|
|
Copyright (c) 2021 Jorge Brunal Perez <[email protected]> |
|
|
|
This file is part of cl4ptr4p. |
|
|
|
gen-req is free software; you can redistribute it and/or |
|
modify it under the terms of the GNU Lesser General Public |
|
License as published by the Free Software Foundation; either |
|
version 2.1 of the License, or (at your option) any later version. |
|
|
|
gen-req is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
Lesser General Public License for more details. |
|
|
|
You should have received a copy of the GNU Lesser General Public |
|
License along with gen-req; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
|
|
|
|
this script does not pretend to cover all the configurations used by Poetry for depedencies. |
|
Dependencies in Poetry for a project can be specified in various forms, |
|
which depend on the type of the dependency and on the optional constraints |
|
that might be needed for it to be installed. |
|
|
|
more info: |
|
https://python-poetry.org/docs/dependency-specification/ |
|
|
|
if you want to use the export of Poetry run: |
|
poetry export -f requirements.txt --output requirements.txt --without-hashes |
|
|
|
Poetry version: 1.1.11 |
|
|
|
ToDO: |
|
|
|
- Wildcard requirements |
|
- Inequality requirements |
|
- path dependencies |
|
- url dependencies |
|
- Multiple constraints dependencies |
|
- git dependencies (branch) |
|
|
|
""" |
|
|
|
|
|
import sys |
|
import os |
|
import tomli |
|
|
|
APP_NAME = "gen-req" |
|
APP_VERSION = "V.1.0" |
|
POETRY_VERSION = "Poetry 1.1.11" |
|
THE_FILE = "pyproject.toml" |
|
EXPORT_FILE = "requirements.txt" |
|
|
|
NOT_ALLOWED_MODULES = ["python"] |
|
|
|
|
|
def clear_screen(): |
|
if sys.platform.startswith("linux"): |
|
os.system("clear") |
|
elif sys.platform.startswith("win"): |
|
os.system("cls") |
|
|
|
|
|
def open_file(): |
|
print() |
|
try: |
|
print("open", THE_FILE, "...") |
|
|
|
toml_dict = None |
|
|
|
with open(THE_FILE, "r") as f: |
|
toml_dict = tomli.load(f) |
|
|
|
package_list = process_file(toml_dict) |
|
export_data(package_list) |
|
except tomli.TOMLDecodeError: |
|
print("Error, definitely not valid.", THE_FILE) |
|
except Exception as e: |
|
print("Exception:", e) |
|
|
|
|
|
def process_file(toml_dict=None): |
|
print("checking", THE_FILE, "...") |
|
|
|
if toml_dict is None: |
|
print("Error: no valid scheme was found.") |
|
sys.exit() |
|
|
|
if "tool" not in toml_dict: |
|
print("Error bad configuration: no 'tool' section") |
|
sys.exit() |
|
|
|
tools = toml_dict["tool"] |
|
|
|
if "poetry" not in tools: |
|
print("Error bad configuration: no 'poetry' section") |
|
sys.exit() |
|
|
|
poetry = tools["poetry"] |
|
|
|
if "dependencies" not in poetry: |
|
print("Error bad configuration: no 'dependencies' section") |
|
sys.exit() |
|
|
|
dependencies = poetry["dependencies"] |
|
|
|
print("listing requirements...") |
|
|
|
req_list = [] |
|
translation_table = dict.fromkeys(map(ord, "~^"), None) |
|
|
|
for key, value in dependencies.items(): |
|
if key.lower() in NOT_ALLOWED_MODULES: |
|
print("Warning: {0} [module not allowed] skipping...".format(key)) |
|
continue |
|
|
|
if isinstance(value, (dict)): |
|
if "git" in value: |
|
if "rev" in value: |
|
dep = "{0} @ git+{1}@{2}".format(key, value["git"], value["rev"]) |
|
print("- {0}".format(dep)) |
|
req_list.append(dep) |
|
|
|
elif "tag" in value: |
|
dep = "{0} @ git+{1}@{2}".format(key, value["git"], value["tag"]) |
|
print("- {0}".format(dep)) |
|
req_list.append(dep) |
|
else: |
|
dep = "{0} @ git+{1}@master".format(key, value["git"]) |
|
print("- {0}".format(dep)) |
|
req_list.append(dep) |
|
|
|
elif "version" in value: |
|
dep_version = value["version"] |
|
print("- {0} {1}".format(key, dep_version)) |
|
|
|
new_value = dep_version.translate(translation_table) |
|
req_list.append("{0}=={1}".format(key, new_value)) |
|
else: |
|
print("Warning: {0} [unknown dependencies format] skipping...".format(key)) |
|
continue |
|
|
|
elif isinstance(value, (tuple, list)): |
|
print("Warning: {0} [Multiple constraints dependencies] skipping...".format(key)) |
|
continue |
|
|
|
else: |
|
print("- {0} {1}".format(key, value)) |
|
|
|
new_value = value.translate(translation_table) |
|
req_list.append("{0}=={1}".format(key, new_value)) |
|
|
|
return req_list |
|
|
|
|
|
def export_data(package_list): |
|
print() |
|
|
|
if len(package_list) <= 0: |
|
print("Error: a list of dependencies was not found.") |
|
sys.exit() |
|
|
|
print("generating requirements...") |
|
|
|
try: |
|
with open(EXPORT_FILE, "w") as f: |
|
intro = "#\n# This file is autogenerated by {0} {1}\n" |
|
intro += "# from {2} ({3})\n" |
|
intro += "#\n" |
|
intro = intro.format(APP_NAME, APP_VERSION, THE_FILE, POETRY_VERSION) |
|
f.write(intro) |
|
|
|
for item in package_list: |
|
line = "{0}\n".format(item) |
|
f.write(line) |
|
|
|
print("Yep, requirements.txt succesfully generated.") |
|
print("now check the 'requirements.txt' file.") |
|
except Exception as e: |
|
print("Oops, definitely can not generate file...", e) |
|
|
|
|
|
def help_cmd(): |
|
print(APP_NAME, APP_VERSION) |
|
print("if you want to use the export of Poetry run:") |
|
print("poetry export -f requirements.txt --output requirements.txt --without-hashes") |
|
|
|
|
|
if __name__ == "__main__": |
|
clear_screen() |
|
help_cmd() |
|
open_file() |