Skip to content

Commit

Permalink
No longer require that the number of targets match requirements
Browse files Browse the repository at this point in the history
In --violations mode, the CLI requires that the number of "--target"
specified is the same as the number of requirements. Many users no
longer care about Python 2, but may write a simple file which is
compatible with Python 2 by chance. In that case, Vermin will return a
Python 2.x compatibility entry, and --violations will fail unless a
target is specified for Python 2. However, there's no way to specify a
target of "I don't care about this version", so these users would be
forced to run with something like "-t=2.7- -t=3.6-". Except this would
fail on their normal code, which is incompatible with Python 2.x.

To get around this, start comparing by the major version of Python. Only
compare the versions which are in common between the requirements and
target. If there are no versions in common, fail. If a target major
version is missing from the requirements, fail.

This allows users who only specify a "3.x-" target to know that Vermin
will succeed on any code, even simple code which is 2.x compatible too.

This fixes netromdk#230.

Signed-off-by: Stephen Brennan <stephen@brennan.io>
  • Loading branch information
brenns10 committed Oct 10, 2023
1 parent 729d04a commit a782d8b
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 12 deletions.
7 changes: 3 additions & 4 deletions vermin/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ 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.\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)
19 changes: 19 additions & 0 deletions vermin/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from math import floor
from functools import reduce

from .printing import nprint

def reverse_range(values):
"""Yields reverse range of values: list(reverse_range([1, 2, 3])) -> [2, 1, 0]."""
return range(len(values) - 1, -1, -1)
Expand Down Expand Up @@ -189,3 +191,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
elif not exact and target < req:
return False
return True

0 comments on commit a782d8b

Please sign in to comment.