Pylint can't find SQLAlchemy query member

2020-05-12 11:45发布

问题:

I have a Flask (v0.10.1) application using Flask-SQLAlchemy (v2.0) and I'm trying to configure Pylint to check it. Running with Python 3.4.2.

First error was:

 Instance of 'SQLAlchemy' has no 'Table' member (no-member)

And I fixed this one ignoring the check for member attributes on SQLAlchemy:

ignored-classes=SQLAlchemy

But I'm having a problem with the query member on entities:

Class 'UserToken' has no 'query' member (no-member)

Is there any way to fix this issue without having to ignore no-member errors on every query call?


Flask bootstrap:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
db.init_app(app)
app.run()

UserToken entity:

from app import db

class UserToken(db.Model):
    user_token_id = db.Column(db.Integer, primary_key=True, index=True)
    token_auth = db.Column(db.String(64), unique=True, nullable=False, index=True)

The controller:

from entities import UserToken

token = UserToken.query.filter(
    UserToken.token_auth == token_hash,
).first()

回答1:

Solution

  1. pip install pylint-flask
  2. Load the installed plugin.

    For example, if you use VS code, please edit setting.json file as follows:

    "python.linting.pylintArgs": ["--load-plugins", "pylint-flask"]

Optional

If having other warnings, define remaining members in generated-members in pylintrc file.



回答2:

Any class you declare as inheriting from db.Model won't have query member until the code runs so Pylint can't detect it.

The workaround for this besides ignoring no-member errors on every query call is to add query on the generated-members list in a Pylint config file since it is a member that will only be created at runtime.

When you run Pylint, it will search for a configuration file as stated in its documentation:

You can specify a configuration file on the command line using the --rcfile option. Otherwise, Pylint searches for a configuration file in the following order and uses the first one it finds:

  1. pylintrc in the current working directory
  2. If the current working directory is in a Python module, Pylint searches up the hierarchy of Python modules until it finds a pylintrc file. This allows you to specify coding standards on a module-by-module basis. Of course, a directory is judged to be a Python module if it contains an __init__.py file
  3. The file named by environment variable PYLINTRC
  4. if you have a home directory which isn’t /root:
    1. .pylintrc in your home directory
    2. .config/pylintrc in your home directory
  5. /etc/pylintrc

So if you don't have a config and you want a system wide default config for pylint you can use pylint --generate-rcfile > /etc/pylintrc. This will generate a commented configuration file according to the current configuration (or the default if you don't have one) that you can edit to your preferences.

p.s.: generated-members on a config file is the right way to deal with this warning, as it's said by the commented config

  # List of members which are set dynamically and missed by pylint inference
  # system, and so shouldn't trigger E0201 when accessed. Python regular
  # expressions are accepted.


回答3:

I meet the same issue when using flask_sqlalchemy. my solution is:

pylint --generate-rcfile>~/.config/pylintrc

and then find the

ignored-modules

line, rewrite it to:

ignored-modules=flask_sqlalchemy

all E1101 errors are gone.

Remeber to read the comment:

# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.


回答4:

After trying a lot of these options, plugins and adding query and all. The only solution that erased those scoped_session errors was using:

  1. pylint --generate-rcfile > pylintrc
  2. look for the ignored_classes and add scoped_session after the comma, leave no space
  3. Run pylint on your module again.


回答5:

In the first solution in this page, following needs to be updated. Its a typo issue,

Instead of "pylint_flask" in this settings.json parameter(in this line: "python.linting.pylintArgs": ["--load-plugins", "pylint_flask"]) it should be "python.linting.pylintArgs": ["--load-plugins", "pylint-flask"]).



回答6:

Here's a version of joeforker's answer that dynamically adds all public methods from the Session object back into a scoped_session's locals at lint-time, instead of hardcoding a few well-known method names.

Define {path}/{to}/pylintplugins.py:

import sys

from astroid import MANAGER, scoped_nodes
from astroid.builder import AstroidBuilder
from sqlalchemy.orm import Session


def register(_linter):
    pass

def transform(cls):
    if cls.name == 'scoped_session':
        builder = AstroidBuilder(MANAGER)
        module_node = builder.module_build(sys.modules[Session.__module__])
        session_cls_node = [
            c for c in module_node.get_children()
            if getattr(c, "type", None) == "class" and c.name == Session.__name__
        ][0]

        for prop in Session.public_methods:
            cls.locals[prop] = [
                c for c in session_cls_node.get_children() 
                if getattr(c, "type", None) == "method" and c.name == prop
            ]

MANAGER.register_transform(scoped_nodes.Class, transform)

And in your .pylintrc file:

load-plugins={path}.{to}.pylintplugins


回答7:

The one that worked for me was switching to flake8 python linter. Below are the steps:

  1. Open VSCode and run Ctrl+shift+P(For Windows Users)
  2. In the VSCode Search prompt, type Python:Select Linter. You will see a list of all Linters and select flake8.
  3. If you do not have flake8 installed as a VScode extension for pylint, it will prompt you to install it. Proceed and install it.


回答8:

Use pylint plugin pylint-flask-sqlalchemy

pip install pylint_flask_sqlalchemy

And in your settings.json of VisualCode

"python.linting.pylintArgs": ["--load-plugins", "pylint_flask_sqlalcheny"]


回答9:

Another alternative is to add scoped_session to the list of ignored classes:

# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=scoped_session


回答10:

After a lot of investigation I was not able to make Pylint to understand this member, so I just added query to the generated-members list and the check is ignored.

It's not a perfect solution but it works.



回答11:

This is how I'm dealing with the issue for scoped_session. Trivial to extend to check for more cls names with SQLAlchemy attributes.

from astroid import MANAGER
from astroid import scoped_nodes

def register(_linter):
    pass

def transform(cls):
    if cls.name == 'scoped_session':
        for prop in ['add', 'delete', 'query', 'commit', 'rollback']:
            cls.locals[prop] = [scoped_nodes.Function(prop, None)]

MANAGER.register_transform(scoped_nodes.Class, transform)

Adapted from https://docs.pylint.org/en/1.6.0/plugins.html . Then make sure pylint loads your plugin.

pylint -E --load-plugins warning_plugin Lib/warnings.py

(or load it in pylintrc)



回答12:

The one that worked for me was switching to flake8 python linter. Below are the steps:

  1. Open VSCode and run Ctrl+Shift+P (for Windows Users)

  2. In the VSCode Search prompt, type Python:Select Linter. You will see a list of all Linters and select flake8.

  3. If you do not have flake8 installed as a VScode extension for pylint, it will prompt you to install it. Proceed and install it.