Skip to content

Commit

Permalink
Split redash/__init__.py to prevent import time side-effects.
Browse files Browse the repository at this point in the history
Refs #3569 and #3466.
  • Loading branch information
jezdez committed Mar 18, 2019
1 parent a8cb709 commit 544af1c
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 110 deletions.
92 changes: 13 additions & 79 deletions redash/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import sys
import logging
import urlparse
import os
import sys
import urllib
import urlparse

import redis
from flask import Flask, current_app
from flask_sslify import SSLify
from werkzeug.contrib.fixers import ProxyFix
from werkzeug.routing import BaseConverter
from statsd import StatsClient
from flask_mail import Mail
from flask_limiter import Limiter
from flask_limiter.util import get_ipaddr
from flask_migrate import Migrate
from statsd import StatsClient
from werkzeug.local import LocalProxy

from redash import settings
from redash.query_runner import import_query_runners
from redash.destinations import import_destinations

from . import settings

__version__ = '7.0.0-beta'


import os
if os.environ.get("REMOTE_DEBUG"):
import ptvsd
ptvsd.enable_attach(address=('0.0.0.0', 5678))
Expand All @@ -37,10 +31,8 @@ def setup_logging():

# Make noisy libraries less noisy
if settings.LOG_LEVEL != "DEBUG":
logging.getLogger("passlib").setLevel("ERROR")
logging.getLogger("requests.packages.urllib3").setLevel("ERROR")
logging.getLogger("snowflake.connector").setLevel("ERROR")
logging.getLogger('apiclient').setLevel("ERROR")
for name in ["passlib", "requests.packages.urllib3", "snowflake.connector", "apiclient"]:
logging.getLogger(name).setLevel("ERROR")


def create_redis_connection():
Expand Down Expand Up @@ -68,71 +60,13 @@ def create_redis_connection():


setup_logging()
redis_connection = create_redis_connection()

mail = Mail()
migrate = Migrate()
mail.init_mail(settings.all_settings())
statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)
limiter = Limiter(key_func=get_ipaddr, storage_uri=settings.LIMITER_STORAGE)

import_query_runners(settings.QUERY_RUNNERS)
import_destinations(settings.DESTINATIONS)
redis_connection = LocalProxy(create_redis_connection)

from redash.version_check import reset_new_version_status
reset_new_version_status()
mail = LocalProxy(Mail)

migrate = LocalProxy(Migrate)

class SlugConverter(BaseConverter):
def to_python(self, value):
# This is ay workaround for when we enable multi-org and some files are being called by the index rule:
# for path in settings.STATIC_ASSETS_PATHS:
# full_path = safe_join(path, value)
# if os.path.isfile(full_path):
# raise ValidationError()

return value

def to_url(self, value):
return value


def create_app():
from redash import authentication, extensions, handlers
from redash.handlers.webpack import configure_webpack
from redash.handlers import chrome_logger
from redash.models import db, users
from redash.metrics.request import provision_app
from redash.utils import sentry

sentry.init()

app = Flask(__name__,
template_folder=settings.STATIC_ASSETS_PATH,
static_folder=settings.STATIC_ASSETS_PATH,
static_path='/static')

# Make sure we get the right referral address even behind proxies like nginx.
app.wsgi_app = ProxyFix(app.wsgi_app, settings.PROXIES_COUNT)
app.url_map.converters['org_slug'] = SlugConverter

if settings.ENFORCE_HTTPS:
SSLify(app, skips=['ping'])

# configure our database
app.config['SQLALCHEMY_DATABASE_URI'] = settings.SQLALCHEMY_DATABASE_URI
app.config.update(settings.all_settings())

provision_app(app)
db.init_app(app)
migrate.init_app(app, db)
mail.init_app(app)
authentication.init_app(app)
limiter.init_app(app)
handlers.init_app(app)
configure_webpack(app)
extensions.init_extensions(app)
chrome_logger.init_app(app)
users.init_app(app)
statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)

return app
limiter = Limiter(key_func=get_ipaddr, storage_uri=settings.LIMITER_STORAGE)
62 changes: 62 additions & 0 deletions redash/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from flask import Flask
from flask_sslify import SSLify
from werkzeug.contrib.fixers import ProxyFix

from . import mail, settings
from .utils import routes


class Redash(Flask):
"""A custom Flask app for Redash"""
def __init__(self, *args, **kwargs):
kwargs.update({
'template_folder': settings.STATIC_ASSETS_PATH,
'static_folder': settings.STATIC_ASSETS_PATH,
'static_path': '/static',
})
super(Redash, self).__init__(__name__, *args, **kwargs)
# Make sure we get the right referral address even behind proxies like nginx.
self.wsgi_app = ProxyFix(self.wsgi_app, settings.PROXIES_COUNT)
# Handle the special case of the org slug
self.url_map.converters['org_slug'] = routes.SlugConverter
# Configure Redash using our settings
self.config.from_object('redash.settings')


def create_app():
from . import authentication, extensions, handlers, limiter, migrate
from .destinations import import_destinations
from .handlers import chrome_logger
from .handlers.webpack import configure_webpack
from .metrics.request import provision_app
from .models import db, users
from .query_runner import import_query_runners
from .utils import sentry
from .version_check import reset_new_version_status

sentry.init()
app = Redash()

# Check and update the cached version for use by the client
app.before_first_request(reset_new_version_status)

# Load query runners and destinations
import_query_runners(settings.QUERY_RUNNERS)
import_destinations(settings.DESTINATIONS)

if settings.ENFORCE_HTTPS:
SSLify(app, skips=['ping'])

provision_app(app)
db.init_app(app)
migrate.init_app(app, db)
mail.init_app(app)
authentication.init_app(app)
limiter.init_app(app)
handlers.init_app(app)
configure_webpack(app)
extensions.init_extensions(app)
chrome_logger.init_app(app)
users.init_app(app)

return app
13 changes: 8 additions & 5 deletions redash/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from flask.cli import FlaskGroup, run_command
from flask import current_app

from redash import create_app, settings, __version__
from redash import settings, __version__
from redash.app import create_app
from redash.cli import users, groups, database, data_sources, organization
from redash.monitor import get_status

Expand All @@ -15,9 +16,11 @@ def create(group):

@app.shell_context_processor
def shell_context():
from redash import models
return dict(models=models)

from redash import models, settings
return {
'models': models,
'settings': settings,
}
return app


Expand Down Expand Up @@ -48,7 +51,7 @@ def status():
@manager.command()
def check_settings():
"""Show the settings as Redash sees them (useful for debugging)."""
for name, item in settings.all_settings().iteritems():
for name, item in current_app.config.iteritems():
print("{} = {}".format(name, item))


Expand Down
5 changes: 0 additions & 5 deletions redash/metrics/celery.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
from __future__ import absolute_import

import logging
import socket
import time

from celery.signals import task_postrun, task_prerun
from redash import settings, statsd_client
from redash.utils import json_dumps

tasks_start_time = {}


@task_prerun.connect
def task_prerun_handler(signal, sender, task_id, task, args, kwargs, **kw):
try:
tasks_start_time[task_id] = time.time()
Expand All @@ -29,7 +25,6 @@ def metric_name(name, tags):
return "{},{}".format(name, tags_string)


@task_postrun.connect
def task_postrun_handler(signal, sender, task_id, task, args, kwargs, retval, state, **kw):
try:
run_time = 1000 * (time.time() - tasks_start_time.pop(task_id))
Expand Down
2 changes: 0 additions & 2 deletions redash/monitor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import ast
import itertools
import json
import base64
from sqlalchemy import union_all
from redash import redis_connection, __version__, settings
from redash.models import db, DataSource, Query, QueryResult, Dashboard, Widget
Expand Down
14 changes: 1 addition & 13 deletions redash/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,7 @@
from funcy import distinct, remove

from .helpers import fix_assets_path, array_from_string, parse_boolean, int_or_none, set_from_string
from .organization import DATE_FORMAT


def all_settings():
from types import ModuleType

settings = {}
for name, item in globals().iteritems():
if not callable(item) and not name.startswith("__") and not isinstance(item, ModuleType):
settings[name] = item

return settings

from .organization import DATE_FORMAT # noqa

REDIS_URL = os.environ.get('REDASH_REDIS_URL', os.environ.get('REDIS_URL', "redis://localhost:6379/0"))
PROXIES_COUNT = int(os.environ.get('REDASH_PROXIES_COUNT', "1"))
Expand Down
2 changes: 1 addition & 1 deletion redash/tasks/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from redash import models, redis_connection, settings, statsd_client
from redash.query_runner import InterruptException
from redash.tasks.alerts import check_alerts_for_query
from redash.utils import gen_query_hash, json_dumps, json_loads, utcnow, mustache_render
from redash.utils import gen_query_hash, json_dumps, utcnow, mustache_render
from redash.worker import celery

logger = get_task_logger(__name__)
Expand Down
15 changes: 15 additions & 0 deletions redash/utils/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from werkzeug.routing import BaseConverter


class SlugConverter(BaseConverter):
def to_python(self, value):
# This is ay workaround for when we enable multi-org and some files are being called by the index rule:
# for path in settings.STATIC_ASSETS_PATHS:
# full_path = safe_join(path, value)
# if os.path.isfile(full_path):
# raise ValidationError()

return value

def to_url(self, value):
return value
13 changes: 10 additions & 3 deletions redash/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@

from celery import Celery
from celery.schedules import crontab
from celery.signals import worker_process_init
from redash import create_app, settings
from redash.metrics import celery as celery_metrics
from celery.signals import task_postrun, task_prerun, worker_process_init

from redash import settings
from redash.app import create_app
from redash.metrics.celery import task_postrun_handler, task_prerun_handler


celery = Celery('redash',
broker=settings.CELERY_BROKER,
Expand Down Expand Up @@ -66,6 +69,10 @@ def __call__(self, *args, **kwargs):

celery.Task = ContextTask

# Connect the task signal handles for Redash metrics
task_prerun.connect(task_prerun_handler)
task_postrun.connect(task_postrun_handler)


# Create Flask app after forking a new worker, to make sure no resources are shared between processes.
@worker_process_init.connect
Expand Down
2 changes: 1 addition & 1 deletion redash/wsgi.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from redash import create_app
from redash.app import create_app

app = create_app()
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
os.environ['REDASH_GOOGLE_CLIENT_SECRET'] = "dummy"
os.environ['REDASH_MULTI_ORG'] = "true"

from redash import create_app
from redash.app import create_app
from redash import redis_connection
from redash.models import db
from redash.utils import json_dumps, json_loads
Expand Down

0 comments on commit 544af1c

Please sign in to comment.