Mercurial > wikked
changeset 254:fa2c3671bcd9
Fixes for running in `mod_wsgi`.
* No more `wsgiutil` for now, since it's easy enough to write the script.
* Added a "context" to wiki parameters to communicate if we're running in normal
mode, initialization mode (creating a new wiki), or background task mode.
* Mercurial source-control uses this context to decide how to setup (or not) a
command server.
* Mercurial source-control is now sharing its command server per process, and
tries to correctly shut it down on exit.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 02 Apr 2014 08:13:12 -0700 |
parents | bef755e567a3 |
children | c27317412100 |
files | wikked/commands/manage.py wikked/scm/mercurial.py wikked/tasks.py wikked/web.py wikked/wiki.py wikked/wsgiutil.py |
diffstat | 6 files changed, 73 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/wikked/commands/manage.py Tue Apr 01 00:14:42 2014 -0700 +++ b/wikked/commands/manage.py Wed Apr 02 08:13:12 2014 -0700 @@ -3,6 +3,7 @@ import shutil import logging from wikked.commands.base import WikkedCommand, register_command +from wikked.wiki import Wiki, WikiParameters, INIT_CONTEXT logger = logging.getLogger(__name__) @@ -40,8 +41,8 @@ logger.info("Initializing new wiki at: %s" % path) from wikked.wiki import WikiParameters, Wiki - parameters = WikiParameters(path) - wiki = Wiki(parameters, for_init=True) + parameters = WikiParameters(path, ctx=INIT_CONTEXT) + wiki = Wiki(parameters) wiki.init() if not ctx.args.bare:
--- a/wikked/scm/mercurial.py Tue Apr 01 00:14:42 2014 -0700 +++ b/wikked/scm/mercurial.py Wed Apr 02 08:13:12 2014 -0700 @@ -179,11 +179,14 @@ return subprocess.check_output(exe) +hg_client = None +cl_lock = threading.Lock() + + class MercurialCommandServerSourceControl(MercurialBaseSourceControl): def __init__(self, root, client=None): MercurialBaseSourceControl.__init__(self, root) self.client = client - self.cl_lock = threading.Lock() def _initRepo(self, root): exe = ['hg', 'init', root] @@ -192,23 +195,54 @@ def _doStart(self): if self.client is None: - import hglib - self.client = hglib.open(self.root) + if hg_client is None: + with cl_lock: + if hg_client is None: + self._createServer() + self.client = hg_client + else: + logger.debug("Re-using existing Mercurial command server.") + self.client = hg_client + + def _createServer(self): + logger.debug("Spawning Mercurial command server.") + import hglib + global hg_client + hg_client = hglib.open(self.root) + + def shutdown_commandserver(num, frame): + global hg_client + if hg_client is not None: + with cl_lock: + if hg_client is not None: + logger.debug("Shutting down Mercurial command server.") + hg_client.close() + hg_client = None + import atexit + atexit.register(shutdown_commandserver, None, None) + try: + import signal + signal.signal(signal.SIGTERM, shutdown_commandserver) + except: + # `mod_wsgi` prevents adding stuff to `SIGTERM` + # so let's not make a big deal if this doesn't + # go through. + pass def getHistory(self, path=None, limit=10): if path is not None: - with self.cl_lock: + with cl_lock: status = self.client.status(include=[path]) if len(status) > 0 and status[0] == '?': return [] needs_files = False if path is not None: - with self.cl_lock: + with cl_lock: repo_revs = self.client.log(files=[path], follow=True, limit=limit) else: needs_files = True - with self.cl_lock: + with cl_lock: repo_revs = self.client.log(follow=True, limit=limit) revisions = [] for rev in repo_revs: @@ -218,7 +252,7 @@ r.timestamp = time.mktime(rev.date.timetuple()) r.description = unicode(rev.desc) if needs_files: - with self.cl_lock: + with cl_lock: rev_statuses = self.client.status(change=rev.node) for rev_status in rev_statuses: r.files.append({ @@ -229,7 +263,7 @@ return revisions def getState(self, path): - with self.cl_lock: + with cl_lock: statuses = self.client.status(include=[path]) if len(statuses) == 0: return STATE_COMMITTED @@ -241,11 +275,11 @@ raise Exception("Unsupported status: %s" % status) def getRevision(self, path, rev): - with self.cl_lock: + with cl_lock: return self.client.cat([path], rev=rev) def diff(self, path, rev1, rev2): - with self.cl_lock: + with cl_lock: if rev2 is None: return self.client.diff(files=[path], change=rev1, git=True) return self.client.diff(files=[path], revs=[rev1, rev2], git=True) @@ -264,13 +298,13 @@ args = cmdbuilder('commit', *paths, debug=True, m=op_meta['message'], A=True, **kwargs) - with self.cl_lock: + with cl_lock: self.client.rawcommand(args) except CommandError as e: raise SourceControlError('commit', e.out, e.args, e.out) def revert(self, paths=None): - with self.cl_lock: + with cl_lock: if paths is not None: self.client.revert(files=paths, nobackup=True) else:
--- a/wikked/tasks.py Tue Apr 01 00:14:42 2014 -0700 +++ b/wikked/tasks.py Wed Apr 02 08:13:12 2014 -0700 @@ -1,6 +1,6 @@ import logging from celery import Celery -from wikked.wiki import Wiki, WikiParameters +from wikked.wiki import Wiki, WikiParameters, BACKGROUND_CONTEXT logger = logging.getLogger(__name__) @@ -16,7 +16,7 @@ self.wiki = None def __enter__(self): - params = WikiParameters(root=self.wiki_root) + params = WikiParameters(self.wiki_root, ctx=BACKGROUND_CONTEXT) self.wiki = Wiki(params) self.wiki.start(False) return self.wiki
--- a/wikked/web.py Tue Apr 01 00:14:42 2014 -0700 +++ b/wikked/web.py Wed Apr 02 08:13:12 2014 -0700 @@ -2,7 +2,7 @@ import os.path import logging from flask import Flask, abort, g -from wikked.wiki import Wiki +from wikked.wiki import Wiki, WikiParameters # Create the main app. @@ -67,6 +67,7 @@ app.set_wiki_params = set_app_wiki_params app.wiki_updater = None +app.set_wiki_params(WikiParameters(wiki_root)) # Set the wiki as a request global, and open/close the database.
--- a/wikked/wiki.py Tue Apr 01 00:14:42 2014 -0700 +++ b/wikked/wiki.py Wed Apr 02 08:13:12 2014 -0700 @@ -25,13 +25,19 @@ pass +NORMAL_CONTEXT = 0 +INIT_CONTEXT = 1 +BACKGROUND_CONTEXT = 2 + + class WikiParameters(object): """ An object that defines how a wiki gets initialized. """ - def __init__(self, root=None): + def __init__(self, root=None, ctx=NORMAL_CONTEXT): if root is None: root = os.getcwd() self.root = root + self.context = ctx self.formatters = self.getFormatters() self.wiki_updater = self.getWikiUpdater() self._config = None @@ -55,7 +61,7 @@ from wikked.db.sql import SQLDatabase return SQLDatabase(self.config) - def scm_factory(self, for_init=False): + def scm_factory(self): self._ensureScmFactory() return self._scm_factory() @@ -136,22 +142,19 @@ # Default to Mercurial. Yes. I just decided that myself. scm_type = 'hg' - if scm_type == 'hg': - logger.debug("Creating Mercurial command server.") - import hglib - client = hglib.open() + if self.context != NORMAL_CONTEXT and scm_type == 'hg': + # Quick workaround for when we're creating a new repo, + # or running background tasks. + # We'll be using the `hg` process instead of the command + # server, since there's no repo there yet, or we just don't + # want to spawn a new process unless we want to. + logger.debug("Forcing `hgexe` source-control for new repo.") + scm_type = 'hgexe' - def shutdown_commandserver(num, frame): - logger.debug("Shutting down Mercurial command server.") - client.close() - import atexit - atexit.register(shutdown_commandserver, None, None) - import signal - signal.signal(signal.SIGTERM, shutdown_commandserver) - + if scm_type == 'hg': def impl(): from wikked.scm.mercurial import MercurialCommandServerSourceControl - return MercurialCommandServerSourceControl(self.root, client) + return MercurialCommandServerSourceControl(self.root) self._scm_factory = impl elif scm_type == 'hgexe': @@ -180,7 +183,7 @@ class Wiki(object): """ The wiki class! This is where the magic happens. """ - def __init__(self, parameters, for_init=False): + def __init__(self, parameters): """ Creates a new wiki instance. It won't be fully functional until you call `start`, which does the actual initialization. This gives you a chance to customize a few more things before @@ -203,7 +206,7 @@ self.fs = parameters.fs_factory() self.index = parameters.index_factory() self.db = parameters.db_factory() - self.scm = parameters.scm_factory(for_init) + self.scm = parameters.scm_factory() self.auth = parameters.auth_factory() self._wiki_updater = parameters.wiki_updater
--- a/wikked/wsgiutil.py Tue Apr 01 00:14:42 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -import os -import sys -import logging -import logging.handlers -from wikked.wiki import WikiParameters - - -def get_wsgi_app(wiki_root, log_file=None, async_update=True): - os.chdir(wiki_root) - logging.basicConfig(stream=sys.stderr) - - if async_update: - import wikked.settings - wikked.settings.WIKI_ASYNC_UPDATE = True - - from wikked.web import app - app.set_wiki_params(WikiParameters(wiki_root)) - - if log_file is not None: - h = logging.handlers.RotatingFileHandler(log_file, maxBytes=4096) - h.setLevel(logging.WARNING) - app.logger.addHandler(h) - - return app -