Attachment 'plugin.patch'
Download 1 * looking for arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-488 to compare with
2 * comparing to arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-488
3 M MoinMoin/server/standalone.py
4 M MoinMoin/server/twistedmoin.py
5 M MoinMoin/_tests/test_pysupport.py
6 M wiki/server/moinmodpy.py
7 M MoinMoin/wikirpc.py
8 M wiki/server/moin.fcg
9 M MoinMoin/Page.py
10 M MoinMoin/multiconfig.py
11 M MoinMoin/formatter/base.py
12 M MoinMoin/macro/TableOfContents.py
13 M MoinMoin/parser/wiki.py
14 M MoinMoin/request.py
15 M MoinMoin/userform.py
16 M MoinMoin/util/pysupport.py
17 M MoinMoin/wikiaction.py
18 M MoinMoin/wikimacro.py
19 M MoinMoin/wikiutil.py
20 M MoinMoin/formatter/text_python.py
21 M MoinMoin/config.py
22
23 * modified files
24
25 --- orig/MoinMoin/Page.py
26 +++ mod/MoinMoin/Page.py
27 @@ -1011,8 +1011,8 @@
28 request.write(''.join(pi_formtext))
29
30 # try to load the parser
31 - Parser = wikiutil.importPlugin("parser", self.pi_format, "Parser",
32 - self.cfg.data_dir)
33 + Parser = wikiutil.importPlugin(self.request.cfg, "parser",
34 + self.pi_format, "Parser")
35 if Parser is None:
36 # default to plain text formatter (i.e. show the page source)
37 del Parser
38 @@ -1090,8 +1090,8 @@
39 self.getFormatterName() in self.cfg.caching_formats):
40 # Everything is fine, now check the parser:
41 if not parser:
42 - parser = wikiutil.importPlugin("parser", self.pi_format, "Parser",
43 - self.cfg.data_dir)
44 + parser = wikiutil.importPlugin(self.request.cfg, "parser",
45 + self.pi_format, "Parser")
46 return getattr(parser, 'caching', False)
47
48 return False
49
50
51 --- orig/MoinMoin/_tests/test_pysupport.py
52 +++ mod/MoinMoin/_tests/test_pysupport.py
53 @@ -2,83 +2,107 @@
54 """
55 MoinMoin - MoinMoin.util.pysupport Tests
56
57 - Module names must start with 'test_' to be included in the tests.
58 -
59 @copyright: 2004 by Jürgen Hermann <ograf@bitart.de>
60 @license: GNU GPL, see COPYING for details.
61 """
62
63 -import unittest, tempfile, os
64 +import unittest, os
65 from MoinMoin.util import pysupport
66 from MoinMoin._tests import request, TestConfig
67
68
69 -class ImportNameTestCase(unittest.TestCase):
70 - """ Test if importName works correctly
71 +class ImportNameFromMoinTestCase(unittest.TestCase):
72 + """ Test importName of MoinMoin modules
73 +
74 + We don't make any testing for files, assuming that moin package is
75 + not broken.
76 """
77
78 - def setUp(self):
79 - """ Create a dummy plugin path with some module inside
80 - """
81 - self.plugin_dir = tempfile.mktemp()
82 - self.parser_dir = os.path.join(self.plugin_dir, 'plugin', 'parser')
83 - os.makedirs(self.parser_dir)
84 - open(os.path.join(self.plugin_dir, 'plugin', '__init__.py'), 'w')
85 - open(os.path.join(self.plugin_dir, 'plugin', 'parser', '__init__.py'), 'w')
86 - f = open(os.path.join(self.parser_dir, 'test.py'), 'w')
87 - f.write('import sys, os\ndef test():\n pass\n')
88 - f.close()
89 -
90 - def tearDown(self):
91 - """ Remove the plugin dir
92 - """
93 - for file in (os.path.join(self.plugin_dir, 'plugin', '__init__.py'),
94 - os.path.join(self.plugin_dir, 'plugin', '__init__.pyc'),
95 - os.path.join(self.plugin_dir, 'plugin', 'parser', '__init__.py'),
96 - os.path.join(self.plugin_dir, 'plugin', 'parser', '__init__.pyc'),
97 - os.path.join(self.parser_dir, 'test.py'),
98 - os.path.join(self.parser_dir, 'test.pyc')):
99 - try:
100 - os.unlink(file)
101 - except OSError:
102 - pass
103 - for dir in (os.path.join(self.plugin_dir, 'plugin', 'parser'),
104 - os.path.join(self.plugin_dir, 'plugin'),
105 - self.plugin_dir):
106 - os.rmdir(dir)
107 -
108 - def testImportName1(self):
109 - """ pysupport: import nonexistant parser from moin
110 -
111 - Must return None."""
112 + def testNonExisting(self):
113 + """ pysupport: import nonexistant moin parser return None """
114 self.failIf(pysupport.importName('MoinMoin.parser.abcdefghijkl',
115 'Parser'))
116
117 - def testImportName2(self):
118 - """ pysupport: import wiki parser from moin
119 + def testExisting(self):
120 + """ pysupport: import wiki parser from moin succeed
121
122 This tests if a module can be imported from the package
123 - MoinMoin. Should never fail, cause importName uses
124 - __import__ to do it."""
125 + MoinMoin. Should never fail!
126 + """
127 self.failUnless(pysupport.importName('MoinMoin.parser.wiki',
128 'Parser'))
129 +
130
131 - def testImportName3(self):
132 - """ pysupport: import nonexistant parser plugin
133 +class ImportNameFromPluginTestCase(unittest.TestCase):
134 + """ Test if importName form wiki plugin package """
135 +
136 + def setUp(self):
137 + """ Check for valid plugin and parser packages """
138 + # Make sure we have valid plugin and parser dir
139 + self.plugindir = os.path.join(request.cfg.data_dir, 'plugin')
140 + assert os.path.exists(os.path.join(self.plugindir, '__init__.py')), \
141 + "Can't run tests, no plugin package"
142 + self.parserdir = os.path.join(self.plugindir, 'parser')
143 + assert os.path.exists(os.path.join(self.parserdir, '__init__.py')), \
144 + "Can't run tests, no plugin.parser package"
145 +
146 + def testNonEsisting(self):
147 + """ pysupport: import nonexistant plugin return None """
148 + name = 'abcdefghijkl'
149 +
150 + # Make sure that the file does not exists
151 + for suffix in ['.py', '.pyc']:
152 + path = os.path.join(self.parserdir, name + suffix)
153 + assert not os.path.exists(path), \
154 + "Can't run test, path exists: %r" % path
155
156 - Must return None."""
157 - self.failIf(pysupport.importName('plugin.parser.abcdefghijkl',
158 + self.failIf(pysupport.importName('plugin.parser.%s' % name,
159 'Parser'))
160
161 - def testImportName4(self):
162 - """ pysupport: import test parser plugin
163 + def testExisting(self):
164 + """ pysupport: import existing plugin succeed
165
166 Tests if a module can be importet from an arbitary path
167 like it is done in moin for plugins. Some strange bug
168 in the old implementation failed on an import of os,
169 cause os does a from os.path import that will stumple
170 - over a poisoned sys.modules."""
171 - self.failUnless(pysupport.importName('plugin.parser.test',
172 - 'test',
173 - self.plugin_dir),
174 - 'Failed to import the test plugin!')
175 + over a poisoned sys.modules.
176 + """
177 + # Save a test plugin
178 + pluginName = 'MoinMoinTestParser'
179 + data = '''
180 +import sys, os
181 +
182 +class Parser:
183 + pass
184 +'''
185 + pluginPath = os.path.join(self.parserdir, pluginName + '.py')
186 +
187 + # File must not exists - or we might destroy user data!
188 + for suffix in ['.py', '.pyc']:
189 + path = os.path.join(self.parserdir, pluginName + suffix)
190 + assert not os.path.exists(path), \
191 + "Can't run test, path exists: %r" % path
192 +
193 + try:
194 + # Write test plugin
195 + f = file(pluginPath, 'w')
196 + f.write(data)
197 + f.close()
198 +
199 + modulename = request.cfg.siteid + '.plugin.parser.' + pluginName
200 + name = 'Parser'
201 + plugin = pysupport.importName(modulename, name)
202 + self.failUnless(plugin, 'Failed to import the test plugin')
203 +
204 + # Check that we got class
205 + self.failUnless(isinstance(plugin(), plugin), \
206 + "Imported name is wrong")
207 +
208 + finally:
209 + # Remove the test plugin, including the pyc file.
210 + try:
211 + os.unlink(pluginPath)
212 + os.unlink(pluginPath + 'c')
213 + except OSError:
214 + pass
215
216
217 --- orig/MoinMoin/config.py
218 +++ mod/MoinMoin/config.py
219 @@ -4,6 +4,10 @@
220
221 import re
222
223 +# Threads flag - if you write a moin server that use threads, import
224 +# config in the server and set this flag to True.
225 +use_threads = False
226 +
227 # Charset - we support only 'utf-8'. While older encodings might work,
228 # we don't have the resources to test them, and there is no real
229 # benefit for the user.
230
231
232 --- orig/MoinMoin/formatter/base.py
233 +++ mod/MoinMoin/formatter/base.py
234 @@ -242,14 +242,12 @@
235 writes out the result instead of returning it!
236 """
237 if not is_parser:
238 - processor = wikiutil.importPlugin("processor",
239 - processor_name, "process",
240 - self.request.cfg.data_dir)
241 + processor = wikiutil.importPlugin(self.request.cfg, "processor",
242 + processor_name, "process")
243 processor(self.request, self, lines)
244 else:
245 - parser = wikiutil.importPlugin("parser",
246 - processor_name, "Parser",
247 - self.request.cfg.data_dir)
248 + parser = wikiutil.importPlugin(self.request.cfg, "parser",
249 + processor_name, "Parser")
250 args = self._get_bang_args(lines[0])
251 if args is not None:
252 lines=lines[1:]
253
254
255 --- orig/MoinMoin/formatter/text_python.py
256 +++ mod/MoinMoin/formatter/text_python.py
257 @@ -178,13 +178,11 @@
258 prints out the result insted of returning it!
259 """
260 if not is_parser:
261 - Dependencies = wikiutil.importPlugin("processor",
262 - processor_name, "Dependencies",
263 - self.request.cfg.data_dir)
264 + Dependencies = wikiutil.importPlugin(self.request.cfg, "processor",
265 + processor_name, "Dependencies")
266 else:
267 - Dependencies = wikiutil.importPlugin("parser",
268 - processor_name, "Dependencies",
269 - self.request.cfg.data_dir)
270 + Dependencies = wikiutil.importPlugin(self.request.cfg, "parser",
271 + processor_name, "Dependencies")
272
273 if Dependencies == None:
274 Dependencies = ["time"]
275
276
277 --- orig/MoinMoin/macro/TableOfContents.py
278 +++ mod/MoinMoin/macro/TableOfContents.py
279 @@ -62,7 +62,8 @@
280
281 def IncludeMacro(self, *args, **kwargs):
282 if self.include_macro is None:
283 - self.include_macro = wikiutil.importPlugin('macro', "Include")
284 + self.include_macro = wikiutil.importPlugin(self.macro.request.cfg,
285 + 'macro', "Include")
286 return self.pre_re.sub('',apply(self.include_macro, args, kwargs)).split('\n')
287
288 def run(self):
289
290
291 --- orig/MoinMoin/multiconfig.py
292 +++ mod/MoinMoin/multiconfig.py
293 @@ -70,9 +70,9 @@
294 module = __import__(name, globals(), {})
295 Config = getattr(module, 'Config', None)
296 if Config:
297 - # Config found, return config instance
298 - cfg = Config()
299 - cfg.siteid = name
300 + # Config found, return config instance using name as
301 + # site identifier (name must be uniqe of our url_re).
302 + cfg = Config(name)
303 return cfg
304 else:
305 # Broken config file, probably old config from 1.2
306 @@ -267,8 +267,9 @@
307 url_mappings = {}
308 SecurityPolicy = None
309
310 - def __init__(self):
311 + def __init__(self, siteid):
312 """ Init Config instance """
313 + self.siteid = siteid
314 if self.config_check_enabled:
315 self._config_check()
316
317 @@ -277,6 +278,9 @@
318
319 # Make sure directories are accessible
320 self._check_directories()
321 +
322 + # Load plugin module
323 + self._loadPluginModule()
324
325 # Normalize values
326 self.default_lang = self.default_lang.lower()
327 @@ -415,7 +419,52 @@
328 also the spelling of the directory name.
329 ''' % {'attr': attr, 'path': path,}
330 raise error.ConfigurationError(msg)
331 +
332 + def _loadPluginModule(self):
333 + """ import plugin module under configname.plugin """
334 + import sys, imp
335 +
336 + # First try to load the module from sys.modules - fast!
337 + name = self.siteid + '.plugin'
338
339 + # Get lock functions - require Python 2.3
340 + try:
341 + acquire_lock = imp.acquire_lock
342 + release_lock = imp.release_lock
343 + except AttributeError:
344 + def acquire_lock(): pass
345 + def release_lock(): pass
346 +
347 + # Lock other threads while we check and import
348 + acquire_lock()
349 + try:
350 + try:
351 + sys.modules[name]
352 + except KeyError:
353 + # Find module on disk and try to load - slow!
354 + fp, path, info = imp.find_module('plugin', [self.data_dir])
355 + try:
356 + try:
357 + # Load the module and set in sys.modules
358 + module = imp.load_module(name, fp, path, info)
359 + sys.modules[self.siteid].plugin = module
360 + finally:
361 + # Make sure fp is closed properly
362 + if fp:
363 + fp.close()
364 + except ImportError, err:
365 + msg = '''
366 +Could not import plugin package from "%(path)s" because of ImportError:
367 +%(err)s.
368 +
369 +Make sure your data directory path is correct, check permissions, and
370 +that the plugin directory has an __init__.py file.
371 +''' % {'path': self.data_dir, 'err': str(err)}
372 + raise error.ConfigurationError(msg)
373 +
374 + finally:
375 + release_lock()
376 +
377 def __getitem__(self, item):
378 """ Make it possible to access a config object like a dict """
379 return getattr(self, item)
380
381
382 --- orig/MoinMoin/parser/wiki.py
383 +++ mod/MoinMoin/parser/wiki.py
384 @@ -273,7 +273,7 @@
385 # can handle)
386 base, ext = os.path.splitext(url)
387 if inline:
388 - Parser = wikiutil.getParserForExtension(self.cfg, ext)
389 + Parser = wikiutil.getParserForExtension(self.request, ext)
390 if Parser is not None:
391 content = file(fpath, 'r').read()
392 # Try to decode text. It might return junk, but we don't
393 @@ -811,16 +811,12 @@
394 elif s_word[:2] == '#!':
395 # first try to find a processor for this (will go away in 1.4)
396 processor_name = s_word[2:].split()[0]
397 - self.processor = wikiutil.importPlugin("processor",
398 - processor_name,
399 - "process",
400 - self.request.cfg.data_dir)
401 + self.processor = wikiutil.importPlugin(
402 + self.request.cfg, "processor", processor_name, "process")
403 # now look for a parser with that name
404 if self.processor is None:
405 - self.processor = wikiutil.importPlugin("parser",
406 - processor_name,
407 - "Parser",
408 - self.request.cfg.data_dir)
409 + self.processor = wikiutil.importPlugin(
410 + self.request.cfg, "parser", processor_name, "Parser")
411 if self.processor:
412 self.processor_is_parser = 1
413
414 @@ -965,11 +961,13 @@
415 if self.cfg.allow_numeric_entities:
416 rules = ur'(?P<ent_numeric>&#\d{1,5};)|' + rules
417
418 + self.request.clock.start('compile_huge_and_ugly')
419 scan_re = re.compile(rules, re.UNICODE)
420 number_re = re.compile(self.ol_rule, re.UNICODE)
421 term_re = re.compile(self.dl_rule, re.UNICODE)
422 indent_re = re.compile("^\s*", re.UNICODE)
423 eol_re = re.compile(r'\r?\n', re.UNICODE)
424 + self.request.clock.stop('compile_huge_and_ugly')
425
426 # get text and replace TABs
427 rawtext = self.raw.expandtabs()
428 @@ -998,16 +996,13 @@
429 processor_name = ''
430 if (line.strip()[:2] == "#!"):
431 processor_name = line.strip()[2:].split()[0]
432 - self.processor = wikiutil.importPlugin("processor",
433 - processor_name,
434 - "process",
435 - self.request.cfg.data_dir)
436 + self.processor = wikiutil.importPlugin(
437 + self.request.cfg, "processor", processor_name, "process")
438 +
439 # now look for a parser with that name
440 if self.processor is None:
441 - self.processor = wikiutil.importPlugin("parser",
442 - processor_name,
443 - "Parser",
444 - self.request.cfg.data_dir)
445 + self.processor = wikiutil.importPlugin(
446 + self.request.cfg, "parser", processor_name, "Parser")
447 if self.processor:
448 self.processor_is_parser = 1
449 if self.processor:
450
451
452 --- orig/MoinMoin/request.py
453 +++ mod/MoinMoin/request.py
454 @@ -274,21 +274,14 @@
455 @return: success code
456 """
457 fallback = 0
458 - try:
459 - Theme = wikiutil.importPlugin('theme', theme_name,
460 - 'Theme',
461 - path=self.cfg.data_dir)
462 + Theme = wikiutil.importPlugin(self.cfg, 'theme', theme_name, 'Theme')
463 + if Theme is None:
464 + fallback = 1
465 + Theme = wikiutil.importPlugin(self.cfg, 'theme',
466 + self.cfg.theme_default, 'Theme')
467 if Theme is None:
468 - fallback = 1
469 - Theme = wikiutil.importPlugin('theme', self.cfg.theme_default,
470 - 'Theme',
471 - path=self.cfg.data_dir)
472 - if Theme is None:
473 - raise ImportError
474 - except ImportError:
475 - fallback = 2
476 - from MoinMoin.theme.modern import Theme
477 -
478 + fallback = 2
479 + from MoinMoin.theme.modern import Theme
480 self.theme = Theme(self)
481
482 return fallback
483 @@ -1592,6 +1585,10 @@
484 @param req: the mod_python request instance
485 """
486 try:
487 + # flags if headers sent out contained content-type or status
488 + self._have_ct = 0
489 + self._have_status = 0
490 +
491 req.add_common_vars()
492 self.mpyreq = req
493 # some mod_python 2.7.X has no get method for table objects,
494 @@ -1601,9 +1598,6 @@
495 else:
496 env=req.subprocess_env
497 self._setup_vars_from_std_env(env)
498 - # flags if headers sent out contained content-type or status
499 - self._have_ct = 0
500 - self._have_status = 0
501 RequestBase.__init__(self)
502
503 except error.FatalError, err:
504
505
506 --- orig/MoinMoin/server/standalone.py
507 +++ mod/MoinMoin/server/standalone.py
508 @@ -41,6 +41,12 @@
509 from MoinMoin.server import Config, switchUID
510 from MoinMoin.request import RequestStandAlone
511
512 +# Set threads flag, so other code can use proper locking
513 +from MoinMoin import config
514 +config.use_threads = True
515 +del config
516 +
517 +# Server globals
518 httpd = None
519 config = None
520 moin_requests_done = 0
521
522
523 --- orig/MoinMoin/server/twistedmoin.py
524 +++ mod/MoinMoin/server/twistedmoin.py
525 @@ -38,6 +38,12 @@
526 from MoinMoin.request import RequestTwisted
527 from MoinMoin.server import Config
528
529 +# Set threads flag, so other code can use proper locking
530 +from MoinMoin import config
531 +config.use_threads = True
532 +del config
533 +
534 +# Server globals
535 config = None
536
537
538
539
540 --- orig/MoinMoin/userform.py
541 +++ mod/MoinMoin/userform.py
542 @@ -343,7 +343,7 @@
543 """ Create theme selection. """
544 cur_theme = self.request.user.valid and self.request.user.theme_name or self.cfg.theme_default
545 options = []
546 - for theme in wikiutil.getPlugins('theme', self.request.cfg.data_dir):
547 + for theme in wikiutil.getPlugins('theme', self.request.cfg):
548 options.append((theme, theme))
549
550 return util.web.makeSelection('theme_name', options, cur_theme)
551
552
553 --- orig/MoinMoin/util/pysupport.py
554 +++ mod/MoinMoin/util/pysupport.py
555 @@ -10,6 +10,9 @@
556 ### Module import / Plugins
557 #############################################################################
558
559 +import sys
560 +
561 +
562 def isImportable(module):
563 """ Check whether a certain module is available.
564 """
565 @@ -34,67 +37,46 @@
566 return modules
567
568
569 -def importName(modulename, name, path=None):
570 - """ Import a named object from a module in the context of this function,
571 - which means you should use fully qualified module paths.
572 +def importName(modulename, name):
573 + """ Import name dynamically from module
574
575 - Return None on failure.
576 + Used to do dynamic import of modules and names that you know their
577 + names only in runtime.
578 +
579 + @param modulename: full qualified mudule name, e.g. x.y.z
580 + @param name: name to import from modulename
581 + @rtype: any object
582 + @return: name from module or None if there is no such name
583 """
584 - if path:
585 - #
586 - # see Python-src/Demo/imputil/knee.py how import should be done
587 - #
588 - import imp, sys
589 -
590 - items = modulename.split('.')
591 - parent = None
592 - real_path = [path]
593 - fqname = None
594 - for part_name in items:
595 - # keep full qualified module name up to date
596 - if fqname is None:
597 - fqname = part_name
598 - else:
599 - fqname = fqname + '.' + part_name
600 - ## this is the place to check sys.modules if the module
601 - ## is already available
602 - ## WARNING: this does not work with farm wikis, cause all
603 - ## farm plugin paths would map to the same 'plugin' top
604 - ## module!
605 - ## We need a dummy module ('wiki_'+sha(path)) to keep them
606 - ## apart (create with imp.new_module()?)
607 - # find & import the module
608 - try:
609 - fp, pathname, stuff = imp.find_module(part_name, real_path or parent.__path__)
610 - except ImportError:
611 - # no need to close fp here, cause its only open if no
612 - # error occurs
613 - return None
614 - try:
615 - try:
616 - mod = imp.load_module(fqname, fp, pathname, stuff)
617 - except ImportError: # ValueError, if fp is no file, not possible
618 - return None
619 - finally:
620 - if fp: fp.close()
621 - # update parent module up to date
622 - if parent is None:
623 - # we only need real_path for the first import, after
624 - # this parent.__path__ is enough
625 - real_path = None
626 - else:
627 - setattr(parent, part_name, mod)
628 - parent = mod
629 - return getattr(mod, name, None)
630 - else:
631 - # this part is for MoinMoin imports, we use __import__
632 - # which will also use sys.modules as cache, but thats
633 - # no harm cause MoinMoin is global anyway.
634 - try:
635 - module = __import__(modulename, globals(), {}, [name]) # {} was: locals()
636 - except ImportError:
637 - return None
638 + try:
639 + module = __import__(modulename, globals(), {}, [name])
640 return getattr(module, name, None)
641 + except ImportError:
642 + return None
643
644 -# if you look for importPlugin: see wikiutil.importPlugin
645
646 +def makeThreadSafe(function, lock=None):
647 + """ Call with a fuction you want to make thread safe
648 +
649 + Call without lock to make the fuction thread safe using one lock per
650 + function. Call with exsiting lock object if you want to make several
651 + fuctions use same lock, e.g. all fuctions that change same data
652 + structure.
653 +
654 + @param fuction: function to make thread safe
655 + @param lock: threading.Lock instance or None
656 + @rtype: function
657 + @retrun: function decoreated with locking
658 + """
659 + if lock is None:
660 + import threading
661 + lock = threading.Lock()
662 +
663 + def decorated(*args, **kw):
664 + lock.acquire()
665 + try:
666 + return function(*args, **kw)
667 + finally:
668 + lock.release()
669 +
670 + return decorated
671
672
673 --- orig/MoinMoin/wikiaction.py
674 +++ mod/MoinMoin/wikiaction.py
675 @@ -749,9 +749,8 @@
676 mimetype = u"text/plain"
677
678 # try to load the formatter
679 - Formatter = wikiutil.importPlugin("formatter",
680 - mimetype.translate({ord(u'/'): u'_', ord(u'.'): u'_'}), "Formatter",
681 - path=request.cfg.data_dir)
682 + Formatter = wikiutil.importPlugin(request.cfg, "formatter",
683 + mimetype.translate({ord(u'/'): u'_', ord(u'.'): u'_'}), "Formatter")
684 if Formatter is None:
685 # default to plain text formatter
686 del Formatter
687 @@ -846,9 +845,12 @@
688 if action in request.cfg.excluded_actions:
689 return None
690
691 - handler = wikiutil.importPlugin("action", action, identifier, request.cfg.data_dir)
692 - if handler: return handler
693 + handler = wikiutil.importPlugin(request.cfg, "action", action, identifier)
694 + if handler is None:
695 + handler = globals().get('do_' + action)
696 +
697 + return handler
698
699 - return globals().get('do_' + action, None)
700 +
701
702
703
704
705 --- orig/MoinMoin/wikimacro.py
706 +++ mod/MoinMoin/wikimacro.py
707 @@ -35,7 +35,7 @@
708 return cfg.macro_names
709 else:
710 lnames = names[:]
711 - lnames.extend(wikiutil.getPlugins('macro', cfg.data_dir))
712 + lnames.extend(wikiutil.getPlugins('macro', cfg))
713 return lnames
714
715 def _make_index_key(index_letters, additional_html=""):
716 @@ -96,7 +96,7 @@
717 self.cfg = self.request.cfg
718
719 def execute(self, macro_name, args):
720 - macro = wikiutil.importPlugin('macro', macro_name, path=self.cfg.data_dir)
721 + macro = wikiutil.importPlugin(self.request.cfg, 'macro', macro_name)
722 if macro:
723 return macro(self, args)
724
725 @@ -129,7 +129,8 @@
726 def get_dependencies(self, macro_name):
727 if self.Dependencies.has_key(macro_name):
728 return self.Dependencies[macro_name]
729 - result = wikiutil.importPlugin('macro', macro_name, 'Dependencies', self.cfg.data_dir)
730 + result = wikiutil.importPlugin(self.request.cfg, 'macro', macro_name,
731 + 'Dependencies')
732 if result != None:
733 return result
734 else:
735 @@ -366,7 +367,7 @@
736 row(_('Global extension macros'),
737 ', '.join(macro.extension_macros) or nonestr)
738 row(_('Local extension macros'),
739 - ', '.join(wikiutil.extensionPlugins('macro', self.cfg.data_dir)) or nonestr)
740 + ', '.join(wikiutil.wikiPlugins('macro', self.cfg)) or nonestr)
741 ext_actions = []
742 for a in action.extension_actions:
743 if not a in self.request.cfg.excluded_actions:
744
745
746 --- orig/MoinMoin/wikirpc.py
747 +++ mod/MoinMoin/wikirpc.py
748 @@ -391,7 +391,8 @@
749 fn = getattr(self, 'xmlrpc_' + method)
750 response = fn(*params)
751 except AttributeError:
752 - fn = wikiutil.importPlugin('xmlrpc', method, 'execute', self.request.cfg.data_dir)
753 + fn = wikiutil.importPlugin(self.request.cfg, 'xmlrpc', method,
754 + 'execute')
755 response = fn(self, *params)
756 except:
757 # report exception back to server
758
759
760 --- orig/MoinMoin/wikiutil.py
761 +++ mod/MoinMoin/wikiutil.py
762 @@ -5,11 +5,13 @@
763 @copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
764 @license: GNU GPL, see COPYING for details.
765 """
766 -
767 +
768 import os, re, difflib
769 +
770 from MoinMoin import util, version, config
771 from MoinMoin.util import pysupport
772
773 +
774 # Exceptions
775 class InvalidFileNameError(Exception):
776 """ Called when we find an invalid file name """
777 @@ -533,77 +535,121 @@
778 ### Plugins
779 #############################################################################
780
781 -def importPlugin(kind, name, function="execute", path=None):
782 +def importPlugin(cfg, kind, name, function="execute"):
783 + """ Import wiki or builtin plugin
784 +
785 + Returns an object from a plugin module or None if module or
786 + 'function' is not found.
787 +
788 + kind may be one of 'action', 'formatter', 'macro', 'processor',
789 + 'parser' or any other directory that exist in MoinMoin or
790 + data/plugin
791 +
792 + Wiki plugins will always override builtin plugins. If you want
793 + specific plugin, use either importWikiPlugin or importName directly.
794 +
795 + @param cfg: wiki config instance
796 + @param kind: what kind of module we want to import
797 + @param name: the name of the module
798 + @param function: the function name
799 + @rtype: callable
800 + @return: "function" of module "name" of kind "kind", or None
801 """
802 - Returns an object from a plugin module or None if module or 'function' is not found
803 - kind may be one of 'action', 'formatter', 'macro', 'processor', 'parser'
804 - or any other directory that exist in MoinMoin or data/plugin
805 + # Try to import from the wiki
806 + plugin = importWikiPlugin(cfg, kind, name, function)
807 + if plugin is None:
808 + # Try to get the plugin from MoinMoin
809 + modulename = 'MoinMoin.%s.%s' % (kind, name)
810 + plugin = pysupport.importName(modulename, function)
811 +
812 + return plugin
813 +
814 +
815 +# Here we cache our plugins until we have a wiki object.
816 +# WARNING: do not access directly in threaded enviromnent!
817 +_wiki_plugins = {}
818 +
819 +def importWikiPlugin(cfg, kind, name, function):
820 + """ Import plugin from the wiki data directory
821
822 + We try to import only ONCE - then cache the plugin, even if we got
823 + None. This way we prevent expensive import of existing plugins for
824 + each call to a plugin.
825 +
826 + @param cfg: wiki config instance
827 @param kind: what kind of module we want to import
828 @param name: the name of the module
829 @param function: the function name
830 @rtype: callable
831 - @return: "function" of module "name" of kind "kind"
832 + @return: "function" of module "name" of kind "kind", or None
833 """
834 - # First try data/plugins
835 - result = None
836 - if path:
837 - result = pysupport.importName("plugin." + kind + "." + name, function, path)
838 - if result == None:
839 - # then MoinMoin
840 - result = pysupport.importName("MoinMoin." + kind + "." + name, function)
841 - return result
842 + global _wiki_plugins
843 +
844 + # Wiki plugins are located under 'wikiconfigname.plugin' module.
845 + modulename = '%s.plugin.%s.%s' % (cfg.siteid, kind, name)
846 + key = (modulename, function)
847 + try:
848 + # Try cache first - fast!
849 + plugin = _wiki_plugins[key]
850 + except KeyError:
851 + # Try to import from disk and cache result - slow!
852 + plugin = pysupport.importName(modulename, function)
853 + _wiki_plugins[key] = plugin
854 +
855 + return plugin
856 +
857 +# If we use threads, make this function thread safe
858 +if config.use_threads:
859 + importWikiPlugin = pysupport.makeThreadSafe(importWikiPlugin)
860
861 def builtinPlugins(kind):
862 - """
863 - Gets a list of modules in MoinMoin.'kind'
864 + """ Gets a list of modules in MoinMoin.'kind'
865
866 @param kind: what kind of modules we look for
867 @rtype: list
868 @return: module names
869 """
870 - plugins = pysupport.importName("MoinMoin." + kind, "modules")
871 - if plugins == None:
872 - return []
873 - else:
874 - return plugins
875 + modulename = "MoinMoin." + kind
876 + plugins = pysupport.importName(modulename, "modules")
877 + return plugins or []
878
879 -def extensionPlugins(kind, path):
880 - """
881 - Gets a list of modules in data/plugin/'kind'
882 +
883 +def wikiPlugins(kind, cfg):
884 + """ Gets a list of modules in data/plugin/'kind'
885
886 @param kind: what kind of modules we look for
887 @rtype: list
888 @return: module names
889 """
890 - plugins = pysupport.importName("plugin." + kind, "modules", path=path)
891 - if plugins == None:
892 - return []
893 - else:
894 - return plugins
895 + # Wiki plugins are located in wikiconfig.plugin module
896 + modulename = '%s.plugin.%s' % (cfg.siteid, kind)
897 + plugins = pysupport.importName(modulename, "modules")
898 + return plugins or []
899
900
901 -def getPlugins(kind, path):
902 - """
903 - Gets a list of module names.
904 +def getPlugins(kind, cfg):
905 + """ Gets a list of plugin names of kind
906
907 @param kind: what kind of modules we look for
908 @rtype: list
909 @return: module names
910 """
911 - builtin_plugins = builtinPlugins(kind)
912 - extension_plugins = extensionPlugins(kind, path)[:] # use a copy to not destroy the value
913 - for module in builtin_plugins:
914 - if module not in extension_plugins:
915 - extension_plugins.append(module)
916 - return extension_plugins
917 + # Copy names from builtin plugins - so we dont destroy the value
918 + all_plugins = builtinPlugins(kind)[:]
919 +
920 + # Add extension plugins without duplicates
921 + for plugin in wikiPlugins(kind, cfg):
922 + if plugin not in all_plugins:
923 + all_plugins.append(plugin)
924 +
925 + return all_plugins
926
927
928 #############################################################################
929 ### Parsers
930 #############################################################################
931
932 -def getParserForExtension(cfg, extension):
933 +def getParserForExtension(request, extension):
934 """
935 Returns the Parser class of the parser fit to handle a file
936 with the given extension. The extension should be in the same
937 @@ -623,8 +669,8 @@
938 if not hasattr(cfg, '_EXT_TO_PARSER'):
939 import types
940 etp, etd = {}, None
941 - for pname in getPlugins('parser', cfg.data_dir):
942 - Parser = importPlugin('parser', pname, 'Parser', cfg.data_dir)
943 + for pname in getPlugins('parser', cfg):
944 + Parser = importPlugin(request.cfg, 'parser', pname, 'Parser')
945 if Parser is not None:
946 if hasattr(Parser, 'extensions'):
947 exts=Parser.extensions
948 @@ -633,9 +679,11 @@
949 etp[ext]=Parser
950 elif str(exts) == '*':
951 etd=Parser
952 - # is this setitem thread save?
953 + # Cache in cfg for current request - this is not thread safe
954 + # when cfg will be cached per wiki in long running process.
955 cfg._EXT_TO_PARSER=etp
956 cfg._EXT_TO_PARSER_DEFAULT=etd
957 +
958 return cfg._EXT_TO_PARSER.get(extension,cfg._EXT_TO_PARSER_DEFAULT)
959
960
961
962
963 --- orig/wiki/server/moin.fcg
964 +++ mod/wiki/server/moin.fcg
965 @@ -20,16 +20,31 @@
966 ## sys.path.insert(0, '/path/to/wikiconfig/dir')
967 ## sys.path.insert(0, '/path/to/farmconfig/dir')
968
969 +# Use threaded version or non-threaded version (default True)?
970 +threads = True
971 +
972 +
973 +# Code ------------------------------------------------------------------
974 +
975 +# Do not touch unless you know what you are doting!
976 +# TODO: move to server packge?
977 +
978 +# Set threads flag, so other code can use proper locking
979 +from MoinMoin import config
980 +config.use_threads = threads
981 +del config
982
983 from MoinMoin.request import RequestFastCGI
984 from MoinMoin.support import thfcgi
985
986 def handle_request(req, env, form):
987 - request = RequestFastCGI(req,env,form)
988 + request = RequestFastCGI(req, env, form)
989 request.run()
990
991 if __name__ == '__main__':
992 - # this is a multi-threaded FastCGI
993 - # use thfcgi.unTHFCGI for a single-threaded instance
994 - fcg = thfcgi.THFCGI(handle_request)
995 + if threads:
996 + fcg = thfcgi.THFCGI(handle_request)
997 + else:
998 + fcg = thfcgi.unTHFCGI(handle_request)
999 +
1000 fcg.run()
1001
1002
1003 --- orig/wiki/server/moinmodpy.py
1004 +++ mod/wiki/server/moinmodpy.py
1005 @@ -42,6 +42,14 @@
1006 ## sys.path.insert(0, '/path/to/farmconfig/dir')
1007
1008
1009 +# Set threads flag, so other code can use proper locking.
1010 +# TODO: It seems that modpy does not use threads, so we don't need to
1011 +# set it here. Do we have another method to check this?
1012 +from MoinMoin import config
1013 +config.use_threads = True
1014 +del config
1015 +
1016 +
1017 from MoinMoin.request import RequestModPy
1018
1019 def handler(request):
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.