Package: vermin
VerminConcurrently detect the minimum Python versions needed to run code. Additionally, since the code is
vanilla Python, and it doesn’t have any external dependencies, it can be run with v3+ but still
includes detection of v2.x functionality.It functions by parsing Python code into an abstract syntax tree (AST), which it traverses and
matches against internal dictionaries with3796rules, covering v2.0-2.7 and v3.0-3.12, divided
into178modules,2614classes/functions/constants members of modules,875kwargs of
functions,4strftime directives,3bytes format directives,2array typecodes,3codecs error handler names,20codecs encodings,78builtin generic annotation types,9builtin dict union (|) types,8builtin dict union merge (|=) types, and2user
function decorators.Backports of the standard library, liketyping, can be enabled for better results. Get full list
of backports via--help.The project is fairly well-tested with4008unit and integration tests that are executed on
Linux, macOS, and Windows.It is recommended to use the most recent Python version to run Vermin on projects since Python’s own
language parser is used to detect language features, like f-strings since Python 3.6 etc.Table of ContentsUsageFeaturesCaveatsConfiguration FileExamplesLinting (showing only target versions violations)API (experimental)Analysis ExclusionsParsable OutputContributingUsageIt is fairly straightforward to use Vermin.Running it from the repository either directly or through specific interpreter:% ./vermin.py /path/to/your/project        # (1) executing via `/usr/bin/env python`
% python3 vermin.py /path/to/your/project  # (2) specifically `python3`Or if installed viaPyPi:% pip install vermin
% vermin /path/to/your/projectHomebrew(pkg):% brew install verminSpack(pkg):% git clone https://github.com/spack/spack.git
% . spack/share/spack/setup-env.sh  # depending on shell
% spack install py-vermin
% spack load py-verminArch Linux (AUR):% yay -S python-verminWhen using continuous integration (CI) tools, likeTravis CI, Vermin can
be used to check that the minimum required versions didn’t change. The following is an excerpt:install:
- ./setup_virtual_env.sh
- pip install vermin
script:
- vermin -t=2.7 -t=3 project_package otherfile.pyVermin can also be used as apre-commithook:repos:-repo:https://github.com/netromdk/verminrev:GIT_SHA_OR_TAG# ex: 'e88bda9' or 'v1.3.4'hooks:-id:vermin# specify your target version here, OR in a Vermin config file as usual:args:['-t=3.8-','--violations']# (if your target is specified in a Vermin config, you may omit the 'args' entry entirely)When using the hook, a target version must be specified via a Vermin config file in your package,
or via theargsoption in your.pre-commit-config.yamlconfig. If you’re passing the target
viaargs, it’s recommended to also include--violations(shown above).If you’re using thevermin-allhook, you can specify any target as you usually would. However,
if you’re using theverminhook, your target must be in the form ofx.y-(as opposed tox.y), otherwise you will run into issues when your staged changes meet a minimum version that
is lower than your target.See thepre-commit docsfor further general information
on how to get hooks set up on your project.FeaturesFeatures detected include v2/v3print exprandprint(expr),long, f-strings, coroutines
(asyncandawait), asynchronous generators (awaitandyieldin same function),
asynchronous comprehensions,awaitin comprehensions, asynchronousfor-loops, boolean
constants, named expressions, keyword-only parameters, positional-only parameters,nonlocal,yield from, exception context cause (raise .. from ..),except*,setliterals,setcomprehensions,dictcomprehensions, infix matrix multiplication,"..".format(..),
imports (import X,from X import Y,from X import *), function calls wrt. name and
kwargs,strftime+strptimedirectives used, function and variable annotations (alsoFinalandLiteral),continueinfinallyblock, modular inversepow(), array
typecodes, codecs error handler names, encodings,%formatting and directives for bytes and
bytearray,withstatement, asynchronouswithstatement, multiple context expressions in awithstatement, multiple context expressions in awithstatement grouped with parenthesis,
unpacking assignment, generalized unpacking, ellipsis literal (...) out of slices, dictionary
union ({..}|{..}), dictionary union merge (a ={..};a |={..}), builtin generic type
annotations (list[str]), function decorators, class decorators, relaxed decorators,metaclassclass keyword, pattern matching withmatch, union types written asX | Y, and
type alias statements (type X = SomeType). It tries to detect and ignore user-defined functions,
classes, arguments, and variables with names that clash with library-defined symbols.CaveatsFor frequently asked questions, check out theFAQ discussions.Self-documenting fstrings detection has been disabled by default because the built-in AST cannot
distinguishf'{a=}'fromf'a={a}', for instance, since it optimizes some information away
(#39). And this incorrectly marks some source
code as using fstring self-doc when only using general fstring. To enable (unstable) fstring
self-doc detection, use--featurefstring-self-doc.Detecting union types (X | YPEP 604) can be
tricky because Vermin doesn’t know all underlying details of constants and types since it parses and
traverses the AST. For this reason, heuristics are employed and this can sometimes yield incorrect
results (#103). To enable (unstable) union types
detection, use--featureunion-types.Function and variable annotations aren’t evaluated at definition time whenfrom __future__ import annotationsis used (PEP 563). This is why--no-eval-annotationsis on by default (since v1.1.1,#66). If annotations are being evaluated at runtime,
like usingtyping.get_type_hintsor evaluating__annotations__of an object,--eval-annotationsshould be used for best results.Configuration FileVermin automatically tries to detect a config file, starting in the current working directory where
it is run, following parent folders until either the root or project boundary files/folders are
reached. However, if--config-fileis specified, no config is auto-detected and loaded.Config file names being looked for:vermin.ini,vermin.conf,.vermin,setup.cfgProject boundary files/folders:.git,.svn,.hg,.bzr,_darcs,.fslckout,.p4root,.pijulA sample config file can be foundhere.Note that Vermin config can be in the same INI file as other configs, like the commonly usedsetup.cfg:[vermin]verbose=1processes=4[flake8]ignore=E111,F821Examples%./vermin.pyverminMinimum required versions: 3.0
Incompatible versions:     2%./vermin.py-t=3.3verminMinimum required versions: 3.0
Incompatible versions:     2
Target versions not met:   3.3%echo$?1%./vermin.py--versionsverminMinimum required versions: 3.0
Incompatible versions:     2
Version range:             2.0, 2.6, 2.7, 3.0%./vermin.py-vexamplesDetecting python files..
Analyzing 6 files using 8 processes..
             /path/to/examples/formatv2.py
2.7, 3.2     /path/to/examples/argparse.py
2.7, 3.0     /path/to/examples/formatv3.py
2.0, 3.0     /path/to/examples/printv3.py
!2, 3.4      /path/to/examples/abc.py
             /path/to/examples/unknown.py
Minimum required versions:   3.4
Incompatible versions:         2%./vermin.py-vv/path/to/examples/abc.pyDetecting python files..
Analyzing using 8 processes..
!2, 3.4      /path/to/examples/abc.py
  'abc' requires 2.6, 3.0
  'abc.ABC' requires !2, 3.4

Minimum required versions: 3.4
Incompatible versions:     2%./vermin.py-vvv/path/to/examples/abc.pyDetecting python files..
Analyzing using 8 processes..
!2, 3.4      /path/to/examples/abc.py
  L1 C7: 'abc' requires 2.6, 3.0
  L2: 'abc.ABC' requires !2, 3.4

Minimum required versions: 3.4
Incompatible versions:     2%./vermin.py-fparsable/path/to/examples/abc.py/path/to/examples/abc.py:1:7:2.6:3.0:'abc' module
/path/to/examples/abc.py:2::!2:3.4:'abc.ABC' member
/path/to/examples/abc.py:::!2:3.4:
:::!2:3.4:SeeParsable Outputfor more information about parsable output format.Linting: Showing only target versions violationsVermin shows lots of useful minimum version results when run normally, but it can also be used as a
linter to show only rules violating specified target versions by using--violations(or--lint) and one or two--targetvalues. Verbosity level 2 is automatically set when showing
only violations, but can be increased if necessary. The final versions verdict is still calculated
and printed at the end and the program exit code signifies whether the specified targets were met
(0) or violated (1). However, if no rules are triggered the exit code will be0due to
inconclusivity.%cattest.pyimport argparse  # 2.7, 3.2
all()            # 2.5, 3.0
enumerate()      # 2.3, 3.0%./vermin.py-t=2.4--t=3--violationstest.py;echo$?Detecting python files..
Analyzing using 8 processes..
2.7, 3.2     test.py
  'all' member requires 2.5, 3.0
  'argparse' module requires 2.7, 3.2

Minimum required versions: 2.7, 3.2
Target versions not met:   2.4-, 3.0
1The two first lines violate the targets but the third line matches and is therefore not shown.API (experimental)Information such as minimum versions, used functionality constructs etc. can also be accessed
programmatically via theverminPython module, though it’s an experimental feature. It is still
recommended to use the command-line interface.>>>importverminasV>>>V.version_strings(V.detect("a = long(1)"))'2.0, !3'>>>config=V.Config()>>>config.add_exclusion("long")>>>V.version_strings(V.detect("a = long(1)",config))'~2, ~3'>>>config.set_verbose(3)>>>v=V.visit("""from argparse import ArgumentParser
... ap = ArgumentParser(allow_abbrev=True)
... """,config)>>>print(v.output_text(),end="")L1C5:'argparse'modulerequires2.7,3.2L2:'argparse.ArgumentParser(allow_abbrev)'requires!2,3.5>>>V.version_strings(v.minimum_versions())'!2, 3.5'Analysis ExclusionsAnalysis exclusion can be necessary in certain cases. The argument--exclude<name>(multiple
can be specified) can be used to exclude modules, members, kwargs, codecs error handler names, or
codecs encodings by name from being analysed via . Consider the following code block that checks ifPROTOCOL_TLSis an attribute ofssl:importssltls_version=ssl.PROTOCOL_TLSv1ifhasattr(ssl,"PROTOCOL_TLS"):tls_version=ssl.PROTOCOL_TLSIt will state that “‘ssl.PROTOCOL_TLS’ requires 2.7, 3.6” but to exclude that from the results, use--exclude'ssl.PROTOCOL_TLS'. Afterwards, only “‘ssl’ requires 2.6, 3.0” will be shown and the
final minimum required versions are v2.6 and v3.0 instead of v2.7 and v3.6.Code can even be excluded on a more fine grained level using the# noverminor# novmcomments at line level. The following yields the same behavior as the previous code block, but only
for that particularifand its body:importssltls_version=ssl.PROTOCOL_TLSv1ifhasattr(ssl,"PROTOCOL_TLS"):# novermintls_version=ssl.PROTOCOL_TLSIn scenarios where multiple tools are employed that use comments for various features, exclusions
can be defined by having#for each comment “segment”:ifhasattr(ssl,"PROTOCOL_TLS"):# noqa # novermin # pylint: disable=no-membertls_version=ssl.PROTOCOL_TLSNote that if a code base does not have any occurrences of# noverminor# novm, speedups up
to 30-40%+ can be achieved by using the--no-parse-commentsargument orparse_comments = noconfig setting.Parsable OutputFor scenarios where the results of Vermin output is required, it is recommended to use the parsable
output format (--formatparsable) instead of the default output. With this format enabled, each
line will be on the form:<file>:<line>:<column>:<py2>:<py3>:<feature>The<line>and<column>are only shown when the verbosity level is high enough, otherwise
they are empty.Each feature detected per processed file will have the<feature>defined on an individual
line. The last line of the processed file will have a special line with the corresponding<file>and no<feature>, constituting the minimum versions of that file:<file>:::<py2>:<py3>:The very last line is the final minimum versions results of the entire scan and therefore has no<file>and<feature>::::<py2>:<py3>:Inspection of example output%./vermin.py-fparsable/path/to/project/path/to/project/abc.py:1:7:2.6:3.0:'abc' module
/path/to/project/abc.py:2::!2:3.4:'abc.ABC' member
/path/to/project/abc.py:::!2:3.4:
/path/to/project/except_star.py:::~2:~3:
/path/to/project/annotations.py:::2.0:3.0:print(expr)
/path/to/project/annotations.py:1::!2:3.0:annotations
/path/to/project/annotations.py:::!2:3.0:
:::!2:3.4:abc.pyrequires!2and3.4via:/path/to/project/abc.py:::!2:3.4:except_star.pyrequires~2and~3via:/path/to/project/except_star.py:::~2:~3:Andannotations.pyrequires!2and3.0via:/path/to/project/annotations.py:::!2:3.0:That means that the final result is!2and3.4, which is shown by the last line::::!2:3.4:ContributingContributions are very welcome, especially adding and updating detection rules of modules,
functions, classes etc. to cover as many Python versions as possible. SeeCONTRIBUTING.mdfor more information.