19

Using flask's app.logger member functions (such as app.logger.error) causes pylint to report E1101 (no-member) errors, even though these members of app.logger are defined at runtime.

This can be reproduced by using the following files:

app.py

import flask
app = flask.Flask(__name__)

@app.route('/')
def say_hello():
    app.logger.debug('A debug message')
    app.logger.error('An error message')
    return 'hello'

requirements.txt

pylint==2.1.0
Flask==1.0.2

Sample commands for reproducing the issue, using virtualenv:

(Python 3.5 is used here, but the issue is not specific to that version)

virtualenv --python=python3.5 env
source env/bin/activate
pip install pip==18.0
pip install -r requirements.txt

And finally, running pylint:

pylint -E app

Returns these errors:

************* Module app
app.py:9:4: E1101: Method 'logger' has no 'debug' member (no-member)
app.py:10:4: E1101: Method 'logger' has no 'error' member (no-member)

Is there a good way to avoid these false positives?

Odysseas
  • 995
  • 9
  • 21

5 Answers5

21

Use create_logger instead.

from flask import Flask
from flask.logging import create_logger

APP = Flask(__name__)
LOG = create_logger(APP)


@APP.route('/')
def say_hello():
    LOG.debug('A debug message')
    LOG.error('An error message')
    return 'hello'
Yan QiDong
  • 3,696
  • 1
  • 24
  • 25
4

Another solution, that worked for me and does not include writing an extra plugin is to set generated-members in my .pylintrc:

...
[TYPECHECK]
generated-members=app.logger
...
DiKorsch
  • 1,240
  • 9
  • 20
3

A solution to prevent these false positives, via pylint plugins:

pylintplugins.py

import sys

from astroid import MANAGER, scoped_nodes, extract_node
from astroid.builder import AstroidBuilder


def register(_linter):
    pass


def transform(f):
    if f.name == 'logger':
        for prop in ['debug', 'info', 'warning', 'error', 'addHandler']:
            f.instance_attrs[prop] = extract_node('def {name}(arg): return'.format(name=prop))


MANAGER.register_transform(scoped_nodes.FunctionDef, transform)

This workaround prevents linting errors on app.logger.debug, app.logger.info, app.logger.warning, app.logger.error and app.logger.addHandler.

In order to be used, the pylintplugins.py file needs to be loaded using the --load-plugins command line option:

PYTHONPATH="." pylint -E app --load-plugins pylintplugins

or by including the following line in the pylintrc configuration file:

load-plugins=pylintplugins
Odysseas
  • 995
  • 9
  • 21
2

Also note that if you are importing app via another python file (such as a view file when using Blueprints):

  1. If you import app like this, you will get lint errors on app.logger.info:

    from myapp import app

  2. If you import app like this, you won't get lint errors on app.logger.info:

    from flask import current_app as app

From the documentation:

Flask solves this issue with the application context. Rather than referring to an app directly, you use the the current_app proxy, which points to the application handling the current activity.

I don't have a good answer as to why, but it works for me using pylint==2.2.2 on python3.6.6. As always, your milage may vary.

Community
  • 1
  • 1
Niklas B
  • 1,839
  • 18
  • 36
2

Expanding on the answer from Yan QiDong:

You can also create the logger yourself:

from flask import Flask
from flask.logging import create_logger

app = Flask(__name__)
app.logger = create_logger(app)

This way you can work as usual with app.logger.warning("Starting the HTTP server...") and pylint won't complain.

Otherwise you could invoke it like pylint --disable=E1101 src/, but this means you are happy to ignore ALL those E1101 errors.

TPPZ
  • 4,447
  • 10
  • 61
  • 106