Back to index

moin  1.9.0~rc2
text_python.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - "text/python" Formatter
00004 
00005     Compiles pages to executable python code.
00006 
00007     @copyright: 2000-2004 by Juergen Hermann <jh@web.de>
00008     @license: GNU GPL, see COPYING for details.
00009 """
00010 
00011 import time
00012 from MoinMoin import wikiutil
00013 
00014 
00015 class Formatter:
00016     """
00017         Inserts '<<<>>>' into the page and adds python code to
00018         self.code_fragments for dynamic parts of the page
00019         (as macros, wikinames...).
00020         Static parts are formatted with an external formatter.
00021         Page must be assembled after the parsing to get working python code.
00022     """
00023 
00024     defaultDependencies = ["time"]
00025 
00026     def __init__(self, request, static=[], formatter=None, **kw):
00027         if formatter:
00028             self.formatter = formatter
00029         else:
00030             from MoinMoin.formatter.text_html import Formatter
00031             self.formatter = Formatter(request, store_pagelinks=1)
00032         self.static = static
00033         self.code_fragments = []
00034         self.__formatter = "formatter"
00035         self.__parser = "parser"
00036         self.request = request
00037         self.__lang = request.current_lang
00038         self.__in_p = 0
00039         self.__in_pre = 0
00040         self.text_cmd_begin = '\nrequest.write('
00041         self.text_cmd_end = ')\n'
00042 
00043     def assemble_code(self, text):
00044         """inserts the code into the generated text
00045         """
00046         # Automatic invalidation due to moin code changes:
00047         # we are called from Page.py, so moincode_timestamp is
00048         # mtime of MoinMoin directory. If we detect, that the
00049         # saved rendering code is older than the MoinMoin directory
00050         # we invalidate it by raising an exception. This avoids
00051         # calling functions that have changed by a code update.
00052         # Hint: we don't check the mtime of the directories within
00053         # MoinMoin, so better do a touch if you only modified stuff
00054         # in a subdirectory.
00055         waspcode_timestamp = int(time.time())
00056         source = ["""
00057 moincode_timestamp = int(os.path.getmtime(os.path.dirname(__file__)))
00058 cfg_mtime = getattr(request.cfg, "cfg_mtime", None)
00059 if moincode_timestamp > %d or cfg_mtime is None or cfg_mtime > %d:
00060     raise Exception("CacheNeedsUpdate")
00061 """ % (waspcode_timestamp, waspcode_timestamp)]
00062 
00063 
00064         text = text.split('<<<>>>', len(self.code_fragments))
00065         source.extend([self.text_cmd_begin, repr(text[0])])
00066         i = 0
00067         for t in text[1:]:
00068             source.extend([self.text_cmd_end,
00069                            self.code_fragments[i],
00070                            self.text_cmd_begin,
00071                            repr(text[i+1])])
00072             i += 1
00073         source.append(self.text_cmd_end)
00074         source.append(self.__adjust_formatter_state())
00075 
00076         self.code_fragments = [] # clear code fragments to make
00077                                  # this object reusable
00078         return "".join(source)
00079 
00080     def __cache_if_no_id(self, name, *args, **kw):
00081         if not 'id' in kw:
00082             return getattr(self.formatter, name)(*args, **kw)
00083         else:
00084             return self.__insert_code('request.write(%s.%s(*%r, **%r))' %
00085                 (self.__formatter, name, args, kw))
00086 
00087     def __getattr__(self, name):
00088         """
00089         For every thing we have no method/attribute use the formatter
00090         unless there's an ID in the keywords.
00091         """
00092         attr = getattr(self.formatter, name)
00093         if callable(attr):
00094             return lambda *args, **kw: self.__cache_if_no_id(name, *args, **kw)
00095         else:
00096             return attr
00097 
00098     def __insert_code(self, call):
00099         """ returns the python code
00100         """
00101         self.code_fragments.append(call)
00102         return '<<<>>>'
00103 
00104     def __insert_fmt_call(self, function, *args, **kw):
00105         return self.__insert_code('request.write(%s.%s(*%r, **%r))' % (
00106             self.__formatter, function, args, kw))
00107 
00108     def __is_static(self, dependencies):
00109         for dep in dependencies:
00110             if dep not in self.static:
00111                 return False
00112         return True
00113 
00114     def __adjust_language_state(self):
00115         """ Add current language state changing code to the cache """
00116         if self.__lang != self.request.current_lang:
00117             self.__lang = self.request.current_lang
00118             return 'request.current_lang = %r\n' % self.__lang
00119         return ''
00120 
00121     def __adjust_formatter_state(self):
00122         result = self.__adjust_language_state()
00123         if self.__in_p != self.formatter.in_p:
00124             result = "%s%s.in_p = %r\n" % (result, self.__formatter,
00125                                            self.formatter.in_p)
00126             self.__in_p = self.formatter.in_p
00127         if self.__in_pre != self.formatter.in_pre:
00128             result = "%s%s.in_pre = %r\n" % (result, self.__formatter,
00129                                            self.formatter.in_pre)
00130             self.__in_pre = self.formatter.in_pre
00131         return result
00132 
00133     # Public methods ---------------------------------------------------
00134 
00135     def pagelink(self, on, pagename='', page=None, **kw):
00136         if on:
00137             return self.__insert_code('page=Page(request, %r, formatter=%s);'
00138                                       'request.write(%s.pagelink(%r, page=page, **%r))' %
00139                                       (pagename, self.__formatter,
00140                                        self.__formatter, on, kw))
00141         else:
00142             return self.__insert_code('request.write(%s.pagelink(%r, page=page, **%r))' %
00143                                       (self.__formatter, on, kw))
00144 
00145     def attachment_link(self, on, url=None, **kw):
00146         return self.__insert_fmt_call('attachment_link', on, url, **kw)
00147 
00148     def attachment_image(self, url, **kw):
00149         return self.__insert_fmt_call('attachment_image', url, **kw)
00150 
00151     def attachment_drawing(self, url, text, **kw):
00152         return self.__insert_fmt_call('attachment_drawing', url, text, **kw)
00153 
00154     def attachment_inlined(self, url, text, **kw):
00155         return self.__insert_fmt_call('attachment_inlined', url, text, **kw)
00156 
00157     def heading(self, on, depth, **kw):
00158         if on:
00159             code = [
00160                 self.__adjust_language_state(),
00161                 'request.write(%s.heading(%r, %r, **%r))' % (self.__formatter,
00162                                                              on, depth, kw),
00163                 ]
00164             return self.__insert_code(''.join(code))
00165         else:
00166             return self.formatter.heading(on, depth, **kw)
00167 
00168     def icon(self, type):
00169         if self.__is_static(['user']):
00170             return self.formatter.icon(type)
00171         else:
00172             return self.__insert_fmt_call('icon', type)
00173 
00174     smiley = icon
00175 
00176     def span(self, on, **kw):
00177         if on and 'comment' in kw.get('css_class', '').split():
00178             return self.__insert_fmt_call('span', on, **kw)
00179         else:
00180             return self.formatter.span(on, **kw)
00181 
00182     def div(self, on, **kw):
00183         if on and 'comment' in kw.get('css_class', '').split():
00184             return self.__insert_fmt_call('div', on, **kw)
00185         else:
00186             return self.formatter.div(on, **kw)
00187 
00188     def macro(self, macro_obj, name, args, markup=None):
00189         if self.__is_static(macro_obj.get_dependencies(name)):
00190             # XXX: why is this necessary??
00191             macro_obj.formatter = self
00192             return macro_obj.execute(name, args)
00193         else:
00194             return self.__insert_code(
00195                 '%srequest.write(%s.macro(macro_obj, %r, %r, %r))' %
00196                 (self.__adjust_formatter_state(),
00197                  self.__formatter, name, args, markup))
00198 
00199     def parser(self, parser_name, lines):
00200         """ parser_name MUST be valid!
00201             prints out the result instead of returning it!
00202         """
00203         try:
00204             Dependencies = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", parser_name, "Dependencies")
00205         except (wikiutil.PluginMissingError, wikiutil.PluginAttributeError):
00206             Dependencies = self.defaultDependencies
00207 
00208         if self.__is_static(Dependencies):
00209             return self.formatter.parser(parser_name, lines)
00210         else:
00211             return self.__insert_code('%s%s.parser(%r, %r)' %
00212                                       (self.__adjust_formatter_state(),
00213                                        self.__formatter,
00214                                        parser_name, lines))
00215 
00216     def startContent(self, content_id='content', newline=True, **kw):
00217         # we need to tell the request about the start of the content
00218         return self.__insert_fmt_call('startContent', content_id, newline, **kw)
00219 
00220     def endContent(self, newline=True):
00221         # we need to tell the request about the end of the content
00222         return self.__insert_fmt_call('endContent', newline)
00223 
00224     def anchorlink(self, on, name='', **kw):
00225         # anchorlink depends on state now, namely the include ID in the request.
00226         if on:
00227             return self.__insert_fmt_call('anchorlink', on, name, **kw)
00228         else:
00229             return self.formatter.anchorlink(on, name=name, **kw)
00230 
00231     def line_anchorlink(self, on, lineno=0):
00232         # anchorlink depends on state now, namely the include ID in the request.
00233         if on:
00234             return self.__insert_fmt_call('line_anchorlink', on, lineno)
00235         else:
00236             return self.formatter.line_anchorlink(on, lineno)
00237 
00238     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1, msg=None):
00239         if on:
00240             # update state of the HTML formatter
00241             self.formatter._in_code_area = 1
00242             self.formatter._in_code_line = 0
00243             self.formatter._code_area_state = [None, show, start, step, start]
00244             return self.__insert_fmt_call('code_area', on, code_id, code_type, show,
00245                                           start, step)
00246         else:
00247             return self.formatter.code_area(False, code_id, code_type, show, start, step)
00248 
00249     def line_anchordef(self, lineno):
00250         return self.__insert_fmt_call('line_anchordef', lineno)
00251 
00252     def anchordef(self, id):
00253         return self.__insert_fmt_call('anchordef', id)