Beispiel #1
0
		def injector(*args,**kw):
			"""
			The Ladon inner injection function is called from the dispatcher. It does run-time type
			checking against the types registered with the service method. If everything is OK the
			user method is called. The result of the userspace-method is then checked before it is
			passed back to the dispatcher.
			"""
			
			# Get the LadonMethodInfo object generated at parsetime which is stored as a member
			# on the function object
			lmi = injector._ladon_method_info
			# Reference the incomming arguments in callargs (filter out the function reference)
			callargs = args[1:]
			
			for argidx in range(len(callargs)):
				# Check the type of each argument against the type which the method has been 
				# registered to take in the order-wise corresponding argument
				if not validate_type(lmi._arg_types[argidx],callargs[argidx]):
					# Raise ArgTypeMismatch
					raise ArgTypeMismatch(
						lmi.sinfo,
						lmi._func_name,
						lmi._arg_names[argidx],
						lmi._arg_types[argidx],
						type(callargs[argidx]))
			
			# Call the userspace service method (**kw will be used to transport Ladon info
			# and tools all the way to userspace of the service method. I.e. the TypeConverter
			# is passed with the keyword "LADON_METHOD_TC")
			res = f(*args,**kw)
			
			# Check the return type
			if not validate_type(lmi._rtype,res):
				# Raise Arg-type mismatch 
				raise ReturnTypeMismatch(lmi.sinfo,lmi._func_name,lmi._rtype,type(res))
			
			# Return the result to the dispatcher
			return res
Beispiel #2
0
        def injector(*args, **kw):
            """
			The Ladon inner injection function is called from the dispatcher. It does run-time type
			checking against the types registered with the service method. If everything is OK the
			user method is called. The result of the userspace-method is then checked before it is
			passed back to the dispatcher.
			"""

            # Get the LadonMethodInfo object generated at parsetime which is stored as a member
            # on the function object
            lmi = injector._ladon_method_info
            # Reference the incomming arguments in callargs (filter out the function reference)
            callargs = args[1:]

            for argidx in range(len(callargs)):
                # Check the type of each argument against the type which the method has been
                # registered to take in the order-wise corresponding argument
                if not validate_type(lmi._arg_types[argidx], callargs[argidx]):
                    # Raise ArgTypeMismatch
                    raise ArgTypeMismatch(lmi.sinfo, lmi._func_name,
                                          lmi._arg_names[argidx],
                                          lmi._arg_types[argidx],
                                          type(callargs[argidx]))

            # Call the userspace service method (**kw will be used to transport Ladon info
            # and tools all the way to userspace of the service method. I.e. the TypeConverter
            # is passed with the keyword "LADON_METHOD_TC")
            res = f(*args, **kw)

            # Check the return type
            if not validate_type(lmi._rtype, res):
                # Raise Arg-type mismatch
                raise ReturnTypeMismatch(lmi.sinfo, lmi._func_name, lmi._rtype,
                                         type(res))

            # Return the result to the dispatcher
            return res
Beispiel #3
0
    def __init__(self, sinfo, f, *def_args, **def_kw):
        global rx_doc_params, rx_doc_rtype, rx_doc_param_lines
        # Inspect the methods's parameters
        argspecs = inspect.getargspec(f)
        # Get the method's doc-string
        doc = inspect.getdoc(f)
        if doc:
            if sys.version_info[0] == 2:
                # Convert the documentation to unicode
                doc = unicode(doc, sinfo.src_encoding)
        # Associate the method with it's parent service-info object
        self.sinfo = sinfo
        # _args: Store for arguments (name, type, documentation and default value)
        self._args = {}
        # _has_keywords: Check if the method takes keyword arguments (**kw). The
        # dispatcher will use this information to decide whether to push ladon's
        # helper tools to the method. Helper tools are sent via keyword arguments
        # therefore keyword arguments are required in the method in order to recieve
        # the helper tools
        self._has_keywords = argspecs.keywords != None
        # _arg_names,_defaults: Store method parameter names and defalut values here.
        # These will be used later in the parsing process.
        self._arg_names = argspecs.args[1:]
        self._defaults = argspecs.defaults
        # Store the raw doc-string in _doc
        self._doc = f.__doc__
        # Extract the method name
        self._func_name = get_function_name(f)
        # Store the parameter types delivered by the user via the ladonize decorator
        self._arg_types = def_args
        # _encoding: is kind of a contract between the method and Ladon's dispatcher.
        # It tells Ladon that it must deliver raw strings to the service method in that
        # encoding thus converting if nessecary. You can think of it as the method's
        # internal encoding. The method itself also commit's itself to the contract by
        # promising to deliver raw strings back to the dispatcher in the same encoding.
        # Raw strings are defined as: str in Python2 and bytes in Python3
        self._encoding = 'UTF-8'
        # _allow_unsafe_conversion: is a user-option that tells Ladon whether it may do
        # unsafe convertions or not. An example of an unsafe convertion is float to int.
        # unsafe convertions are disabled by default
        self._allow_unsafe_conversion = False
        # _rtype: The return type of the method.
        try:
            self._rtype = def_kw['rtype']
        except KeyError as ke:
            # return type must be specified.
            raise ReturnTypeUndefined(sinfo, self._func_name)
        # _rtype_doc: Initialize the return type doc-string container
        self._rtype_doc = []
        # overwrite the default values of user options if they are passed on via the
        # ladonize decorator.
        if 'encoding' in def_kw:
            self._encoding = def_kw['encoding']
        if 'allow_unsafe_conversion' in def_kw:
            self._allow_unsafe_conversion = def_kw['allow_unsafe_conversion']
        # Let the parent service's TypeManager object parse, analyze and store the return type
        self._multipart_response_required = sinfo.typemanager.analyze_param(
            self._rtype)
        self._multipart_request_required = False
        if self._defaults == None:
            self._defaults = tuple()
        # Identify which arguments are mandatory and which are optional
        self._mandatory_args = self._arg_names[:-len(self._defaults)]
        self._optional_args = self._arg_names[-len(self._defaults):]
        # Check that the number of type defines (via the ladonize decorator) matches then
        # number of method arguments
        if len(self._arg_names) != len(def_args):
            raise ArgDefCountMismatch(sinfo, self._func_name, self._arg_types,
                                      self._arg_names)

        alen = len(self._arg_names)
        dlen = len(self._defaults)
        for argidx in range(len(self._arg_names)):
            # Let the parent service's TypeManager object parse, analyze and store the argument
            self._multipart_request_required |= sinfo.typemanager.analyze_param(
                def_args[argidx])

            has_default = False
            # find the index of this parameter's default value in _defaults. If index is below
            # 0 it means it has no default value, thus a mandatory parameter.
            defidx = dlen - (alen - argidx)
            if defidx > -1:
                # Optional parameter, check and store it's default value
                has_default = True
                def_val = self._defaults[defidx]
                def_type = type(self._defaults[defidx])
                # Check that the default value matches the user defined type
                if not validate_type(def_args[argidx], def_val):
                    # type mismatch
                    raise DefaultArgTypeMismatch(sinfo, self._func_name,
                                                 self._arg_names[argidx],
                                                 self._arg_types[argidx],
                                                 def_type)

            # Store the info about this parameter
            self._args[self._arg_names[argidx]] = {
                'name': self._arg_names[argidx],
                'type': def_args[argidx],
                'optional': has_default,
                'doc': []
            }
            if has_default:
                self._args[self._arg_names[argidx]]['default'] = def_val

        # Parse the methods documentation
        self._method_doc = []
        if doc:
            # Make sure all newlines in doc are Unix style, then split it into lines
            doc = doc.replace('\r\n', '\n')
            doclines = doc.split('\n')

            cur_argname = None
            for dline in doclines:
                # Iterate through doc lines and do regular expression matches to
                # find method, argument and return documentation

                # Does the current doc line match the beginning of a parameters documentation
                param_match = rx_doc_params.match(dline)
                if param_match:
                    # Set the parameter as current
                    cur_argname = param_match.groups()[0]
                    # if not an actual parameter cancel param documentation
                    if not cur_argname in self._args:
                        cur_argname = None
                        continue
                    # Add the first doc line to the parameter doc lines
                    docline1 = param_match.groups()[1].strip()
                    if docline1:
                        self._args[cur_argname]['doc'] += [docline1]
                    continue

                # Does the current doc line match the beginning of the return documentation
                rtype_match = rx_doc_rtype.match(dline)
                if rtype_match:
                    # Add the first doc line to the return doc lines
                    cur_argname = '_rtype'
                    docline1 = rtype_match.groups()[0].strip()
                    if docline1:
                        self._rtype_doc += [docline1]
                    continue

                # does the doc line look like a param or return line
                param_line_match = rx_doc_param_lines.match(dline)
                # Add it to the current param doc that is being processed
                if param_line_match and cur_argname:
                    if cur_argname == '_rtype':
                        self._rtype_doc += [dline.strip()]
                    else:
                        self._args[cur_argname]['doc'] += [
                            param_line_match.groups()[0].strip()
                        ]
                    continue

                # If this doc line cannot be mapped to an argument or the return documentation, then
                # add it to the methods documentation
                if not len(dline.strip()) and cur_argname:
                    if cur_argname == '_rtype':
                        self._rtype_doc += [dline.strip()]
                    else:
                        self._args[cur_argname]['doc'] += [dline.strip()]
                    continue

                # If no match found, reset the current param if one is in focus
                cur_argname = None
                self._method_doc += [dline]

        self.__doc__ = '\n'.join(self._method_doc)
Beispiel #4
0
	def __init__(self,sinfo,f,*def_args,**def_kw):
		global rx_doc_params,rx_doc_rtype,rx_doc_param_lines
		# Inspect the methods's parameters
		argspecs = inspect.getargspec(f)
		# Get the method's doc-string
		doc = inspect.getdoc(f)
		if doc:
			if sys.version_info[0]==2:
				# Convert the documentation to unicode
				doc = unicode(doc,sinfo.src_encoding)
		# Associate the method with it's parent service-info object
		self.sinfo = sinfo
		# _args: Store for arguments (name, type, documentation and default value)
		self._args = {}
		# _has_keywords: Check if the method takes keyword arguments (**kw). The
		# dispatcher will use this information to decide whether to push ladon's
		# helper tools to the method. Helper tools are sent via keyword arguments
		# therefore keyword arguments are required in the method in order to recieve
		# the helper tools
		self._has_keywords = argspecs.keywords!=None
		# _arg_names,_defaults: Store method parameter names and defalut values here. 
		# These will be used later in the parsing process.
		self._arg_names = argspecs.args[1:]
		self._defaults = argspecs.defaults
		# Store the raw doc-string in _doc
		self._doc = f.__doc__
		# Extract the method name
		self._func_name = get_function_name(f)
		# Store the parameter types delivered by the user via the ladonize decorator
		self._arg_types = def_args
		# _encoding: is kind of a contract between the method and Ladon's dispatcher.
		# It tells Ladon that it must deliver raw strings to the service method in that
		# encoding thus converting if nessecary. You can think of it as the method's
		# internal encoding. The method itself also commit's itself to the contract by
		# promising to deliver raw strings back to the dispatcher in the same encoding.
		# Raw strings are defined as: str in Python2 and bytes in Python3
		self._encoding = 'UTF-8'
		# _allow_unsafe_conversion: is a user-option that tells Ladon whether it may do
		# unsafe convertions or not. An example of an unsafe convertion is float to int.
		# unsafe convertions are disabled by default
		self._allow_unsafe_conversion = False
		# _rtype: The return type of the method.
		try:
			self._rtype = def_kw['rtype']
		except KeyError as ke:
			# return type must be specified.
			raise ReturnTypeUndefined(sinfo,self._func_name)
		# _rtype_doc: Initialize the return type doc-string container
		self._rtype_doc = []
		# overwrite the default values of user options if they are passed on via the
		# ladonize decorator.
		if 'encoding' in def_kw:
			self._encoding = def_kw['encoding']
		if 'allow_unsafe_conversion' in def_kw:
			self._allow_unsafe_conversion = def_kw['allow_unsafe_conversion']
		# Let the parent service's TypeManager object parse, analyze and store the return type
		self._multipart_response_required = sinfo.typemanager.analyze_param(self._rtype)
		self._multipart_request_required = False
		if self._defaults==None:
			self._defaults = tuple()
		# Identify which arguments are mandatory and which are optional
		self._mandatory_args = self._arg_names[:-len(self._defaults)]
		self._optional_args = self._arg_names[-len(self._defaults):]
		# Check that the number of type defines (via the ladonize decorator) matches then
		# number of method arguments
		if len(self._arg_names)!=len(def_args):
			raise ArgDefCountMismatch(sinfo,self._func_name,self._arg_types,self._arg_names)
		
		alen = len(self._arg_names)
		dlen = len(self._defaults)
		for argidx in range(len(self._arg_names)):
			# Let the parent service's TypeManager object parse, analyze and store the argument
			self._multipart_request_required |= sinfo.typemanager.analyze_param(def_args[argidx])

			has_default = False
			# find the index of this parameter's default value in _defaults. If index is below
			# 0 it means it has no default value, thus a mandatory parameter.
			defidx = dlen - (alen - argidx)
			if defidx>-1:
				# Optional parameter, check and store it's default value
				has_default = True
				def_val = self._defaults[defidx]
				def_type = type(self._defaults[defidx])
				# Check that the default value matches the user defined type
				if not validate_type(def_args[argidx],def_val):
					# type mismatch
					raise DefaultArgTypeMismatch(
						sinfo,
						self._func_name,
						self._arg_names[argidx],
						self._arg_types[argidx],
						def_type)

			# Store the info about this parameter
			self._args[self._arg_names[argidx]] = {
				'name': self._arg_names[argidx],
				'type': def_args[argidx],
				'optional': has_default,
				'doc': []}
			if has_default:
				self._args[self._arg_names[argidx]]['default'] = def_val
		
		# Parse the methods documentation
		self._method_doc = []
		if doc:
			# Make sure all newlines in doc are Unix style, then split it into lines
			doc = doc.replace('\r\n','\n')
			doclines = doc.split('\n')
			
			cur_argname = None
			for dline in doclines:
				# Iterate through doc lines and do regular expression matches to
				# find method, argument and return documentation
				
				# Does the current doc line match the beginning of a parameters documentation
				param_match = rx_doc_params.match(dline)
				if param_match:
					# Set the parameter as current
					cur_argname = param_match.groups()[0]
					# if not an actual parameter cancel param documentation
					if not cur_argname in self._args:
						cur_argname = None
						continue
					# Add the first doc line to the parameter doc lines
					docline1 = param_match.groups()[1].strip()
					if docline1:
						self._args[cur_argname]['doc'] += [docline1]
					continue
				
				# Does the current doc line match the beginning of the return documentation
				rtype_match = rx_doc_rtype.match(dline)
				if rtype_match:
					# Add the first doc line to the return doc lines
					cur_argname = '_rtype'
					docline1 = rtype_match.groups()[0].strip()
					if docline1:
						self._rtype_doc += [docline1]
					continue
				
				# does the doc line look like a param or return line
				param_line_match = rx_doc_param_lines.match(dline)
				# Add it to the current param doc that is being processed
				if param_line_match and cur_argname:
					if cur_argname=='_rtype':
						self._rtype_doc += [ dline.strip() ]
					else:
						self._args[cur_argname]['doc'] += [param_line_match.groups()[0].strip()]
					continue
				
				# If this doc line cannot be mapped to an argument or the return documentation, then
				# add it to the methods documentation
				if not len(dline.strip()) and cur_argname:
					if cur_argname=='_rtype':
						self._rtype_doc += [ dline.strip() ]
					else:
						self._args[cur_argname]['doc'] += [dline.strip()]
					continue
				
				# If no match found, reset the current param if one is in focus
				cur_argname = None
				self._method_doc += [ dline ]
				
		self.__doc__ = '\n'.join(self._method_doc)
Beispiel #5
0
        def injector(*args, **kw):
            """
			The Ladon inner injection function is called from the dispatcher. It does run-time type
			checking against the types registered with the service method. If everything is OK the
			user method is called. The result of the userspace-method is then checked before it is
			passed back to the dispatcher.
			"""

            if def_kw.get('tasktype', False) == True:
                return f(*args, **kw)

            # Get the LadonMethodInfo object generated at parsetime which is stored as a member
            # on the function object
            lmi = injector._ladon_method_info
            # Reference the incomming arguments in callargs (filter out the function reference)
            callargs = args[1:]

            for argidx in range(len(callargs)):
                # Check the type of each argument against the type which the method has been
                # registered to take in the order-wise corresponding argument
                if not validate_type(lmi._arg_types[argidx], callargs[argidx]):
                    # Raise ArgTypeMismatch
                    raise ArgTypeMismatch(lmi.sinfo, lmi._func_name,
                                          lmi._arg_names[argidx],
                                          lmi._arg_types[argidx],
                                          type(callargs[argidx]))

            # Call the userspace service method (**kw will be used to transport Ladon info
            # and tools all the way to userspace of the service method. I.e. the TypeConverter
            # is passed with the keyword "LADON_METHOD_TC")
            debug_mode = log.get_loglevel() >= 6
            if debug_mode:
                # Build a request_dict for debugging
                req_dict = {}
                for argidx in range(len(callargs)):
                    req_dict[lmi._arg_names[argidx]] = expand_value(
                        callargs[argidx],
                        lmi._arg_types[argidx],
                        service_name=lmi.sinfo.servicename,
                        method_name=lmi._func_name)
                log_line = [
                    'Method:%s.%s' % (lmi.sinfo.servicename, lmi._func_name),
                    'RequestArgs:%s' % str(req_dict),
                    'RequestKeywordArgs:%s' % str(kw)
                ]

            if debug_mode:
                try:
                    start = time.time()
                    res = f(*args, **kw)
                    log_line.insert(
                        0, 'ExecutionTime:%s' % str(time.time() - start))
                except Exception as e:
                    log_line += ['Exception:%s' % str((log.get_traceback(), ))]
                    log.debug('\t%s' % ('\t'.join(log_line)))
                    raise e

                log_line += ['ReturnValue:%s' % str(result_to_dict(lmi, res))]
                log.debug('\t%s' % ('\t'.join(log_line)))

            else:
                res = f(*args, **kw)
            # Check the return type
            if not validate_type(lmi._rtype, res):
                # Raise Arg-type mismatch
                raise ReturnTypeMismatch(lmi.sinfo, lmi._func_name, lmi._rtype,
                                         type(res))

            # Return the result to the dispatcher
            return res