Skip to content

Commit

Permalink
Merge pull request #12874 from rtibbles/snake_like_path
Browse files Browse the repository at this point in the history
Fix the CLI --pythonpath parameter
  • Loading branch information
rtibbles authored Dec 20, 2024
2 parents 9966f00 + 4a905a3 commit 03d0c50
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 34 deletions.
58 changes: 36 additions & 22 deletions kolibri/utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import signal
import sys
import traceback
from pkgutil import find_loader
from contextlib import contextmanager

import click
from django.core.management import execute_from_command_line
Expand All @@ -19,32 +19,35 @@
from kolibri.plugins.utils import enable_plugin
from kolibri.plugins.utils import iterate_plugins
from kolibri.utils import server
from kolibri.utils.compat import module_exists
from kolibri.utils.conf import OPTIONS
from kolibri.utils.debian_check import check_debian_user
from kolibri.utils.main import initialize
from kolibri.utils.main import set_django_settings_and_python_path
from kolibri.utils.main import setup_logging


logger = logging.getLogger(__name__)


# We use Unicode strings for Python 2.7 throughout the codebase, so choosing
# to silence these warnings.
# Ref:
# https://github.com/learningequality/kolibri/pull/5494#discussion_r318057385
# https://github.com/PythonCharmers/python-future/issues/22
click.disable_unicode_literals_warning = True
@contextmanager
def _patch_python_path(pythonpath):
if pythonpath:
sys.path.insert(0, pythonpath)
try:
yield
finally:
if pythonpath:
sys.path.remove(pythonpath)


def validate_module(ctx, param, value):
if value:
try:
if not find_loader(value):
raise ImportError
except ImportError:
raise click.BadParameter(
"{param} must be a valid python module import path"
)
with _patch_python_path(ctx.params.get("pythonpath")):
if not module_exists(value):
raise click.BadParameter(
"{param} must be a valid python module import path"
)
return value


Expand Down Expand Up @@ -72,8 +75,11 @@ def validate_module(ctx, param, value):

pythonpath_option = click.Option(
param_decls=["--pythonpath"],
type=click.Path(exists=True, file_okay=False),
type=click.Path(exists=True, file_okay=False, resolve_path=True),
help="Add a path to the Python path",
# Set this to is_eager to ensure the option is set
# before we attempt to import the settings module
is_eager=True,
)

skip_update_option = click.Option(
Expand All @@ -91,11 +97,10 @@ def validate_module(ctx, param, value):
)


base_params = [debug_option, debug_database_option, noinput_option]
base_params = [debug_option, debug_database_option, noinput_option, pythonpath_option]

initialize_params = base_params + [
settings_option,
pythonpath_option,
skip_update_option,
]

Expand Down Expand Up @@ -135,6 +140,8 @@ def invoke(self, ctx):
debug=ctx.params.get("debug"),
debug_database=ctx.params.get("debug_database"),
)
# We want to allow overriding the Python path for commands that don't require Django
set_django_settings_and_python_path(None, ctx.params.get("pythonpath"))
for param in base_params:
ctx.params.pop(param.name)
return super(KolibriCommand, self).invoke(ctx)
Expand Down Expand Up @@ -163,6 +170,8 @@ def invoke(self, ctx):
debug=ctx.params.get("debug"),
debug_database=ctx.params.get("debug_database"),
)
# We want to allow overriding the Python path for commands that don't require Django
set_django_settings_and_python_path(None, ctx.params.get("pythonpath"))
for param in base_params:
ctx.params.pop(param.name)
return super(KolibriGroupCommand, self).invoke(ctx)
Expand Down Expand Up @@ -380,7 +389,7 @@ def plugin():
pass


@plugin.command(help="Enable Kolibri plugins")
@plugin.command(cls=KolibriCommand, help="Enable Kolibri plugins")
@click.argument("plugin_names", nargs=-1)
@click.option("-d", "--default-plugins", default=False, is_flag=True)
def enable(plugin_names, default_plugins):
Expand All @@ -400,7 +409,7 @@ def enable(plugin_names, default_plugins):
raise exception


@plugin.command(help="Disable Kolibri plugins")
@plugin.command(cls=KolibriCommand, help="Disable Kolibri plugins")
@click.argument("plugin_names", nargs=-1)
@click.option("-a", "--all-plugins", default=False, is_flag=True)
def disable(plugin_names, all_plugins):
Expand All @@ -420,7 +429,9 @@ def disable(plugin_names, all_plugins):
raise exception


@plugin.command(help="Set Kolibri plugins to be enabled and disable all others")
@plugin.command(
cls=KolibriCommand, help="Set Kolibri plugins to be enabled and disable all others"
)
@click.argument("plugin_names", nargs=-1)
@click.pass_context
def apply(ctx, plugin_names):
Expand All @@ -442,7 +453,7 @@ def apply(ctx, plugin_names):
raise exception


@plugin.command(help="List all available Kolibri plugins")
@plugin.command(cls=KolibriCommand, help="List all available Kolibri plugins")
def list():
plugins = [plugin for plugin in iterate_plugins()]
lang = "en"
Expand Down Expand Up @@ -504,7 +515,10 @@ def _get_env_vars():
yield _format_env_var(envvar, v)


@configure.command(help="List all available environment variables to configure Kolibri")
@configure.command(
cls=KolibriCommand,
help="List all available environment variables to configure Kolibri",
)
def list_env():
click.echo_via_pager(_get_env_vars())

Expand Down
22 changes: 10 additions & 12 deletions kolibri/utils/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.management import call_command
from django.core.management.base import handle_default_options
from django.db.utils import DatabaseError

import kolibri
Expand Down Expand Up @@ -112,14 +111,6 @@ def _migrate_databases():
call_command("loaddata", "scopedefinitions")


class DefaultDjangoOptions(object):
__slots__ = ["settings", "pythonpath"]

def __init__(self, settings, pythonpath):
self.settings = settings
self.pythonpath = pythonpath


def setup_logging(debug=False, debug_database=False):
"""
Configures logging in cases where a Django environment is not supposed
Expand Down Expand Up @@ -291,6 +282,15 @@ def _upgrades_after_django_setup(updated, version):
logging.error(e)


def set_django_settings_and_python_path(django_settings, pythonpath):

if django_settings:
os.environ["DJANGO_SETTINGS_MODULE"] = django_settings

if pythonpath and pythonpath not in sys.path:
sys.path.insert(0, pythonpath)


def initialize( # noqa C901
skip_update=False,
settings=None,
Expand All @@ -307,9 +307,7 @@ def initialize( # noqa C901

setup_logging(debug=debug, debug_database=debug_database)

default_options = DefaultDjangoOptions(settings, pythonpath)

handle_default_options(default_options)
set_django_settings_and_python_path(settings, pythonpath)

version = get_version()

Expand Down

0 comments on commit 03d0c50

Please sign in to comment.