1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
Types
=====
Three kinds of data types can be used as input or output by WSME.
Native types
------------
The native types are a fixed set of standard Python types that
different protocols map to their own basic types.
The native types are :
- ``.. wsme:type:: bytes``
A pure-ASCII string (:py:class:`wsme.types.bytes` which is :py:class:`str` in
Python 2 and :py:class:`bytes` in Python 3).
- ``.. wsme:type:: text``
A unicode string (:py:class:`wsme.types.text` which is :py:class:`unicode` in
Python 2 and :py:class:`str` in Python 3).
- ``.. wsme:type:: int``
An integer (:py:class:`int`)
- ``.. wsme:type:: float``
A float (:py:class:`float`)
- ``.. wsme:type:: bool``
A boolean (:py:class:`bool`)
- ``.. wsme:type:: Decimal``
A fixed-width decimal (:py:class:`decimal.Decimal`)
- ``.. wsme:type:: date``
A date (:py:class:`datetime.date`)
- ``.. wsme:type:: datetime``
A date and time (:py:class:`datetime.datetime`)
- ``.. wsme:type:: time``
A time (:py:class:`datetime.time`)
- Arrays
This is a special case. When stating a list datatype, always state its content
type as the unique element of a list. Example::
class SomeWebService(object):
@expose([str])
def getlist(self):
return ['a', 'b', 'c']
- Dictionaries
Statically typed mappings are allowed. When exposing a dictionary datatype,
you can specify the key and value types, with a restriction on the key value
that must be a 'pod' type. Example::
class SomeType(object):
amap = {str: SomeOthertype}
There are other types that are supported out of the box. See the
:ref:`pre-defined-user-types`.
User types
----------
User types allow you to define new, almost-native types.
The idea is that you may have Python data that should be transported as base
types by the different protocols, but needs conversion to/from these base types,
or needs to validate data integrity.
To define a user type, you just have to inherit from
:class:`wsme.types.UserType` and instantiate your new class. This instance
will be your new type and can be used as @\ :class:`wsme.expose` or
@\ :class:`wsme.validate` parameters.
Note that protocols can choose to specifically handle a user type or
a base class of user types. This is case with the two pre-defined
user types, :class:`wsme.types.Enum` and :data:`wsme.types.binary`.
.. _pre-defined-user-types:
Pre-defined user types
~~~~~~~~~~~~~~~~~~~~~~
WSME provides some pre-defined user types:
- :class:`binary <wsme.types.binary>` -- for transporting binary data as base64
strings.
- :class:`Enum <wsme.types.Enum>` -- enforce that the values belongs to a
pre-defined list of values.
These types are good examples of how to define user types. Have
a look at their source code!
Here is a little example that combines :class:`binary <wsme.types.binary>`
and :class:`Enum <wsme.types.Enum>`::
ImageKind = Enum(str, 'jpeg', 'gif')
class Image(object):
name = unicode
kind = ImageKind
data = binary
.. data:: wsme.types.binary
The :class:`wsme.types.BinaryType` instance to use when you need to
transfer base64 encoded data.
.. autoclass:: wsme.types.BinaryType
.. autoclass:: wsme.types.Enum
Complex types
-------------
Complex types are structured types. They are defined as simple Python classes
and will be mapped to adequate structured types in the various protocols.
A base class for structured types is provided, :class:`wsme.types.Base`,
but is not mandatory. The only thing it adds is a default constructor.
The attributes that are set at the class level will be used by WSME to discover
the structure. These attributes can be:
- A datatype -- Any native, user or complex type.
- A :class:`wsattr <wsme.wsattr>` -- This allows you to add more information about
the attribute, for example if it is mandatory.
- A :class:`wsproperty <wsme.wsproperty>` -- A special typed property. Works
like standard ``property`` with additional properties like
:class:`wsattr <wsme.wsattr>`.
Attributes having a leading '_' in their name will be ignored, as well as the
attributes that are not in the above list. This means the type can have methods,
they will not get in the way.
Example
~~~~~~~
::
Gender = wsme.types.Enum(str, 'male', 'female')
Title = wsme.types.Enum(str, 'M', 'Mrs')
class Person(wsme.types.Base):
lastname = wsme.types.wsattr(unicode, mandatory=True)
firstname = wsme.types.wsattr(unicode, mandatory=True)
age = int
gender = Gender
title = Title
hobbies = [unicode]
Rules
~~~~~
A few things you should know about complex types:
- The class must have a default constructor --
Since instances of the type will be created by the protocols when
used as input types, they must be instantiable without any argument.
- Complex types are registered automatically
(and thus inspected) as soon a they are used in expose or validate,
even if they are nested in another complex type.
If for some reason you need to control when type is inspected, you
can use :func:`wsme.types.register_type`.
- The datatype attributes will be replaced.
When using the 'short' way of defining attributes, ie setting a
simple data type, they will be replaced by a wsattr instance.
So, when you write::
class Person(object):
name = unicode
After type registration the class will actually be equivalent to::
class Person(object):
name = wsattr(unicode)
You can still access the datatype by accessing the attribute on the
class, along with the other wsattr properties::
class Person(object):
name = unicode
register_type(Person)
assert Person.name.datatype is unicode
assert Person.name.key == "name"
assert Person.name.mandatory is False
- The default value of instance attributes is
:data:`Unset <wsme.Unset>`.
::
class Person(object):
name = wsattr(unicode)
p = Person()
assert p.name is Unset
This allows the protocol to make a clear distinction between null values
that will be transmitted, and unset values that will not be transmitted.
For input values, it allows the code to know if the values were, or were not,
sent by the caller.
- When 2 complex types refer to each other, their names can be
used as datatypes to avoid adding attributes afterwards:
::
class A(object):
b = wsattr('B')
class B(object):
a = wsattr(A)
Predefined Types
~~~~~~~~~~~~~~~~
.. default-domain:: wsme
- .. autotype:: wsme.types.File
:members:
|