Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/galaxy/util/pastescript/loadwsgi.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) | |
2 # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php | |
3 | |
4 # Mostly taken from PasteDeploy and stripped down for Galaxy | |
5 | |
6 import inspect | |
7 import os | |
8 import re | |
9 import sys | |
10 | |
11 import pkg_resources | |
12 from six import iteritems | |
13 from six.moves.urllib.parse import unquote | |
14 | |
15 from galaxy.util.getargspec import getfullargspec | |
16 from galaxy.util.properties import NicerConfigParser | |
17 | |
18 | |
19 __all__ = ('loadapp', 'loadserver', 'loadfilter', 'appconfig') | |
20 | |
21 # ---- from paste.deploy.compat -------------------------------------- | |
22 | |
23 """Python 2<->3 compatibility module""" | |
24 | |
25 | |
26 def print_(template, *args, **kwargs): | |
27 template = str(template) | |
28 if args: | |
29 template = template % args | |
30 elif kwargs: | |
31 template = template % kwargs | |
32 sys.stdout.writelines(template) | |
33 | |
34 | |
35 if sys.version_info < (3, 0): | |
36 def reraise(t, e, tb): | |
37 exec('raise t, e, tb', dict(t=t, e=e, tb=tb)) | |
38 else: | |
39 def reraise(t, e, tb): | |
40 exec('raise e from tb', dict(e=e, tb=tb)) | |
41 | |
42 # ---- from paste.deploy.util ---------------------------------------- | |
43 | |
44 | |
45 def fix_type_error(exc_info, callable, varargs, kwargs): | |
46 """ | |
47 Given an exception, this will test if the exception was due to a | |
48 signature error, and annotate the error with better information if | |
49 so. | |
50 | |
51 Usage:: | |
52 | |
53 try: | |
54 val = callable(*args, **kw) | |
55 except TypeError: | |
56 exc_info = fix_type_error(None, callable, args, kw) | |
57 raise exc_info[0], exc_info[1], exc_info[2] | |
58 """ | |
59 if exc_info is None: | |
60 exc_info = sys.exc_info() | |
61 if (exc_info[0] != TypeError or | |
62 str(exc_info[1]).find('argument') == -1 or | |
63 getattr(exc_info[1], '_type_error_fixed', False)): | |
64 return exc_info | |
65 exc_info[1]._type_error_fixed = True | |
66 argspec = inspect.formatargspec(*getfullargspec(callable)) | |
67 args = ', '.join(map(_short_repr, varargs)) | |
68 if kwargs and args: | |
69 args += ', ' | |
70 if kwargs: | |
71 kwargs = sorted(kwargs.keys()) | |
72 args += ', '.join('%s=...' % n for n in kwargs) | |
73 gotspec = '(%s)' % args | |
74 msg = '%s; got %s, wanted %s' % (exc_info[1], gotspec, argspec) | |
75 exc_info[1].args = (msg,) | |
76 return exc_info | |
77 | |
78 | |
79 def _short_repr(v): | |
80 v = repr(v) | |
81 if len(v) > 12: | |
82 v = v[:8] + '...' + v[-4:] | |
83 return v | |
84 | |
85 | |
86 def fix_call(callable, *args, **kw): | |
87 """ | |
88 Call ``callable(*args, **kw)`` fixing any type errors that come out. | |
89 """ | |
90 try: | |
91 val = callable(*args, **kw) | |
92 except TypeError: | |
93 exc_info = fix_type_error(None, callable, args, kw) | |
94 reraise(*exc_info) | |
95 return val | |
96 | |
97 | |
98 def lookup_object(spec): | |
99 """ | |
100 Looks up a module or object from a some.module:func_name specification. | |
101 To just look up a module, omit the colon and everything after it. | |
102 """ | |
103 parts, target = spec.split(':') if ':' in spec else (spec, None) | |
104 module = __import__(parts) | |
105 | |
106 for part in parts.split('.')[1:] + ([target] if target else []): | |
107 module = getattr(module, part) | |
108 | |
109 return module | |
110 | |
111 # ---- from paste.deploy.loadwsgi ------------------------------------ | |
112 | |
113 ############################################################ | |
114 # Utility functions | |
115 ############################################################ | |
116 | |
117 | |
118 def import_string(s): | |
119 return pkg_resources.EntryPoint.parse("x=" + s).load(False) | |
120 | |
121 | |
122 def _aslist(obj): | |
123 """ | |
124 Turn object into a list; lists and tuples are left as-is, None | |
125 becomes [], and everything else turns into a one-element list. | |
126 """ | |
127 if obj is None: | |
128 return [] | |
129 elif isinstance(obj, (list, tuple)): | |
130 return obj | |
131 else: | |
132 return [obj] | |
133 | |
134 | |
135 def _flatten(lst): | |
136 """ | |
137 Flatten a nested list. | |
138 """ | |
139 if not isinstance(lst, (list, tuple)): | |
140 return [lst] | |
141 result = [] | |
142 for item in lst: | |
143 result.extend(_flatten(item)) | |
144 return result | |
145 | |
146 | |
147 ############################################################ | |
148 # Object types | |
149 ############################################################ | |
150 | |
151 | |
152 class _ObjectType(object): | |
153 | |
154 name = None | |
155 egg_protocols = None | |
156 config_prefixes = None | |
157 | |
158 def __init__(self): | |
159 # Normalize these variables: | |
160 self.egg_protocols = [_aslist(p) for p in _aslist(self.egg_protocols)] | |
161 self.config_prefixes = [_aslist(p) for p in _aslist(self.config_prefixes)] | |
162 | |
163 def __repr__(self): | |
164 return '<%s protocols=%r prefixes=%r>' % ( | |
165 self.name, self.egg_protocols, self.config_prefixes) | |
166 | |
167 def invoke(self, context): | |
168 assert context.protocol in _flatten(self.egg_protocols) | |
169 return fix_call(context.object, | |
170 context.global_conf, **context.local_conf) | |
171 | |
172 | |
173 class _App(_ObjectType): | |
174 | |
175 name = 'application' | |
176 egg_protocols = ['paste.app_factory', 'paste.composite_factory', | |
177 'paste.composit_factory'] | |
178 config_prefixes = [['app', 'application'], ['composite', 'composit'], | |
179 'pipeline', 'filter-app'] | |
180 | |
181 def invoke(self, context): | |
182 if context.protocol in ('paste.composit_factory', | |
183 'paste.composite_factory'): | |
184 return fix_call(context.object, | |
185 context.loader, context.global_conf, | |
186 **context.local_conf) | |
187 elif context.protocol == 'paste.app_factory': | |
188 return fix_call(context.object, context.global_conf, **context.local_conf) | |
189 else: | |
190 assert 0, "Protocol %r unknown" % context.protocol | |
191 | |
192 | |
193 APP = _App() | |
194 | |
195 | |
196 class _Filter(_ObjectType): | |
197 name = 'filter' | |
198 egg_protocols = [['paste.filter_factory', 'paste.filter_app_factory']] | |
199 config_prefixes = ['filter'] | |
200 | |
201 def invoke(self, context): | |
202 if context.protocol == 'paste.filter_factory': | |
203 return fix_call(context.object, | |
204 context.global_conf, **context.local_conf) | |
205 elif context.protocol == 'paste.filter_app_factory': | |
206 def filter_wrapper(wsgi_app): | |
207 # This should be an object, so it has a nicer __repr__ | |
208 return fix_call(context.object, | |
209 wsgi_app, context.global_conf, | |
210 **context.local_conf) | |
211 return filter_wrapper | |
212 else: | |
213 assert 0, "Protocol %r unknown" % context.protocol | |
214 | |
215 | |
216 FILTER = _Filter() | |
217 | |
218 | |
219 class _Server(_ObjectType): | |
220 name = 'server' | |
221 egg_protocols = [['paste.server_factory', 'paste.server_runner']] | |
222 config_prefixes = ['server'] | |
223 | |
224 def invoke(self, context): | |
225 if context.protocol == 'paste.server_factory': | |
226 return fix_call(context.object, | |
227 context.global_conf, **context.local_conf) | |
228 elif context.protocol == 'paste.server_runner': | |
229 def server_wrapper(wsgi_app): | |
230 # This should be an object, so it has a nicer __repr__ | |
231 return fix_call(context.object, | |
232 wsgi_app, context.global_conf, | |
233 **context.local_conf) | |
234 return server_wrapper | |
235 else: | |
236 assert 0, "Protocol %r unknown" % context.protocol | |
237 | |
238 | |
239 SERVER = _Server() | |
240 | |
241 | |
242 # Virtual type: (@@: There's clearly something crufty here; | |
243 # this probably could be more elegant) | |
244 class _PipeLine(_ObjectType): | |
245 name = 'pipeline' | |
246 | |
247 def invoke(self, context): | |
248 app = context.app_context.create() | |
249 filters = [c.create() for c in context.filter_contexts] | |
250 filters.reverse() | |
251 for filter_ in filters: | |
252 app = filter_(app) | |
253 return app | |
254 | |
255 | |
256 PIPELINE = _PipeLine() | |
257 | |
258 | |
259 class _FilterApp(_ObjectType): | |
260 name = 'filter_app' | |
261 | |
262 def invoke(self, context): | |
263 next_app = context.next_context.create() | |
264 filter_ = context.filter_context.create() | |
265 return filter_(next_app) | |
266 | |
267 | |
268 FILTER_APP = _FilterApp() | |
269 | |
270 | |
271 class _FilterWith(_App): | |
272 name = 'filtered_with' | |
273 | |
274 def invoke(self, context): | |
275 filter_ = context.filter_context.create() | |
276 filtered = context.next_context.create() | |
277 if context.next_context.object_type is APP: | |
278 return filter_(filtered) | |
279 else: | |
280 # filtering a filter | |
281 def composed(app): | |
282 return filter_(filtered(app)) | |
283 return composed | |
284 | |
285 | |
286 FILTER_WITH = _FilterWith() | |
287 | |
288 ############################################################ | |
289 # Loaders | |
290 ############################################################ | |
291 | |
292 | |
293 def loadapp(uri, name=None, **kw): | |
294 return loadobj(APP, uri, name=name, **kw) | |
295 | |
296 | |
297 def loadfilter(uri, name=None, **kw): | |
298 return loadobj(FILTER, uri, name=name, **kw) | |
299 | |
300 | |
301 def loadserver(uri, name=None, **kw): | |
302 return loadobj(SERVER, uri, name=name, **kw) | |
303 | |
304 | |
305 def appconfig(uri, name=None, relative_to=None, global_conf=None): | |
306 context = loadcontext(APP, uri, name=name, | |
307 relative_to=relative_to, | |
308 global_conf=global_conf) | |
309 return context.config() | |
310 | |
311 | |
312 _loaders = {} | |
313 | |
314 | |
315 def loadobj(object_type, uri, name=None, relative_to=None, | |
316 global_conf=None): | |
317 context = loadcontext( | |
318 object_type, uri, name=name, relative_to=relative_to, | |
319 global_conf=global_conf) | |
320 return context.create() | |
321 | |
322 | |
323 def loadcontext(object_type, uri, name=None, relative_to=None, | |
324 global_conf=None): | |
325 if '#' in uri: | |
326 if name is None: | |
327 uri, name = uri.split('#', 1) | |
328 else: | |
329 # @@: Ignore fragment or error? | |
330 uri = uri.split('#', 1)[0] | |
331 if name is None: | |
332 name = 'main' | |
333 if ':' not in uri: | |
334 raise LookupError("URI has no scheme: %r" % uri) | |
335 scheme, path = uri.split(':', 1) | |
336 scheme = scheme.lower() | |
337 if scheme not in _loaders: | |
338 raise LookupError( | |
339 "URI scheme not known: %r (from %s)" | |
340 % (scheme, ', '.join(_loaders.keys()))) | |
341 return _loaders[scheme]( | |
342 object_type, | |
343 uri, path, name=name, relative_to=relative_to, | |
344 global_conf=global_conf) | |
345 | |
346 | |
347 def _loadconfig(object_type, uri, path, name, relative_to, | |
348 global_conf): | |
349 isabs = os.path.isabs(path) | |
350 # De-Windowsify the paths: | |
351 path = path.replace('\\', '/') | |
352 if not isabs: | |
353 if not relative_to: | |
354 raise ValueError( | |
355 "Cannot resolve relative uri %r; no relative_to keyword " | |
356 "argument given" % uri) | |
357 relative_to = relative_to.replace('\\', '/') | |
358 if relative_to.endswith('/'): | |
359 path = relative_to + path | |
360 else: | |
361 path = relative_to + '/' + path | |
362 if path.startswith('///'): | |
363 path = path[2:] | |
364 path = unquote(path) | |
365 loader = ConfigLoader(path) | |
366 if global_conf: | |
367 loader.update_defaults(global_conf, overwrite=False) | |
368 return loader.get_context(object_type, name, global_conf) | |
369 | |
370 | |
371 _loaders['config'] = _loadconfig | |
372 | |
373 | |
374 def _loadegg(object_type, uri, spec, name, relative_to, | |
375 global_conf): | |
376 loader = EggLoader(spec) | |
377 return loader.get_context(object_type, name, global_conf) | |
378 | |
379 | |
380 _loaders['egg'] = _loadegg | |
381 | |
382 | |
383 def _loadfunc(object_type, uri, spec, name, relative_to, | |
384 global_conf): | |
385 | |
386 loader = FuncLoader(spec) | |
387 return loader.get_context(object_type, name, global_conf) | |
388 | |
389 | |
390 _loaders['call'] = _loadfunc | |
391 | |
392 ############################################################ | |
393 # Loaders | |
394 ############################################################ | |
395 | |
396 | |
397 class _Loader(object): | |
398 | |
399 def get_app(self, name=None, global_conf=None): | |
400 return self.app_context( | |
401 name=name, global_conf=global_conf).create() | |
402 | |
403 def get_filter(self, name=None, global_conf=None): | |
404 return self.filter_context( | |
405 name=name, global_conf=global_conf).create() | |
406 | |
407 def get_server(self, name=None, global_conf=None): | |
408 return self.server_context( | |
409 name=name, global_conf=global_conf).create() | |
410 | |
411 def app_context(self, name=None, global_conf=None): | |
412 return self.get_context( | |
413 APP, name=name, global_conf=global_conf) | |
414 | |
415 def filter_context(self, name=None, global_conf=None): | |
416 return self.get_context( | |
417 FILTER, name=name, global_conf=global_conf) | |
418 | |
419 def server_context(self, name=None, global_conf=None): | |
420 return self.get_context( | |
421 SERVER, name=name, global_conf=global_conf) | |
422 | |
423 _absolute_re = re.compile(r'^[a-zA-Z]+:') | |
424 | |
425 def absolute_name(self, name): | |
426 """ | |
427 Returns true if the name includes a scheme | |
428 """ | |
429 if name is None: | |
430 return False | |
431 return self._absolute_re.search(name) | |
432 | |
433 | |
434 class ConfigLoader(_Loader): | |
435 | |
436 def __init__(self, filename): | |
437 self.filename = filename = filename.strip() | |
438 defaults = { | |
439 'here': os.path.dirname(os.path.abspath(filename)), | |
440 '__file__': os.path.abspath(filename) | |
441 } | |
442 self.parser = NicerConfigParser(filename, defaults=defaults) | |
443 self.parser.optionxform = str # Don't lower-case keys | |
444 with open(filename) as f: | |
445 self.parser.read_file(f) | |
446 | |
447 def update_defaults(self, new_defaults, overwrite=True): | |
448 for key, value in iteritems(new_defaults): | |
449 if not overwrite and key in self.parser._defaults: | |
450 continue | |
451 self.parser._defaults[key] = value | |
452 | |
453 def get_context(self, object_type, name=None, global_conf=None): | |
454 if self.absolute_name(name): | |
455 return loadcontext(object_type, name, | |
456 relative_to=os.path.dirname(self.filename), | |
457 global_conf=global_conf) | |
458 section = self.find_config_section( | |
459 object_type, name=name) | |
460 if global_conf is None: | |
461 global_conf = {} | |
462 else: | |
463 global_conf = global_conf.copy() | |
464 defaults = self.parser.defaults() | |
465 global_conf.update(defaults) | |
466 local_conf = {} | |
467 global_additions = {} | |
468 get_from_globals = {} | |
469 for option in self.parser.options(section): | |
470 if option.startswith('set '): | |
471 name = option[4:].strip() | |
472 global_additions[name] = global_conf[name] = ( | |
473 self.parser.get(section, option)) | |
474 elif option.startswith('get '): | |
475 name = option[4:].strip() | |
476 get_from_globals[name] = self.parser.get(section, option) | |
477 else: | |
478 if option in defaults: | |
479 # @@: It's a global option (?), so skip it | |
480 continue | |
481 local_conf[option] = self.parser.get(section, option) | |
482 for local_var, glob_var in get_from_globals.items(): | |
483 local_conf[local_var] = global_conf[glob_var] | |
484 if object_type in (APP, FILTER) and 'filter-with' in local_conf: | |
485 filter_with = local_conf.pop('filter-with') | |
486 else: | |
487 filter_with = None | |
488 if 'require' in local_conf: | |
489 for spec in local_conf['require'].split(): | |
490 pkg_resources.require(spec) | |
491 del local_conf['require'] | |
492 if section.startswith('filter-app:'): | |
493 context = self._filter_app_context( | |
494 object_type, section, name=name, | |
495 global_conf=global_conf, local_conf=local_conf, | |
496 global_additions=global_additions) | |
497 elif section.startswith('pipeline:'): | |
498 context = self._pipeline_app_context( | |
499 object_type, section, name=name, | |
500 global_conf=global_conf, local_conf=local_conf, | |
501 global_additions=global_additions) | |
502 elif 'use' in local_conf: | |
503 context = self._context_from_use( | |
504 object_type, local_conf, global_conf, global_additions, | |
505 section) | |
506 else: | |
507 context = self._context_from_explicit( | |
508 object_type, local_conf, global_conf, global_additions, | |
509 section) | |
510 if filter_with is not None: | |
511 filter_with_context = LoaderContext( | |
512 obj=None, | |
513 object_type=FILTER_WITH, | |
514 protocol=None, | |
515 global_conf=global_conf, local_conf=local_conf, | |
516 loader=self) | |
517 filter_with_context.filter_context = self.filter_context( | |
518 name=filter_with, global_conf=global_conf) | |
519 filter_with_context.next_context = context | |
520 return filter_with_context | |
521 return context | |
522 | |
523 def _context_from_use(self, object_type, local_conf, global_conf, | |
524 global_additions, section): | |
525 use = local_conf.pop('use') | |
526 context = self.get_context( | |
527 object_type, name=use, global_conf=global_conf) | |
528 context.global_conf.update(global_additions) | |
529 context.local_conf.update(local_conf) | |
530 if '__file__' in global_conf: | |
531 # use sections shouldn't overwrite the original __file__ | |
532 context.global_conf['__file__'] = global_conf['__file__'] | |
533 # @@: Should loader be overwritten? | |
534 context.loader = self | |
535 | |
536 if context.protocol is None: | |
537 # Determine protocol from section type | |
538 section_protocol = section.split(':', 1)[0] | |
539 if section_protocol in ('application', 'app'): | |
540 context.protocol = 'paste.app_factory' | |
541 elif section_protocol in ('composit', 'composite'): | |
542 context.protocol = 'paste.composit_factory' | |
543 else: | |
544 # This will work with 'server' and 'filter', otherwise it | |
545 # could fail but there is an error message already for | |
546 # bad protocols | |
547 context.protocol = 'paste.%s_factory' % section_protocol | |
548 | |
549 return context | |
550 | |
551 def _context_from_explicit(self, object_type, local_conf, global_conf, | |
552 global_addition, section): | |
553 possible = [] | |
554 for protocol_options in object_type.egg_protocols: | |
555 for protocol in protocol_options: | |
556 if protocol in local_conf: | |
557 possible.append((protocol, local_conf[protocol])) | |
558 break | |
559 if len(possible) > 1: | |
560 raise LookupError( | |
561 "Multiple protocols given in section %r: %s" | |
562 % (section, possible)) | |
563 if not possible: | |
564 raise LookupError( | |
565 "No loader given in section %r" % section) | |
566 found_protocol, found_expr = possible[0] | |
567 del local_conf[found_protocol] | |
568 value = import_string(found_expr) | |
569 context = LoaderContext( | |
570 value, object_type, found_protocol, | |
571 global_conf, local_conf, self) | |
572 return context | |
573 | |
574 def _filter_app_context(self, object_type, section, name, | |
575 global_conf, local_conf, global_additions): | |
576 if 'next' not in local_conf: | |
577 raise LookupError( | |
578 "The [%s] section in %s is missing a 'next' setting" | |
579 % (section, self.filename)) | |
580 next_name = local_conf.pop('next') | |
581 context = LoaderContext(None, FILTER_APP, None, global_conf, | |
582 local_conf, self) | |
583 context.next_context = self.get_context( | |
584 APP, next_name, global_conf) | |
585 if 'use' in local_conf: | |
586 context.filter_context = self._context_from_use( | |
587 FILTER, local_conf, global_conf, global_additions, | |
588 section) | |
589 else: | |
590 context.filter_context = self._context_from_explicit( | |
591 FILTER, local_conf, global_conf, global_additions, | |
592 section) | |
593 return context | |
594 | |
595 def _pipeline_app_context(self, object_type, section, name, | |
596 global_conf, local_conf, global_additions): | |
597 if 'pipeline' not in local_conf: | |
598 raise LookupError( | |
599 "The [%s] section in %s is missing a 'pipeline' setting" | |
600 % (section, self.filename)) | |
601 pipeline = local_conf.pop('pipeline').split() | |
602 if local_conf: | |
603 raise LookupError( | |
604 "The [%s] pipeline section in %s has extra " | |
605 "(disallowed) settings: %s" | |
606 % (', '.join(local_conf.keys()))) | |
607 context = LoaderContext(None, PIPELINE, None, global_conf, | |
608 local_conf, self) | |
609 context.app_context = self.get_context( | |
610 APP, pipeline[-1], global_conf) | |
611 context.filter_contexts = [ | |
612 self.get_context(FILTER, pname, global_conf) | |
613 for pname in pipeline[:-1]] | |
614 return context | |
615 | |
616 def find_config_section(self, object_type, name=None): | |
617 """ | |
618 Return the section name with the given name prefix (following the | |
619 same pattern as ``protocol_desc`` in ``config``. It must have the | |
620 given name, or for ``'main'`` an empty name is allowed. The | |
621 prefix must be followed by a ``:``. | |
622 | |
623 Case is *not* ignored. | |
624 """ | |
625 possible = [] | |
626 for name_options in object_type.config_prefixes: | |
627 for name_prefix in name_options: | |
628 found = self._find_sections( | |
629 self.parser.sections(), name_prefix, name) | |
630 if found: | |
631 possible.extend(found) | |
632 break | |
633 if not possible: | |
634 raise LookupError( | |
635 "No section %r (prefixed by %s) found in config %s" | |
636 % (name, | |
637 ' or '.join(map(repr, _flatten(object_type.config_prefixes))), | |
638 self.filename)) | |
639 if len(possible) > 1: | |
640 raise LookupError( | |
641 "Ambiguous section names %r for section %r (prefixed by %s) " | |
642 "found in config %s" | |
643 % (possible, name, | |
644 ' or '.join(map(repr, _flatten(object_type.config_prefixes))), | |
645 self.filename)) | |
646 return possible[0] | |
647 | |
648 def _find_sections(self, sections, name_prefix, name): | |
649 found = [] | |
650 if name is None: | |
651 if name_prefix in sections: | |
652 found.append(name_prefix) | |
653 name = 'main' | |
654 for section in sections: | |
655 if section.startswith(name_prefix + ':'): | |
656 if section[len(name_prefix) + 1:].strip() == name: | |
657 found.append(section) | |
658 return found | |
659 | |
660 | |
661 class EggLoader(_Loader): | |
662 | |
663 def __init__(self, spec): | |
664 self.spec = spec | |
665 | |
666 def get_context(self, object_type, name=None, global_conf=None): | |
667 if self.absolute_name(name): | |
668 return loadcontext(object_type, name, | |
669 global_conf=global_conf) | |
670 entry_point, protocol, ep_name = self.find_egg_entry_point( | |
671 object_type, name=name) | |
672 return LoaderContext( | |
673 entry_point, | |
674 object_type, | |
675 protocol, | |
676 global_conf or {}, {}, | |
677 self, | |
678 distribution=pkg_resources.get_distribution(self.spec), | |
679 entry_point_name=ep_name) | |
680 | |
681 def find_egg_entry_point(self, object_type, name=None): | |
682 """ | |
683 Returns the (entry_point, protocol) for the with the given | |
684 ``name``. | |
685 """ | |
686 if name is None: | |
687 name = 'main' | |
688 possible = [] | |
689 for protocol_options in object_type.egg_protocols: | |
690 for protocol in protocol_options: | |
691 pkg_resources.require(self.spec) | |
692 entry = pkg_resources.get_entry_info( | |
693 self.spec, | |
694 protocol, | |
695 name) | |
696 if entry is not None: | |
697 possible.append((entry.load(), protocol, entry.name)) | |
698 break | |
699 if not possible: | |
700 # Better exception | |
701 dist = pkg_resources.get_distribution(self.spec) | |
702 raise LookupError( | |
703 "Entry point %r not found in egg %r (dir: %s; protocols: %s; " | |
704 "entry_points: %s)" | |
705 % (name, self.spec, | |
706 dist.location, | |
707 ', '.join(_flatten(object_type.egg_protocols)), | |
708 ', '.join(_flatten([ | |
709 list((pkg_resources.get_entry_info(self.spec, prot, name) or {}).keys()) | |
710 for prot in protocol_options] or '(no entry points)')))) | |
711 if len(possible) > 1: | |
712 raise LookupError( | |
713 "Ambiguous entry points for %r in egg %r (protocols: %s)" | |
714 % (name, self.spec, ', '.join(_flatten(protocol_options)))) | |
715 return possible[0] | |
716 | |
717 | |
718 class FuncLoader(_Loader): | |
719 """ Loader that supports specifying functions inside modules, without | |
720 using eggs at all. Configuration should be in the format: | |
721 use = call:my.module.path:function_name | |
722 | |
723 Dot notation is supported in both the module and function name, e.g.: | |
724 use = call:my.module.path:object.method | |
725 """ | |
726 | |
727 def __init__(self, spec): | |
728 self.spec = spec | |
729 if ':' not in spec: | |
730 raise LookupError("Configuration not in format module:function") | |
731 | |
732 def get_context(self, object_type, name=None, global_conf=None): | |
733 obj = lookup_object(self.spec) | |
734 return LoaderContext( | |
735 obj, | |
736 object_type, | |
737 None, # determine protocol from section type | |
738 global_conf or {}, | |
739 {}, | |
740 self, | |
741 ) | |
742 | |
743 | |
744 class LoaderContext(object): | |
745 | |
746 def __init__(self, obj, object_type, protocol, | |
747 global_conf, local_conf, loader, | |
748 distribution=None, entry_point_name=None): | |
749 self.object = obj | |
750 self.object_type = object_type | |
751 self.protocol = protocol | |
752 # assert protocol in _flatten(object_type.egg_protocols), ( | |
753 # "Bad protocol %r; should be one of %s" | |
754 # % (protocol, ', '.join(map(repr, _flatten(object_type.egg_protocols))))) | |
755 self.global_conf = global_conf | |
756 self.local_conf = local_conf | |
757 self.loader = loader | |
758 self.distribution = distribution | |
759 self.entry_point_name = entry_point_name | |
760 | |
761 def create(self): | |
762 return self.object_type.invoke(self) | |
763 | |
764 def config(self): | |
765 conf = AttrDict(self.global_conf) | |
766 conf.update(self.local_conf) | |
767 conf.local_conf = self.local_conf | |
768 conf.global_conf = self.global_conf | |
769 conf.context = self | |
770 return conf | |
771 | |
772 | |
773 class AttrDict(dict): | |
774 """ | |
775 A dictionary that can be assigned to. | |
776 """ | |
777 pass |