Pylint reporting Cyclic Import on file without imports - python

So Pylint (1.4.3) is reporting a Cyclic Import and it doesn't make much sense.
First of all, the file that's report has no import statements.
Second of all no files import the reference file. a __init__.py file loads configuration values from development_config (file in question) but no files
import said file.
So why is Pylint giving me this warning?
Pylint warning
************* Module heart_beat.development_config
R: 1, 0: Cyclic import (heart_beat -> heart_beat.views -> heart_beat.models) (cyclic-import)
R: 1, 0: Cyclic import (heart_beat -> heart_beat.views) (cyclic-import)
development_config
""" -------------------------- DATA BASE CONFINGURATION --------------------"""
SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/test.db'
SQLALCHEMY_ECHO = False
""" -------------------------- Flask Application Config --------------------"""
THREADS_PER_PAGE = 8
VERSION = "0.1"
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
#from register_assets import register_all
app = Flask(__name__, static_url_path='/static')
# the environment variable LIMBO_SETTINGS is set in runserver, run_unit_tests
# or limbo.wsgi.
def load_configs():
"""Take all configs found in development_config.py."""
app.config.from_pyfile("development_config.py", silent=False)
load_configs()
# global SQLAlchemy configuration
db = SQLAlchemy(app)
#Create and register all static asset bundles.
#register_all(app)
#NOTE: DON'T LISTEN TO YOUR IDE! heart_beat.views is used and required.
import heart_beat.views # views contains all URL routes, Importing sets routes.
def setup_db():
"""Database creation in a file rather then a statement for easier tests."""
db.create_all()
def teardown_db():
"""Database deletion in a file rather then a statement for easier tests."""
db.drop_all()
setup_db()
views.py
from flask import request
from . import app
from . import db
from . import models
from . import exceptions as ex
models.py
import datetime
from . import exceptions
from . import db
from . import app

I believe this is currently a bug in pylint. Things that require analysis over multiple modules (for example cyclic-import and duplicate-code detection) get thrown as refactors into the last-parsed module file. For me, this ended up being an empty __init__.py file that had both of these dropped into it.
Both of these refactor messages though contain the actual module names which are problematic:
For cyclic-import, the problematic modules are listed in the parenthesis
For duplicate-code, the problematic modules are listed on the following lines starting with ==
The grouping of these is not limited to the print-out of modules, it also effects the summary report % errors / warnings by module in which that final parsed file gets the counts for the refactors and none of the modules it actually concerns get any counts from them.

Related

Why do I get a flask weakref error when importing my SQLAlchemy db from a submodule?

I currently have a flask project set up as follows (I did make a few modifications here to try and get the smallest working example, so some of this may be changed slightly)
from extensions import db
def create_api():
# Create API app
api = Flask(__name__)
# Configure... the configs
api.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get("DB_URL", "default")
# Register information to run api
register_extensions(api)
register_models(api)
register_urls(api)
# Return the api object
return api
create_api().run()
My register extension and register models methods look as follows:
def register_models(api):
with api.app_context():
db.create_all()
def register_extensions(api):
db.init_app(api)
bcrypt.init_app(api)
The extension module:
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
# Establish SQLAlchemy Database extension
db = SQLAlchemy()
# Establish Bcrypt extension for hashing passwords
bcrypt = Bcrypt()
My project directory looks something like
-FSBS
-API
- __init__.py
- app.py
- extensions.py
- routes.py
Everything works great like this. However- If I change the import in app.py from
from extensions import db to from FSBS.API.extensions import db, all my attempts to use db start throwing "KeyError: <weakref at 0x00000251C4B1DAD0; to 'Flask' at 0x00000251C0ED3FA0>".
This is somewhat problematic because I would like to start refactoring my routes into a subfolder, where I have to use from FSBS.API.extensions import db.
Not only that, but I don't understand why this would make a difference, so any advice on solving this little puzzle would be greatly appriciated.
I've recently encountered the exact same problem while trying to put my routes into a submodule.
I can't explain why this happens, but from what I can tell, it has something to do with the Flask-SQLAlchemy version - I've initially tried running version 3.0.2.
What solved the problem for me was a downgrade to version 2.5.1

How to remove older css files after webassets rebuilds the scss for a python flask app?

I have a Python 3.9 flask app which uses the flask_assets library.
My flask init.py file looks like:
import logging
import os
from flask import Flask, request, current_app
from config import Config
from flask_assets import Environment
from app.utils.assets import bundles
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
assets = Environment(app)
assets.debug = True
assets.versions = 'timestamp'
# assets.cache = False
from app.main import bp as main_bp
app.register_blueprint(main_bp)
assets.register(bundles)
if not app.debug and not app.testing:
app.logger.setLevel(logging.INFO)
app.logger.info('Application starting.')
return app
Since flask_assets is built on top of webassets, I import the Environment and a css Bundle I created which compiles my scss code to css.
Here is how my Bundle looks like:
from flask_assets import Bundle
bundles = {
'css': Bundle (
'scss/_main.scss',
'scss/_base.scss',
'scss/_typography.scss',
'scss/_page_home.scss',
'scss/_page_technote.scss',
filters='pyscss',
depends=('**/*.scss'),
output='css/style.%(version)s.scss.css'
)
}
The problem I have:
Every time I make a change to my scss files, the css successfully rebuilds with a new version for cache busting. However, the older css files remain.
What's the best automatic way to remove them every time a rebuild happens? Is there any reason for keeping the older files?
Also - side question - is it possible for the Bundle object to automatically consider all files of certain type in a directory? Rather than me listing every file individually?
Here is how my files look like:
Thank you!
You can use rmtree. Even though I believe a better way exists, this does it.
from os.path import join
from shutil import rmtree
app = Flask(__name__)
rmtree(join(app.static_folder, 'css'), ignore_errors=True)
You can improve this by checking the file meta by date or similar to not delete the unchanged ones.

Retrieving config from a blueprint in Sanic app

I have a Sanic application, and want to retrieve app.config from a blueprint as it holds MONGO_URL, and I will pass it to a repository class from the blueprint.
However, I could not find how to get app.config in a blueprint. I have also checked Flask solutions, but they are not applicable to Sanic.
My app.py:
from sanic import Sanic
from routes.authentication import auth_route
from routes.user import user_route
app = Sanic(__name__)
app.blueprint(auth_route, url_prefix="/auth")
app.blueprint(user_route, url_prefix="/user")
app.config.from_envvar('TWEETBOX_CONFIG')
app.run(host='127.0.0.1', port=8000, debug=True)
My auth blueprint:
import jwt
from sanic import Blueprint
from sanic.response import json, redirect
from domain.user import User
from repository.user_repository import UserRepository
...
auth_route = Blueprint('authentication')
mongo_url = ?????
user_repository = UserRepository(mongo_url)
...
#auth_route.route('/signin')
async def redirect_user(request):
...
The Sanic way...
Inside a view method, you can access the app instance from the request object. And, therefore access your configuration.
#auth_route.route('/signin')
async def redirect_user(request):
configuration = request.app.config
2021-10-10 Update
There are two newer ways to get to the configuration values (or, perhaps more accuratlely getting the application instance from which you can get the configuration). The first version might be more on point to answering the question of how to get to the config from the blueprint. However, the second option is probably the preferred method since it is precisely intended for this kind of use.
Alternative #1
Blueprints have access to the Sanic applications they are attached to beginning with v21.3.
Therefore, if you have a blueprint object, you can trace that back to the application instance, and therefore also the config value.
app = Sanic("MyApp")
bp = Blueprint("MyBlueprint")
app.blueprint(bp)
assert bp.apps[0] is app
The Blueprint.apps property is a set because it is possible to attach a single blueprint to multiple applications.
Alternative #2
Sanic has a built-in method for retrieving an application instance from the global scope beginning in v20.12. This means that once an application has been instantiated, you can retrieve it using: Sanic.get_app().
app = Sanic("MyApp")
assert Sanic.get_app() is app
This method will only work if there is a single Sanic instance available. If you have multiple application instances, you will need to use the optional name argument:
app1 = Sanic("MyApp")
app2 = Sanic("MyOtherApp")
assert Sanic.get_app("MyApp") is app1
I would suggest a slightly different approach, based on the 12 Factor App (very interesting read which, among others, provides a nice guideline on how to protect and isolate your sensitive info).
The general idea is to place your sensitive and configuration variables in a file that is going to be gitignored and therefore will only be available locally.
I will try to present the method I tend to use in order to be as close as possible to the 12 Factor guidelines:
Create a .env file with your project variables in it:
MONGO_URL=http://no_peeking_this_is_secret:port/
SENSITIVE_PASSWORD=for_your_eyes_only
CONFIG_OPTION_1=config_this
DEBUG=True
...
(Important) Add .env and .env.* on your .gitignore file, thus protecting your sensitive info from been uploaded to GitHub.
Create an env.example (be careful not to name it with a . in the beginning, because it will get ignored).
In that file, you can put an example of the expected configuration in order to be reproducible by simply copy, paste, rename to .env.
In a file named settings.py, use decouple.config to read your config file into variables:
from decouple import config
MONGO_URL = config('MONGO_URL')
CONFIG_OPTION_1 = config('CONFIG_OPTION_1', default='')
DEBUG = config('DEBUG', cast=bool, default=True)
...
Now you can use these variables wherever is necessary for your implementation:
myblueprint.py:
import settings
...
auth_route = Blueprint('authentication')
mongo_url = settings.MONGO_URL
user_repository = UserRepository(mongo_url)
...
As a finisher, I would like to point out that this method is framework (and even language) agnostic so you can use it on Sanic as well as Flask and everywhere you need it!
I think you can create a config.py to save your configuration, just like
config.py
config = {
'MONGO_URL':'127.0.0.1:27017'
}
and use it in app.py
from config import config
mongo_url = config['MONGO_URL']
There is a variable named current_app in Flask. You can use current_app.config["MONGO_URL"].
But I am not familiar with Sanic.

Python module and __all__

I trying to understand how to manage module with __all. For example, I have following structured code:
main.py
|=> /database
|=> __init__.py
|=> engine (with variables engine, session, etc.)
now I want to be able to import session and engine instances directly from database module like:
from database import session
I tried to add line __all__ = ['session'] or __all__ = ['engine.session'] to __init__py but when I trying to do import I've got an exception AttributeError: 'modile' object has not attribute 'engine.session'.
Is there any way to achieve wanted behavior?
Listing names in __all__ does not, by itself, import items into a module. All it does is list names to import from that module if you used from database import * syntax.
Import session into database/__init__.py:
from .engine import session

GAE Webapp: the cost of importing a bunch of request handlers

My python GAE app's central application file looks like this:
import webapp2
import homepage
import user_auth
import user_confirm
import admin_user
import admin_config
import config
app = webapp2.WSGIApplication([
(user_auth.get_login_url(), user_auth.LoginHandler),
(user_auth.get_logout_url(), user_auth.LogoutHandler),
("/user/confirm", user_confirm.UserConfirmHandler),
("/admin/config", admin_config.AdminConfigHandler),
("/admin/user/add", admin_user.AdminAddUserHandler),
("/admin/user", admin_user.AdminUserHandler),
("/", homepage.HomepageHandler),
], debug=True)
As you can see, I must import a bunch of request handlers, but for each request, only one of them is used, the other imports are just useless!
That's a big waste of memory and performance because those unnecessary imports also import other things on their own. Does Google App Engine have some "caching" mechanism or something that makes these unnecessary imports negligible? I think not.
How can I avoid them? I just haven't found out the way to import 1 Request Handler per request. If I put all the routing to app.yaml, that would work the way I want, but it makes things complex because I must write app = webapp2.WSGIApplication(... for every request handler file and repeat those boring urls twice (both in the python file and in app.yaml).
Found the way here, already built into webapp2
http://webapp-improved.appspot.com/guide/routing.html#lazy-handlers

Categories