Pylint ignore error

Edit this page

Back to top

Edit this page

Toggle table of contents sidebar

In order to control messages, pylint accepts the following values:

  • a symbolic message: no-member, undefined-variable etc.

  • a numerical ID: E1101, E1102 etc.

  • The name of the group of checks. You can grab those with pylint --list-groups.
    For example, you can disable / enable all the checks related to type checking, with
    typecheck or all the checks related to variables with variables

  • Corresponding category of the checks

    • C convention related checks

    • R refactoring related checks

    • W various warnings

    • E errors, for probable bugs in the code

    • F fatal, if an error occurred which prevented pylint from doing further processing.

  • All the checks with all

Block disables#

This describes how the pragma controls operate at a code level.

The pragma controls can disable / enable:

  • All the violations on a single line

a, b = ... # pylint: disable=unbalanced-tuple-unpacking
  • All the violations on the following line

# pylint: disable-next=unbalanced-tuple-unpacking
a, b = ...
  • All the violations in a single scope

def test():
    # Disable all the no-member violations in this function
    # pylint: disable=no-member
    ...
  • All the violations in a block. For instance, each separate branch of an
    if statement is considered a separate block, as in the following example:

def meth5(self):
    # pylint: disable=no-member
    # no error
    print(self.bla)
    if self.blop:
        # pylint: enable=no-member
        # enable all no-members for this block
        print(self.blip)
    else:
        # This is affected by the scope disable
        print(self.blip)
    # pylint: enable=no-member
    print(self.blip)
    if self.blop:
        # pylint: disable=no-member
        # disable all no-members for this block
        print(self.blip)
    else:
        # This emits a violation
        print(self.blip)
  • If the violation occurs on a block starting line, then it applies only to that line

if self.blop: # pylint: disable=no-member; applies only to this line
    # Here we get an error
    print(self.blip)
else:
    # error
    print(self.blip)

Here’s an example with all these rules in a single place:

"""pylint option block-disable"""

__revision__ = None

class Foo(object):
    """block-disable test"""

    def __init__(self):
        pass

    def meth1(self, arg):
        """this issues a message"""
        print(self)

    def meth2(self, arg):
        """and this one not"""
        # pylint: disable=unused-argument
        print(self
              + "foo")

    def meth3(self):
        """test one line disabling"""
        # no error
        print(self.bla) # pylint: disable=no-member
        # error
        print(self.blop)

    def meth4(self):
        """test re-enabling"""
        # pylint: disable=no-member
        # no error
        print(self.bla)
        print(self.blop)
        # pylint: enable=no-member
        # error
        print(self.blip)

    def meth5(self):
        """test IF sub-block re-enabling"""
        # pylint: disable=no-member
        # no error
        print(self.bla)
        if self.blop:
            # pylint: enable=no-member
            # error
            print(self.blip)
        else:
            # no error
            print(self.blip)
        # no error
        print(self.blip)

    def meth6(self):
        """test TRY/EXCEPT sub-block re-enabling"""
        # pylint: disable=no-member
        # no error
        print(self.bla)
        try:
            # pylint: enable=no-member
            # error
            print(self.blip)
        except UndefinedName: # pylint: disable=undefined-variable
            # no error
            print(self.blip)
        # no error
        print(self.blip)

    def meth7(self):
        """test one line block opening disabling"""
        if self.blop: # pylint: disable=no-member
            # error
            print(self.blip)
        else:
            # error
            print(self.blip)
        # error
        print(self.blip)

    def meth8(self):
        """test late disabling"""
        # error
        print(self.blip)
        # pylint: disable=no-member
        # no error
        print(self.bla)
        print(self.blop)

    def meth9(self):
        """test next line disabling"""
        # no error
        # pylint: disable-next=no-member
        print(self.bla)
        # error
        print(self.blop)

Detecting useless disables#

As pylint gets better and false positives are removed,
disables that became useless can accumulate and clutter the code.
In order to clean them you can enable the useless-suppression warning.

January 4, 2021

10 minute read

Lint

pylint

As pylint has too many options, it’s recommended to use the pylint config file:

# file ~/.pylintrc, can be generated by pylint --generate-rcfile

[MASTER]

[MESSAGES CONTROL]
disable=
    C0116, # Missing function or method docstring (missing-function-docstring)
    W1203, # Use lazy % formatting in logging functions (logging-fstring-interpolation)

[format]
max-line-length = 88

[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME

[VARIABLES]

# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
    spark

But we can also ignore some warnings directly in the pylint command:

pylint . -j 0 --disable=C0116,W1203

To show all the inline ignored pylint alerts: pylint --enable=suppressed-message

Ignore Unused Argument given a Function Name Expression

Use dummy variable to ignore the Pylint warning on unused-argument.

flake8

# ignore W503 because of black format. BTW, flake8 also has W504 which is in contrary to W503.
# ignore E501, line too long because we have the same check at Pylint side already.
flake8 . 
  --exclude=venv 
  --extend-ignore=E203,E501,W503, 
  --max-complexity=7 
  --show-source 
  --statistics 
  --count 
  --jobs=auto

flake8 [a_file_path]

To show all the inline ignored flake8 alerts: flake8 --disable-noqa || true

There’s a very nice flake8 plugin called flake8-cognitive-complexity which checks the Cognitive Complexity in addition to the Cyclomatic Complexity provided by flake8 out of the box. We dont need to add extra parameter to use the Cognitive Complexity in flake8, it’s set to --max-cognitive-complexity=7 by default once the plugin is installed. By the way, Sonar sets the Cognitive Complexity threshold to 15 by default.

To fix imported but not used error in __init__.py file, could by all attribute (the most elegant) or by –per-file-ignores.

bandit

The bandit config file format is not well documented, I passed a lot of time to test the config.

$ cat .bandit
# https://github.com/PyCQA/bandit/issues/400
exclude_dirs:
  - "./venv/*"

# https://github.com/PyCQA/bandit/pull/633
assert_used:
  skips:
    - "*/*_test.py"
    - "*/test_*.py"
# without specifying -c ./bandit, it doesn't work
$ bandit . -r -c ./.bandit

ossaudit

ossaudit uses Sonatype OSS Index to audit Python packages for known vulnerabilities.

It can check installed packages and/or packages specified in dependency files. The following formats are supported with dparse:

  • PIP requirement files
  • Pipfile
  • Pipfile.lock
  • tox.ini
  • conda.yml
# check installed packages and packages listed in two requirements files
$ ossaudit --installed --file requirements.txt --file requirements-dev.txt
Found 0 vulnerabilities in 214 packages

Github has already provided, free of charge, the vulnerable dependencies alert.

mypy

For projects having sqlalchemy, we often install the sqlalchemy-stubs plugin as sqlalchemy uses some dynamic classes.

And also django-stubs, pandas-stubs, types-setuptools, types-requests etc.

mypy config file:

[mypy]
ignore_missing_imports = True # We recommend using this approach only as a last resort: it's equivalent to adding a # type: ignore to all unresolved imports in your codebase.
plugins = sqlmypy # sqlalchemy-stubs
exclude = (?x)(
    ^venv
    | ^build
  )

running mypy:

mypy .
mypy . --exclude [a regular expression that matches file path]
mypy . --exclude venv[//] # exclude venv folder under the root

When using mypy, it would be better to use mypy against to all files in the project, but ont some of them,

ignore lint error in one line

linter ignore in one line
pylint (2 spaces)# pylint: disable={errorIdentifier}
flake8 (2 spaces)# noqa: {errorIdentifier}
bandit (2 spaces)# nosec
mypy (2 spaces)# type: ignore
multiple linters (2 spaces)# type: ignore # noqa: {errorIdentifier} # pylint: disable={errorIdentifier}

To ignore Pylint within a code block

# https://stackoverflow.com/a/48836605/5095636
import sys
sys.path.append("xx/xx")

# pylint: disable=wrong-import-position
from x import (  # noqa: E402
    a,
    b,
)
from y import c  # noqa: E402

# pylint: enable=wrong-import-position

Format

isort

isort . --profile=black --virtual-env=venv --recursive --check-only
isort . --profile=black --virtual-env=venv --recursive
isort [a_file_path]

Be very careful with isort, it’s not uncompromising, especially for some codes that dynamically import some modules inside a function instead of from the beginning of a file. People use often this to avoid circular import problem. Always run the tests after the isort.

black

black . --check
black .
black [a_file_path]

Using black with other tools: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html

VSCode

Just my 2 cents, try the errorlens extension in VSCode, it will lint all the warnings/errors on live when coding, it’s really cool.

And don’t forget to install the official SonarLint extension, it will give you extra lint. It eats a lot of memory with its java processes nevertheless.

  "isort.args": [
    "--profile",
    "black"
  ],
  "python.formatting.provider": "none",
  "[python]": {
    "editor.defaultFormatter": "ms-python.black-formatter",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      // "source.organizeImports": true
    },
  },
  "python.linting.banditEnabled": true,
  "python.linting.banditArgs": [
    "-r",
    "-c",
    "~/pyproject.toml"
  ],
  "python.linting.ignorePatterns": [
    ".vscode/*.py",
    "**/site-packages/**/*.py",
    "venv/"
  ],
  "python.linting.mypyEnabled": true,
  "python.linting.mypyArgs": [
    "--follow-imports=silent",
    "--ignore-missing-imports",
    "--show-column-numbers",
    "--no-pretty",
    "--warn-return-any",
    "--warn-unused-configs",
    "--show-error-codes"
  ],
  "sonarlint.connectedMode.connections.sonarqube": [
    {
      "serverUrl": "https://sonar.xxx",
      "connectionId": "sonar.xxx"
    }
  ],
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    // "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "[jsonc]": {
    "editor.defaultFormatter": "vscode.json-language-features"
  },

pyproject.toml

pyproject.toml is the new standard in Python introduced by PEP 518 (2016) for build system requirements, PEP 621 (2020) for project metadata, and PEP 660 (2021) for wheel based editable installs.

It’s fun to know why Python authority chose this name, and very interesting to understand their POV of different file formats :smile:.

All the the major tools (setuptools, pip-tools, poetry) support this new standard, and the repo awesome-pyproject maintains a list of Python tools which are compatible to pyproject.toml.

We cannot officially declare flake8 config in pyproject.toml.

Hereunder an example of its content for the lint part.

[tool.isort]
profile = "black"

[tool.mypy]
ignore_missing_imports = true
warn_return_any = true
warn_unused_configs = true
show_error_codes = true
# disallow_untyped_defs = true
# strict = true
exclude = [
    "^venv/", # we don't need to exclude `.venv` in mypy as hidden folders are excluded by default
    "^build/",
    "^_local_test/",
]

[tool.bandit]
# we dont need to exclude `.venv` in bandit as it uses wildcast here
exclude_dirs = ["venv", "_local_test"]
[tool.bandit.assert_used]
skips = ["*/*_test.py", "*/test_*.py"]

[tool.pylint.main]
# ! type to use pyspark-stubs
# extension-pkg-allow-list = ["pyspark"]
# ignored-modules = ["pyspark"]
jobs = 0
# [tool.pylint.typecheck]
# # ! type to use pyspark-stubs
# generated-members = ["pyspark.sql.functions"]
[tool.pylint.basic]
good-names = [
  "df"  # for dataframe
]
[tool.pylint.variables]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins = ["spark"]
[tool.pylint."messages control"]
disable = [
    "missing-class-docstring",
    "missing-function-docstring",
    "logging-fstring-interpolation",
]
[tool.pylint.miscellaneous]
notes = ["FIXME"]
[tool.pylint.format]
max-line-length = 88
expected-line-ending-format = "LF"

# the default doesn't ignore comment line with words between `#` and `http` like:
# the url is https://pylint.pycqa.org/en/latest/user_guide/configuration/all-options.html#ignore-long-lines
ignore-long-lines = "^\s*(#)+.*<?https?://"

[tool.pytest.ini_options]
addopts="""
    -v -s
    --cov {source_folder}
    --cov-report=html
    --cov-report=xml
    --junitxml=junit/test-results.xml
    --cov-report=term-missing:skip-covered
    --cov-fail-under=95
    """

Git pre-commit

https://pre-commit.com/

“Git hook scripts are useful for identifying simple issues before submission to code review. We run our hooks on every commit to automatically point out issues in code such as missing semicolons, trailing whitespace, and debug statements. By pointing these issues out before code review, this allows a code reviewer to focus on the architecture of a change while not wasting time with trivial style nitpicks.”

pip install pre-commit
pre-commit install

# install the script along with the hook environments in one command
# https://pre-commit.com/index.html#pre-commit-install-hooks
pre-commit install --install-hooks

# Auto-update pre-commit config to the latest repos' versions.
pre-commit autoupdate

# Clean out cached pre-commit files.
pre-commit clean

# Clean unused cached repos.
pre-commit gc

# Run single check
pre-commit run black

# continuous integration
# https://pre-commit.com/index.html#usage-in-continuous-integration
pre-commit run --all-files
# check only files which have changed
pre-commit run --from-ref origin/HEAD --to-ref HEAD

# Azure pipeline example with cache
https://pre-commit.com/index.html#azure-pipelines-example

# automatically enabling pre-commit on repositories
# https://pre-commit.com/index.html#automatically-enabling-pre-commit-on-repositories
git config --global init.templateDir ~/.git-template
pre-commit init-templatedir ~/.git-template

Online examples

pylint github pre-commit-config.yaml

Create a file named .pre-commit-config.yaml to the root of your project

Although each lint has its own config to exclude some files from checking, pre-commit also has the key exclude with list value or regex to exclude file from sending to linter.

language: system means using the executables from the same environment of current Python interpreter.

When using mypy in pre-commit, it would be better run pre-commit run --all-files, mypy doesn’t work well with only diff files sent by pre-commit run --from-ref origin/${pullrequest_target_branch_name} --to-ref HEAD.

# Installation:
# pip install pre-commit
# pre-commit install
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: check-json
        exclude: devcontainer.json
      - id: check-yaml
      - id: check-toml
      - id: end-of-file-fixer
      - id: trailing-whitespace
      - id: debug-statements
      - id: requirements-txt-fixer
      - id: detect-private-key
      - id: mixed-line-ending
        args: ["--fix=lf"]
      - id: check-added-large-files
      - id: no-commit-to-branch
  - repo: https://github.com/Lucas-C/pre-commit-hooks
    rev: v1.3.1
    hooks:
      - id: forbid-crlf
      - id: remove-crlf
      - id: forbid-tabs
      - id: remove-tabs
  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.0.0-alpha.1
    hooks:
      - id: prettier
  - repo: https://github.com/pre-commit/pygrep-hooks
    rev: v1.9.0
    hooks:
      - id: python-check-blanket-type-ignore
      - id: python-check-mock-methods
      - id: python-no-log-warn
      - id: python-use-type-annotations
  - repo: https://github.com/asottile/pyupgrade
    rev: v3.1.0
    hooks:
      - id: pyupgrade
  - repo: local
    hooks:
      - id: isort
        name: isort
        entry: isort
        language: system
        types: [python]
      - id: black
        name: black
        entry: black
        language: system
        types: [python]
      - id: bandit
        name: bandit
        entry: bandit
        language: system
        types: [python]
        args:
          - -c
          - pyproject.toml
      - id: pylint
        name: pylint
        entry: pylint
        language: system
        types: [python]
      - id: flake8
        name: flake8
        entry: flake8
        language: system
        types: [python]
      - id: mypy
        name: mypy
        language: system
        entry: mypy
        types: [python]
      - id: pytest
        name: pytest
        types: [python]
        entry: pytest
        language: system
        pass_filenames: false
        always_run: true


Be aware that especially in a local environment, we often use venv, in such case, it would be better to use above system level lint executables instead of below public ones, the checks will be more accurate.

# example of using online linters
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: check-json
      - id: check-yaml
      - id: check-toml
      - id: end-of-file-fixer
      - id: trailing-whitespace
      - id: debug-statements
      - id: requirements-txt-fixer
      - id: detect-private-key
      - id: mixed-line-ending
        args: ['--fix=lf']
      - id: check-added-large-files
      - id: no-commit-to-branch
  - repo: https://github.com/Lucas-C/pre-commit-hooks
    rev: v1.3.1
    hooks:
      - id: forbid-crlf
      - id: remove-crlf
      - id: forbid-tabs
      - id: remove-tabs
  - repo: https://github.com/psf/black
    rev: 22.8.0
    hooks:
      - id: black
        name: "Format with Black"
  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort
        args: ["--profile", "black"]
  - repo: https://github.com/pycqa/flake8
    rev: 5.0.4
    hooks:
      - id: flake8
        additional_dependencies:
          - flake8-bugbear
          - flake8-comprehensions
          - flake8-simplify
  - repo: https://github.com/pre-commit/mirrors-pylint
    rev: "v3.0.0a5"
    hooks:
      - id: pylint
  - repo: https://github.com/pre-commit/mirrors-mypy
    # it might be better to use local venv installed mypy because it has access to all the modules installed in the venv
    rev: v0.981
    hooks:
      - id: mypy
        additional_dependencies:
          # just for example
          - types-dataclasses >= 0.1.3
          - click >= 8.1.0
  - repo: https://github.com/Lucas-C/pre-commit-hooks-bandit
    rev: v1.0.5
    hooks:
      - id: python-bandit-vulnerability-check
        args:
          - --recursive
          - .
          - -c
          - ./.bandit
  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v2.7.1
    hooks:
      - id: prettier
  - repo: local
    hooks:
    - id: bandit
      name: local bandit
      entry: bandit
      language: python
      language_version: python3
      types: [python]

Install the git hook scripts

$ pre-commit install
pre-commit installed at .git/hooks/pre-commit

$ pre-commit install --hook-type post-merge
pre-commit installed at .git/hooks/post-merge

$ pre-commit install --hook-type pre-merge-commit
pre-commit installed at .git/hooks/pre-merge-commit

You could also run pre-commit install --hook-type pre-push to register pre-push hooks.

Run against all the files

“it’s usually a good idea to run the hooks against all of the files when adding new hooks (usually pre-commit will only run on the changed files during git hooks)”

pre-commit run --all-files

Run for changed files only in CI

Please check also this official doc.

git fetch origin
pre-commit run --from-ref origin/${pullrequest_target_branch_name} --to-ref HEAD

When using mypy, it would be better to use mypy against to all files in the project, but not the changed one only.

Git commit

Each time we use git commit to stage some files, these files will be sent to pre-commit to be checked against to the hooks defined in .pre-commit-config.yaml.

Temporarily disabling hooks

The official doc gives the example how to disable explicitly hooks by hooks’ ids: SKIP=flake8 git commit -m "foo", but if you want to disable completely all the hooks, an easy way might be found here by using git commit --no-verify or its shortcut git commit -n. If you use pre-commit during push, you can disable pre-commit during push by git push --no-verify or git push -n.

Automatically enabling pre-commit on repositories

https://pre-commit.com/#automatically-enabling-pre-commit-on-repositories

Usage in continuous integration

https://pre-commit.com/#usage-in-continuous-integration

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.

Already on GitHub?
Sign in
to your account


Closed

MrJoosh opened this issue

Aug 3, 2020

· 33 comments

Comments

@MrJoosh

When using PyLint i can put a comment after an import to ignore an import failure of that specific import as below:

from airflow.models import BaseOperator  # pylint: disable=import-error

I can’t find a way to do the same using PyLance. I’ve seen information on how to do it at a project/workspace level, but it’s something that i’d only like to do for specific edge cases (such as for airflow imports on a windows machine without airflow installed)

Apologies if this is already implemented and i’ve missed the docs on the syntax!

malthe, bernardelli, mg-christian-axelsson, Yakuza-UA, jdix531, pikeas, hkennyv, GabDug, submagr, garylavayou, and 35 more reacted with thumbs up emoji

@erictraut

For type checkers like Pylance, the standard way to suppress diagnostics is to use a «# type: ignore» comment at the end of the line. This behavior is documented in PEP 484.

tcdejong, cnaccio, trallnag, chiafeilee, Nikronic, bernardelli, nixon-voxell, ppcamp, Zaoyee, miguelsmatos, and 43 more reacted with thumbs up emoji

@erictraut

You can also disable or enable individual diagnostic rules within a file by placing a comment at the top of the file:

# pyright: reportMissingImports=false, reportUnusedVariable=warning, reportUntypedBaseClass=error
d00astro, mdko, miguelsmatos, ablascobraso, Goblin80, GabDug, garylavayou, diivi, penut85420, oleg-butko, and 15 more reacted with thumbs up emoji
dinhanhthi reacted with hooray emoji
Kei-sss reacted with heart emoji

@MrJoosh

So the correct way to suppress the improt warnings would be to do the below:
from airflow import DAG # type: ignore
If so, apologies!

@erictraut

Yes, that will suppress all Pylance diagnostics on that line.

Or if you’d prefer to suppress a diagnostic rule throughout the file, you could use:

# pyright: reportMissingImports=false
MrJoosh, wdscxsj, Nikronic, skarfie123, GabDug, vipulnj, diivi, rakeshku93, mirkobrombin, shiftybit, and 3 more reacted with thumbs up emoji

@MrJoosh

Ok, that’s great — thanks.
I didn’t know if there was a feature for suppressing the exact warning/error on a particular line, but for my particular use case this covers it.
Thanks again for your help!
Josh

@ggregoire

For type checkers like Pylance, the standard way to suppress diagnostics is to use a «# type: ignore» comment at the end of the line. This behavior is documented in PEP 484.

I thought it was # noqa, at least it’s what I’ve seen in a lot of projects (but I’m not that familiar with Python).

Perhaps it could help to have a note in the README? (even if it’s from a standard)

Anyway, thanks! Very useful!

@jakebailey

noqa is something we supported in MPLS as it was used by a few linters, but as Pylance is essentially a type checker, it started with # type: ignore comments and we haven’t ported over # noqa support quite yet (but is in the backlog to get to parity).

@erictraut

noqa is not part of the Python standard whereas # type: ignore is. I think we should stick to standards and encourage their use.

@maresb

Are there any plans to implement line-level suppression of individual error types?

I imagine something like

# type: reportMissingImports=false, reportUnusedVariable=warning

which would behave like # pyright: but for just that line.

rben01, mg-christian-axelsson, Yakuza-UA, dnmiller, sernst, pikeas, SubaruArai, NowanIlfideme, petterssonandreas, sdwilsh, and 30 more reacted with thumbs up emoji

@jakebailey

We currently don’t have plans for this, but I will reopen this issue and mark it as an enhancement so that others can vote on the issue if they want to see something like this implemented.

@Yakuza-UA

Is this 100% supported?

I’ve just tried to add # type: ignore at the end of the line on which Pylance reports MissingImport error, but it is still reported. Tried to reload VSCode, but it didn’t help. Pylance is installed on remote development server and I use Remote SSH to connect.

VSCode version 1.55.0
Pylance 2021.3.4

@jakebailey

If that doesn’t work, then that’s a bug with our current ignore support and would be worth a new issue if you can reproduce it.

@pikeas

+1 for line level suppression of individual errors!

For compatibility with mypy, please consider # type: ignore[code1, code2, ...], skipping unrecognized codes. This would enable users to do things like # type: ignore[arg-type, reportUnknownArgumentType].

If there’s a proposal to unify error codes with MyPy, that would also be great!

@guhcampos

Let’me add to the discussion with a current use case of mine:

    try:
        import orjson as json  
    except ImportError:
        import json  
    return json.dumps

Pylance complains that:

  1. orjson cannot be found if it’s not installed (which is kind of the point here, detect if it’s there and use it if it is) and
  2. that json is being redefined, which is also kind of the point

Adding a # type: ignore in the second sentence works, even if it’s a weird, non descriptive message — there’s nothing wrong with the typing here — but adding that commend to the first sentence gives me a unused 'type: ignore' comment complaint instead, so I’m stuck with either =/

@erictraut

Using import statements within a try and except block is not uncommon in Python, but this construct does not work well with static type checking. If you cannot find a better alternative to this construct, here’s another approach you can use to help static type checkers like pylance/pyright:

from typing import TYPE_CHECKING

try:
    import orjson as json
except ImportError:
    if not TYPE_CHECKING:
        import json

By including the if not TYPE_CHECKING, you can tell the static type checker that it should assume that you’re going to import the library within the try block.

@guhcampos

That’s a good tip, in particular because adding support for noqa here would not fix the problem on the mypy side (which is what my CI is running) so the pattern works even better.

I ended up returning from inside the try/except because I needed a closure to decode orjson’s dumps anyway, which also works if anyone is curious

@wb8tyw

With pylint and other linters that I use, you can use a comment on a previous line to just suppress one specific instance of a report.

Putting a comment at the end of a line will likely cause a line to then trip a line-too-long issue.

    # pylint: disable=broad-except
    except Exception as err:

This does not seem to work with pylance. Disabling a setting is global no matter where I put it, at the end of a line, above the line, or below the line.

Event this below does not work because the true setting causes these issues to be flagged.

pyright: reportMissingModuleSource=false

from six.moves.configparser import ConfigParser
from six.moves.configparser import DuplicateSectionError

pyright: reportMissingModuleSource=true

I am converting a project from python2/GTK-2 to python3/gtk-3 and need to be able to disable linting messages for issues that I will be fixing later so that I can get reports of more important issues or new issues that are being introduced in the conversion.

@erictraut

Pylance isn’t a «linter», at least by the traditional definition. Linters are concerned with code style issues like line length and naming conventions. Pylance doesn’t concern itself with code style issues. If you want to enforce those types of issues, then pylint is a good choice.

Pylance is built on pyright, which is a static type checker, so it follows the type checking standards laid out in PEP 484 and related specifications. PEP 484 specifies that # type: ignore at the end of a line is the way to silence type-related errors.

I’ll point out that there’s almost always a better way to eliminate a type error than silencing it with a # type: ignore comment. Pyright even supports a mode where # type: ignore is disallowed, and I use that mode in my team’s code base because I don’t want anyone to use # type: ignore. IMO, it should be a choice of last resort when all other options fail. If you need suggestions for how to address specific type errors, feel free to post questions in the discussion section, and we can provide advice.

@wb8tyw

The «#type: ignore» at the end of the line is not working on a single line only as described earlier in this issue.

It is operating on the entire file no matter where the comment is.

This is on the pylance bundled with Visual Studio code version 1.58.2 using the python program.

I know there is almost always a better way. In my case it would cause to much of a delay to do that as many of the issues will be resolved either by dropping python2 support or by future planed changes.

@erictraut

The «#type: ignore» at the end of the line is not working on a single line only as described earlier in this issue.

I’m not able to repro this. Could you provide a code sample that exhibits this bug? Or if the code in question is available in a public github repo, I can take a look at it there.

Another technique you might find useful… you can disable specific classes of errors at the project or file level.

@SaidBySolo

+1 for this feature When using a third-party library, i want that line to be ignored by pyright if it is partially Unknown

@jakebailey
jakebailey

changed the title
Allow line level suppression of import errors

Allow line level suppression of errors

Aug 16, 2021

@joaompinto

Are there any plans to implement line-level suppression of individual error types?

I imagine something like

# type: reportMissingImports=false, reportUnusedVariable=warning

which would behave like # pyright: but for just that line.

Can you provide an example of a line of code where you would find valuable to ignore a specific type check error while still reporting others ?

@jakebailey
jakebailey

changed the title
Allow line level suppression of errors

Allow line level suppression of specific errors

Aug 27, 2021

@javl

Can you provide an example of a line of code where you would find valuable to ignore a specific type check error while still reporting others ?

Not the OP, but I have an example:
I’m working on an addon for Blender, using VS Code. There is a series of modules I’m importing. This a small part of them:

import bpy
from mathutils import Vector, Color
from bpy.types import Mesh, MeshPolygon, Image

These are specific for Blender. When using the python interpreter that comes with Blender, and setting some extra module paths using python.analysis.extraPaths I can get the bpy import to work, but mathutils and bpy.types are build-in modules which somehow don’t get recognized.
As my code works fine, I’d love to be able to hide those specifc import errors. But I still want to be able to get errors when importing other modules.

Kind of a niche use case, but still a valid one in my opinion (and I can imagine other users having similar problems every now and then).

@erictraut

@javl, you can place a # type: ignore at the end of a particular import statement. This will suppress the import error(s) for only that statement, not other import statements within the file. Does that address your use case?

Another potential workaround that you might want to consider is to use local type stub files. If you create a file called typings/mathutils/__init__.pyi and include declarations for the symbols (e.g., the Vector and Color classes), pylance will use the type stub to resolve the import. You can even include a general «catch-all» type definition in the type stub file that will handle all symbols (albeit without specific type details):

from typing import Any
def __getattr__(name: str) -> Any: ...

@javl

@erictraut
Haha, turns out I misunderstood. I thought the type in # type: ignore was supposed to be an error/warning type (something like reportMissingImports, kind of how you can specify specific errors to ignore using eslint when using JavaScript.
Just adding the line with type works like a charm!

@adonig

I found out you can combine type: ignore and pylint disable=... with a # symbol:

try:
    from .local import *  # type: ignore # pylint: disable=wildcard-import
except ImportError:
    pass

The # type: ignore has to come first though. It doesn’t work the other way around.

@garfieldnate

I’m trying to get rid of an «unused import» warning on a single line, and none of the solutions mentioned above work:

  • # type: ignore does not work
  • # pyright: reportUnusedImport=false at the top of the file or on the same line does not work

I have noqa: F401 to handle Flake8, but must live with the pylance warning for now.

The reason I need this is because I’m using grequests, which monkey-patches SSL and therefore must be imported before any other module imports SSL. In my Flask application, the best way to guarantee this is to place the import in my config.py (or wsgi.py or other early-loaded module).

image

@erictraut

The message "grequests" is not accessed is not a warning or an error, so there is nothing to suppress. That message is shown when you hover over the «grayed-out» text that indicates the import is not accessed. It indicates why the module name was grayed out.

If it really bothers you that the text is grayed out, you could add a meaningless reference to the module, perhaps under an if TYPE_CHECKING: conditional so it doesn’t execute at runtime.

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    # Reference grequests so it is not flagged as unused
    print(grequests)

@erictraut

SaidBySolo, bersbersbers, daudef, AjayKMehta, jrc, asears, fuergaosi233, warrenseine, Benature, and joaopalmeiro reacted with thumbs up emoji
maresb, SaidBySolo, bersbersbers, garfieldnate, daudef, asears, mew1033, briancline, M4a1x, and warrenseine reacted with hooray emoji
SaidBySolo, daudef, lcnittl, asears, and briancline reacted with heart emoji

@SaidBySolo

I’ve been waiting for this to be released. Very good news!

Thanks for your hard work!

@bschnurr

This issue has been fixed in version 2022.3.4, which we’ve just released. You can find the changelog here: CHANGELOG.md

@adam-grant-hendry

For type checkers like Pylance, the standard way to suppress diagnostics is to use a «# type: ignore» comment at the end of the line. This behavior is documented in PEP 484.

I thought it was # noqa, at least it’s what I’ve seen in a lot of projects (but I’m not that familiar with Python).

Perhaps it could help to have a note in the README? (even if it’s from a standard)

Anyway, thanks! Very useful!

# noqa is flake8

@adam-grant-hendry

> This is now implemented in the form of # pyright: ignore[xxx] comments. For full documentation, refer to https://github.com/microsoft/pyright/blob/main/docs/comments.md#line-level-diagnostic-suppression.

It still does not support multi-comment ignores like most other linters. e.g.

# pyright: ignore[reportPrivateImportUsage]  # noqa: F401

I’ll open a bug report.

Actually, the ignore works if on the same line as the offending line. Sometimes black will split a long line with a tuple and move the comment to the line with the closing ).

logo

Start with silence, not with noise. But do start!

Project/Repo:

MIT License
Supported Python Versions
CalVer 2022.1025
PyPI Version
PyPI Downloads

Code Quality/CI:

GitHub CI Status
GitLab CI Status
Type Checked with mypy
Code Coverage
Code Style: sjfmt

Name role since until
Manuel Barkhau (mbarkhau@gmail.com) author/maintainer 2020-06

Developer Ergonomics

The main issue with Pylint is developer ergonomics. The messages produced by Pylint can be valuable, but you have to put in some work before you can use it productively. If you have an established codebase, you’ll probably have to:

  • research configuration options
  • disable many unhelpful messages
  • blindly litter your code with pylint:disable comments.

Using Pylint-Ignore you can benefit from Pylint today. You won’t have to wade through endless message noise first, won’t have to spend time with configuration, you won’t have to change anything about your code. Even though you start with all messages ignored for existing code, you can benefit from Pylint right away for any new code you write.

What about the wall of messages for your existing code? Well, at least you can gradually improve your situation, as you have time for it. In other words, you may be dug into a hole right now, but at least you can stop digging yourself any deeper and gradually start climbing back out.

How it Works

The pylint-ignore command is a thin wrapper around the pylint package. You can get started immediately by running the following commands:

$ pip install pylint-ignore

Installing collected packages: astroid,isort,pylint,pylint-ignore
...
Successfully installed pylint-ignore-2020.1006

$ pylint-ignore src/

************* Module src/mymodule.py
src/mymodule.py:290:0: W0102: Dangerous default value sys.argv[1:] (builtins.list) as argument (dangerous-default-value)
...

-------------------------------------------------------------------
Your code has been rated at 9.92/10

$ echo $?     # exit status != 0
28

The pylint-ignore command reads a file called pylint-ignore.md, which you should keep as part of your repository. This file contains messages that should be ignored and it is automatically updated with new entries if you specify the --update-ignorefile parameter.

$ pylint-ignore src/ --update-ignorefile

-------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 9.92/10, +0.08)

$ echo $?     # exit status == 0
0

The original message no longer shows up in the output, and it is instead logged in the pylint-ignore.md, which will now look something like this:

...
## File src/mymodule.py - Line 290 - W0102 (dangerous-default-value)

- message: Dangerous default value sys.argv[1:] (builtins.list) as argument
- author : Manuel Barkhau <mbarkhau@gmail.com>
- date   : 2020-07-17T21:15:25

```
  289:
> 290: def main(args: Sequence[str] = sys.argv[1:]) -> ExitCode:
  291:     try:
```

Ideally, you should only do this once when you start to use Pylint, and going forward the file will only get smaller. As your time permits, the recommended approach to using pylint-ignore is the following:

  1. If a message refers to a valid issue (errors and warnings in particular), update your code so the issue is resolved.
  2. If a message is a false positive, add a comment of this form to your code:
    # pylint:disable=<symbol> ; explain why this is a false positive
  3. If it is a useless message (e.g. a whitespace rule that conflicts with the behaviour of your code formatter) which should always be ignored, then do so via your pylintrc or setup.cfg file.

In principal these are the same options you have when using Pylint by itself. For the above example, dangerous-default-value is a useful message in general) just not in this particular case. You might take the approach of option 2. and add a pylint:disable comment:

def main(args: Sequence[str] = sys.argv[1:]) -> ExitCode:
    # pylint:disable=dangerous-default-value;  args is not mutated
    # mypy prevents this because Sequence[str] does not support mutation
    try:

With this change, the next time you run pylint-ignore --update-ignorefile, the corresponding entry will disappear and the backlog will shrink.

CLI Usage

The pylint-ignore command has only two options, any other options on the command line are passed-through to pylint. For example, pylint-ignore --help will behave exactly the same as pylint --help. The options pylint-ignore provides are:

Usage: pylint-ignore [options]

Options:
  --ignorefile=<FILE>    Path to ignore file [default: pylint-ignore.md]
  --update-ignorefile    Update the ignorefile, adds new messages,
                         removes any messages that are no longer
                         emmitted by pylint (were fixed or disabled)

Normally the pylint-ignore command will not update the pylint-ignore.md file. This is appropriate for

  • Normal development if it’s your policy to not introduce any new issues.
  • CI/CD build systems, where you want to report any issues that were newly introduced.

If you fix an issue or explicitly disable a message, you can cleanup obsolete entries by adding the --update-ignorefile argument. For example:

$ pylint-ignore --update-ignorefile src/ test/

Caveat: If you change some code for which there is an entry in the pylint-ignore.md file, the entry may no longer be matched up with the message as it is generated by Pylint. Usually, changes in line numbers will be detected as long as the code itself did not change and your build will not fail if that is the case. Hopefully you will not feel the need to habitually use --update-ignorefile but you may need to use it occasionally, simply to refresh an existing entry that became invalid. You can of course also take such an occasion as an opportunity to deal with the underlying issue.

Integration with pre-commit

pylint-ignore can be used as a pre-commit hook, but inherits the limitations of pylint described in Pre-commit integration.

To include pylint-ignore as a pre-commit hook using the provided plugin, add the following entry to a .pre-commit-config.yaml file in the repository being linted:

  - repo: https://github.com/mbarkhau/pylint-ignore
    rev: "2021.1024"
    hooks:
      - id: pylint-ignore

If you find that pylint will only function correctly when run in the local Python environment (as described in Pre-commit integration) then the following local hook entry can instead be used for pylint-ignore:

repos:
  - repo: local
    hooks:
      - id: pylint-ignore
        name: pylint-ignore
        entry: pylint-ignore
        language: system
        types: [python]
        require_serial: true
        # args: [
        #   "--rcfile",
        #   "setup.cfg",
        #   "src/",
        #   "test/",
        #   "--ignore-paths",
        #   "scripts/,fixtures/,setup.py",
        # ]

The args: [...] property can be added to the entries as required.

To test you can use pre-commit run:

$ pre-commit run pylint-ignore --all-files

Configuration

Pylint’s behaviour can be configured in the usual way, see Command-line arguments and configuration files for details.

The pylint-ignore.md file

You can view an example file here: fixtures/pylint-ignore.md. You can consider this file as a backlog of possible issues. The entries are sorted first by category, i.e. errors and warnings first then by frequency. You can change the path/filename using the --ignorefile parameter: pylint-ignore --ignorefile=etc/pylint-backlog.md

The pylint-ignore.md file uses a bespoke format but it is valid markdown. This choice is primarily so that you can read it and review it more easily on platforms such as github/gitlab/bitbucket. You don’t have to edit the file and it is not a format that any other program has to parse, so I think this is a reasonable choice.

What does this approach solve, why not just use Pylint by itself?

Why use Pylint-Ignore

Problem 1: Noise

There is a reason flake8 is used so much more often than Pylint. The problem of «noise» is acknowledged early in the documentation of Pylint. In fact, the frustration of using Pylint is so obvious that it is even the topic of the project’s tag-line: «It’s not just a linter that annoys you!».

Pylint-Ignore doesn’t get rid of this noise of course, but it does put in a dedicated place, rather than Spam in your terminal. Each issue with your code is one entry in a file, rather than a line that you have to scan again and again.

Once you’ve established silence as your baseline, you can trust that you only have to deal with two states: OK and FAIL. This vastly reduces cognitive load for developers and makes it possible for you to integrate the linter into your CI system, even if you haven’t yet dealt with every last Pylint message.

Problem 2: Setup Cost

I won’t belabour this point, but it’s better if you can spend as little time as possible to just get started using a useful tool, rather than putting it off into the future, possibly indefinitely or only using it occasionally rather than making it a part of your regular workflow.

That being said, the sooner you take the time to pay down this setup cost, and to disable messages in your configuration that are actual noise, the more useful Pylint will be for you. Every useless message will increase the likelihood that you miss one of the more important messages.

Even if you’ve setup Pylint perfectly and are done with the initial cleanup of your codebase, there might be reason for you to continue to use Pylint-Ignore in your development workflow.

Problem 3: Diligence is Scarce

Without Pylint-Ignore, chances are, you (or your teammates) will be overzealous with the disable section of your configuration. Sooner or later, you will be short on time and effectively turn the linter off. Who will later know or care to look if the message was disabled because it is genuinely useless or if you just had other priorities at that moment? You can try to remind yourself to review things, you can add a TODO comment which you hopefully remember to grep for regularly, but there is a high chance that such good intentions will sooner or later go by the wayside.

With Pylint-Ignore, you have a dedicated file in your repository, which is more explicit and visible than the other options. The entries are ordered by importance, rather than appearing more or less randomly on your terminal. You can focus your diligence on other things, and deal with minor linting issues when you have time, or perhaps leave them open as a first contribution for a new member of your team, just so they can get used to your workflow.

Problem 4: Malicious Compliance

You may find some messages useful, but with an existing codebase, the work would be too much at once. You don’t want to disable it, but you don’t want to start with it enabled either. An example is perhaps the missing-function-docstring message. If you were to enabled it, you may find a pattern like this emerge:

def get_author_name() -> str:
    """Gets the author name and returns it as a string."""

In case it’s not obvious, the above doc-string is redundant, it adds no new information relative to what is already contained in the function name and types. In other words, the temptation is to pacify the linter by changing the code in ways that are at best a useless waste of time and at worst they are malicious and counterproductive.

You are in control, as you can just ignore and commit a change if you feel that ignoring the linter is justified by your current priorities. With Pylint-Ignore you don’t have to halt the train because a delicate flower fainted in wagon 2 fainted at the sight of oil spraying all over her luggage and make your journey on-time. The pylint-ignore.md will keep track of the issue and you can deal with it once you’ve arrived at the station, not while you’re running at full steam.

Problem 5: False Positives

While you can and should deal with most false positives using disable comments, there are some cases where you’d rather not do that and some cases where that isn’t even possible. For such edge cases, you can just permanently leave an entry in the pylint-ignore.md and still benefit from an otherwise useful message if new cases pop up.

Motivation/Why use Pylint

If you are not convinced of the usefulness of Pylint, linters, or static analysis in general (and perhaps think they are mostly make-work for people who are overly pedantic) let me show you what convinced me to use Pylint.

Dynamic Code and Scope Related Bugs

Some code may syntactically valid Python and will even execute without raising an error and yet is almost certainly not what the author intended. To be sure, if you have proper testing, you will catch such bugs, but even so, static analysis may pay its dues if it can help you catch such bugs more quickly.

def frobnicate(seq: Sequence[int]):
    total = 0
    for value in seq:
        total += value
    return value / len(seq)

The above code will «work», depending on how you call it, it won’t even throw an error and yet it is almost certainly not correct. Were the function longer than 5 lines, the bug would perhaps be less obvious. Just recently I spent at least an hour tracking down such a bug, which had made it into production. In any case, Pylint will report the following message for such code:

W0631: Using possibly undefined loop variable 'value'
  (undefined-loop-variable)

There are other messages, related to name shadowing and unused arguments that I have found to be particularly useful in that they have pointed to actual bugs, rather than «mere» preferences or «best-practices» according to the authors of Pylint.

Supporting Code Review

The perhaps most important aspect of a linter, whenever working with other people, is that the feedback related to mundane issues of code styling will come from a computer rather than from another person. This is a benefit, both the reviewer and to the author:

  • It is not fun to spend valuable time on giving repetitive feedback on mundane issues that can be automated.
  • It’s not fun, perhaps even embarrassing to have your own stupid mistakes be pointed out during review.

A linter also allows you to establish a rule that will end discussions about subjective style preferences. Everybody might agree that any particular style is stupid, but the endless discussion about code style is even more stupid. So, establish this rule: If it passes the linter, the discussion is over (except of course if the linter only passes because it was maliciously disabled). This is a similar argument as is made for the use of code formatters and its main value is that it allows you to focus your review time on the actual problem related to the code.

Catching Python Gotchas

Junior programmers and even (experienced programmers who are new to Python) may not be aware of common pitfalls of working with Python. They need every help they can get and you can look at static analysis as a form of codified knowledge and automated communication from experienced programmers. The code that you write may be perfect, but hell is other people, and Pylint can help to keep some minimum standards in your projects, even when you on-board new developers.

A Nudge to Improve

When you’re hacking away, focused entirely on solving the actual problem, getting the actual work™ done, you can end up with some code that may well work, may well pass all of your tests, may well be efficient and may even be (let’s just postulate for the sake of argument) optimal and correct. That still doesn’t mean, that anybody but the author can understand it.

R0902 Too many instance attributes (10/7) (too-many-instance-attributes)

Simple code metrics such as too-many-locals, too-many-branches, etc. may well be subjective, pedantic, paternalistic gamification nonsense, and of course such metrics should not be turned into targets. The question is, how do you use them. If they are triggering very often, then the threshold may well be too low and you should increase it to a tolerable level. If your code is perfect as it is, then there is no shame, perhaps it’s even a badge of honor to add a comment such as this:

class ToCoolForSchool():
    # pylint:disable=too-many-branches
    # behold this glorious perfection 🤘 — rockstar@unicorn.io
    ...

Such cases aside, a common reason for complicated code is that the author was too lazy didn’t have the time to re-factor their code so that others could also understand it. I would caution against code-golfing such cases, just to satisfy the linter. Just consider the message as a nudge to at least take a second look, to a least consider looking for obviously better ways to structure your code.

Alternatives

To the extent that the excessive noise of Pylint has scared you away from using it, I hope you will find pylint-ignore helpful to at least get started. Here is another approach you may prefer.

Selective Opt-In

An alternative approach is suggested by Itamar Turner-Trauring with «Why Pylint is both useful and unusable, and how you can actually use it» is in essence to do the following:

  • First, setup Pylint to do nothing: disable all messages
  • Selectively enable some checks and keep them if they are valuable
  • Repeat

Obviously, this is abbreviated, so I encourage you to read his article if selective whitelisting is a better approach for you. For me, this approach suffers from the diligence issue, as it requires you to revisit the configuration at least a few times, keep track of what you’ve found to be valuable and so you run a higher risk of neglecting it.

Automated disable comments

Another automated approach is to use pylint-silent. Be sure to use version control if you consider this approach.

Changelog for https://github.com/mbarkhau/pylint-ignore

2021.1024

  • Add/document support for pre-commit.
    Thank you James Quilty for this contribution github/issue/9
    github/pull/10 :heart:.

  • Fix github #11: MessagesHandlerMixIn has been removed in pylint 2.12

  • Breaking: Drop support for Python<3.7

  • Breaking: Drop support for pylint<2.4

    Due to internal API changes in pylint, older versions of pylint
    are no longer supported. If you need to use an older version of
    pylint, you will have to pin pylint-ignore==2021.1021.

2021.1020

  • Fix github #6: Issue with paths on windows.

Thank you davidsheldon

2021.1019

  • Fix github #5: Missing fixture files in source distribution
  • Fix github #4: Bug related to trailing-whitespace

Thank you @agraul (Alexander Graul) for finding these issues.

2021.1018

  • Fix github #2: Typerror

2020.1014

  • Fix gitlab #2: Bug related to invokation with invalid arguments (which caused the underlying pylint error to be hidden).

2020.1013

  • Fix: bugs related to multiprocessing on MacOSX and Windows

2020.1012

  • Fix: enable use of --jobs=<n> with n > 1

2020.1008 — 2020.1011

  • Fix: compatability with python 2.7 and pylint 1.9.5

2020.1007

  • Add overview section to pylint-ignore.md file.
  • Fix: Handling of issues not related to a specific file or line (e.g. duplicate-code across multiple files)
  • Fix: parsing of --ignorefile argument.
  • Fix: parsing of --jobs argument.
  • Allow ignore of (E) error messages again, prioritize entries, instead in ignorefile.

2020.1006

  • Don’t ignore messages of type (E) error, for probable bugs in the code

2020.1003

  • Initial release

Понравилась статья? Поделить с друзьями:
  • Pvz heroes ошибка подключения к сети как исправить
  • Pyinstaller matplotlib error
  • Pvz 2 error
  • Pyinstaller fatal error in launcher unable to create process using
  • Pvz 2 download failed because the resources could not be found как исправить