2021-06-24 08:28:11 +02:00
|
|
|
import re
|
2021-06-24 11:13:01 +02:00
|
|
|
import os
|
2021-06-24 11:35:20 +02:00
|
|
|
import subprocess
|
|
|
|
from datetime import datetime
|
2021-06-24 11:13:01 +02:00
|
|
|
from urllib.request import urlretrieve
|
2021-06-24 08:28:11 +02:00
|
|
|
|
2021-06-24 11:13:01 +02:00
|
|
|
def user_response_multi_choices(message, choices):
|
|
|
|
print(message)
|
2021-06-24 08:33:54 +02:00
|
|
|
for i, choice in enumerate(choices):
|
2021-06-24 11:13:01 +02:00
|
|
|
print(f' {i+1}. {choice}')
|
2021-06-24 07:36:28 +02:00
|
|
|
|
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
nb_choices = len(choices)
|
2021-06-28 21:01:37 +02:00
|
|
|
resp = input(f'Choose option [1-{nb_choices}] : ')
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 11:13:01 +02:00
|
|
|
if not resp.isdigit() or int(resp) not in range(1,nb_choices+1):
|
|
|
|
print("")
|
|
|
|
return user_response_multi_choices(message, choices)
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 11:13:01 +02:00
|
|
|
return int(resp)
|
2021-06-24 07:36:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
def user_response_yes_no(message):
|
2021-06-28 21:01:37 +02:00
|
|
|
resp = input(message + ' [Y/n] : ').lower()
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
if resp not in ['y', 'n']:
|
2021-06-24 11:13:01 +02:00
|
|
|
print("")
|
2021-06-24 08:33:54 +02:00
|
|
|
return user_response_yes_no(message)
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
return resp == 'y'
|
2021-06-24 07:36:28 +02:00
|
|
|
|
|
|
|
|
2021-06-24 11:13:01 +02:00
|
|
|
def get_mapping_files_from_pipreqs(tmp_path="/tmp/.py-req-guesser"):
|
|
|
|
"""
|
|
|
|
Retrieve import to package name mapping file and standard lib module list
|
|
|
|
This list comes from https://github.com/bndr/pipreqs
|
|
|
|
"""
|
|
|
|
|
|
|
|
skip_download = False
|
|
|
|
|
|
|
|
if not os.path.exists(tmp_path):
|
|
|
|
os.mkdir(tmp_path)
|
|
|
|
|
|
|
|
mapping_filepath = f"{tmp_path}/mapping"
|
|
|
|
stdlib_filepath = f"{tmp_path}/stdlib"
|
|
|
|
|
|
|
|
if os.path.exists(mapping_filepath) and os.path.exists(stdlib_filepath):
|
|
|
|
# File have already been downloaded
|
|
|
|
skip_download = True
|
|
|
|
|
|
|
|
if not skip_download:
|
|
|
|
msg = "We will download a mapping file from https://github.com/bndr/pipreqs\n" \
|
|
|
|
"Thanks to the maintainers of Pipreqs for keeping the mapping file "\
|
|
|
|
"and the STDlib module list up to date\n" \
|
|
|
|
f"Do you agree to downloading these files in '{tmp_path}' ?"
|
|
|
|
|
|
|
|
if not user_response_yes_no(msg):
|
|
|
|
print("\n\n[ERROR]Pipreqs mapping files are required, I encourage you to inspect the code to make sure everything is safe and rerun this")
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
print("")
|
|
|
|
# FIXME : This is not really scalable...
|
|
|
|
mapping_url = "https://raw.githubusercontent.com/bndr/pipreqs/90102acdbb23c09574d27df8bd1f568d34e0cfd3/pipreqs/mapping"
|
|
|
|
stdlib_url = "https://raw.githubusercontent.com/bndr/pipreqs/90102acdbb23c09574d27df8bd1f568d34e0cfd3/pipreqs/stdlib"
|
|
|
|
|
2021-06-28 17:50:21 +02:00
|
|
|
try:
|
|
|
|
urlretrieve(mapping_url, mapping_filepath)
|
|
|
|
urlretrieve(stdlib_url, stdlib_filepath)
|
|
|
|
except:
|
|
|
|
print("[ERROR] Internet access is required to fetch mapping files from https://github.com/bndr/pipreqs")
|
|
|
|
exit(1)
|
|
|
|
|
2021-06-24 11:13:01 +02:00
|
|
|
|
|
|
|
from_import_to_package_mapping = {}
|
|
|
|
from_package_to_import_mapping = {}
|
|
|
|
with open(mapping_filepath, 'r') as f:
|
|
|
|
for line in f.readlines():
|
|
|
|
import_name, package_name = line.strip().split(":")
|
|
|
|
|
|
|
|
from_import_to_package_mapping[import_name] = package_name
|
|
|
|
from_package_to_import_mapping[package_name] = import_name
|
|
|
|
|
|
|
|
with open(stdlib_filepath, 'r') as f:
|
|
|
|
stdlib = set([l.strip() for l in f.readlines()])
|
|
|
|
|
|
|
|
return stdlib, from_import_to_package_mapping, from_package_to_import_mapping
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-24 07:36:28 +02:00
|
|
|
def load_packages_from_requirements(filepath):
|
2021-06-24 08:33:54 +02:00
|
|
|
# TODO : Handle when multiple version conditions
|
|
|
|
# TODO : Handle greater than (>). If version contains >, should take the greatest available version at the date. Should fit with minor versions ?
|
|
|
|
with open(filepath, 'r') as f:
|
|
|
|
lines = f.readlines()
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
split_reg = re.compile(r'==|<=|>=|<|>')
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
packages = {}
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
for line in lines:
|
|
|
|
splitted = re.split(split_reg, line.strip())
|
|
|
|
if len(splitted) > 1:
|
|
|
|
version = splitted[-1]
|
|
|
|
else:
|
|
|
|
version = None
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 23:13:06 +02:00
|
|
|
packages[splitted[0].lower()] = version
|
2021-06-24 07:36:28 +02:00
|
|
|
|
2021-06-24 08:33:54 +02:00
|
|
|
return packages
|
2021-06-24 07:36:28 +02:00
|
|
|
|
|
|
|
|
2021-06-28 20:52:09 +02:00
|
|
|
def get_local_modules(print_modules=False, force_guess=None):
|
|
|
|
"""
|
|
|
|
Gather list of the local python modules so we don't query pypi for those modules
|
|
|
|
Lets say we have the following file structure :
|
|
|
|
/project
|
|
|
|
- main.py
|
|
|
|
/utils
|
|
|
|
- common.py
|
|
|
|
common.py will be imported in main.py using 'from utils import common'
|
|
|
|
We therefore need to include the folder 'utils' in our exclusion list
|
|
|
|
"""
|
|
|
|
if force_guess is None:
|
|
|
|
force_guess = set()
|
|
|
|
|
|
|
|
file_paths = subprocess.check_output('find . -name "*.py" -printf "%P\\n"', shell=True).decode().strip().split("\n")
|
|
|
|
|
|
|
|
modules = set()
|
|
|
|
|
|
|
|
for file_path in file_paths:
|
|
|
|
module = file_path.split('/')[0]
|
|
|
|
if '.py' in module:
|
|
|
|
module = module[:-3]
|
|
|
|
|
|
|
|
if module not in force_guess:
|
|
|
|
modules.add(module)
|
|
|
|
|
|
|
|
if print_modules:
|
|
|
|
print("\nWe detected the following local project modules :")
|
|
|
|
for module in modules:
|
|
|
|
print(" " + module)
|
|
|
|
print("We won't attempt to guess version for these packages (local files)")
|
|
|
|
print("In case of conflict, this can be overriden using --force_guess {package1},{package2},...")
|
|
|
|
|
|
|
|
return modules
|
2021-06-24 11:13:01 +02:00
|
|
|
|
|
|
|
|
2021-06-24 11:35:20 +02:00
|
|
|
def get_date_last_modified_python_file():
|
|
|
|
timestamp = subprocess.check_output('git log -n 1 --all --pretty="format:%ct" -- "*.py"', shell=True).decode()
|
|
|
|
|
|
|
|
if len(timestamp) == 0:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return datetime.fromtimestamp(int(timestamp))
|
|
|
|
|
|
|
|
|
2021-06-28 18:39:47 +02:00
|
|
|
def validate_cwd_is_git_repo():
|
|
|
|
try:
|
|
|
|
subprocess.check_output("git rev-parse --is-inside-work-tree 2>/dev/null", shell=True)
|
|
|
|
except:
|
|
|
|
# git rev-parse return non-zero exit code if not in repo
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2021-06-24 07:36:28 +02:00
|
|
|
def detect_os():
|
2021-06-24 08:33:54 +02:00
|
|
|
pass
|
2021-06-24 07:36:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_python_version():
|
2021-06-24 08:33:54 +02:00
|
|
|
pass
|