# -*- coding: utf-8 -*-

"""
MMPP, the MoinMoin Monkey-Patch Package.

This package comprises a set of monkey-patches (e.g. hotfixes which get
applied as the code is ran, without really patching the source code of
the program) for MoinMoin, along with a small function which wires all
the patches together in the proper order.
"""

__all__ = ["wire"]
__mmpp_hook__ = "__mmpp_hook__"

# This one keeps track of already-wired patches.
__wired = set()


def hook(func):
    """
    Mark a function as hook which should be called to wire a monkey-patch.
    This function can be used as a decorator.

    :Parameters:
        - `func`: Function being decorated.
    """
    func.__mmpp_hook__ = True
    return func



# TODO Add dependency support to monkey patches.
#
def wire(patch=None, patches=None):
    """
    Wires a patch or a set of patches.

    If no parameters are given, all the patches in the `mmpp` module
    will be installed.

    :Parameters:
        - `patch`: Specify the name of a single patch.
        - `patches`: List or tuple of names of the patches which should be
          applied.
    """
    if patches is None and patch is not None:
        patches = [patch]
    if patches is None:
        patches = patchModuleNames()

    [wireModule(patch) for patch in patches]


from traceback import print_exc
import sys

# FIXME Logging should work *always*, it currently does for the standalone
#       server, but not when serving inside Apache with mod_python.
#
def wireModule(name):
    """
    Tries to wire all hooks from a particular module given its name.

    :Parameters:
        - `name`: Module name. The module must reside inside the ``mmpp``
          package; more exactly it should reside in the same ``mmpp``
          package as the ``__init__.py`` whose `wireModule` implementation
          is being called.
    """
    global __wired

    try:
        mod = __import__("%s.%s" % (__name__, name), {}, {}, name)
    except Exception, e:
        print "[mmpp] Could not import %s.%s: %r" \
                % (__name__, name, e)
        print_exc(file=sys.stdout)

    for item in mod.__dict__.itervalues():
        if callable(item) and getattr(item, __mmpp_hook__, False):
            if item in __wired:
                print "[mmpp] Skipping %s.%s.%s()" \
                        % (__name__, name, item.func_name)
                print_exc(file=sys.stdout)
                continue
            try:
                item()
                __wired.add(item)
                print "[mmpp] Wired %s.%s.%s()" \
                        % (__name__, name, item.func_name)
            except Exception, e:
                print "[mmpp] Could not wire %s.%s.%s(): %r" \
                        % (__name__, name, item.func_name, e)
                print_exc(file=sys.stdout)



import re
from os import path, listdir
validModuleName = re.compile(r"""^[a-z_][a-z0-9_]*\.py$""", re.I).match

def patchModuleNames():
    """
    Obtains a list of module names residing inside the ``mmpp`` package.
    """
    return (name[:-3] for name
            in listdir(path.abspath(path.dirname(__file__)))
            if validModuleName(name) and name != "__init__.py")
    

