Changeset - 7c2704784297
[Not reviewed]
stable
0 5 0
Mads Kiilerich (mads) - 18 months ago 2024-09-22 19:08:29
mads@kiilerich.com
Grafted from: ec090899c2b7
wsgi: Refactor to introduce make_app_raw function with the essentials of make_app

Command line commands and WSGI applications have different environments and
runtime models. They deserve different handling.

Keep make_app doing as before so WSGI scripts keep working as before.

All "app" usage from command line is changed to use make_app_raw directly.
5 files changed with 10 insertions and 4 deletions:
0 comments (0 inline, 0 general)
kallithea/bin/kallithea_cli_base.py
Show inline comments
 
@@ -58,29 +58,29 @@ def register_command(needs_config_file=F
 

	
 
    If one of the needs_config_file flags are true, a config file must be specified
 
    with -c and it is read and logging is configured. The configuration is
 
    available in the kallithea.CONFIG dict.
 

	
 
    If config_file_initialize_app is true, Kallithea, TurboGears global state
 
    (including tg.config), and database access will also be fully initialized.
 
    """
 
    cli_command = cli.command(hidden=hidden)
 
    if needs_config_file or config_file_initialize_app:
 
        def annotator(annotated):
 
            @click.option('--config_file', '-c', help="Path to .ini file with app configuration.",
 
                type=click.Path(dir_okay=False, exists=True, readable=True), required=True)
 
            @functools.wraps(annotated) # reuse meta data from the wrapped function so click can see other options
 
            def runtime_wrapper(config_file, *args, **kwargs):
 
                path_to_ini_file = os.path.realpath(config_file)
 
                config = paste.deploy.appconfig('config:' + path_to_ini_file)
 
                cp = configparser.ConfigParser(strict=False)
 
                cp.read_string(read_config(path_to_ini_file, strip_section_prefix=annotated.__name__))
 
                logging.config.fileConfig(cp,
 
                    {'__file__': path_to_ini_file, 'here': os.path.dirname(path_to_ini_file)})
 
                if needs_config_file:
 
                    annotated(*args, config=config, **kwargs)
 
                if config_file_initialize_app:
 
                    kallithea.config.application.make_app(config.global_conf, **config.local_conf)
 
                    kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
 
                    annotated(*args, **kwargs)
 
            return cli_command(runtime_wrapper)
 
        return annotator
 
    return cli_command
kallithea/bin/kallithea_cli_celery.py
Show inline comments
 
@@ -19,35 +19,35 @@ import kallithea
 
import kallithea.bin.kallithea_cli_base as cli_base
 
from kallithea.lib import celery_app
 
from kallithea.lib.utils2 import asbool
 

	
 

	
 
@cli_base.register_command(needs_config_file=True)
 
@click.argument('celery_args', nargs=-1)
 
def celery_run(celery_args, config):
 
    """Start Celery worker(s) for asynchronous tasks.
 

	
 
    This commands starts the Celery daemon which will spawn workers to handle
 
    certain asynchronous tasks for Kallithea.
 

	
 
    Any extra arguments you pass to this command will be passed through to
 
    Celery. Use '--' before such extra arguments to avoid options to be parsed
 
    by this CLI command.
 
    """
 

	
 
    if not asbool(config.get('use_celery')):
 
        raise Exception('Please set use_celery = true in .ini config '
 
                        'file before running this command')
 

	
 
    kallithea.CELERY_APP.config_from_object(celery_app.make_celery_config(config))
 

	
 
    kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app(config.global_conf, **config.local_conf)
 
    kallithea.CELERY_APP.loader.on_worker_process_init = lambda: kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
 

	
 
    args = list(celery_args)
 
    # args[0] is generally ignored when prog_name is specified, but -h *needs* it to be 'worker' ... but will also suggest that users specify 'worker' explicitly
 
    if not args or args[0] != 'worker':
 
        args.insert(0, 'worker')
 

	
 
    # inline kallithea.CELERY_APP.start in order to allow specifying prog_name
 
    assert celery_command.params[0].name == 'app'
 
    celery_command.params[0].default = kallithea.CELERY_APP
 
    celery_command.main(args=args, prog_name='kallithea-cli celery-run -c CONFIG_FILE --')
kallithea/bin/vcs_hooks.py
Show inline comments
 
@@ -95,49 +95,49 @@ def push_action(ui, repo, node, node_las
 
    caches! The function should perhaps be renamed.
 

	
 
    Called as Mercurial hook changegroup.kallithea_push_action .
 

	
 
    The pushed changesets is given by the revset 'node:node_last'.
 
    """
 
    revs = [ascii_str(repo[r].hex()) for r in mercurial.scmutil.revrange(repo, [b'%s:%s' % (node, node_last)])]
 
    hooks.process_pushed_raw_ids(revs)
 

	
 

	
 
def _git_hook_environment(repo_path):
 
    """
 
    Create a light-weight environment for stand-alone scripts and return an UI and the
 
    db repository.
 

	
 
    Git hooks are executed as subprocess of Git while Kallithea is waiting, and
 
    they thus need enough info to be able to create an app environment and
 
    connect to the database.
 
    """
 
    extras = get_hook_environment()
 

	
 
    path_to_ini_file = extras['config']
 
    config = paste.deploy.appconfig('config:' + path_to_ini_file)
 
    #logging.config.fileConfig(ini_file_path) # Note: we are in a different process - don't use configured logging
 
    kallithea.config.application.make_app(config.global_conf, **config.local_conf)
 
    kallithea.config.application.make_app_raw(config.global_conf, **config.local_conf)
 

	
 
    # fix if it's not a bare repo
 
    if repo_path.endswith(os.sep + '.git'):
 
        repo_path = repo_path[:-5]
 

	
 
    repo = db.Repository.get_by_full_path(repo_path)
 
    if not repo:
 
        raise OSError('Repository %s not found in database' % repo_path)
 

	
 
    return repo
 

	
 

	
 
def post_receive(repo_path, git_stdin_lines):
 
    """Called from Git post-receive hook.
 
    The returned value is used as hook exit code and must be 0.
 
    """
 
    try:
 
        repo = _git_hook_environment(repo_path)
 
    except HookEnvironmentError as e:
 
        sys.stderr.write("Skipping Kallithea Git post-receive hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e))
 
        return 0
 

	
 
    # the post push hook should never use the cached instance
 
    scm_repo = repo.scm_instance_no_cache()
kallithea/config/application.py
Show inline comments
 
@@ -27,42 +27,48 @@ __all__ = ['make_app']
 

	
 
def wrap_app(app):
 
    """Wrap the TG WSGI application in Kallithea middleware"""
 
    config = app.config
 

	
 
    # we want our low level middleware to get to the request ASAP. We don't
 
    # need any stack middleware in them - especially no StatusCodeRedirect buffering
 
    app = SimpleHg(app, config)
 
    app = SimpleGit(app, config)
 

	
 
    # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
 
    if config.get('url_scheme_variable') or asbool(config.get('force_https')) or asbool(config.get('use_htsts')):
 
        app = HttpsFixup(app, config)
 

	
 
    app = PermanentRepoUrl(app, config)
 

	
 
    # Optional and undocumented wrapper - gives more verbose request/response logging, but has a slight overhead
 
    if asbool(config.get('use_wsgi_wrapper')):
 
        app = RequestWrapper(app, config)
 

	
 
    return app
 

	
 

	
 
def make_app(global_conf, **app_conf):
 
    """Return WSGI app with logging Mercurial stdout/stderr - to be used as
 
    Paste or mod_wsgi entry point"""
 
    return make_app_raw(global_conf, **app_conf)
 

	
 

	
 
def make_app_raw(global_conf, **app_conf):
 
    """
 
    Set up Kallithea with the settings found in the PasteDeploy configuration
 
    file used.
 

	
 
    :param global_conf: The global settings for Kallithea (those
 
        defined under the ``[DEFAULT]`` section).
 
    :return: The Kallithea application with all the relevant middleware
 
        loaded.
 

	
 
    This is the PasteDeploy factory for the Kallithea application.
 

	
 
    ``app_conf`` contains all the application-specific settings (those defined
 
    under ``[app:main]``.
 
    """
 
    assert app_conf.get('sqlalchemy.url')  # must be called with a Kallithea .ini file, which for example must have this config option
 
    assert global_conf.get('here') and global_conf.get('__file__')  # app config should be initialized the paste way ...
 

	
 
    return base_config.make_wsgi_app(global_conf, app_conf, wrap_app=wrap_app)
kallithea/tests/scripts/manual_test_concurrency.py
Show inline comments
 
@@ -26,49 +26,49 @@ Original author and date, and relevant c
 

	
 
"""
 

	
 
import logging
 
import os
 
import shutil
 
import sys
 
import tempfile
 
from os.path import dirname
 
from subprocess import PIPE, Popen
 

	
 
from paste.deploy import appconfig
 
from sqlalchemy import engine_from_config
 

	
 
import kallithea.config.application
 
from kallithea.lib.utils2 import get_crypt_password
 
from kallithea.model import db, meta
 
from kallithea.model.base import init_model
 
from kallithea.model.repo import RepoModel
 
from kallithea.tests.base import HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
 

	
 

	
 
rel_path = dirname(dirname(dirname(dirname(os.path.abspath(__file__)))))
 
conf = appconfig('config:development.ini', relative_to=rel_path)
 
kallithea.config.application.make_app(conf.global_conf, **conf.local_conf)
 
kallithea.config.application.make_app_raw(conf.global_conf, **conf.local_conf)
 

	
 
USER = TEST_USER_ADMIN_LOGIN
 
PASS = TEST_USER_ADMIN_PASS
 
HOST = 'server.local'
 
METHOD = 'pull'
 
DEBUG = True
 
log = logging.getLogger(__name__)
 

	
 

	
 
class Command(object):
 

	
 
    def __init__(self, cwd):
 
        self.cwd = cwd
 

	
 
    def execute(self, cmd, *args):
 
        """Runs command on the system with given ``args``.
 
        """
 

	
 
        command = cmd + ' ' + ' '.join(args)
 
        log.debug('Executing %s', command)
 
        if DEBUG:
 
            print(command)
 
        p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
 
        stdout, stderr = p.communicate()
0 comments (0 inline, 0 general)