Package eoxserver :: Package core :: Module interfaces
[hide private]
[frames] | no frames]

Source Code for Module eoxserver.core.interfaces

  1  #------------------------------------------------------------------------------- 
  2  # $Id: interfaces.py 2123 2012-11-20 15:22:17Z schindlerf $ 
  3  # 
  4  # Project: EOxServer <http://eoxserver.org> 
  5  # Authors: Stephan Krause <stephan.krause@eox.at> 
  6  #          Stephan Meissl <stephan.meissl@eox.at> 
  7  # 
  8  #------------------------------------------------------------------------------- 
  9  # Copyright (C) 2011 EOX IT Services GmbH 
 10  # 
 11  # Permission is hereby granted, free of charge, to any person obtaining a copy 
 12  # of this software and associated documentation files (the "Software"), to deal 
 13  # in the Software without restriction, including without limitation the rights 
 14  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
 15  # copies of the Software, and to permit persons to whom the Software is  
 16  # furnished to do so, subject to the following conditions: 
 17  # 
 18  # The above copyright notice and this permission notice shall be included in all 
 19  # copies of this Software or works derived from this Software. 
 20  # 
 21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 22  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 23  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 24  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 25  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 26  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 27  # THE SOFTWARE. 
 28  #------------------------------------------------------------------------------- 
 29   
 30  """ 
 31  This module contains the core logic for interface declaration and validation. 
 32  """ 
 33   
 34  import types 
 35  import logging 
 36   
 37  from eoxserver.core.exceptions import InternalError, TypeMismatch, ConfigError 
 38   
 39   
 40  logger = logging.getLogger(__name__) 
 41   
 42  global RUNTIME_VALIDATION_LEVEL 
 43   
 44  RUNTIME_VALIDATION_LEVEL = "trust" 
45 46 #------------------------------------------------------------------------------- 47 # Argument classes 48 #------------------------------------------------------------------------------- 49 50 -class Arg(object):
51 """ 52 This is the common base class for arguments of any kind; it can be used 53 in interface declarations as well to represent an argument of arbitrary 54 type. 55 56 The constructor requires a ``name`` argument which denotes the argument 57 name. The validation will check at class creation time if the method of an 58 implementing class defines an argument of the given name, so you should 59 always use valid Python variable names here (you can use arbitrary strings 60 for return value declarations though). 61 62 Furthermore, the constructor accepts a ``default`` keyword argument which 63 defines a default value for the declared argument. The validation will 64 check at class creation time if this default value is present in the 65 implementing class and fail if it is not. 66 67 Its methods are intended for internal use in runtime validation. 68 """ 69
70 - def __init__(self, name, **kwargs):
71 self.name = name 72 73 if "default" in kwargs: 74 self.default = kwargs["default"] 75 self.optional = True 76 else: 77 self.default = None 78 self.optional = False
79
80 - def isOptional(self):
81 """ 82 Returns ``True`` if the argument is optional, meaning that a default 83 value has been defined for it, ``False`` otherwise. 84 """ 85 86 return self.optional
87
88 - def isValid(self, arg_value):
89 """ 90 Returns ``True`` if ``arg_value`` is an acceptable value for the 91 argument, ``False`` otherwise. Acceptable values are either the default 92 value if it has been defined or values of the expected type. 93 """ 94 95 return (self.optional and self.default == arg_value) or \ 96 self.isValidType(arg_value)
97
98 - def isValidType(self, arg_value):
99 """ 100 Returns ``True`` if the argument value ``arg_value`` has a valid type, 101 ``False`` otherwise. This method is overridden by :class:`Arg` 102 subclasses in order to check for individual types. The base class 103 implementation always returns ``True`` meaning that all types of 104 argument values are accepted. 105 """ 106 107 return True
108
109 - def getExpectedType(self):
110 """ 111 Returns the expected type name; used in error messages only. This 112 method is overridden by :class:`Arg` subclasses in order to customize 113 error reporting. The base class implementation returns ``""``. 114 """ 115 116 return ""
117
118 -class StrArg(Arg):
119 """ 120 Represents an argument of type :class:`str`. 121 """
122 - def isValidType(self, arg_value):
123 return isinstance(arg_value, str)
124
125 - def getExpectedType(self):
126 return "str"
127
128 -class UnicodeArg(Arg):
129 """ 130 Represents an argument of type :class:`unicode`. 131 """
132 - def isValidType(self, arg_value):
133 return isinstance(arg_value, unicode)
134
135 - def getExpectedType(self):
136 return "unicode"
137
138 -class StringArg(Arg):
139 """ 140 Represents an argument of types :class:`str` or :class:`unicode`. 141 """
142 - def isValidType(self, arg_value):
143 return (isinstance(arg_value, str) or isinstance(arg_value, unicode))
144
145 - def getExpectedType(self):
146 return "str' or 'unicode"
147
148 -class BoolArg(Arg):
149 """ 150 Represents an argument of type :class:`bool`. 151 """
152 - def isValidType(self, arg_value):
153 return isinstance(arg_value, bool)
154
155 - def getExpectedType(self):
156 return "bool"
157
158 -class IntArg(Arg):
159 """ 160 Represents an argument of type :class:`int`. 161 """
162 - def isValidType(self, arg_value):
163 return isinstance(arg_value, int)
164
165 - def getExpectedType(self):
166 return "int"
167
168 -class LongArg(Arg):
169 """ 170 Represents an argument of type :class:`long`. 171 """
172 - def isValidType(self, arg_value):
173 return isinstance(arg_value, long)
174
175 - def getExpectedType(self):
176 return "long"
177
178 -class FloatArg(Arg):
179 """ 180 Represents an argument of type :class:`float`. 181 """
182 - def isValidType(self, arg_value):
183 return isinstance(arg_value, float)
184
185 - def getExpectedType(self):
186 return "float"
187
188 -class RealArg(Arg):
189 """ 190 Represents a real number argument, i.e. an argument of types :class:`int`, 191 :class:`long` or :class:`float`. 192 """
193 - def isValidType(self, arg_value):
194 return isinstance(arg_value, int) or \ 195 isinstance(arg_value, long) or \ 196 isinstance(arg_value, float)
197
198 - def getExpectedType(self):
199 return "int', 'long' or 'float"
200
201 -class ComplexArg(Arg):
202 """ 203 Represents a complex number argument of type :class:`complex`. 204 """
205 - def isValidType(self, arg_value):
206 return isinstance(arg_value, complex)
207
208 - def getExpectedType(self):
209 return "complex"
210
211 -class IterableArg(Arg):
212 """ 213 Represents an iterable argument. 214 """
215 - def isValidType(self, arg_value):
216 return hasattr(arg_value, "__iter__")
217
218 - def getExpectedType(self):
219 return "iterable (pseudo-type)"
220
221 -class SubscriptableArg(Arg):
222 """ 223 Represents a subscriptable argument. 224 """
225 - def isValidType(self, arg_value):
226 return hasattr(arg_value, "__getitem__")
227
228 - def getExpectedType(self):
229 return "subscriptable (pseudo-type)"
230
231 -class ListArg(IterableArg, SubscriptableArg):
232 """ 233 Represents an argument of type :class:`list`. 234 """
235 - def isValidType(self, arg_value):
236 return isinstance(arg_value, list)
237
238 - def getExpectedType(self):
239 return "list"
240
241 -class DictArg(IterableArg, SubscriptableArg):
242 """ 243 Represents an argument of type :class:`dict`. 244 """ 245
246 - def isValidType(self, arg_value):
247 return isinstance(arg_value, dict)
248
249 - def getExpectedType(self):
250 return "dict"
251
252 -class ObjectArg(Arg):
253 """ 254 Represents an new-style class argument. The range of accepted objects can 255 be restricted by providing the ``arg_class`` keyword argument to the 256 constructor. Runtime validation will then check if the argument value is 257 an instance of ``arg_class`` (or one of its subclasses) and fail otherwise. 258 """ 259
260 - def __init__(self, name, **kwargs):
261 super(ObjectArg, self).__init__(name, **kwargs) 262 263 if "arg_class" in kwargs: 264 try: 265 issubclass(kwargs["arg_class"], object) 266 except: 267 raise InternalError("Argument class must be a new-style class.") 268 269 self.arg_class = kwargs["arg_class"] 270 else: 271 self.arg_class = object
272
273 - def isValidType(self, arg_value):
274 return isinstance(arg_value, self.arg_class)
275
276 - def getExpectedType(self):
277 return self.arg_class.__name__
278
279 -class PosArgs(Arg):
280 """ 281 Represents arbitrary positional arguments as supported by Python with 282 the ``method(self, *args)`` syntax. The range of accepted objects can 283 be restricted by providing the ``arg_class`` keyword argument to the 284 constructor. Runtime validation will then check if the argument value is 285 an instance of ``arg_class`` (or one of its subclasses) and fail otherwise. 286 287 Note that a :class:`PosArgs` argument declaration can only be followed by 288 a :class:`KwArgs` declaration, otherwise validation will fail. 289 """
290 - def __init__(self, name, **kwargs):
291 self.name = name 292 self.optional = True 293 self.default = None 294 295 if "arg_class" in kwargs: 296 try: 297 if issubclass(kwargs["arg_class"], object): 298 self.arg_class = kwargs["arg_class"] 299 else: 300 raise InternalError("Argument class must be a new-style class.") 301 except: 302 raise InternalError("Argument class must be a new-style class.") 303 else: 304 self.arg_class = None
305
306 - def isValidType(self, arg_value):
307 return self.arg_class is None or isinstance(arg_value, self.arg_class)
308
309 - def getExpectedType(self):
310 if self.arg_class is None: 311 return "any" 312 else: 313 return self.arg_class.__name__
314
315 -class KwArgs(Arg):
316 """ 317 Represents arbitrary keyword arguments as supported by Python with the 318 ``method(self, **kwargs)`` syntax. Note that this must always be the 319 last input argument declaration in a method, otherwise validation will fail. 320 """
321 - def __init__(self, name, **kwargs):
322 self.name = name 323 self.optional = True 324 self.default = None
325
326 #------------------------------------------------------------------------------- 327 # Method class 328 #------------------------------------------------------------------------------- 329 330 -class Method(object):
331 """ 332 The :class:`Method` is used for method declarations in interfaces. Its 333 constructor accepts an arbitrary number of positional arguments representing 334 input arguments to the method to be defined, and one optional keyword 335 argument ``returns`` which represents the methods return value, if any. 336 337 All arguments must be instances of :class:`Arg` or one of its subclasses. 338 339 The methods of the :class:`Method` class are intended for internal use by 340 the :class:`Interface` validation algorithms only. 341 """
342 - def __init__(self, *args, **kwargs):
343 self.validateArgs(args) 344 345 self.named_args = [] 346 self.pos_args = None 347 self.kwargs = None 348 349 for arg in args: 350 if isinstance(arg, PosArgs): 351 self.pos_args = arg 352 elif isinstance(arg, KwArgs): 353 self.kwargs = arg 354 else: 355 self.named_args.append(arg) 356 357 self.returns = kwargs.get("returns", None)
358
359 - def validateArgs(self, args):
360 """ 361 Validate the input arguments. That is, check if they are in the 362 right order and no argument is defined more than once. Raises 363 :exc:`~.InternalError` if the arguments do not validate. 364 365 Used internally by the constructor during instance creation. 366 """ 367 opt_args_flag = False 368 pos_args_flag = False 369 kwargs_flag = False 370 371 names = [] 372 373 for arg in args: 374 if not isinstance(arg, Arg): 375 raise InternalError("Method arguments must be instances of Arg.") 376 377 if opt_args_flag: 378 if not arg.isOptional(): 379 raise InternalError("Mandatory arguments must precede optional arguments.") 380 381 if pos_args_flag: 382 if not isinstance(arg, KwArgs): 383 raise InternalError("Only keyword arguments may follow optional positional argument block.") 384 385 if kwargs_flag: 386 raise InternalError("No arguments allowed after keyword arguments block.") 387 388 if arg.isOptional(): 389 opt_args_flag = True 390 if isinstance(arg, PosArgs): 391 pos_args_flag = True 392 elif isinstance(arg, KwArgs): 393 kwargs_flag = True 394 395 if arg.name in names: 396 raise InternalError("Argument named '%s' appears multiple times." % arg.name) 397 else: 398 names.append(arg.name)
399
400 - def validateImplementation(self, impl_method):
401 """ 402 This method is at implementation class creation time to check if the 403 implementing class method conforms to the method declaration. It expects 404 the corresponding method as its single input argument ``impl_method``. 405 It makes extensive use of Python's great introspection capabilities. 406 407 Raises :exc:`~.InternalError` in case the implementation does not 408 validate. 409 """ 410 411 if len(self.named_args) != impl_method.func_code.co_argcount - 1: 412 raise InternalError("Number of arguments does not match") 413 414 for i in range(0, len(self.named_args)): 415 if self.named_args[i].name != impl_method.func_code.co_varnames[i+1]: 416 raise InternalError("Expected argument named '%s', got '%s'." % (self.named_args[i].name, impl_method.func_code.co_varnames[i+1])) 417 418 if self.pos_args is not None and impl_method.func_code.co_flags & 4 == 0: 419 raise InternalError("Expected positional argument block.") 420 421 if self.kwargs is not None and impl_method.func_code.co_flags & 8 == 0: 422 raise InternalError("Expected keyword argument block.")
423
424 - def validateType(self, method_name, *args, **kwargs):
425 """ 426 This method is called for runtime argument type validation. It gets the 427 input of the implementing method and checks it against the argument 428 declarations. 429 430 Raises :exc:`~.TypeMismatch` if validation fails. 431 """ 432 433 # map arguments to self.named_args, self.pos_args and 434 # self.kwargs 435 436 intf_named_args = {} 437 438 for arg in self.named_args: 439 intf_named_args[arg.name] = arg 440 441 impl_named_args = {} 442 impl_pos_args = [] 443 impl_kwargs = {} 444 445 for i in range(0, min(len(self.named_args), len(args))): 446 impl_named_args[self.named_args[i].name] = (self.named_args[i], args[i]) 447 448 if len(args) > len(self.named_args): 449 if self.pos_args is not None: 450 impl_pos_args = args[len(self.named_args):] 451 else: 452 self._raiseWrongNumberOfArguments(method_name, len(args)) 453 454 for name, arg_value in kwargs.items(): 455 if name in intf_named_args: 456 if name in impl_named_args: 457 raise TypeError("%s() got multiple values for keyword argument '%s'" % ( 458 method_name, 459 name 460 )) 461 else: 462 impl_named_args[name] = (intf_named_args[name], arg_value) 463 else: 464 if self.kwargs is not None: 465 impl_kwargs[name] = arg_value 466 else: 467 raise TypeError("%s() got an unexpected keyword argument '%s'" % ( 468 method_name, 469 name 470 )) 471 472 if len(impl_named_args) < len(filter(lambda arg: not arg.isOptional(), self.named_args)): 473 self._raiseWrongNumberOfArguments(method_name, len(impl_named_args), len(kwargs) > 0) 474 475 # finally, validate 476 477 msgs = [] 478 479 logger.debug("validateType(): start validation") 480 481 for arg, arg_value in impl_named_args.values(): 482 if not arg.isValid(arg_value): 483 msgs.append("%s(): Invalid type for argument '%s'. Expected '%s', got '%s'." % ( 484 method_name, 485 arg.name, 486 arg.getExpectedType(), 487 str(type(arg_value)) 488 )) 489 490 if self.pos_args is not None: 491 if not self.pos_args.isValid(impl_pos_args): 492 msgs.append("%s(): Invalid type for positional arguments. Expected '%s' only." % ( 493 method_name, 494 self.pos_args.getExpectedType() 495 )) 496 497 logger.debug("validateType(): finish validation") 498 499 if len(msgs) > 0: 500 raise TypeMismatch("\n".join(msgs))
501
502 - def validateReturnType(self, method_name, ret_value):
503 """ 504 This method is called for runtime argument type validation. It expects 505 the method name ``method_name`` and the return value ``ret_value`` as 506 input and checks the return value against the return value declaration, 507 if any. 508 509 Raises :exc:`~.TypeMismatch` if validation fails. 510 """ 511 512 if self.returns is not None and \ 513 not self.returns.isValid(ret_value): 514 raise TypeMismatch("%s(): Invalid return type. Expected '%s', got '%s'" % ( 515 method_name, 516 self.returns.getExpectedType(), 517 str(type(ret_value)) 518 ))
519
520 - def _raiseWrongNumberOfArguments(self, method_name, argcount, kwargs_present=False):
521 if any(map(lambda arg : arg.isOptional(), self.named_args)): 522 if argcount > len(self.named_args): 523 adverb = "at most" 524 count = len(self.named_args) 525 else: 526 adverb = "at least" 527 count = len(filter(lambda arg : not arg.isOptional(), self.named_args)) 528 else: 529 adverb = "exactly" 530 count = len(self.named_args) 531 532 if kwargs_present: 533 prefix = "non-keyword " 534 else: 535 prefix = "" 536 537 raise TypeError("%s() takes %s %d %sarguments (%d given)" % ( 538 method_name, 539 adverb, 540 count, 541 prefix, 542 argcount 543 ))
544
545 #------------------------------------------------------------------------------- 546 # Interface class 547 #------------------------------------------------------------------------------- 548 549 -class InterfaceMetaClass(type):
550 - def __new__(cls, name, bases, class_dict):
551 if "INTERFACE_CONF" in class_dict: 552 local_conf = class_dict["INTERFACE_CONF"] 553 else: 554 local_conf = {} 555 556 class_dict["__iconf__"] = cls._mergeConfs(local_conf, bases, "__iconf__") 557 558 return type.__new__(cls, name, bases, class_dict)
559 560 @classmethod
561 - def _mergeConfs(mcls, local_conf, bases, conf_name):
562 base_confs = [] 563 for base in bases: 564 if hasattr(base, conf_name): 565 base_confs.append(getattr(base, conf_name)) 566 567 conf = {} 568 for base_conf in reversed(base_confs): 569 conf.update(base_conf) 570 conf.update(local_conf) 571 572 return conf
573
574 -class Interface(object):
575 """ 576 This is the base class for all interface declarations. Derive from it or 577 one of its subclasses to create your own interface declaration. 578 579 The :class:`Interface` class has only class variables (the method 580 declarations) and class methods. 581 """ 582 583 __metaclass__ = InterfaceMetaClass 584 585 @classmethod
586 - def implement(InterfaceCls, ImplementationCls):
587 """ 588 This method takes an implementing class as input, validates it, and 589 returns the implementation. 590 591 In the validation step, :meth:`Method.validateImplementation` 592 is called for each method declared in the interface. 593 :exc:`~.InternalError` is raised if a method is not found or if 594 the method signature does not match the declaration. 595 596 If validation has passed, the implementation is getting prepared. The 597 implementation inherits from the implementing class. The ``__ifclass__`` 598 magic attribute is added to the class dictionary. If runtime 599 validation has been enabled, the methods of the implementing class 600 defined in the interface are replaced by descriptors (instances of 601 :class:`WarningDescriptor` or :class:`FailingDescriptor`). 602 603 Finally, the implementation class is generated and returned. 604 """ 605 606 name = InterfaceCls._getName(ImplementationCls) 607 bases = InterfaceCls._getBases(ImplementationCls) 608 609 InterfaceCls._validateImplementation(bases) 610 611 class_dict = InterfaceCls._getClassDict(ImplementationCls, bases) 612 613 return type(name, bases, class_dict)
614 615 @classmethod
616 - def _getName(InterfaceCls, ImplementationCls):
617 return "_Impl_%s_%s" % (ImplementationCls.__module__.replace(".", "_"), ImplementationCls.__name__)
618 619 @classmethod
620 - def _getBases(InterfaceCls, ImplementationCls):
621 bases = (ImplementationCls,) 622 623 return bases
624 625 @classmethod
626 - def _getClassDict(InterfaceCls, ImplementationCls, bases):
627 # Runtime validation levels 628 # * "trust" - do not enforce type checking at runtime (default) 629 # * "warn" - print warnings to the log file if type check fails 630 # * "fail" - raise exception if type check fails 631 runtime_validation_level = InterfaceCls._getRuntimeValidationLevel(ImplementationCls) 632 633 if runtime_validation_level.lower() == "trust": 634 class_dict = {} 635 636 elif runtime_validation_level.lower() == "warn": 637 class_dict = {} 638 639 interface_methods = InterfaceCls._getMethods() 640 641 for name, method in interface_methods.items(): 642 func = InterfaceCls._getBaseMethod(name, bases) 643 class_dict[name] = WarningDescriptor(method, func) 644 645 elif runtime_validation_level.lower() == "fail": 646 class_dict = {} 647 648 interface_methods = InterfaceCls._getMethods() 649 650 logger.debug("Interface._getClassDict(): Interface Methods: %s" % str(interface_methods)) 651 652 for name, method in interface_methods.items(): 653 func = InterfaceCls._getBaseMethod(name, bases) 654 class_dict[name] = FailingDescriptor(method, func) 655 656 else: 657 class_dict = {} 658 659 class_dict.update({"__ifclass__": InterfaceCls}) 660 661 return class_dict
662 663 @classmethod
664 - def _validateImplementation(InterfaceCls, bases):
665 interface_methods = InterfaceCls._getMethods() 666 667 implementation_dict = {} 668 for base in reversed(bases): 669 for cls in reversed(base.__mro__): # TODO: check if this gives the right MRO in the case of multiple inheritance 670 for name, attr in cls.__dict__.items(): 671 if type(attr) is types.FunctionType: 672 implementation_dict[name] = attr 673 674 for name, method in interface_methods.items(): 675 if name in implementation_dict: 676 method.validateImplementation(implementation_dict[name]) 677 else: 678 raise InternalError("Method '%s' not found in implementation." % name)
679 680 @classmethod
681 - def _getMethods(InterfaceCls):
682 interface_methods = {} 683 684 for name, attr in InterfaceCls.__dict__.items(): 685 if isinstance(attr, Method): 686 interface_methods[name] = attr 687 688 for InterfaceBase in reversed(InterfaceCls.__mro__): 689 for name, attr in InterfaceBase.__dict__.items(): 690 if isinstance(attr, Method): 691 interface_methods[name] = attr 692 693 return interface_methods
694 695 @classmethod
696 - def _getBaseMethod(InterfaceCls, method_name, bases):
697 for base in bases: 698 for cls in base.__mro__: 699 if method_name in cls.__dict__: 700 return cls.__dict__[method_name] 701 702 raise InternalError("Base method %s() not found." % method_name)
703 704 @classmethod
705 - def _getRuntimeValidationLevel(InterfaceCls, ImplementationCls):
706 global_level = RUNTIME_VALIDATION_LEVEL.lower() 707 intf_level = InterfaceCls.__iconf__.get("runtime_validation_level") 708 if "IMPL_CONF" in ImplementationCls.__dict__: 709 impl_level = ImplementationCls.IMPL_CONF.get("runtime_validation_level") 710 else: 711 impl_level = None 712 713 levels = (global_level, intf_level, impl_level) 714 715 if "fail" in levels: 716 return "fail" 717 elif "warn" in levels: 718 return "warn" 719 else: 720 return "trust"
721
722 #------------------------------------------------------------------------------- 723 # Descriptors and Wrappers 724 #------------------------------------------------------------------------------- 725 726 -class ValidationDescriptor(object):
727 """ 728 This is the common base class for :class:`WarningDescriptor` and 729 :class:`FailingDescriptor`. The constructor expects the method declaration 730 ``method`` and the implementing function ``func`` as input. 731 732 The :meth:`__get__` method returns a callable wrapper around the 733 instance it is called with, the method declaration and the function that 734 implements the method. It is that object 735 that gets finally invoked when runtime validation is enabled. 736 """ 737
738 - def __init__(self, method, func):
739 self.method = method 740 self.func = func
741
742 - def __get__(self, instance, owner):
743 if instance is not None: 744 return self._wrap(instance) 745 else: 746 raise InternalError("%s() is not a class method." % self.func.func_name)
747
748 - def _wrap(self, instance):
749 return None
750
751 -class ValidationWrapper(object):
752 """ 753 This is the common base class for :class:`WarningWrapper` and 754 :class:`FailingWrapper`. Its constructor expects the method declaration, 755 the implementing function and the instance as input. 756 """
757 - def __init__(self, method, func, instance):
758 self.method = method 759 self.func = func 760 self.instance = instance
761
762 -class WarningDescriptor(ValidationDescriptor):
763 - def _wrap(self, instance):
764 return WarningWrapper(self.method, self.func, instance)
765
766 -class WarningWrapper(ValidationWrapper):
767 """ 768 This wrapper is callable. Its :meth:`__call__` method expects arbitrary 769 positional and keyword arguments, validates them against the method 770 declaration using :meth:`Method.validateType`, calls the implementing 771 function with these arguments and returns whatever it returns, calling 772 :meth:`Method.validateReturnType`. 773 774 If the validation methods raise a :exc:`~.TypeMismatch` exception the 775 exception text is logged as a warning, but the normal process of execution 776 goes on. 777 """
778 - def __call__(self, *args, **kwargs):
779 try: 780 self.method.validateType(self.func.func_name, *args, **kwargs) 781 except TypeMismatch, e: 782 logger.warning(str(e)) 783 784 ret_value = self.func(self.instance, *args, **kwargs) 785 786 try: 787 self.method.validateReturnType(self.func.func_name, ret_value) 788 except TypeMismatch, e: 789 logger.warning(str(e)) 790 791 return ret_value
792
793 -class FailingDescriptor(ValidationDescriptor):
794 - def _wrap(self, instance):
795 return FailingWrapper(self.method, self.func, instance)
796
797 -class FailingWrapper(ValidationWrapper):
798 """ 799 This wrapper is callable. Its :meth:`__call__` method expects arbitrary 800 positional and keyword arguments, validates them against the method 801 declaration using :meth:`Method.validateType`, calls the implementing 802 function with these arguments and returns whatever it returns, calling 803 :meth:`Method.validateReturnType`. 804 805 If the validation methods raise a :exc:`~.TypeMismatch` exception it will 806 not be caught and thus cause the program to fail. 807 """
808 - def __call__(self, *args, **kwargs):
809 self.method.validateType(self.func.func_name, *args, **kwargs) 810 811 ret_value = self.func(self.instance, *args, **kwargs) 812 813 self.method.validateReturnType(self.func.func_name, ret_value) 814 815 return ret_value
816
817 #------------------------------------------------------------------------------- 818 # Config Reader 819 #------------------------------------------------------------------------------- 820 821 -class IntfConfigReader(object):
822 """ 823 This is the configuration reader for :mod:`eoxserver.core.interfaces`. 824 825 Its constructor expects a :class:`Config` instance ``config`` as input. 826 """ 827
828 - def __init__(self, config):
829 self.config = config
830
831 - def validate(self):
832 """ 833 Validates the configuration. Raises :exc:`~.ConfigError` if the 834 ``runtime_validation_level`` configuration setting in the 835 ``core.interfaces`` section contains an invalid value. 836 """ 837 value = self.getRuntimeValidationLevel() 838 839 if value and value not in ("trust", "warn", "fail"): 840 raise ConfigError("'runtime_validation_level' parameter must be one of: 'trust', 'warn' or 'fail'.")
841
843 """ 844 Returns the global runtime validation level setting or ``None`` if it 845 is not defined. 846 """ 847 self.config.getConfigValue("core.interfaces", "runtime_validation_level")
848