Commits

Fabian Büchler  committed c0edd9d

NOT FINISHED - integrating pull requests #2,3,4.

  • Participants
  • Parent commits 38c880c

Comments (0)

Files changed (9)

File markupmirror/markup/base.py

-from django.utils.encoding import force_unicode
+from __future__ import absolute_import, unicode_literals
+import logging
+
+try:
+    from django.utils.encoding import smart_text, smart_bytes
+except ImportError:
+    from django.utils.encoding import (
+        smart_unicode as smart_text, smart_str as smart_bytes)
 
 from markupmirror.exceptions import *
 from markupmirror import settings
 
-import logging
-import inspect
 
+__all__ = ('markup_pool', 'register_markup', 'BaseMarkup')
 
-logger = logging.getLogger("markupmirror")
+
+LOG = logging.getLogger("markupmirror")
 
 
 class BaseMarkup(object):
     """
     codemirror_mode = ''
     title = ""
+    requires = {}
+    _requirements = {}
 
     @classmethod
     def get_name(cls):
         """
         return cls.__name__.replace("Markup", "", 1).lower()
 
-    def convert(self, markup):
+    @property
+    def requirements(self):
+        """Provide access to loaded requirements."""
+        return self._requirements
+
+    def convert(self, markup, *args, **kwargs):
         """Main conversion method. Must be implemented in subclasses."""
         return markup
 
-    def before_convert(self, markup):
+    def before_convert(self, markup, *args, **kwargs):
         """Called before ``convert``. Can be used to separate the main
         conversion through a third-party library (e.g. Markdown) from
         additional logic.
         """
         return markup
 
-    def after_convert(self, markup):
+    def after_convert(self, markup, *args, **kwargs):
         """Called after ``convert``. Similar to ``before_convert``."""
         return markup
 
-    def __call__(self, markup, request=None, model_instance=None):
+    def __call__(self, markup, *args, **kwargs):
         """Main entry point. Calls ``before_convert``, ``convert`` and
         ``after_convert`` in that order.
 
         """
-        def call(method, markup):
-            """Call method with the markup and if specified, the request"""
-            kwargs = {}
-            available = inspect.getargspec(method.im_func)[0]
-            if 'request' in available:
-                kwargs['request'] = request
-            if 'model_instance' in available:
-                kwargs['model_instance'] = model_instance
-            return method(markup, **kwargs)
-        before = call(self.before_convert, markup)
-        converted = call(self.convert, before)
-        after = call(self.after_convert, converted)
-        return force_unicode(after)
+        converted = self.before_convert(markup, *args, **kwargs)
+        converted = self.convert(converted, *args, **kwargs)
+        converted = self.after_convert(converted, *args, **kwargs)
+        return smart_text(converted)
 
 
 class MarkupPool(object):
 
     @property
     def markups(self):
-        """Default our markups from settings.
+        """List registered markup types.
 
-        Done here to avoid doing this during project settings.py construction
+        Defaults to the ``MARKUPMIRROR_MARKUP_TYPES`` defined in settings.
+
+        First access loads and registeres the configured markup converters
+        with this pool.
+
         """
         if self._markups is None:
             self._markups = {}
             for markup in settings.MARKUPMIRROR_MARKUP_TYPES.values():
-                markup = self.markup_from(markup)
+                markup = self.load_markup(markup)
                 if markup is not None:
                     self.register_markup(markup)
         return self._markups
 
         markup_name = markup.get_name()
 
-        # Make sure we can import all the required things
-        if not hasattr(markup, 'required') and hasattr(markup, 'requires'):
-            unfulfilled = self.fulfill_required(markup)
-            if unfulfilled:
-                errors = "\n\t".join("%s:%s" % (requirement, error)
-                    for requirement, error in unfulfilled)
-                logger.warning("Couldn't register markup %s:%s\n\t%s",
+        # Make sure we can import all the required modules
+        if markup.requires:
+            failed = self.load_requirements(markup)
+            if failed:
+                errors = "\n\t".join(
+                    "%s:%s" % (requirement, error)
+                    for requirement, error in failed)
+                LOG.warning(
+                    "Couldn't register markup %s:%s\n\t%s",
                     markup_name, markup, errors)
                 return
 
         self.markups[markup_name] = markup()
 
-    def markup_from(self, markup):
-        """Import markup from the specified markup.
+    def load_markup(self, markup):
+        """Import markup converter class from specified path.
 
         If already an object, then return as is otherwise, import it first.
 
         if not isinstance(markup, basestring):
             return markup
 
-        parts = markup.split('.')
+        markup = smart_bytes(markup)
+        parts = markup.split(b'.')
         name = parts[-1]
-        leadup = '.'.join(parts[:-1])
+        leadup = b'.'.join(parts[:-1])
         loaded = __import__(leadup, globals(), locals(), [name], -1)
         return getattr(loaded, name)
 
-    def fulfill_required(self, markup):
+    def load_requirements(self, markup):
         """See that we can import everything in the requires field.
 
         Return list of [(requirement, error), ...]
         for all the requirements that couldn't be fullfilled
 
         """
-        requires = markup.requires
-        required = markup.required = {}
-        unfulfilled = []
+        failed = []
 
-        if isinstance(requires, basestring):
-            requires = [requires]
+        for requirement, import_path in markup.requires.items():
+            import_path = smart_bytes(import_path)
+            parts = import_path.split(b'.')
+            try:
+                req_callable = parts[-1]
+                leadup = b'.'.join(parts[:-1])
+                loaded = __import__(
+                    leadup, globals(), locals(), [req_callable], -1)
+                markup._requirements[requirement] = getattr(
+                    loaded, req_callable)
+            except ImportError as error:
+                failed.append((requirement, error))
 
-        for req in requires:
-            parts = req.split('.')
-            try:
-                name = parts[-1]
-                leadup = '.'.join(parts[:-1])
-                loaded = __import__(leadup, globals(), locals(), [name], -1)
-                required[name] = getattr(loaded, name)
-            except ImportError as error:
-                unfulfilled.append((name, error))
-
-        return unfulfilled
+        return failed
 
     def unregister_markup(self, markup_name):
         """Unregisters a markup converter with the name ``markup_name``.
 
 markup_pool = MarkupPool()  # Instance of ``MarkupPool`` for public use.
 register_markup = markup_pool.register_markup
-
-
-__all__ = ('markup_pool', 'register_markup', 'BaseMarkup')

File markupmirror/markup/markdown_.py

+from __future__ import absolute_import, unicode_literals
+
 from django.utils.translation import ugettext_lazy as _
 
 from markupmirror import settings
 from markupmirror.markup.base import BaseMarkup
-from markupmirror.markup.base import register_markup
+
+
+__all__ = ('MarkdownMarkup',)
 
 
 class MarkdownMarkup(BaseMarkup):
 
     """
     codemirror_mode = 'text/x-markdown'
-    title = _(u"Markdown")
-    requires = ("markdown.Markdown", )
+    title = _("Markdown")
+    requires = {
+        'markdown': 'markdown.Markdown',
+    }
 
     def __init__(self):
         self.extensions = settings.MARKUPMIRROR_MARKDOWN_EXTENSIONS
         self.output_format = settings.MARKUPMIRROR_MARKDOWN_OUTPUT_FORMAT
-        self.markdown = self.required['Markdown'](
+        self.markdown = self.requirements['markdown'](
             extensions=self.extensions,
             output_format=self.output_format)
 
-    def convert(self, markup):
+    def convert(self, markup, *args, **kwargs):
         return self.markdown.convert(markup)
-
-__all__ = ('MarkdownMarkup',)

File markupmirror/markup/plaintext.py

     codemirror_mode = 'text/plain'
     title = _("Plain text")
 
-    def convert(self, markup):
+    def convert(self, markup, *args, **kwargs):
         return urlize(linebreaks(markup))

File markupmirror/markup/restructuredtext.py

-from django.utils.encoding import smart_str
+from __future__ import absolute_import, unicode_literals
+
+try:
+    from django.utils.encoding import smart_bytes
+except ImportError:
+    from django.utils.encoding import smart_str as smart_bytes
+
 from django.utils.translation import ugettext_lazy as _
 
 from markupmirror.markup.base import BaseMarkup
-from markupmirror.markup.base import register_markup
+
+
+__all__ = ('ReStructuredTextMarkup',)
 
 
 class ReStructuredTextMarkup(BaseMarkup):
 
     """
     codemirror_mode = 'text/x-rst'
-    title = _(u"reStructuredText")
-    requires = ('docutils.core.publish_parts', )
+    title = _("reStructuredText")
+    requires = {
+        'rest': 'docutils.core.publish_parts',
+    }
 
     def __init__(self):
-        self.restructuredtext = self.required['publish_parts']
+        self.restructuredtext = self.requirements['rest']
 
-    def convert(self, markup):
+    def convert(self, markup, *args, **kwargs):
         parts = self.restructuredtext(
-            source=smart_str(markup),
+            source=smart_bytes(markup),
             writer_name='html4css1')
         # Intentionally return ``html_body`` instead of ``fragment`` as
         # Django's templatetag does. ``html_body`` also includes the document's
         # headlines (=== or ---), they would be stripped off the result
         # otherwise.
         return parts['html_body']
-
-__all__ = ('ReStructuredTextMarkup',)

File markupmirror/markup/textile_.py

+from __future__ import absolute_import, unicode_literals
+
 from django.utils.translation import ugettext_lazy as _
 
 from markupmirror import settings
 from markupmirror.markup.base import BaseMarkup
-from markupmirror.markup.base import register_markup
+
+
+__all__ = ('TextileMarkup',)
 
 
 class TextileMarkup(BaseMarkup):
 
     """
     codemirror_mode = 'text/plain'
-    title = _(u"Textile")
-    requires = ("textile.textile", )
+    title = _("Textile")
+    requires = {
+        'textile': 'textile.textile',
+    }
 
     def __init__(self):
         self.textile_settings = settings.MARKUPMIRROR_TEXTILE_SETTINGS
-        self.textile = self.required['textile']
+        self.textile = self.requirements['textile']
 
-    def convert(self, markup):
+    def convert(self, markup, *args, **kwargs):
         return self.textile(markup, **self.textile_settings)
-
-__all__ = ('TextileMarkup',)

File markupmirror/urls.py

 
 
 urlpatterns = [
-    '',
     url(r'^preview/$', MarkupPreview.as_view(), name='preview'),
     url(r'^base/$',
         TemplateView.as_view(

File tests/tests/markup_base.py

         """
         class DummyMarkup(BaseMarkup):
 
-            def before_convert(self, markup):
+            def before_convert(self, markup, *args, **kwargs):
                 return markup.replace("1", "2", 1)
 
-            def convert(self, markup):
+            def convert(self, markup, *args, **kwargs):
                 return markup.replace("2", "3", 1)
 
-            def after_convert(self, markup):
+            def after_convert(self, markup, *args, **kwargs):
                 return markup.replace("3", "4", 1)
 
         dummy_markup = DummyMarkup()

File tests/tests/markup_markdown.py

         """
         markdown_markup = MarkdownMarkup()
         # === becomes h2 because of headerid(level=2) extension
+        import ipdb; ipdb.set_trace()
         self.assertHTMLEqual(
             markdown_markup(MARKUP),
             textwrap.dedent("""\

File tests/urls.py

 from __future__ import absolute_import, unicode_literals
 
-from django.conf.urls.defaults import include
+from django.conf.urls.defaults import include, patterns
 
 import markupmirror.urls
 
 
-urlpatterns = [
+urlpatterns = patterns(
+    '',
     (r'^markupmirror/', include(markupmirror.urls.preview)),
-]
+)