1

For example, let's say I have three Python files; one file is the init.py which has routines for creating the namespaces of my Flask API and initializing the Flask app:

from flask import Flask
app = Flask(...)
... initialize the namespace etc. ...

... and another Python file with the definitions of the API Resource subclasses that exist in the enpoint.py file. It contains a few Python classes that make use of decorators from Flask-RESTX to wire up the endpoints:

@namespace.route("/path")
class EndpointApi:
    def get():
        ...

The third file is main.py which simply starts the Flask server running. Unfortunately for me though, it contains an import which is flagged by pylint.

My app is working fine, but when I run pylint, it tells me there are unused imports. If I remove the imports, then the logic in the decorator that adds the route to the Flask API does not execute, and the result is that the endpoint is no longer added to the API.

Is there some way to add a class file (like endpoints.py) without importing it? I want pylint to stop warning me about unused imports, when clearly I'm using the decorator to call some global function that adds the API Resource handlers to Flask.

Sure, I could ignore the pylint error with a comment, but is there a better way? I am truly disgusted with placing a comment on every line of an import statement which I'm sure is not an "unused-import" (I have about 30).

Obviously, I could just refactor the decorator pattern into its constituent parts, and extract the relevant code to be included inside the main.py file. The equivalent code would look like this in main.py:

from endpoint import EndpointApi
EndpointApi = namespace.route("/path")(EndpointApi)

This is exactly the same code that's run in the decorator, so pylint considers my EndpointApi to be unused even though the decorator is using it to append a "/path" route to the namespace. Removing the decorator and adding the equivalent code to main.py decreases maintainability because now the relevant parts of EndpointApi are in two different files instead of all being defined one.

Edit

No, from endpoint import * makes it worse:

main.py:3:0: W0614: Unused import(s) EndpointApi, Resource and ns from wildcard import of endpoint (unused-wildcard-import)

Minimal example

flask-restx-hello $ pylint *py; for x in *py; do echo $x; cat $x; done
************* Module main
main.py:3:0: W0611: Unused EndpointApi imported from endpoint (unused-import)

------------------------------------------------------------------
Your code has been rated at 9.29/10 (previous run: 7.86/10, +1.43)

endpoint.py
"""docstring"""
from flask_restx import Resource

from init import ns


@ns.route('/hello')
class EndpointApi(Resource):
    """docstring"""

    def get(self):
        """docstring"""

        return {'hello': 'world'}

init.py
"""docstring"""
from flask import Flask
from flask_restx import Api

app = Flask(__name__)
api = Api(app)
ns = api.namespace('sick', description='crazy', path='/root/haha')

main.py
"""docstring"""
from init import app
from endpoint import EndpointApi

if __name__ == '__main__':
    app.run(debug=True)

$ cat requirements.txt
aniso8601==9.0.1
attrs==22.1.0
click==8.0.4
dataclasses==0.8
Flask==2.0.3
flask-restx==0.5.1
importlib-metadata==4.8.3
itsdangerous==2.0.1
Jinja2==3.0.3
jsonschema==4.0.0
MarkupSafe==2.0.1
pkg-resources==0.0.0
pyrsistent==0.18.0
pytz==2022.2.1
six==1.16.0
typing-extensions==4.1.1
Werkzeug==2.0.3
zipp==3.6.0
activedecay
  • 10,129
  • 5
  • 47
  • 71
  • Just disable the rule for that whole block of imports then: https://pylint.pycqa.org/en/latest/user_guide/messages/message_control.html?highlight=disable#block-disables – jonrsharpe Sep 23 '22 at 19:53
  • Could you provide a [mre]? This may or may not be a pylint error. – Sören Sep 23 '22 at 19:56
  • I assume that your main script does not actually reference `EndpointApi` or `AnotherEndpoint` - so specifically naming those items is indeed pointless. I assume that just `import endpoints` would also generate an "unused import" warning - so perhaps try `import endpoints as _`? – jasonharper Sep 23 '22 at 19:58
  • @Sören If I remove main.py's `from endpoint import EndpointApi` then there are no api endpoints in flask. The pylint warning does not detect when a global variable is modified in a decorator. – activedecay Sep 23 '22 at 20:49

2 Answers2

1

Pylint isn't wrong. You aren't using endpoint anywhere in main.py. The only reason you're importing endpoint is to execute the decorator. Which is fine, but there is no way for pylint to know that.

In this case, it's ok to ignore the warning.

Sören
  • 1,803
  • 2
  • 16
  • 23
  • It's not OK to ignore the warnings where I work with other folks who don't understand this subtle behavior and delete unused-imports instead of parsing the code in their head and/or testing/checking their changes. Of course we understand this now, but adding a comment to ignore warnings on 30+ imports is not playing nice with others and is unclean and potentially disastrous. Pylint *is* wrong because the import runs a bit of code that effects a global which ends up being used in the final program; we clearly cannot simply delete the code flagged as unused-import. – activedecay Sep 25 '22 at 14:02
  • First of all: pylint is a linter. Some false positives have to be expected. It even says so in the readme: "Pylint isn’t smarter than you: it may warn you about things that you have conscientiously done or check for some things that you don’t care about." – Sören Sep 25 '22 at 16:23
  • Second of all: The warning isn't wrong. You are importing EndpointApi in main.py, you aren't using it in main.py. That's all the warning is telling you. – Sören Sep 25 '22 at 16:23
  • Third: The only reason you're doing the import is because you want the side effect of the import. That alone is a code smell. You've weighed that against the solution without the decorator, you prefer your code as-is (and I agree with you on that). That's ok, but that's an indicator that this requires a big fat comment WHY this is the correct thing to do. – Sören Sep 25 '22 at 16:23
  • Fourth: If you're still worried about other devs removing the imports (good thing that you are) - write a test! That's what CI systems are for! – Sören Sep 25 '22 at 16:23
  • Fifth: And now that you've done your due diligence it's up to you: Do you keep the noise, or do you squelch the warnings? – Sören Sep 25 '22 at 16:24
  • 1
    100% agree with everything you said, and thanks for your comments. You understand what I'm shooting for perfectly well, and you understand the ecosystem we live in with our programming compadres -- no code is safe from change without a test case. I think there's a refactor I can perform using the zoo class in the example Flask_RESTX repository; they don't have unused import warnings from what I can tell using my brain-compiler. Maybe we can conclude that it's best to refactor the code to eliminate the code smells, or write a test to cover the endpoints. Cheers – activedecay Sep 26 '22 at 18:16
0

If you don't want to disable each of the import errors on every single link like this:

    # pylint: disable=no-name-in-module

you could use:

    # pylint: disable=import-error

Commenting this once in the file will disable pylint import errors for the entire file.

Additionally, this post on stack overflow may help: Is it possible to ignore one single specific line with Pylint?

For more info on W0611 pylint unused imports: https://pylint.pycqa.org/en/latest/user_guide/messages/warning/unused-import.html

Kelsey
  • 174
  • 14
  • disabling the warning is easy, but i was wondering if there's something else i can do in the language to still have the class files imported and not create more pylint errors at the same time – activedecay Sep 23 '22 at 20:06
  • 1
    Isn't that kind of treating the symptom and not the problem? – Rusty Weber Sep 23 '22 at 22:21