To implement a function, you need to inherit its class from class pyws.functions.Function and implement its method call, also it must have attributes name, return_type and args. To call a function, a server just calls it as any callable object, so there is a default implemenetation of method __call__:
An abstract function class. Implements simple context handling and argument values validation.
the name of the function;
the type of values returning by the function (standard form only), see Type specification;
function arguments specification, a pyws.functions.args.Dict type (standard form only) with arguments as fields, see Arguments & fields specification and Type specification;
shows whether the function requires a context to run.
Validates argument values and checks if the function requires a context. If it does, argument context is added to other arguments and method call is called.
This method must implement the logic of the function. args is a dict of validated argument values including key context if it is required.
However, there is an easier way to implement a function, namely using pyws.functions.NativeFunctionAdapter class:
This class allows to wrap any native python function so that it behaved like pyws.functions.Function.
origin is a native Python function to be wrapped. name is the name of the function, if it is not specified, origin.__name__ is used. return_type and needs_context correspond to pyws.functions.Function arguments. args is a tuple, each of its elements may be:
If args is not specified all arguments will be treated as strings, the same thing with return_type. Argument names are infered from origin.func_code.co_varnames.
>>> from pyws.functions import NativeFunctionAdapter
>>> from pyws.functions.args import String
>>> from pyws.functions.args import Dict, Field
>>> def nothing():
... pass
>>> a = NativeFunctionAdapter(nothing)
>>> a.origin == nothing
True
>>> a.name
'nothing'
>>> a.return_type
<class 'pyws.functions.args.types.simple.String'>
>>> issubclass(a.args, Dict)
True
>>> a.args.fields
[]
>>> a(context=None)
>>> def add(a, b):
... c = a + b
... return c
>>> a = NativeFunctionAdapter(add, name='concat')
>>> a.name
'concat'
>>> len(a.args.fields)
2
>>> f = a.args.fields[0]
>>> type(f)
<class 'pyws.functions.args.field.Field'>
>>> f.name
'a'
>>> f.type
<class 'pyws.functions.args.types.simple.String'>
>>> a.return_type
<class 'pyws.functions.args.types.simple.String'>
>>> a = NativeFunctionAdapter(
... add,
... name='sum',
... return_type=int,
... args=(int,),
... )
>>> a.name
'sum'
>>> f = a.args.fields[0]
>>> f.type
<class 'pyws.functions.args.types.simple.Integer'>
>>> a.return_type
<class 'pyws.functions.args.types.simple.Integer'>
>>> def empty_value(a):
... return 'hello ' + a
>>> a = NativeFunctionAdapter(empty_value, args=((str, 'world'), ))
>>> a(context=None, a=None) # note that 'world' is used instead of None
'hello world'
Simply calls origin function with args argument values.
One of the main differences between python and WSDL is that the former uses duck typing, whereas the latter requires strict types specification, therefore we need to be able to specify types. In most cases, when you need to specify an argument or a field, you can do it in two forms:
A field combines a name, a type and a value that represents an empty value.
name is the name of a field. type_ is its type and none_value represents an empty value, callables are accepted as well. type_ can be specified in both standard and simplified form, more on that read in chapter Type specification. none_value is used to replace None, if field’s none_value is None then that of its type is used.
In pyws in all cases, when you need to specify a type, you can do it in two forms:
pyws supports the following types:
Represents strings, simplified form is str, default none_value is ''.
Represents integer numbers, simplified form is int, default none_value is None.
Represents floating point numbers, simplified form is float, default none_value is None.
Represents date values, simplified form is date, default none_value is None.
Represents datetime values, simplified form is datetime, default none_value is None.
Represents dicts, simplified form is {0: 'dict_name', 'field_name': field_type, ...}, default none_value is None. If you want to create your own Dict type, you have to inherit it from this type and specify fields attribute, which is a list of fields in either standard or simplified form. Or you can just use DictOf factory or simplified form. The name is required because it will be propagated to WSDL description.
Represents lists, simplified form is [element_type], default none_value is []. If you want to create your own List type, you have to inherit it from this type and specify element_type attribute in either standard or simplified form, and element_none_value. Or you can just use ListOf factory or simplified form.
Three helper functions are also at your disposal:
This function creates a dict type with the specified name and fields.
>>> from pyws.functions.args import DictOf, Field
>>> dct = DictOf(
... 'HelloWorldDict', Field('hello', str), Field('hello', int))
>>> issubclass(dct, Dict)
True
>>> dct.__name__
'HelloWorldDict'
>>> len(dct.fields)
2
This function creates a list type with element type element_type and an empty element value element_none_value.
>>> from pyws.functions.args import Integer, ListOf
>>> lst = ListOf(int)
>>> issubclass(lst, List)
True
>>> lst.__name__
'IntegerList'
>>> lst.element_type == Integer
True
This function creates a standard form type from a simplified form.
>>> from datetime import date, datetime
>>> from pyws.functions.args import TypeFactory
>>> from pyws.functions.args import String, Integer, Float, Date, DateTime
>>> TypeFactory(str) == String
True
>>> TypeFactory(float) == Float
True
>>> TypeFactory(date) == Date
True
>>> TypeFactory(datetime) == DateTime
True
>>> from operator import attrgetter
>>> from pyws.functions.args import Dict
>>> dct = TypeFactory({0: 'HelloWorldDict', 'hello': str, 'world': int})
>>> issubclass(dct, Dict)
True
>>> dct.__name__
'HelloWorldDict'
>>> fields = sorted(dct.fields, key=attrgetter('name'))
>>> len(dct.fields)
2
>>> fields[0].name == 'hello'
True
>>> fields[0].type == String
True
>>> fields[1].name == 'world'
True
>>> fields[1].type == Integer
True
>>> from pyws.functions.args import List
>>> lst = TypeFactory([int])
>>> issubclass(lst, List)
True
>>> lst.__name__
'IntegerList'
>>> lst.element_type == Integer
True