Giter Site home page Giter Site logo

netromdk / vermin Goto Github PK

View Code? Open in Web Editor NEW
424.0 4.0 25.0 1.63 MB

Concurrently detect the minimum Python versions needed to run code

License: MIT License

Python 99.56% Makefile 0.20% Shell 0.14% PowerShell 0.10%
python python-2 python-3 parser source-code-analysis detection version-checker concurrency multiprocessing abstract-syntax-tree

vermin's Introduction

Test Status Analyze Status CodeQL Status Coverage PyPI version Commits since last Downloads CII best practices

Vermin

Concurrently 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 with 3796 rules, covering v2.0-2.7 and v3.0-3.12, divided into 178 modules, 2614 classes/functions/constants members of modules, 875 kwargs of functions, 4 strftime directives, 3 bytes format directives, 2 array typecodes, 3 codecs error handler names, 20 codecs encodings, 78 builtin generic annotation types, 9 builtin dict union (|) types, 8 builtin dict union merge (|=) types, and 2 user function decorators.

Backports of the standard library, like typing, can be enabled for better results. Get full list of backports via --help.

The project is fairly well-tested with 4008 unit 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 Contents

Usage

It 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 via PyPi:

% pip install vermin
% vermin /path/to/your/project

Homebrew (pkg):

% brew install vermin

Spack (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-vermin

Arch Linux (AUR):

% yay -S python-vermin

When using continuous integration (CI) tools, like Travis 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.py

Vermin can also be used as a pre-commit hook:

repos:
  - repo: https://github.com/netromdk/vermin
    rev: 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 the args option in your .pre-commit-config.yaml config. If you're passing the target via args, it's recommended to also include --violations (shown above).

If you're using the vermin-all hook, you can specify any target as you usually would. However, if you're using the vermin hook, your target must be in the form of x.y- (as opposed to x.y), otherwise you will run into issues when your staged changes meet a minimum version that is lower than your target.

See the pre-commit docs for further general information on how to get hooks set up on your project.

Features

Features detected include v2/v3 print expr and print(expr), long, f-strings, coroutines (async and await), asynchronous generators (await and yield in same function), asynchronous comprehensions, await in comprehensions, asynchronous for-loops, boolean constants, named expressions, keyword-only parameters, positional-only parameters, nonlocal, yield from, exception context cause (raise .. from ..), except*, set literals, set comprehensions, dict comprehensions, infix matrix multiplication, "..".format(..), imports (import X, from X import Y, from X import *), function calls wrt. name and kwargs, strftime + strptime directives used, function and variable annotations (also Final and Literal), continue in finally block, modular inverse pow(), array typecodes, codecs error handler names, encodings, % formatting and directives for bytes and bytearray, with statement, asynchronous with statement, multiple context expressions in a with statement, multiple context expressions in a with statement 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, metaclass class keyword, pattern matching with match, union types written as X | 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.

Caveats

For frequently asked questions, check out the FAQ discussions.

Self-documenting fstrings detection has been disabled by default because the built-in AST cannot distinguish f'{a=}' from f'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 --feature fstring-self-doc.

Detecting union types (X | Y PEP 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 --feature union-types.

Function and variable annotations aren't evaluated at definition time when from __future__ import annotations is used (PEP 563). This is why --no-eval-annotations is on by default (since v1.1.1, #66). If annotations are being evaluated at runtime, like using typing.get_type_hints or evaluating __annotations__ of an object, --eval-annotations should be used for best results.

Configuration File

Vermin 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-file is specified, no config is auto-detected and loaded.

Config file names being looked for: vermin.ini, vermin.conf, .vermin, setup.cfg

Project boundary files/folders: .git, .svn, .hg, .bzr, _darcs, .fslckout, .p4root, .pijul

A sample config file can be found here.

Note that Vermin config can be in the same INI file as other configs, like the commonly used setup.cfg:

[vermin]
verbose = 1
processes = 4

[flake8]
ignore = E111,F821

Examples

% ./vermin.py vermin
Minimum required versions: 3.0
Incompatible versions:     2

% ./vermin.py -t=3.3 vermin
Minimum required versions: 3.0
Incompatible versions:     2
Target versions not met:   3.3
% echo $?
1

% ./vermin.py --versions vermin
Minimum required versions: 3.0
Incompatible versions:     2
Version range:             2.0, 2.6, 2.7, 3.0

% ./vermin.py -v examples
Detecting 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.py
Detecting 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.py
Detecting 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 -f parsable /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:

See Parsable Output for more information about parsable output format.

Linting: Showing only target versions violations

Vermin 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 --target values. 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 be 0 due to inconclusivity.

% cat test.py
import argparse  # 2.7, 3.2
all()            # 2.5, 3.0
enumerate()      # 2.3, 3.0

% ./vermin.py -t=2.4- -t=3 --violations test.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
1

The 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 the vermin Python module, though it's an experimental feature. It is still recommended to use the command-line interface.

>>> import vermin as V
>>> 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="")
L1 C5: 'argparse' module requires 2.7, 3.2
L2: 'argparse.ArgumentParser(allow_abbrev)' requires !2, 3.5
>>> V.version_strings(v.minimum_versions())
'!2, 3.5'

Analysis Exclusions

Analysis 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 if PROTOCOL_TLS is an attribute of ssl:

import ssl
tls_version = ssl.PROTOCOL_TLSv1
if hasattr(ssl, "PROTOCOL_TLS"):
  tls_version = ssl.PROTOCOL_TLS

It 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 # novermin or # novm comments at line level. The following yields the same behavior as the previous code block, but only for that particular if and its body:

import ssl
tls_version = ssl.PROTOCOL_TLSv1
if hasattr(ssl, "PROTOCOL_TLS"):  # novermin
  tls_version = ssl.PROTOCOL_TLS

In scenarios where multiple tools are employed that use comments for various features, exclusions can be defined by having # for each comment "segment":

if hasattr(ssl, "PROTOCOL_TLS"):  # noqa # novermin # pylint: disable=no-member
  tls_version = ssl.PROTOCOL_TLS

Note that if a code base does not have any occurrences of # novermin or # novm, speedups up to 30-40%+ can be achieved by using the --no-parse-comments argument or parse_comments = no config setting.

Parsable Output

For scenarios where the results of Vermin output is required, it is recommended to use the parsable output format (--format parsable) 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 -f parsable /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.py requires !2 and 3.4 via:

/path/to/project/abc.py:::!2:3.4:

except_star.py requires ~2 and ~3 via:

/path/to/project/except_star.py:::~2:~3:

And annotations.py requires !2 and 3.0 via:

/path/to/project/annotations.py:::!2:3.0:

That means that the final result is !2 and 3.4, which is shown by the last line:

:::!2:3.4:

Contributing

Contributions are very welcome, especially adding and updating detection rules of modules, functions, classes etc. to cover as many Python versions as possible. See CONTRIBUTING.md for more information.

vermin's People

Contributors

brenns10 avatar cclauss avatar cosmicexplorer avatar darkheir avatar dependabot[bot] avatar eutropios avatar felixonmars avatar gousaiyang avatar indrat avatar loganswartz avatar moreati avatar netromdk avatar ngtvspc avatar pyrco avatar samuelb avatar schcriher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vermin's Issues

`--exclude` unusable due to a small bug

Describe the bug
Cannot use the --exclude CLI flag due an internal bug.

To Reproduce

$ python3 vermin.py --exclude x test.py
Traceback (most recent call last):
  File "/path/to/vermin.py", line 4, in <module>
    main()
  File "/path/to/vermin/main.py", line 14, in main
    args = Arguments(sys.argv[1:]).parse(config)
  File "/path/to/vermin/arguments.py", line 289, in parse
    config.add_exclusion(self.__args[i + 1])
  File "/path/to/vermin/config.py", line 280, in add_exclusion
    self.__exclusions.add(name)
AttributeError: 'list' object has no attribute 'add'

Environment

  • Vermin version: 1.1.1

Suggested fix

The problem is due to this and this. Either Config.exclusions() should always return a set, or use self.__exclusions = set(other_config.exclusions()).

Error when processing function argument's annotations

Describe the bug
A clear and concise description of what the bug is.

When trying to test the minimum version of my new project, the error as attached below occurred.

After some tests, as I would understand, this is due to the detection of the Literal annotation in vermin.source_visitor.__handle_FunctionDef, when the annotation of function arguments are used in form of typing.xxx.

To Reproduce
Steps to reproduce the behavior.

# error-haunted code
import typing

def foo(arg1: typing.Union[str, int], arg2: typing.Literal['bar'], arg3: typing.Optional[bool]):
    pass

# AttributeError: 'Attribute' object has no attribute 'id'

Each argument's annotation as in the above function will eventually reproduce the error. However, if you do the other way round, vermin will be able to handle:

# successful code
from typing import Union, Literal, Optional

def foo(arg1: Union[str, int], arg2: Literal['bar'], arg3: Optional[bool]):
    pass

# Minimum required versions: 3.8
# Incompatible versions:     2

Expected behavior
A clear and concise description of what you expected to happen.

The processing of function argument's annotations should not raise any error :)

Environment (please complete the following information):

  • Vermin version or Git commit: v0.10.0

Additional context
Add any other context about the problem here.

Below is the complete traceback from the error.

multiprocessing.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/project/.venv/lib/python3.8/site-packages/vermin/processor.py", line 59, in process_individual
    visitor.tour(res.node)
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 707, in tour
    self.visit(node)
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 360, in visit
    return visitor(node)
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 716, in generic_visit
    super(SourceVisitor, self).generic_visit(node)
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 368, in generic_visit
    self.visit(item)
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 360, in visit
    return visitor(node)
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 1261, in visit_FunctionDef
    if self.__handle_FunctionDef(node):
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 1237, in __handle_FunctionDef
    self.generic_visit(node)
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 716, in generic_visit
    super(SourceVisitor, self).generic_visit(node)
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 368, in generic_visit
    self.visit(item)
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 360, in visit
    return visitor(node)
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 1261, in visit_FunctionDef
    if self.__handle_FunctionDef(node):
  File "/project/.venv/lib/python3.8/site-packages/vermin/source_visitor.py", line 1253, in __handle_FunctionDef
    (isinstance(ann, ast.Subscript) and ann.value.id == "Literal"):
AttributeError: 'Attribute' object has no attribute 'id'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/project/.venv/bin/vermin", line 8, in <module>
    sys.exit(main())
  File "/project/.venv/lib/python3.8/site-packages/vermin/main.py", line 44, in main
    (mins, incomp, unique_versions, backports) = processor.process(paths, processes)
  File "/project/.venv/lib/python3.8/site-packages/vermin/processor.py", line 88, in process
    for proc_res in pool.imap(process_individual, ((path, config) for path in paths)):
  File "/profile/.pyenv/versions/3.8.0/lib/python3.8/multiprocessing/pool.py", line 865, in next
    raise value
AttributeError: 'Attribute' object has no attribute 'id'

FYI, the variable's values are as following:

  • ann = <_ast.Subscript object>
  • ann.value = <_ast.Attribute object>
  • ann.value.value = <_ast.Name object>
  • ann.value.value.id = 'typing'
  • ann.value.attr = 'xxx' (the name from typing module)

Using functools.lru_cache decorator without parentheses only possible in >= Python 3.8

Using the functools.lru_cache decorator without parentheses is only possible in Python 3.8 and later.

# a.py
from functools import lru_cache

@lru_cache
def a():
    pass
$ vermin --version
1.1.0
$ vermin a.py
Minimum required versions: 3.2
Incompatible versions:     2
$ python3.7 a.py
Traceback (most recent call last):
  File "a.py", line 4, in <module>
    @lru_cache
  File ".../lib/python3.7/functools.py", line 490, in lru_cache
    raise TypeError('Expected maxsize to be an integer or None')
TypeError: Expected maxsize to be an integer or None
$ python3.8 a.py
$

Relevant quote from the documentation:

@functools.lru_cache(user_function)
@functools.lru_cache(maxsize=128, typed=False)
[...]
If user_function is specified, it must be a callable. This allows the lru_cache decorator to be applied directly to a user function, leaving the maxsize at its default value of 128:
[...}
Changed in version 3.8: Added the user_function option.

Make #novermin comments work better with other linter comments

Sometimes we need to use multiple linter comments in one line (see also pylint-dev/pylint#2485). Consider the following code snippet, which has three linter comments in one line:

import os

if hasattr(os, 'sched_getaffinity'):
    CPU_CNT = len(os.sched_getaffinity(0))  # type: ignore[attr-defined] # pylint: disable=no-member # novermin
else:
    ...

It would be good if everything works no matter how these comments are ordered, which is the current behavior of Pylint and Flake8. But for vermin, currently it only works (i.e. correctly exclude the line from analysis) when # novermin is the last comment. # novermin # type: ignore[attr-defined] # pylint: disable=no-member and # type: ignore[attr-defined] # novermin # pylint: disable=no-member do not work (for some reason Mypy requires the type comment to be the first comment, but that's not a vermin problem.)

I see a relevant change in the master branch. A caveat is that extracting comments with this regular expression may introduce false positive # novermin comments if # novermin happens to be in a string literal. Maybe using the tokenize module to detect COMMENT tokens is a better way.

Environment

  • Vermin version 0.10.3

Vermin configuration file

Many tools support loading configuration from config files in addition to command line arguments (e.g. flake8, mypy, pytest). It would be good if vermin can also do this. In some sense, declaring project configuration in a config file is more clear and discoverable than saving command line arguments in a script or Makefile, and is much better than typing out the command line arguments manually in the long term.

Proposed config file name and format

  • Many tools have chosen the INI format, and put all configuration items in one specific section. For vermin the file name could be vermin.ini and the section name could be vermin.
  • Tools tend to look for multiple files to find configuration (e.g. toolname.ini, setup.cfg, tox.ini and such). This allows a project to put configuration for different tools in different sections in a single file like setup.cfg. For vermin, it is a good start to only look at vermin.ini, and this may be extended in the future.
  • Tools may look for a config file in the "containing directory" of processed files and the parent directories. For vermin, to simplify, it is a good start to only look at the current working directory (assuming users are running vermin in project root), and this may be extended in the future.

Here is a sample vermin.ini:

[vermin]
# declaring minimum compatible version in a config file is good
target = 3.4

# key names in the INI file can be the same as the "long form" argument name in CLI
exclude = foo.bar.baz
backport = argparse,typing

# alternative way to express arrays:
backport =
    argparse
    typing
# this is supported by flake8 and pytest, not sure how INI format handles arrays

feature = fstring-self-doc

Configuration priority

  • When using CLI, manually passed command line arguments override the items in the config file
  • When using the Python API, we may have two options:
    • Don't read the config file at all
    • Manually passed config objects override the items in the config file (Does it make sense to look for a config file in the current working directory? We're passing code strings and not dealing with any files.)

Finally, it could be good to print a verbose message like "loading configuration from file ..." when a configuration file is discovered and used. This will let users know that a config file is in effect and help them diagnose configuration problems.

Type annotations available in python 3.4

Describe the bug
I have a project which runs fine on python 3.4 and it uses type annotations. There are two reasons vermin says that my code requires python 3.5:

  • the typing package is missing (which can be installed from a backport but detecting it is out of scope of vermin), this is correct
  • for annotations it prints annotations require 3.5: this is not correct in my opinion as my code runs fine with python 3.4 and AFAIK type annotations for function definitions were added in the very early version of python 3 (python 3.0 maybe?)

To Reproduce
Run vermin on the master branch of https://github.com/csernazs/pytest-httpserver.

Expected behavior
Annotations were added in an earlier python version so that would be checked instead, not 3.5.

Environment (please complete the following information):

  • I installed the latest version of vermin from pypi

#novermin exclusion comment does not work for all rules

Describe the bug

The # novermin exclusion comment does not work for all rules/syntactic structures.

To Reproduce

Taking the code in the README as an example:

In [127]: vermin.detect('''\
     ...: import ssl
     ...: tls_version = ssl.PROTOCOL_TLSv1
     ...: if hasattr(ssl, "PROTOCOL_TLS"):  # novermin
     ...:   tls_version = ssl.PROTOCOL_TLS
     ...: ''')
Out[127]: [(2, 6), (3, 0)]

In [128]: vermin.detect('''\
     ...: import ssl
     ...: tls_version = ssl.PROTOCOL_TLSv1
     ...: if hasattr(ssl, "PROTOCOL_TLS"):
     ...:   tls_version = ssl.PROTOCOL_TLS  # novermin
     ...: ''')
Out[128]: [(2, 7), (3, 6)]

When # novermin is moved to the same line of the code to be excluded, it fails to exclude the code (ssl.PROTOCOL_TLS in this case).

Expected behavior

# novermin should always exclude the code on the same line from analysis (or the next line if # novermin is the only content of a line).

Environment (please complete the following information):

  • Vermin version 1.0.3

Additional context

The problem is that in source_visitor.py, not all visit_* functions call if self.__is_no_line(node.lineno): return on start. Thus visit_Attribute and many other functions are not doing exclusion.

Question: Should syntax-level rules ever be excluded? (Should visit_JoinedStr exclude f'{1+1}' # novermin?)

False positives when detecting generalized unpacking and bytes formatting

When I try the latest version of vermin (from the master branch, with recent implemenation of generalized unpacking and bytes formatting detection) against some code of my projects, I noticed some false positives when detecting generalized unpacking and bytes formatting.

Case 1:

d = {'a': 'b'}
dict(**d)

Expected: No issues detected (Minimum required versions: ~2, ~3)

Actual: L2: generalized unpacking requires 3.5+

Case 2:

'%x' % 66

Expected: No issues detected (Minimum required versions: ~2, ~3)

Actual: L1: bytes `%` formatting requires 3.5+ (or 2.6+ as `str` synonym)

In this commit, why is isinstance(node.left, ast.Str) necessary? Is it because of some compatibility reasons? b'' is 2.6+, but '%x' % 66 is ~2, ~3.

Erroneous detection of nested curly braces in string formatting

Describe the bug

Python 2.6 introduced string formatting like 'foo {0}'.format('bar')

Python 2.7 allowed you to leave out the index and do something like 'foo {}'.format('bar')

If you want to use literal curly braces in your string, you can nest them like so: 'foo {{}} {0}'.format('bar')

This works properly in Python 2.6, but vermin claims that it requires Python 2.7.

To Reproduce

With the following file:

'foo {{}} {0}'.format('bar')

I see:

$ vermin test.py 
Minimum required versions: 2.7, 3.0

Expected behavior

However, if I run this code with Python 2.6 it works as expected. If I add a print statement or run the code in an interpreter, I get foo {} bar.

Environment (please complete the following information):

vermin 1.2.0

Target version check should also be successful if the version is newer

Is your feature request related to a problem? Please describe.
vermin supports the parameter -t to specify target python version the project of file should support. This check is successful only if the target version is exactly the same as the determined minimum required version.

Example with vermin version 0.8.1:

% cat test.py
print(f"This is a f-string. {__name__}")

% vermin -q -t=3.6 test.py
Minimum required versions: 3.6
Incompatible versions:     2
# exit code: 0

% vermin -q -t=3.7 test.py
Minimum required versions: 3.6
Incompatible versions:     2
Target versions not met:   3.7
# exit code: 1

You can see in the last execution that vermin target version check is unsuccessful even so the test code would be also be perfectly compatible with Python version 3.7 too.

Describe the solution you'd like
I want that the vermin target version check is successful if the target version is fulfilled and if the target version is newer as the determined one.

Describe alternatives you've considered
Well, I could get the determined minimum version from the vermin output and build my condition myself somehow. While this would be fine when vermin is used within a test suite, it not a good option when using vermin a cli utility directly.

Machine parseable output

Is your feature request related to a problem? Please describe.
We are using vermin from our CI and want to provide as much feedback as possible when something fails. So we'd like to be able to get the results from vermin and feed back in to the reports given.

Describe the solution you'd like
All result data in a format that can easily be parsed by another piece of software:

  • Aggregated minimum versions
  • List of detected requirements with file, line, column, versions and reason

E.g.:

foo.py:17:5:2.6:3.0:__future__.print_function

Describe alternatives you've considered
Parsing the current output. It is likely to break in the future though as it can easily change given that it's for humans, and not other software.

Vermin Cannot Detect `yield from`

Describe the bug
It seems like vermin cannot detect yield from expression, which introduced in Python 3.3 (PEP 380).

To Reproduce
Just run vermin -vvv on a Python code which includes the yield from expression.

Expected behavior
LX CX: 'yield from' expression requires v3.3+

Environment (please complete the following information):

  • Vermin 0.7.0

Omit files that meet the targets when using --violations

When running vermin with --violations I'd like to see only the files that cause a failure to meet the targets.

Current behavior

What currently happens is that every file will at least output the minimum version required. As an example:

$ vermin --backport argparse --backport typing -t=2.6 -t=3.5 --violations lib/spack/spack/
$ vermin  --backport argparse --backport typing -t=2.6 -t=3.5 --violations lib/spack/spack/
Detecting python files..
Analyzing 445 files using 4 processes..
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/__init__.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/abi.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/__init__.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/analyzer_base.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/config_args.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/environment_variables.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/install_files.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/analyzers/libabigail.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/architecture.py
2.7, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/audit.py
  `"..{}..".format(..)` requires 2.7, 3.0

2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/binary_distribution.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/bootstrap.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_environment.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/aspell_dict.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/autotools.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/cached_cmake.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/cmake.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/cuda.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/gnu.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/intel.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/makefile.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/maven.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/meson.py
2.1, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/octave.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/oneapi.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/perl.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/python.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/qmake.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/r.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/rocm.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/ruby.py
2.1, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/scons.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/sip.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/sourceforge.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/sourceware.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/waf.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/build_systems/xorg.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/caches.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/ci.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/ci_needs_workaround.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/ci_optimization.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/__init__.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/activate.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/add.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/analyze.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/arch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/audit.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/blame.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/build_env.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/buildcache.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/cd.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/checksum.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/ci.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/clean.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/clone.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/commands.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/common/__init__.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/common/arguments.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/common/env_utility.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/compiler.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/compilers.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/concretize.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/config.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/containerize.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/create.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/deactivate.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/debug.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/dependencies.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/dependents.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/deprecate.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/dev_build.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/develop.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/docs.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/edit.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/env.py
2.3, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/extensions.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/external.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/find.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/flake8.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/gc.py
2.4, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/gpg.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/graph.py
2.0, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/help.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/info.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/install.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/license.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/list.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/load.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/location.py
2.0, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/log_parse.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/maintainers.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/mark.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/mirror.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/module.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/modules/__init__.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/modules/lmod.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/modules/tcl.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/monitor.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/patch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/pkg.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/providers.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/pydoc.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/python.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/reindex.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/remove.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/repo.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/resource.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/restage.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/solve.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/spec.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/stage.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/style.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/test.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/test_env.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/tutorial.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/undevelop.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/uninstall.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/unit_test.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/unload.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/url.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/verify.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/versions.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/cmd/view.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compiler.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/__init__.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/aocc.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/apple_clang.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/arm.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/cce.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/clang.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/fj.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/gcc.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/intel.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/nag.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/nvhpc.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/oneapi.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/pgi.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/xl.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/compilers/xl_r.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/concretize.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/config.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/container/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/container/images.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/container/writers/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/container/writers/docker.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/container/writers/singularity.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/database.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/dependency.py
2.6, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/directives.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/directory_layout.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/environment.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/error.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/extensions.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/fetch_strategy.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/filesystem_view.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/graph.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hash_types.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/__init__.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/extensions.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/licensing.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/module_file_generation.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/monitor.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/permissions_setters.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/sbang.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/hooks/write_install_manifest.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/install_test.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/installer.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/main.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/mirror.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/mixins.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/modules/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/modules/common.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/modules/lmod.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/modules/tcl.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/monitor.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/multimethod.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/operating_systems/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/operating_systems/cray_backend.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/operating_systems/cray_frontend.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/operating_systems/linux_distro.py
2.3, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/operating_systems/mac_os.py
2.6, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/package.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/package_prefs.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/package_test.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/parse.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/patch.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/paths.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/pkgkit.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/platforms/__init__.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/platforms/cray.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/platforms/darwin.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/platforms/linux.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/platforms/test.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/projections.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/provider_index.py
2.6, 3.5     /home/culpo/PycharmProjects/spack/lib/spack/spack/relocate.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/repo.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/report.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/reporter.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/reporters/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/reporters/cdash.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/reporters/junit.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/resource.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/s3_handler.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/__init__.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/buildcache_spec.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/cdash.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/compilers.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/config.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/container.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/database_index.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/env.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/environment.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/gitlab_ci.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/merged.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/mirrors.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/modules.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/packages.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/projections.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/repos.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/spec.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/schema/upstreams.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/solver/__init__.py
2.6, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/solver/asp.py
2.6, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/spec.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/spec_list.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/stage.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/store.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/subprocess_context.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/tengine.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/__init__.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/abi.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/architecture.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/audit.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/bindist.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/bootstrap.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/build_distribution.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/build_environment.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/build_system_guess.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/build_systems.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/buildrequest.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/buildtask.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cache_fetch.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cc.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/ci.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/__init__.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/activate.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/analyze.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/arch.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/audit.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/blame.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/build_env.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/buildcache.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/cd.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/ci.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/clean.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/commands.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/common/__init__.py
2.5, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/common/arguments.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/compiler.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/concretize.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/config.py
2.5, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/create.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/debug.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/dependencies.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/dependents.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/deprecate.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/dev_build.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/develop.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/env.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/extensions.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/external.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/fetch.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/find.py
2.4, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/flake8.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/gc.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/gpg.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/graph.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/help.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/info.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/init_py_functions.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/install.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/is_git_repo.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/license.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/list.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/load.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/location.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/maintainers.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/mark.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/mirror.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/module.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/pkg.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/print_shell_vars.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/providers.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/python.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/reindex.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/repo.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/resource.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/spec.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/stage.py
2.5, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/test.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/undevelop.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/uninstall.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/unit_test.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/url.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/verify.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/versions.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd/view.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cmd_extensions.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/compilers/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/compilers/basics.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/compilers/detection.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/concretize.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/concretize_preferences.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/config.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/config_values.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/conftest.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/cli.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/conftest.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/docker.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/images.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/schema.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/container/singularity.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/cvs_fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/database.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/directives.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/directory_layout.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/environment_modifications.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/fetch_strategy.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/flag_handlers.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/git_fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/graph.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/hg_fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/install.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/installer.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/link_paths.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/__init__.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/argparsewriter.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/file_list.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/filesystem.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/lang.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/link_tree.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/lock.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/tty/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/tty/log.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/llnl/util/tty/tty.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/main.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/make_executable.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/mirror.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/module_parsing.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/modules/__init__.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/modules/common.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/modules/conftest.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/modules/lmod.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/modules/tcl.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/monitor.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/multimethod.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/namespace_trie.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/operating_system.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/optional_deps.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/package_class.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/package_hash.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/package_sanity.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/packages.py
2.6, 3.1     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/packaging.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/patch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/pattern.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/permissions.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/provider_index.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/relocate.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/repo.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/s3_fetch.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/sbang.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/schema.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spack_yaml.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spec_dag.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spec_list.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spec_semantics.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spec_syntax.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/spec_yaml.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/stage.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/svn_fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/tengine.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/test_activations.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/test_suite.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/url_fetch.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/url_parse.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/url_substitution.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/__init__.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/editor.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/environment.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/executable.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/file_cache.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/log_parser.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/mock_package.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/prefix.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/spack_lock_wrapper.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/spack_yaml.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/util_gpg.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/util_string.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/util/util_url.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/variant.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/verification.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/versions.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/test/views.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/test/web.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/url.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/user_environment.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/util/__init__.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/classes.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/compression.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/cpus.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/crypto.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/util/debug.py
2.3, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/editor.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/environment.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/executable.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/file_cache.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/file_permissions.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/gpg.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/hash.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/util/imp/__init__.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/imp/imp_importer.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/imp/importlib_importer.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/lock.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/log_parse.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/mock_package.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/module_cmd.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/naming.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/package_hash.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/path.py
2.5, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/pattern.py
~2, ~3       /home/culpo/PycharmProjects/spack/lib/spack/spack/util/prefix.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/s3.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/spack_json.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/spack_yaml.py
2.2, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/string.py
2.4, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/timer.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/url.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/util/web.py
2.6, 3.2     /home/culpo/PycharmProjects/spack/lib/spack/spack/variant.py
2.5, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/verify.py
2.6, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/version.py
Minimum required versions: 2.7, 3.5
Target versions not met:   2.6, 3.5

Proposed behavior

What I would ideally like to see is instead:

$ vermin  --backport argparse --backport typing -t=2.6 -t=3.5 --violations lib/spack/spack/
Detecting python files..
Analyzing 445 files using 4 processes..
2.7, 3.0     /home/culpo/PycharmProjects/spack/lib/spack/spack/audit.py
  `"..{}..".format(..)` requires 2.7, 3.0
Minimum required versions: 2.7, 3.5
Target versions not met:   2.6, 3.5

Additional context

Trying to address the request in spack/spack#24550

pathlib module missing in checks

Describe the bug
The pathlib module is available since python 3.4 and is not in the checks

To Reproduce
Code using the pathlib module not having a minimum version of 3.4

Expected behavior
The minimum version should then be python 3.4 and no python 2 support

Environment (please complete the following information):
Vermin 0.4.4

Builtin generic annotations in fully qualified form not detected

>>> vermin.detect('from collections.abc import Reversible; Reversible[int]')
[None, (3, 9)]
>>> vermin.detect('import collections.abc; collections.abc.Reversible[int]')
[None, (3, 6)]

Both forms should be detected.

Environment (please complete the following information):

Add support for annotations

For builtin or user defined type typing lib is not imported so vermin is not detecting any minimum required version.
As long a type annotation is set python 2 and python 3 lower than 3.5 will not be supported

# since python 3.5
def foo(bar: int) -> str:
    pass

# since python 3.6
foo: str = 'bar'

Please change Arch AUR name to python-vermin

Sorry, you sent me a message about this but I first deleted my repo clone and thus could not reply to you(!)

I have changed the name of the Arch AUR package from vermin to python-vermin because that is more consistent with the Arch naming scheme. I had prepared a PR long ago for this but the Arch devs took ages to approve it and by the time they did I had forgotten about my PR (and it was out of date). So yes, please change yay -S vermin to yay -S python-vermin in the README here.

How to run on program without .py extension, and how to see usage message?

My issues are best explained by looking at each of the commands I execute below and their output:

pc:~/my-prog ls -l
total 4
-rwxr-xr-x 1 mark mark 51 Oct 16 12:53 my-prog

pc:~/my-prog cat my-prog 
#!/usr/bin/env python3
print('This is my program')

pc:~/my-prog ../env/bin/vermin my-prog 
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin ./my-prog 
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin .
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin -h
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin --help
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin --version
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/vermin -v
Detecting python files..
No files specified to analyze!

pc:~/my-prog ../env/bin/pip list 
Package    Version
---------- -------
pip        19.3   
setuptools 40.8.0 
vermin     0.8.0  

So the issues I am asking about are:

  1. How can I run vermin on a python program without a .py extension?
  2. There seems to be no usage message? Nor a way to check the version?
  3. Also, I originally installed that env/ virtualenv dir within the source folder (like users normally do for a project) but there seems to be no way to stop vermin recursing through that dir?

Vermin Cannot Detect Dict Comprehensions

Describe the bug

Vermin cannot detect dictionary comprehensions, which were introduced with 2.7 and 3.0 (PEP 274).

Before PEP 274:

dict([(key, value) for list_dict in list_of_dicts for key, value in list_dict.items()])

After PEP 274:

{key: value for list_dict in list_of_dicts for key, value in list_dict.items()}

To Reproduce
Just run vermin -vvv on a Python code which includes a dict comprehension.

Expected behavior
LX CX: Dict comprehension requires (2.7, 3.0)

Environment (please complete the following information):

  • Vermin 0.7.0

PEP 614 detection

As described in PEP 614, in Python 3.9 the decorator grammar allows arbitrary valid expressions after the @ sign. Previously the grammar was restricted. Whenever we detect a use of decorator grammar beyond the old restricted grammar, we can report the code as 3.9+.

Sample code for testing:

@[property][0]
def foo(): pass

False positive builtin name detection in exception handler body

Describe the bug
Names in exception handler body could be incorrectly determined as builtin features used.

To Reproduce

File:

for file in ['foo']:
    try:
        1 / 0
    except ZeroDivisionError:
        print(file)

Result:

Detecting python files..
Analyzing using 8 processes..
2.0, !3      test.py
  print(expr) requires 2.0, 3.0
  L5 C14: 'file' member requires 2.0, !3

Minimum required versions: 2.0
Incompatible versions:     3

Expected behavior

Should not detect file as Python 2 builtin file member.

Environment

  • Vermin version: 1.1.1

Additional context
This is due to commit bd9ded9 for addressing #45. To match the current detection heuristics (outside of exception handlers), we should not look for builtin exceptions in the body attribute of an ExceptHandler node, but only look for them inside the type attribute.

It would also be better to recognize file as a user-defined name in this case. There are many kinds for syntactic structures which create new variables (bind names) in Python:

  • Assignment (regular assignment, augmented assignment, annotated assignment, walrus)
  • import statements
  • Function and class definitions (the function/class name is a variable in the containing scope)
  • Function argument list (an argument is a local variable of the function)
  • for loops and comprehensions
  • with ... as ...
  • except ... as ...
  • And pattern matching in 3.10 may also bind names

False positive generalized unpacking detection with starred expression as assignment target

Starred expressions are allowed as assignment targets for Python < 3.5:

root@f1b4a742d8fc:/# python3.4
Python 3.4.10 (default, Mar 29 2019, 18:50:06)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> *x, y = [1, 2, 3]
>>> x
[1, 2]
>>> y
3
>>> z = *x, y
  File "<stdin>", line 1
SyntaxError: can use starred expression only as assignment target

Currently vermin falsely report this case as 3.5+:

>>> vermin.detect('*x, y = [1, 2, 3]')
[None, (3, 5)]

Maybe we need to mark some flags such as self.__seen_assign_target in visit_Assign and treat the targets attribute specially.

Environment

  • Vermin version 1.0.3

Conditional imports based on version?

Very cool project!

I am wonder if it might be able to be extended to handle import statement that are conditional on the python version. For example, the following python code works in python 2 and 3 but the minpy output seems unsure:

import sys
is_py2 = sys.version[0] == '2'
if is_py2:
    import Queue as Queue
else:
    import queue as Queue

minpy:

'Queue' requires (2.0, None)
'queue' requires (None, 3.0)
Versions could not be combined: [2.0, None] and (None, 3.0)
File with incompatible versions: /Users/valencik/projects/scrap/test_imports.py
Note: Some files had incompatible versions so the results might not be correct!
Could not determine minimum required versions!

Perhaps there's a more canonical way to branch on versions that I am unaware of.

False positives regarding module member detection with relative import

Describe the bug
When a module (imported with relative import) in my own package has the same name as a standard library module, vermin may emit false positive detection results.

To Reproduce

user@machine:/tmp/test/mypackage
$ ls
__init__.py  main.py  typing.py
user@machine:/tmp/test/mypackage
$ cat main.py
from .typing import Final
user@machine:/tmp/test/mypackage
$ cat typing.py
from typing_extensions import Final
user@machine:/tmp/test/mypackage
$ vermin -vv .
Detecting python files..
Analyzing 3 files using 8 processes..
~2, ~3       /tmp/test/mypackage/__init__.py
!2, 3.8      /tmp/test/mypackage/main.py
  'typing' requires !2, 3.5
  'typing.Final' requires !2, 3.8

~2, ~3       /tmp/test/mypackage/typing.py
Tip: You're using potentially backported modules: typing
If so, try using the following for better results: --backport typing

Minimum required versions: 3.8
Incompatible versions:     2

Expected behavior
Should not detect from .typing import Final as 3.8+.

Environment:
Vermin version 0.10.2

Non Deterministic Vermin Runs

Describe the bug
Subsequent runs of vermin . on the same directory have different results.

To Reproduce

  1. Clone the calibre repository. git clone https://github.com/kovidgoyal/calibre
  2. git checkout 7460a12a4b
  3. Run vermin . at least twice and verify the results differ. I have had some subsequent runs report the same information. Here is the output from 2 subsequent runs of vermin:
(apatite) shawn@deepthink:~/.apatite/repos/calibre$ vermin .
Detecting python files..
Analyzing 1364 files using 8 processes..
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/overdrive.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/setup/win-ci.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/builtins.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/edelweiss.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/rtf2xml/options_trem.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/gui2/store/stores/chitanka_plugin.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/queue.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/urllib.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/socketserver.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/ozon.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/setup/plugins_mirror.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/http_cookie.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/setup/linux-installer.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/gui2/store/stores/beam_ebooks_de_plugin.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/devices/winusb.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/amazon.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/reprlib.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/html_entities.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/bypy/linux/site.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/google_images.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/douban.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/google.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/gui2/linux_file_dialogs.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/utils/winreg/lib.py
Note: Some files had incompatible versions so the results might not be correct!
Minimum required versions: 3.4
Incompatible versions:     2
(apatite) shawn@deepthink:~/.apatite/repos/calibre$ vermin .
Detecting python files..
Analyzing 1364 files using 8 processes..
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/utils/winreg/lib.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/utils/mreplace.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/setup/win-ci.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/http_cookie.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/devices/winusb.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/builtins.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/reprlib.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/odf/thumbnail.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/google_images.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/edelweiss.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/functools.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/utils/smtplib.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/srv/utils.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/queue.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/ozon.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/douban.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/overdrive.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/polyglot/socketserver.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/google.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/utils/monotonic.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/gui2/__init__.py
File with incompatible versions: /home/shawn/.apatite/repos/calibre/src/calibre/ebooks/metadata/sources/amazon.py
Note: Some files had incompatible versions so the results might not be correct!
Minimum required versions: 2.7
Incompatible versions:     3

(One thing I noticed is that the order of files analyzed seems to be different, maybe a clue? I have no idea)

Expected behavior
Subsequent runs of vermin should be consistent.

Environment (please complete the following information):
Vermin 0.6.0
Linux deepthink 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Python 3.6.8

Additional context
We found this issue while developing the version detector module for https://github.com/mahmoud/awesome-python-applications. Vermin is incredibly useful, just stumbling onto it was probably my biggest contribution to the apatite (the cli tool building the dataset for the awesome python list). We were looking at all sorts of really inaccurate methods for Python 2/3 detection previously.

Incorrect detection of self-documenting f-strings

Describe the bug

False-positives are returned for self-documenting f-strings.

To Reproduce

$ cat /tmp/test.py
a = 1
print(f"Hello world={a}")
$ vermin /tmp/test.py
Minimum required versions: 3.8
Incompatible versions:     2

Expected behavior

Expected the minimum required version for this code to be 3.6. A self-documenting f-string has the equals sign on the other side of the variable (i.e. {a=}).

Environment (please complete the following information):

  • Vermin version 0.9.2

Thanks in advance!

"Could not determine minimum required versions!" <= is this really true?

Describe the bug
When vermin does not believe it determine minimum versions, the output is confusing and hard to interpret.

To Reproduce

$ vermin -v vermin/__init__.py vermin/printing.py
Detecting python files..
Analyzing 2 files using 4 processes..
             /c/Users/hwine/Desktop/wsl_test/vermin/vermin/__init__.py
2.0, 3.0     /c/Users/hwine/Desktop/wsl_test/vermin/vermin/printing.py
Minimum required versions: 2.0, 3.0

and

$ vermin -vvv vermin/__init__.py
Detecting python files..
Analyzing using 4 processes..
             /c/Users/hwine/Desktop/wsl_test/vermin/vermin/__init__.py
Could not determine minimum required versions!

Expected behavior
I can't discern the difference between the reports for the two files above. When I examine the files, I can not see any significant difference. I.e. why is one "2.0, 3.0" and the other "Could not determine...".

That ambiguity makes the output harder to interpret. And harder to convince a coworker of the value. Which combines to make the tool seem less predictable. ๐Ÿ˜ฟ

Environment (please complete the following information):

$ vermin
Vermin 0.6.2

Additional context

Here's my use case: As a developer working on porting lots of legacy scripts and modules from py2 to py3, I want to know where to focus my effort.

With a slight change to the output, I think I'd have something I could not only explain easier to colleagues, but also drive a nice dashboard:

$ vermin -v vermin/__init__.py vermin/printing.py
Detecting python files..
Analyzing 2 files using 4 processes..
2    3       /c/Users/hwine/Desktop/wsl_test/vermin/vermin/__init__.py
2.0, 3.0     /c/Users/hwine/Desktop/wsl_test/vermin/vermin/printing.py
Minimum required versions: 2.0, 3.0

Where 2 & 3 means "vermin sees no reason this won't work in that version".

The value of the -vvv line/function output is tremendous in a large migration. But I need to be able to sell the outer layer as well.

I understand these are "sugar coating", and may not fit your vision. Would there be any objection to finessing the output as above behind a --pretty flag? I could take a run at a PR for that.

Target version via argument

It should be possible to give a minimum required version by CLI, and if it isn't met then it must sys.exit(-1) so it can be used with other tools to enforce minimum versions.

The argument could be -t=V where V should support values such as 2, 3, 2.7, 3.3 etc. Support multiple versions, like v2 and v3 at the same time. Allow only 0, 1 or 2 occurrences of the argument.

When the argument isn't specified, Vermin should function as normal.

False positive rule matching

This code snippet

import os
s = 'a'
s.replace('b', 'c')

is incorrectly triggering the os.replace rule.

Another example:

import typing
ping.Final

Substitute ping with any suffix of typing and you will reproduce the bug (i.e. typing.Final incorrectly detected). This looks like an incorrect implementation of suffix matching somewhere. If the import statement is commented out, the bug does not happen.

Environment:

  • Vermin version 0.10.5

vermin does not identify dict has_key is not available in py3

Given file with:

d = {"foo": "bar"}
if d.has_key("foo"):
    print("d has the key!")

behavior in Python 3.x

d = {}
d.has_key("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'has_key'

vermin does not report a problem with py3 although you will get an error.

output snipit:

vermin.exe -t=3.6 -v C:\py2
Detecting python files..
Analyzing 2 files using 4 processes..
2.0, 3.0     C:\py2\dict_has_key.py

Reference:
https://portingguide.readthedocs.io/en/latest/dicts.html

A systematic way to generate rules, and possible enhancements

Hello! Recently my friend (@JarryShaw) and I found that a lot of rules are missing in vermin. So I wrote a small tool to parse Python documentation to find New in version ... and Changed in version ... indicators. Then the information can be extracted and turned into vermin rules in a semi-automated fashion (with manual inspection).

Based on our result, I am adding a lot of rules in PR #34

Also, we find that these language features could also be detected, if you are interested in implementing them:

If you are interested in finding rules systematically, you can possibly refer to our results to get more information.

Note: some drawbacks of parsing the documentation:

  • Some changes may be undocumented, or expressed vaguely in the documentation (string parsing is error prone, need manual verification)
  • The major differences of Python 2 vs Python 3 grammar is not clearly documented in the official documentation, more rules might be needed regarding this

Backports not properly ignored

Describe the bug

Even if I add --backport argparse to vermin, it still reports a minimum version requirement of Python 2.7.

To Reproduce

$ cat > foo.py
import argparse
$ vermin --backport argparse -vvvv foo.py
Detecting python files..
Analyzing using 4 processes..
2.7, 3.2     /Users/Adam/Downloads/foo.py
  L1 C7: 'argparse' requires 2.7, 3.2

Minimum required versions: 2.7, 3.2

Expected behavior

Any version of Python should work for this file.

Environment

  • Vermin 0.10.4
  • macOS 10.15.7
  • Python 3.8.5

Additional information

If you have any trouble reproducing this, I suspect that this is a macOS + Python 3.8 bug. See https://docs.python.org/3.8/library/multiprocessing.html#contexts-and-start-methods:

Changed in version 3.8: On macOS, the spawn start method is now the default. The fork start method should be considered unsafe as it can lead to crashes of the subprocess. See bpo-33725.

One workaround would be to force it to always use the fork start method by replacing multiprocessing with multiprocessing.get_context('fork') everywhere it's used. We recently had to do this for Spack: spack/spack#18124

A more permanent workaround would be to ensure that both spawn and fork work as expected.

Detection for builtin exceptions

With the current detection heuristics in vermin, the use of builtin exceptions cannot be effectively detected, although we already have a section for builtin exceptions in rules.py.

Raising exceptions:
raise ModuleNotFoundError -> not detected, because ModuleNotFoundError is a plain name
raise ModuleNotFoundError('some message') -> correctly detected as 3.6+, because there is a function call
raise shutil.SameFileError -> correctly detected as 3.4+, because shutil.SameFileError is a dotted name (i.e. involves an attribute access)

Catching exceptions:

try:
    import zlib
    import gzip
    gzip.GzipFile
except (ModuleNotFoundError, AttributeError):
    has_gz_support = False
else:
    has_gz_support = True

should be detected as 3.6+, but currently not detected.

Maybe we can handle raise and except statements specially to detected them. It might also be good to pull rules for builtin exceptions out of MOD_MEM_REQS (create a new category of rules), if that makes things easier.

Environment

  • Vermin version 0.10.3

Bytes literal reported as requiring 3+ instead of 2.6+

Describe the bug
A Python file containing a byte literal like b'foo' is reported to require Python versions greater than 3:

byte strings (b'..') require 3+

While this is true in some sense due to PEP3112 it's also a fact that Python 2.6+ defines bytes as a synonym for str and accepts byte literals.

To Reproduce
Analyze any Python file that defines a byte literal

Expected behavior
Since the syntax for byte literals is allowed from Python 2.6+ I would expect the requirements to be Python 2.6+, not Python 3+

Environment (please complete the following information):

  • Vermin version: 0.9.0

Additional context
Nothing relevant to report

Make results more understandable

Instead of

Minimum required versions: !2, 3.4

It should say something like:

Minimum required versions: 3.4
Incompatible versions: 2

Or:

Requires 3.4 as minimum version.
Incompatible with version 2!

With multiple versions it could be:

Requires 2.7 or 3.3 as minimum version.

Python API

It would be good if vermin can export a module-level function that accepts a code string and reports its minimum version. This allows programmatic use of vermin in Python without having to invoke the CLI.

In fact, I'm occasionally using one of your test utility functions to run vermin against short code snippets in IPython without having to create a file to store the code. However this function is in the tests package and thus is not accessible from the vermin package. Also, this test function only returns the minimum version of the code. Where to report details of each rule matched and other tips (like "You're using potentially backported modules") is a problem.

Incorrectly marks python version when code has if statement/hasattr check

Describe the bug
Vermin incorrectly marks code as requiring >= 3.6 when conditionals handles earlier python versions as well.

From Paho MQTT (client.py), the if hasattr( allows for prior versions to 3.6 as well.

        # Create SSLContext object
        if tls_version is None:
            tls_version = ssl.PROTOCOL_TLSv1
            # If the python version supports it, use highest TLS version automatically
            if hasattr(ssl, "PROTOCOL_TLS"):
                tls_version = ssl.PROTOCOL_TLS
        context = ssl.SSLContext(tls_version)

To Reproduce
Run vermin on the code example, or clone paho mqtt and run it on the master branch.

Expected behavior
Minimum version 3.4.

Environment (please complete the following information):

  • Vermin 0.6.1
  • Python 3.7.4

Other comments
Excellent tool, thank you for creating it and keep up the good work! ๐Ÿ‘

Handling Backported Packages

Is your feature request related to a problem? Please describe.
When using the backported typing module vermin indicates the files using it are not compatible with any version of py2. This is not correct, as this module is compatible with 2.7+ and 3.4+. It is flagging this usage of typing because it was not included in the standard library until version 3.5.

Describe the solution you'd like
One solution we were discussing was giving vermin the capability to parse a projects requirements.txt and give the user the ability to disable rules based on conflicts in the requirements.txt

However after discussing this with some of my co-workers, (I knew I was missing something obvious), the package name specified to PyPI doesn't necessarily have anything to do with the imported module name. Simply parsing the requirements.txt isn't a robust solution.

Describe alternatives you've considered
I'm at a loss here for good ideas. I would really like to support something as official as a backport of a standard library. Perhaps vermin could classify certain errors as warning instead, and caution the user that they need to have typing installed to work with py2. Have you considered anything along those lines? A lot of other static analysis tools make a clear distinction between different types of issues. (Critical, Error, Warning, Info, etc.)

Additional context
This feature is mostly stemming from my usage of vermin, which is primarily to analyze applications and collect statistics about their py2/3 compatibility.

Only show violated rules when target version is given

When the --target argument is given, we expect vermin to work more like a linter: it has a predefined goal, matches some rules and report violations (and set exit code to 0 or 1 accordingly). Typically linters emit as few output lines as possible when there is no violation found, and only display violated rules when found. This helps users spot rule violations more easily, compare this:

$ python3 vermin.py -t=2.7 -t=3.0 --format parsable .
/path/to/vermin/misc/travis/after_success.sh:4:7: error: invalid syntax: if [[ $TRAVIS_PYTHON_VERSION != 2.7 && $TRAVIS_PYTHON_VERSION != 3.4 ]]; then
/path/to/vermin/misc/travis/script.sh:4:7: error: invalid syntax: if [[ $TRAVIS_PYTHON_VERSION = 2.7 || $TRAVIS_PYTHON_VERSION = 3.4 ]]; then
(many lines omitted ...)
/path/to/vermin/runtests.py:8::2.7:3.1:'unittest.main(exit)'
/path/to/vermin/runtests.py:::2.7:3.1:
(many lines omitted ...)
:::2.7:3.1:

with this:

$ python3 vermin.py -t=2.7 -t=3.0 --format parsable .
/path/to/vermin/runtests.py:8::2.7:3.1:'unittest.main(exit)'
/path/to/vermin/runtests.py:::2.7:3.1:
:::2.7:3.1:

We can either:

  • Add a flag like --only-show-violated (maybe a shorter name) to do this, this flag only makes sense when --target is given
  • Make this behavior to be the default when --target is given (this is backwards incompatible behavior change)

Exclusion comment doesn't work with indentation

Describe the bug
If you indent # novermin then it is not properly caught and vermin will give an incorrect result. And you generally want to indent comments to the same level as the surrounding code.

To Reproduce
Apply this diff to the unit tests:

diff --git a/tests/comment_exclusions.py b/tests/comment_exclusions.py
index e7fb17f..cf66287 100644
--- a/tests/comment_exclusions.py
+++ b/tests/comment_exclusions.py
@@ -12,6 +12,10 @@ class VerminCommentsExclusionsTests(VerminTest):
     self.assertIn(2, visitor.no_lines())
     self.assertEqual([(0, 0), (0, 0)], visitor.minimum_versions())
 
+    visitor = visit("if True:\n # novermin\n import email.parser.FeedParser")
+    self.assertIn(2, visitor.no_lines())
+    self.assertEqual([(0, 0), (0, 0)], visitor.minimum_versions())
+
     # Testing at end of line w/o spacing.
     visitor = visit("import email.parser.FeedParser  # novm")
     self.assertIn(1, visitor.no_lines())

Environment (please complete the following information):

PEP 585 support in 3.7+ using __future__

Describe the bug
Vermin reports the minimum version is Python 3.9, even though the code will run correctly in Python 3.7+.

To Reproduce
Steps to reproduce the behavior.

Run the following code in Python 3.7, 3.8, 3.9

from __future__ import annotations

def fn(x: list[int]) -> list[int]:
    a: list[int] = []
    return a

fn([])

Output:

Detecting python files..
Analyzing using 12 processes..
!2, 3.9      /Users/tyler.yep/Documents/Github/probs/yo.py
  L1 C5: '__future__' module requires 2.1, 3.0
  L1 C5: '__future__.annotations' member requires !2, 3.7
  L4: annotations requires !2, 3.0
  L4: builtin generic type annotation (list[..]) requires !2, 3.9
  L5: variable annotations requires !2, 3.6
  L5: builtin generic type annotation (list[..]) requires !2, 3.9
  L6: builtin generic type annotation (list[..]) requires !2, 3.9

Minimum required versions: 3.9
Incompatible versions:     2

Expected behavior
Minimum version should be Python 3.7

Environment:

  • Vermin latest version (1.1.0)

Code in name().method(kwargs=...) form not detected

There are three slightly different code snippets, triggering different detection behaviors of vermin:

from zipfile import ZipFile
ZipFile.writestr(compress_type=None)

Correct: detected as 2.7+, 3.2+

from zipfile import ZipFile
ZipFile().writestr(compress_type=None)

Wrong: detected as ~2, ~3

from zipfile import ZipFile
zf = ZipFile()
zf.writestr(compress_type=None)

Correct: detected as 2.7+, 3.2+

However if the rule is not a kwargs rule, this bug does not happen:

from zipfile import ZipFile
ZipFile.setpassword()
from zipfile import ZipFile
ZipFile().setpassword()
from zipfile import ZipFile
zf = ZipFile()
zf.setpassword()

All these three cases are correctly detected as 2.6+, 3.0+.

Environment

  • Vermin version 0.10.3

Log version-specific features that raise requirements

Currently running vermin on a project folder will display the minimum required Python version. However, it doesn't go into any detail for why a project requires that version.

C:\>vermin U:\Documents\Scripts\wfc_helper
Detecting python files..
Analyzing 13 files using 4 processes..
Minimum required versions: 3.6
Incompatible versions:     2

To help users learn why a version is required, I propose listing what features a project uses to require a specific version of Python.

C:\>vermin U:\Documents\Scripts\wfc_helper
Detecting python files..
Analyzing 13 files using 4 processes..
Minimum required versions: 3.6
Incompatible versions:     2
Features:                  f-strings (3.6)
                           import typing (3.5)

Alternatively, if a folder is specified, Vermin could specify which file or line number caused the current requirements.

RFE: ignore hidden directories

Is your feature request related to a problem? Please describe.

I heard about vermin, decided to check it out, and ran pipx run vermin ~/projects/check-python-versions. Then I saw it complain about files inside .tox/

File with incompatible versions: /home/mg/projects/check-python-versions/.tox/coverage/lib/python3.6/site-packages/coverage/backward.py

Describe the solution you'd like

It would be nice (and much faster!) if the directory crawler part of vermin ignored hidden directories such as .git and .tox.

AssertionError with --versions

Describe the bug

Since version 1.0.0 I'm seeing the following error with a previously error-free project:

$ vermin --versions .                                                                                                                                                                                                                       

Minimum required versions: 3.6
Incompatible versions:     2
Traceback (most recent call last):
  File "/usr/local/bin/vermin", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.8/site-packages/vermin/main.py", line 102, in main
    print("Version range:             {}".format(version_strings(unique_versions)))
  File "/usr/local/lib/python3.8/site-packages/vermin/utility.py", line 86, in version_strings
    assert(amount >= 1 and amount <= 2)
AssertionError

Expected behavior

Previously with vermin-0.10.1:

$ vermin --versions .                                                                                                                                                                                                                       

Minimum required versions: 3.6
Incompatible versions:     2
Version range:             2.7, 3.1, 3.5, 3.6

Missing Python 3.7 features, incorrectly reports as 3.5-compatible

Describe the bug
When testing Python code that uses features from v3.7 the script incorrectly reports minimal version as 3.5

To Reproduce

  1. Create a file with this contents:
#!/usr/bin/env python3

import subprocess

proc = subprocess.run('ls -l -a',
                      shell=True,
#                      universal_newlines=True,
                      text=True,    # available since 3.7+
                      stderr=subprocess.STDOUT,
                      stdout=subprocess.PIPE)
proc.check_returncode()
print(proc.stdout)
  1. Execute
    vermin -vvvv bug.py

Expected behavior
The script detects that text parameter is available in Python 3.7+

Actual behavior

Detecting python files..
Analyzing using 12 processes..
!2, 3.5      /Users/user/src/other/vermin_bug/subprocess_bug.py
  L3 C7: 'subprocess' requires 2.4, 3.0
  L5: 'subprocess.run' requires !2, 3.5
  L5: True/False constant requires v2.2+.
  print(expr) requires 2+ or 3+

Minimum required versions: 3.5
Incompatible versions:     2

Environment (please complete the following information):

  • Vermin version: 0.9.1

vermin doesn't print why it thinks a file has incompatible versions

Describe the bug
vermin doesn't print why it thinks a file has incompatible versions making it very difficult to figure out what is wrong with the code.

To Reproduce
E.g. this file:

try:
  import socketserver
except ImportError:
  import SocketServer

Expected behavior
Output similar to the success case, e.g.:

$ vermin -p=1 -vvvv ./test.py 
Detecting python files..
Analyzing using 1 processes..
File with incompatible versions: test.py
!2, !3      /local/home/ossman/devel/vermin/test.py
  L2 C9: 'socketserver' requires !2, 3.0
  L4 C9: 'SocketServer' requires 2.0, !3
Note: Some files had incompatible versions so the results might not be correct!
Minimum required versions: ~2, ~3

Environment (please complete the following information):

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.