PLAT-1780 Remove dependency on django-extensions

This commit is contained in:
Jeremy Bowman
2017-10-24 18:41:14 -04:00
parent 8afa17f31d
commit ba06372cac
23 changed files with 298 additions and 36 deletions

View File

@@ -0,0 +1,14 @@
"""
Configuration for the openedx.core.djangoapps.util Django application
"""
from django.apps import AppConfig
class UtilConfig(AppConfig):
"""
Let Django know that this is an app with management commands.
"""
label = 'open_edx_util'
name = 'openedx.core.djangoapps.util'
verbose_name = 'Open edX Utilities'

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
"""
print_setting
=============
Django command to output a single Django setting.
Useful when paver or a shell script needs such a value.
This handles the one specific use case of the "print_settings" command from
django-extensions that we were actually using.
"""
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
"""print_setting command"""
help = "Print the value of a single Django setting."
def add_arguments(self, parser):
parser.add_argument(
'setting',
help='Specifies the setting to be printed.'
)
def handle(self, *args, **options):
setting = options.get('setting')
if not hasattr(settings, setting):
raise CommandError('%s not found in settings.' % setting)
print(getattr(settings, setting))

View File

@@ -0,0 +1,185 @@
# -*- coding: utf-8 -*-
"""
reset_db
========
Django command to drop and recreate a database.
Useful when running tests against a database which may previously have
had different migrations applied to it.
This handles the one specific use case of the "reset_db" command from
django-extensions that we were actually using.
originally from http://www.djangosnippets.org/snippets/828/ by dnordberg
"""
import logging
import django
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from six.moves import configparser, input
class Command(BaseCommand):
help = "Resets the database for this project."
def add_arguments(self, parser):
parser.add_argument(
'-R', '--router', action='store', dest='router', default='default',
help='Use this router-database other than defined in settings.py')
def handle(self, *args, **options):
"""
Resets the database for this project.
Note: Transaction wrappers are in reverse as a work around for
autocommit, anybody know how to do this the right way?
"""
router = options.get('router')
dbinfo = settings.DATABASES.get(router)
if dbinfo is None:
raise CommandError("Unknown database router %s" % router)
engine = dbinfo.get('ENGINE').split('.')[-1]
user = password = database_name = database_host = database_port = ''
if engine == 'mysql':
(user, password, database_name, database_host, database_port) = parse_mysql_cnf(dbinfo)
user = dbinfo.get('USER') or user
password = dbinfo.get('PASSWORD') or password
owner = user
database_name = dbinfo.get('NAME') or database_name
if database_name == '':
raise CommandError("You need to specify DATABASE_NAME in your Django settings file.")
database_host = dbinfo.get('HOST') or database_host
database_port = dbinfo.get('PORT') or database_port
verbosity = int(options.get('verbosity', 1))
if engine in ('sqlite3', 'spatialite'):
import os
try:
logging.info("Unlinking %s database" % engine)
os.unlink(database_name)
except OSError:
pass
elif engine in ('mysql',):
import MySQLdb as Database
kwargs = {
'user': user,
'passwd': password,
}
if database_host.startswith('/'):
kwargs['unix_socket'] = database_host
else:
kwargs['host'] = database_host
if database_port:
kwargs['port'] = int(database_port)
connection = Database.connect(**kwargs)
drop_query = 'DROP DATABASE IF EXISTS `%s`' % database_name
utf8_support = 'CHARACTER SET utf8'
create_query = 'CREATE DATABASE `%s` %s' % (database_name, utf8_support)
logging.info('Executing... "' + drop_query + '"')
connection.query(drop_query)
logging.info('Executing... "' + create_query + '"')
connection.query(create_query)
elif engine in ('postgresql', 'postgresql_psycopg2', 'postgis'):
if engine == 'postgresql' and django.VERSION < (1, 9):
import psycopg as Database # NOQA
elif engine in ('postgresql', 'postgresql_psycopg2', 'postgis'):
import psycopg2 as Database # NOQA
conn_params = {'database': 'template1'}
if user:
conn_params['user'] = user
if password:
conn_params['password'] = password
if database_host:
conn_params['host'] = database_host
if database_port:
conn_params['port'] = database_port
connection = Database.connect(**conn_params)
connection.set_isolation_level(0) # autocommit false
cursor = connection.cursor()
drop_query = "DROP DATABASE \"%s\";" % database_name
logging.info('Executing... "' + drop_query + '"')
try:
cursor.execute(drop_query)
except Database.ProgrammingError as e:
logging.exception("Error: %s" % str(e))
create_query = "CREATE DATABASE \"%s\"" % database_name
if owner:
create_query += " WITH OWNER = \"%s\" " % owner
create_query += " ENCODING = 'UTF8'"
if engine == 'postgis' and django.VERSION < (1, 9):
# For PostGIS 1.5, fetch template name if it exists
from django.contrib.gis.db.backends.postgis.base import DatabaseWrapper
postgis_template = DatabaseWrapper(dbinfo).template_postgis
if postgis_template is not None:
create_query += ' TEMPLATE = %s' % postgis_template
if settings.DEFAULT_TABLESPACE:
create_query += ' TABLESPACE = %s;' % settings.DEFAULT_TABLESPACE
else:
create_query += ';'
logging.info('Executing... "' + create_query + '"')
cursor.execute(create_query)
else:
raise CommandError("Unknown database engine %s" % engine)
if verbosity >= 2:
print("Reset successful.")
def parse_mysql_cnf(dbinfo):
"""
Attempt to parse mysql database config file for connection settings.
Ideally we would hook into django's code to do this, but read_default_file is handled by the mysql C libs
so we have to emulate the behaviour
Settings that are missing will return ''
returns (user, password, database_name, database_host, database_port)
"""
read_default_file = dbinfo.get('OPTIONS', {}).get('read_default_file')
if read_default_file:
config = configparser.RawConfigParser({
'user': '',
'password': '',
'database': '',
'host': '',
'port': '',
'socket': '',
})
import os
config.read(os.path.expanduser(read_default_file))
try:
user = config.get('client', 'user')
password = config.get('client', 'password')
database_name = config.get('client', 'database')
database_host = config.get('client', 'host')
database_port = config.get('client', 'port')
socket = config.get('client', 'socket')
if database_host == 'localhost' and socket:
# mysql actually uses a socket if host is localhost
database_host = socket
return user, password, database_name, database_host, database_port
except configparser.NoSectionError:
pass
return '', '', '', '', ''

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytest
from django.core.management import call_command, CommandError
def test_without_args(capsys):
with pytest.raises(CommandError, message='Error: too few arguments'):
call_command('print_setting')
def test_with_setting_args(capsys):
call_command('print_setting', 'DEBUG')
out, err = capsys.readouterr()
assert 'False' in out
assert 'INSTALLED_APPS' not in out