mirror of
https://github.com/bndr/pipreqs.git
synced 2025-06-06 03:25:21 +00:00
Merge pull request #239 from mapattacker/mode
New Option --mode for "gt", "compat", and "non-pin"
This commit is contained in:
commit
cb4add28ef
@ -10,4 +10,5 @@ Development Lead
|
|||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
None yet. Why not be the first?
|
* Jake Teo <mapattacker@gmail.com>
|
||||||
|
* Jerome Chan <cjerome94@gmail.com>
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
History
|
History
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
0.4.11 (2020-03-29)
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* Implement '--mode' (Jake Teo, Jerome Chan)
|
||||||
|
|
||||||
0.4.8 (2017-06-30)
|
0.4.8 (2017-06-30)
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -49,7 +49,10 @@ Usage
|
|||||||
--force Overwrite existing requirements.txt
|
--force Overwrite existing requirements.txt
|
||||||
--diff <file> Compare modules in requirements.txt to project imports.
|
--diff <file> Compare modules in requirements.txt to project imports.
|
||||||
--clean <file> Clean up requirements.txt by removing modules that are not imported in project.
|
--clean <file> Clean up requirements.txt by removing modules that are not imported in project.
|
||||||
--no-pin Omit version of output packages.
|
--mode <scheme> Enables dynamic versioning with <compat>, <gt> or <non-pin> schemes.
|
||||||
|
<compat> | e.g. Flask~=1.1.2
|
||||||
|
<gt> | e.g. Flask>=1.1.2
|
||||||
|
<no-pin> | e.g. Flask
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
41
pipreqs/pipreqs.py
Executable file → Normal file
41
pipreqs/pipreqs.py
Executable file → Normal file
@ -31,7 +31,11 @@ Options:
|
|||||||
imports.
|
imports.
|
||||||
--clean <file> Clean up requirements.txt by removing modules
|
--clean <file> Clean up requirements.txt by removing modules
|
||||||
that are not imported in project.
|
that are not imported in project.
|
||||||
--no-pin Omit version of output packages.
|
--mode <scheme> Enables dynamic versioning with <compat>,
|
||||||
|
<gt> or <non-pin> schemes.
|
||||||
|
<compat> | e.g. Flask~=1.1.2
|
||||||
|
<gt> | e.g. Flask>=1.1.2
|
||||||
|
<no-pin> | e.g. Flask
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function, absolute_import
|
from __future__ import print_function, absolute_import
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@ -161,21 +165,21 @@ def filter_line(line):
|
|||||||
return len(line) > 0 and line[0] != "#"
|
return len(line) > 0 and line[0] != "#"
|
||||||
|
|
||||||
|
|
||||||
def generate_requirements_file(path, imports):
|
def generate_requirements_file(path, imports, symbol):
|
||||||
with _open(path, "w") as out_file:
|
with _open(path, "w") as out_file:
|
||||||
logging.debug('Writing {num} requirements: {imports} to {file}'.format(
|
logging.debug('Writing {num} requirements: {imports} to {file}'.format(
|
||||||
num=len(imports),
|
num=len(imports),
|
||||||
file=path,
|
file=path,
|
||||||
imports=", ".join([x['name'] for x in imports])
|
imports=", ".join([x['name'] for x in imports])
|
||||||
))
|
))
|
||||||
fmt = '{name}=={version}'
|
fmt = '{name}' + symbol + '{version}'
|
||||||
out_file.write('\n'.join(
|
out_file.write('\n'.join(
|
||||||
fmt.format(**item) if item['version'] else '{name}'.format(**item)
|
fmt.format(**item) if item['version'] else '{name}'.format(**item)
|
||||||
for item in imports) + '\n')
|
for item in imports) + '\n')
|
||||||
|
|
||||||
|
|
||||||
def output_requirements(imports):
|
def output_requirements(imports, symbol):
|
||||||
generate_requirements_file('-', imports)
|
generate_requirements_file('-', imports, symbol)
|
||||||
|
|
||||||
|
|
||||||
def get_imports_info(
|
def get_imports_info(
|
||||||
@ -397,6 +401,18 @@ def clean(file_, imports):
|
|||||||
logging.info("Successfully cleaned up requirements in " + file_)
|
logging.info("Successfully cleaned up requirements in " + file_)
|
||||||
|
|
||||||
|
|
||||||
|
def dynamic_versioning(scheme, imports):
|
||||||
|
"""Enables dynamic versioning with <compat>, <gt> or <non-pin> schemes."""
|
||||||
|
if scheme == "no-pin":
|
||||||
|
imports = [{"name": item["name"], "version": ""} for item in imports]
|
||||||
|
symbol = ""
|
||||||
|
elif scheme == "gt":
|
||||||
|
symbol = ">="
|
||||||
|
elif scheme == "compat":
|
||||||
|
symbol = "~="
|
||||||
|
return imports, symbol
|
||||||
|
|
||||||
|
|
||||||
def init(args):
|
def init(args):
|
||||||
encoding = args.get('--encoding')
|
encoding = args.get('--encoding')
|
||||||
extra_ignore_dirs = args.get('--ignore')
|
extra_ignore_dirs = args.get('--ignore')
|
||||||
@ -457,14 +473,21 @@ def init(args):
|
|||||||
"use --force to overwrite it")
|
"use --force to overwrite it")
|
||||||
return
|
return
|
||||||
|
|
||||||
if args.get('--no-pin'):
|
if args["--mode"]:
|
||||||
imports = [{'name': item["name"], 'version': ''} for item in imports]
|
scheme = args.get("--mode")
|
||||||
|
if scheme in ["compat", "gt", "no-pin"]:
|
||||||
|
imports, symbol = dynamic_versioning(scheme, imports)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid argument for mode flag, "
|
||||||
|
"use 'compat', 'gt' or 'no-pin' instead")
|
||||||
|
else:
|
||||||
|
symbol = "=="
|
||||||
|
|
||||||
if args["--print"]:
|
if args["--print"]:
|
||||||
output_requirements(imports)
|
output_requirements(imports, symbol)
|
||||||
logging.info("Successfully output requirements")
|
logging.info("Successfully output requirements")
|
||||||
else:
|
else:
|
||||||
generate_requirements_file(path, imports)
|
generate_requirements_file(path, imports, symbol)
|
||||||
logging.info("Successfully saved requirements file in " + path)
|
logging.info("Successfully saved requirements file in " + path)
|
||||||
|
|
||||||
|
|
||||||
|
130
tests/test_pipreqs.py
Executable file → Normal file
130
tests/test_pipreqs.py
Executable file → Normal file
@ -113,11 +113,9 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
Test that all modules we will test upon are in requirements file
|
Test that all modules we will test upon are in requirements file
|
||||||
"""
|
"""
|
||||||
pipreqs.init(
|
pipreqs.init({'<path>': self.project, '--savepath': None, '--print': False,
|
||||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
'--use-local': None, '--force': True, '--proxy':None, '--pypi-server':None,
|
||||||
'--use-local': None, '--force': True, '--proxy': None,
|
'--diff': None, '--clean': None, '--mode': None})
|
||||||
'--pypi-server': None, '--diff': None, '--clean': None}
|
|
||||||
)
|
|
||||||
assert os.path.exists(self.requirements_path) == 1
|
assert os.path.exists(self.requirements_path) == 1
|
||||||
with open(self.requirements_path, "r") as f:
|
with open(self.requirements_path, "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
@ -132,11 +130,9 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
Test that items listed in requirements.text are the same
|
Test that items listed in requirements.text are the same
|
||||||
as locals expected
|
as locals expected
|
||||||
"""
|
"""
|
||||||
pipreqs.init(
|
pipreqs.init({'<path>': self.project, '--savepath': None, '--print': False,
|
||||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
'--use-local': True, '--force': True, '--proxy':None, '--pypi-server':None,
|
||||||
'--use-local': True, '--force': True, '--proxy': None,
|
'--diff': None, '--clean': None, '--mode': None})
|
||||||
'--pypi-server': None, '--diff': None, '--clean': None}
|
|
||||||
)
|
|
||||||
assert os.path.exists(self.requirements_path) == 1
|
assert os.path.exists(self.requirements_path) == 1
|
||||||
with open(self.requirements_path, "r") as f:
|
with open(self.requirements_path, "r") as f:
|
||||||
data = f.readlines()
|
data = f.readlines()
|
||||||
@ -149,11 +145,9 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
Test that we can save requirements.txt correctly
|
Test that we can save requirements.txt correctly
|
||||||
to a different path
|
to a different path
|
||||||
"""
|
"""
|
||||||
pipreqs.init(
|
pipreqs.init({'<path>': self.project, '--savepath': self.alt_requirement_path,
|
||||||
{'<path>': self.project, '--savepath': self.alt_requirement_path,
|
'--use-local': None, '--proxy':None, '--pypi-server':None, '--print': False,
|
||||||
'--use-local': None, '--proxy': None, '--pypi-server': None,
|
'--diff': None, '--clean': None, '--mode': None})
|
||||||
'--print': False, "--diff": None, "--clean": None}
|
|
||||||
)
|
|
||||||
assert os.path.exists(self.alt_requirement_path) == 1
|
assert os.path.exists(self.alt_requirement_path) == 1
|
||||||
with open(self.alt_requirement_path, "r") as f:
|
with open(self.alt_requirement_path, "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
@ -169,11 +163,9 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
with open(self.requirements_path, "w") as f:
|
with open(self.requirements_path, "w") as f:
|
||||||
f.write("should_not_be_overwritten")
|
f.write("should_not_be_overwritten")
|
||||||
pipreqs.init(
|
pipreqs.init({'<path>': self.project, '--savepath': None, '--use-local': None,
|
||||||
{'<path>': self.project, '--savepath': None, '--use-local': None,
|
'--force': None, '--proxy':None, '--pypi-server':None, '--print': False,
|
||||||
'--force': None, '--proxy': None, '--pypi-server': None,
|
'--diff': None, '--clean': None, '--mode': None})
|
||||||
'--print': False, '--diff': None, '--clean': None}
|
|
||||||
)
|
|
||||||
assert os.path.exists(self.requirements_path) == 1
|
assert os.path.exists(self.requirements_path) == 1
|
||||||
with open(self.requirements_path, "r") as f:
|
with open(self.requirements_path, "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
@ -211,39 +203,79 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
Test --ignore parameter
|
Test --ignore parameter
|
||||||
"""
|
"""
|
||||||
pipreqs.init(
|
pipreqs.init(
|
||||||
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
||||||
'--print': False, '--use-local': None, '--force': True,
|
'--print': False, '--use-local': None, '--force': True,
|
||||||
'--proxy': None, '--pypi-server': None,
|
'--proxy':None, '--pypi-server':None,
|
||||||
'--ignore': '.ignored_dir,.ignore_second', '--diff': None,
|
'--ignore':'.ignored_dir,.ignore_second',
|
||||||
'--clean': None}
|
'--diff': None,
|
||||||
)
|
'--clean': None,
|
||||||
with open(
|
'--mode': None
|
||||||
os.path.join(
|
}
|
||||||
self.project_with_ignore_directory,
|
)
|
||||||
"requirements.txt"
|
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||||
), "r"
|
|
||||||
) as f:
|
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
for item in ['click', 'getpass']:
|
for item in ['click', 'getpass']:
|
||||||
self.assertFalse(item.lower() in data)
|
self.assertFalse(item.lower() in data)
|
||||||
|
|
||||||
def test_omit_version(self):
|
def test_dynamic_version_no_pin_scheme(self):
|
||||||
"""
|
"""
|
||||||
Test --no-pin parameter
|
Test --mode=no-pin
|
||||||
"""
|
"""
|
||||||
pipreqs.init(
|
pipreqs.init(
|
||||||
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
||||||
'--print': False, '--use-local': None, '--force': True,
|
'--print': False, '--use-local': None, '--force': True,
|
||||||
'--proxy': None, '--pypi-server': None, '--diff': None,
|
'--proxy': None, '--pypi-server': None,
|
||||||
'--clean': None, '--no-pin': True}
|
'--diff': None,
|
||||||
)
|
'--clean': None,
|
||||||
with open(os.path.join(
|
'--mode': 'no-pin'
|
||||||
self.project_with_ignore_directory,
|
}
|
||||||
"requirements.txt"), "r"
|
)
|
||||||
) as f:
|
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
for item in ['beautifulsoup4==4.8.1', 'boto==2.49.0']:
|
for item in ['beautifulsoup4', 'boto']:
|
||||||
self.assertFalse(item.lower() in data)
|
self.assertTrue(item.lower() in data)
|
||||||
|
|
||||||
|
def test_dynamic_version_gt_scheme(self):
|
||||||
|
"""
|
||||||
|
Test --mode=gt
|
||||||
|
"""
|
||||||
|
pipreqs.init(
|
||||||
|
{'<path>': self.project_with_ignore_directory, '--savepath': None, '--print': False,
|
||||||
|
'--use-local': None, '--force': True,
|
||||||
|
'--proxy': None,
|
||||||
|
'--pypi-server': None,
|
||||||
|
'--diff': None,
|
||||||
|
'--clean': None,
|
||||||
|
'--mode': 'gt'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||||
|
data = f.readlines()
|
||||||
|
for item in data:
|
||||||
|
symbol = '>='
|
||||||
|
message = 'symbol is not in item'
|
||||||
|
self.assertIn(symbol, item, message)
|
||||||
|
|
||||||
|
def test_dynamic_version_compat_scheme(self):
|
||||||
|
"""
|
||||||
|
Test --mode=compat
|
||||||
|
"""
|
||||||
|
pipreqs.init(
|
||||||
|
{'<path>': self.project_with_ignore_directory, '--savepath': None, '--print': False,
|
||||||
|
'--use-local': None, '--force': True,
|
||||||
|
'--proxy': None,
|
||||||
|
'--pypi-server': None,
|
||||||
|
'--diff': None,
|
||||||
|
'--clean': None,
|
||||||
|
'--mode': 'compat'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||||
|
data = f.readlines()
|
||||||
|
for item in data:
|
||||||
|
symbol = '~='
|
||||||
|
message = 'symbol is not in item'
|
||||||
|
self.assertIn(symbol, item, message)
|
||||||
|
|
||||||
def test_clean(self):
|
def test_clean(self):
|
||||||
"""
|
"""
|
||||||
@ -252,14 +284,15 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
pipreqs.init(
|
pipreqs.init(
|
||||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||||
'--use-local': None, '--force': True, '--proxy': None,
|
'--use-local': None, '--force': True, '--proxy': None,
|
||||||
'--pypi-server': None, '--diff': None, '--clean': None}
|
'--pypi-server': None, '--diff': None, '--clean': None,
|
||||||
|
'--mode': None}
|
||||||
)
|
)
|
||||||
assert os.path.exists(self.requirements_path) == 1
|
assert os.path.exists(self.requirements_path) == 1
|
||||||
pipreqs.init(
|
pipreqs.init(
|
||||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||||
'--use-local': None, '--force': None, '--proxy': None,
|
'--use-local': None, '--force': None, '--proxy': None,
|
||||||
'--pypi-server': None, '--diff': None,
|
'--pypi-server': None, '--diff': None,
|
||||||
'--clean': self.requirements_path, '--no-pin': True}
|
'--clean': self.requirements_path, '--mode': 'non-pin'}
|
||||||
)
|
)
|
||||||
with open(self.requirements_path, "r") as f:
|
with open(self.requirements_path, "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
@ -274,7 +307,8 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
pipreqs.init(
|
pipreqs.init(
|
||||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||||
'--use-local': None, '--force': True, '--proxy': None,
|
'--use-local': None, '--force': True, '--proxy': None,
|
||||||
'--pypi-server': None, '--diff': None, '--clean': None}
|
'--pypi-server': None, '--diff': None, '--clean': None,
|
||||||
|
'--mode': None}
|
||||||
)
|
)
|
||||||
assert os.path.exists(self.requirements_path) == 1
|
assert os.path.exists(self.requirements_path) == 1
|
||||||
modules_clean = [m for m in self.modules if m != cleaned_module]
|
modules_clean = [m for m in self.modules if m != cleaned_module]
|
||||||
@ -282,7 +316,7 @@ class TestPipreqs(unittest.TestCase):
|
|||||||
{'<path>': self.project_clean, '--savepath': None,
|
{'<path>': self.project_clean, '--savepath': None,
|
||||||
'--print': False, '--use-local': None, '--force': None,
|
'--print': False, '--use-local': None, '--force': None,
|
||||||
'--proxy': None, '--pypi-server': None, '--diff': None,
|
'--proxy': None, '--pypi-server': None, '--diff': None,
|
||||||
'--clean': self.requirements_path, '--no-pin': True}
|
'--clean': self.requirements_path, '--mode': 'non-pin'}
|
||||||
)
|
)
|
||||||
with open(self.requirements_path, "r") as f:
|
with open(self.requirements_path, "r") as f:
|
||||||
data = f.read().lower()
|
data = f.read().lower()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user