Skip to content

Commit

Permalink
Merge pull request #234 from brenns10/fix_mismatched_requirements
Browse files Browse the repository at this point in the history
No longer require that the number of targets match requirements
  • Loading branch information
netromdk authored Nov 24, 2023
2 parents a0647aa + 0c8574c commit b06c831
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 12 deletions.
77 changes: 77 additions & 0 deletions tests/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
remove_whitespace, main, sort_line_column, sort_line_column_parsable, version_strings,\
format_title_descs, DEFAULT_PROCESSES
from vermin.formats import ParsableFormat
from vermin.utility import compare_requirements

from .testutils import VerminTest, current_version, ScopedTemporaryFile, detect, visit, touch, \
working_dir
Expand Down Expand Up @@ -1054,3 +1055,79 @@ def test_pessimistic_syntax_error(self):

def test_default_processes(self):
self.assertEqual(cpu_count(), DEFAULT_PROCESSES)

def test_compare_requirements_py2_and_py3_compatible(self):
reqs = [(2, 7), (3, 6)]

# User provides only one target
self.assertFalse(compare_requirements(reqs, [(True, (2, 6))]))
self.assertFalse(compare_requirements(reqs, [(False, (2, 6))]))
self.assertTrue(compare_requirements(reqs, [(True, (2, 7))]))
self.assertTrue(compare_requirements(reqs, [(False, (2, 7))]))
self.assertFalse(compare_requirements(reqs, [(True, (3, 3))]))
self.assertFalse(compare_requirements(reqs, [(False, (3, 3))]))
self.assertTrue(compare_requirements(reqs, [(True, (3, 6))]))
self.assertTrue(compare_requirements(reqs, [(False, (3, 6))]))
self.assertFalse(compare_requirements(reqs, [(True, (3, 7))]))
self.assertTrue(compare_requirements(reqs, [(False, (3, 7))]))

# Missing and invalid targets
self.assertFalse(compare_requirements(reqs, []))
self.assertFalse(compare_requirements(reqs, [(True, (4, 1))]))

# User provides multiple valid requirements, return true when both are
# satisfied.
self.assertTrue(compare_requirements(reqs, [(True, (2, 7)), (False, (3, 7))]))
self.assertFalse(compare_requirements(reqs, [(True, (2, 7)), (True, (3, 7))]))

# User provides valid along with invalid version: fail because the target
# major version is missing
self.assertFalse(compare_requirements(reqs, [(True, (2, 7)), (False, (4, 7))]))
self.assertFalse(compare_requirements(reqs, [(True, (2, 7)), (True, (4, 7))]))

def test_compare_requirements_py2_only(self):
reqs = [(2, 7)]

# Correct major version, compare against minor version
self.assertFalse(compare_requirements(reqs, [(True, (2, 6))]))
self.assertFalse(compare_requirements(reqs, [(False, (2, 6))]))
self.assertTrue(compare_requirements(reqs, [(True, (2, 7))]))
self.assertTrue(compare_requirements(reqs, [(False, (2, 7))]))

# The user specifies the wrong major version: this will always fail
self.assertFalse(compare_requirements(reqs, [(True, (3, 3))]))
self.assertFalse(compare_requirements(reqs, [(False, (3, 3))]))
self.assertFalse(compare_requirements(reqs, [(True, (3, 6))]))
self.assertFalse(compare_requirements(reqs, [(False, (3, 6))]))
self.assertFalse(compare_requirements(reqs, [(True, (3, 7))]))
self.assertFalse(compare_requirements(reqs, [(False, (3, 7))]))
self.assertFalse(compare_requirements(reqs, [(True, (4, 1))]))

# Missing target: fail
self.assertFalse(compare_requirements(reqs, []))

# Multiple targets: fail because one target major version is missing
self.assertFalse(compare_requirements(reqs, [(False, (2, 7)), (False, (3, 6))]))

def test_compare_requirements_py3_only(self):
reqs = [(3, 6)]
# The user specifies the wrong major version: this will always fail
self.assertFalse(compare_requirements(reqs, [(True, (2, 6))]))
self.assertFalse(compare_requirements(reqs, [(False, (2, 6))]))
self.assertFalse(compare_requirements(reqs, [(True, (2, 7))]))
self.assertFalse(compare_requirements(reqs, [(False, (2, 7))]))
self.assertFalse(compare_requirements(reqs, [(True, (4, 1))]))

# Correct major version, compare against minor version
self.assertFalse(compare_requirements(reqs, [(True, (3, 3))]))
self.assertFalse(compare_requirements(reqs, [(False, (3, 3))]))
self.assertTrue(compare_requirements(reqs, [(True, (3, 6))]))
self.assertTrue(compare_requirements(reqs, [(False, (3, 6))]))
self.assertFalse(compare_requirements(reqs, [(True, (3, 7))]))
self.assertTrue(compare_requirements(reqs, [(False, (3, 7))]))

# Missing and invalid requirements
self.assertFalse(compare_requirements(reqs, []))

# Multiple targets: fail because one target amjor version is missing
self.assertFalse(compare_requirements(reqs, [(False, (2, 7)), (False, (3, 6))]))
9 changes: 5 additions & 4 deletions vermin/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ def print_usage(full=False):
print(" --target=V | -t=V\n"
" Target version that files must abide by. Can be specified once or twice.\n"
" A '-' can be appended to match target version or smaller, like '-t=3.5-'.\n"
" If not met Vermin will exit with code 1. Note that the amount of target\n"
" versions must match the amount of minimum required versions detected.\n"
" However, if used in conjunction with --violations, and no rules are\n"
" triggered, it will exit with code 0.\n")
" If not met Vermin will exit with code 1. Vermin will only compare target\n"
" versions with the same major version, so if you do not care about Python\n"
" 2, you can just specify one target for Python 3. However, if used in\n"
" conjunction with --violations, and no rules are triggered, it will exit\n"
" with code 0.\n")
print(" --no-target (default)\n"
" Don't expect certain target version(s).\n")
print(" --processes=N | -p=N\n"
Expand Down
10 changes: 2 additions & 8 deletions vermin/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .detection import detect_paths
from .processor import Processor
from .arguments import Arguments
from .utility import version_strings, dotted_name
from .utility import version_strings, dotted_name, compare_requirements
from .backports import Backports

def main():
Expand Down Expand Up @@ -144,16 +144,10 @@ def main():
# don't fail wrt. targets.
all_inconclusive = config.only_show_violations() and len(reqs) > 0 and \
all(req == (0, 0) for req in reqs)
if not all_inconclusive and\
not (len(reqs) == len(targets) and
all(((exact and target == req) or (not exact and target >= req)) for
((exact, target), req) in zip(targets, reqs))):
if not all_inconclusive and not compare_requirements(reqs, targets):
if not parsable:
vers = ["{}{}".format(dotted_name(t), "-" if not e else "") for (e, t) in targets]
nprint("Target versions not met: {}".format(version_strings(vers)), config)
if len(targets) < len(reqs):
nprint("Note: Number of specified targets ({}) doesn't match number of detected minimum "
"versions ({}).".format(len(targets), len(reqs)), config)
sys.exit(1)

sys.exit(0)
17 changes: 17 additions & 0 deletions vermin/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,20 @@ def parse_target(target):
return None

return (exact, elms)

def compare_requirements(reqs, targets):
maj_to_req = {ver[0]: ver for ver in reqs}
maj_to_target = {ver[0]: (exact, ver) for (exact, ver) in targets}
common_major_versions = set(maj_to_req.keys()) & set(maj_to_target.keys())
if not common_major_versions:
return False
if set(maj_to_target.keys()) - common_major_versions:
return False # target major version missing from the requirements
for maj in common_major_versions:
exact, target = maj_to_target[maj]
req = maj_to_req[maj]
if exact and target != req:
return False
if not exact and target < req:
return False
return True

0 comments on commit b06c831

Please sign in to comment.