Types¶
Doctor types validate request parameters passed to logic functions. Every request parameter that gets passed to your logic function should define a type from one of those below. See quick type creation for functions that allow you to create types easily on the fly.
String¶
A String
type represents a str and allows you to
define several attributes for validation.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.format
- An identifier indicating a complex datatype with a string representation. For example date, to represent an ISO 8601 formatted date string. The following formats are supported:- date - Will parse the string as a datetime.datetime instance. Expects the format ‘%Y-%m-%d’
- date-time - Will parse the string as a datetime.datetime instance. Expects a valid ISO8601 string. e.g. ‘2018-02-21T16:09:02Z’
- email - Does basic validation that the string is an email by checking for an ‘@’ character in the string.
- time - Will parse the string as a datetime.datetime instance. Expects the format ‘%H:%M:%S’
- uri - Will validate the string is a valid URI.
max_length
- The maximum length of the string.min_length
- The minimum length of the string.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.pattern
- A regex pattern the string should match anywhere whitin it. Uses re.search.trim_whitespace
- If True the string will be trimmed of whitespace.
Example¶
from doctor.types import String
class FirstName(String):
description = "A user's first name."
min_length = 1
max_length = 255
trim_whitespace = True
Number¶
A Number
type represents a float and allows you to
define several attributes for validation.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.exclusive_maximum
- If True andmaximum
is set, the maximum value should be treated as exclusive (value can not be equal to maximum).exclusive_minimum
- If True andminimum
is set, the minimum value should be treated as exclusive (value can not be equal to minimum).maximum
- The maximum value allowed.minimum
- The minimum value allowed.multiple_of
- The value is required to be a multiple of this value.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.
Example¶
from doctor.types import Number
class AverageRating(Number):
description = 'The average rating.'
exclusive_maximum = False
exclusive_minimum = True
minimum = 0.00
maximum = 10.0
Integer¶
An Integer
type represents an int and allows you to
define several attributes for validation.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.exclusive_maximum
- If True and themaximum
is set, the maximum value should be treated as exclusive (value can not be equal to maximum).exclusive_minimum
- If True and theminimum
is set, the minimum value should be treated as exclusive (value can not be equal to minimum).maximum
- The maximum value allowed.minimum
- The minimum value allowed.multiple_of
- The value is required to be a multiple of this value.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.
Example¶
from doctor.types import Integer
class Age(Integer):
description = 'The age of the user.'
exclusive_maximum = False
exclusive_minimum = True
minimum = 1
maximum = 120
Boolean¶
A Boolean
type represents a bool. This type will
convert several common strings used as booleans to a boolean type when
instaniated. The following str values (case-insensitve) will be converted to
a boolean:
- ‘true’/’false’
- ‘on’/’off’
- ‘1’/’0’
It also accepts typical truthy inputs e.g. True, False, 1, 0.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.
Example¶
from doctor.types import Boolean
class Accept(Boolean):
description = 'Indicates if the user accepted the agreement or not.'
Enum¶
An Enum
type represents a str that should be one of
any defined values and allows you to define several attributes for validation.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.enum
- A list of str containing valid values.case_insensitive
- A boolean indicating if the values of the enum attribute are case insensitive or not.lowercase_value
- A boolean indicating if the input value should be converted to lowercased or not. This will happen prior to any validation.uppercase_value
- A boolean indicating if the input value should be converted to uppercased or not. This will happen prior to any validation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.
Example¶
from doctor.types import Enum
class Color(Enum):
description = 'A color.'
enum = ['blue', 'green', 'purple', 'yellow']
Object¶
An Object
type represents a dict and allows you to
define properties and required properties.
Attributes¶
additional_properties
- If True, additional properties (that is, ones not defined inproperties
) will be allowed.description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.properties
- A dict containing a mapping of property name to expected type.property_dependencies
- A dict containing a mapping of property name to a list of properties it depends on. This means if the property name is present then any dependent properties must also be present, otherwise aTypeSystemError
will be raised. See JSON Schema dependencies for further information.required
- A list of required properties.title
- An optional title for your object. This value will be used when generating documentation about objects in requests and responses.
Example¶
from doctor.types import Object, boolean, enum, string
class Contact(Object):
description = 'An address book contact.'
additional_properties = True
properties = {
'name': string('The contact name', min_length=1, max_length=200),
'is_primary', boolean('Indicates if this is a primary contact.'),
'type': enum('The type of contact.', enum=['Friend', 'Family']),
}
required = ['name']
# If the optional `type` is specified, then `is_primary` will be required.
property_dependencies = {
'type': ['is_primary'],
}
Array¶
An Array
type represents a list and allows you to
define properties and required properties.
Attributes¶
additional_items
- Ifitems
is a list and this is True then additional items whose types aren’t defined are allowed in the list.description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.items
- The type each item should be, or a list of types where the position of the type in the list represents the type at that position in the array the item should be.min_items
- The minimum number of items allowed in the list.max_items
- The maximum number of items allowed in the list.nullable
- Indicates if the value of this type is allowed to be None.param_name
- The name of the request parameter that should map to your logic function annotated parameter. If not specified it expects the request parameter will be named the same as the logic function parameter name.parser
- An optional function to parse the request parameter before it’s passed to the type. See custom type parser.unique_items
- If True, items in the array should be unique from one another.
Example¶
from doctor.types import Array, string
class Countries(Array):
description = 'An array of countries.'
items = string('A country')
min_items = 0
max_items = 5
unique_items = True
UnionType¶
A UnionType
allows you to specify that a type can be
one of n types defined in the types
attribute.
The first type in the list of types that is valid will be used.
Attributes¶
description
- A human readable description of what the type represents. This will be used when generating documentation.example
- An example value to send to the endpoint when generating API documentation. This is optional and a default example value will be generated for you.types
- A list of allowed types the value could be. If the value doesn’t match any of the types aTypeSystemError
will be raised.
Example¶
from doctor.types import string, UnionType
S = string('Starts with S.', pattern=r'^S.*')
T = string('Starts with T.', pattern=r'^T.*')
class SOrT(UnionType):
description = 'A string that starts with `S` or `T`.'
types = [S, T]
# Valid values:
SOrT('S is the first letter.')
SOrT('T is the first letter.')
# Invalid value:
SOrT('Does not start with S or T.')
JsonSchema¶
A JsonSchema
type is primarily meant to ease the transition
from doctor 2.x.x to 3.0.0. It allows you to specify an already defined
schema file to represent a type. You can use a definition within the schema
as your type or the root type of the schema.
This type will use the json schema to set the description, example and native
type attributes of the class. This type should not be used directly, instead
you should use json_schema_type()
to create your class.
Attributes¶
definition_key
- The key of the definition within your schema that should be used for the type.description
- A human readable description of what the type represents. This will be used when generating documentation. This value will automatically get loaded from your schema definition.example
- An example value to send to the endpoint when generating API documentation. This value will automatically get loaded from your schema definition.schema_file
- The full path to the schema file. This attribute is required to be defined on your class.
Example¶
annotation.yaml
---
$schema: 'http://json-schema.org/draft-04/schema#'
description: An annotation.
definitions:
annotation_id:
description: Auto-increment ID.
example: 1
type: integer
name:
description: The name of the annotation.
example: Example Annotation.
type: string
type: object
properties:
annotation_id:
$ref: '#/definitions/annotation_id'
name:
$ref: '#/definitions/name'
additionalProperties: false
Using `definition_key`
from doctor.types import json_schema_type
AnnotationId = json_schema_type(
'/full/path/to/annoation.yaml', definition_key='annotation_id')
Without `definition_key`
from doctor.types import json_schema_type
Annotation = json_schema_type('/full/path/to/annotation.yaml')
Quick Type Creation¶
Each type also has a function that can be used to quickly create a new type without having to define large classes. Each of these functions takes the description of the type as the first positional argument and any attributes the type accepts can be passed as keyword arguments. The following functions are provided:
array()
- Create a newArray
type.boolean()
- Create a newBoolean
type.enum()
- Create a newEnum
type.integer()
- Create a newInteger
type.json_schema_type()
- Create a newJsonSchema
type.new_type()
- Create a new user defined type.number()
- Create a newNumber
type.string()
- Create a newString
type.
Examples¶
from doctor.errors import TypeSystemError
from doctor.types import (
array, boolean, enum, integer, json_schema_type, new_type, number,
string, String)
# Create a new array type of countries
Countries = array('List of countries', items=string('Country'), min_items=1)
# Create a new boolean type
Agreed = boolean('Indicates if user agreed or not')
# Create a new enum type
Color = enum('A color', enum=['blue', 'green', 'red'])
# Create a new integer type
AnnotationId = integer('Annotation PK', minimum=1)
# Create a new jsonschema type
Annotation = json_schema_type(schema_file='/path/to/annotation.yaml')
# Create a new type based on a String
class FooString(String):
must_start_with_foo = True
def __new__(cls, *args, **kwargs):
value = super().__new__(cls, *args, **kwargs)
if cls.must_start_with_foo:
if not value.lower().startswith('foo'):
raise TypeSystemError('Must start with foo', cls=cls)
MyFooString = new_type(FooString)
# Create a new number type
ProductRating = number('Product rating', maximum=10, minimum=1)
# Create a new string type
FirstName = string('First name', min_length=2, max_length=255)
# Create a new type based on FirstName, but is allowed to be None
NullableFirstName = new_type(FirstName, nullable=True)
Custom Type Parser¶
Note
The parser
attribute only applies for
non-json requests (application/x-www-form-urlencoded). If the
request uses a json body, it will be parsed as normal and any callable
defined will not be executed.
In some instances you don’t have control over what data gets sent to an endpoint
due to legacy integrations. If you need the ability to transform a request
parameter before it gets validated by the type, you can specify a custom
parser
attribute. It’s value should be a
callable that accepts a value that is the request parameter and returns the parsed
value. The callable should raise a ParserError
if it
fails to parse the value.
# types.py
from typing import List
from doctor.errors import ParserError
from doctor.types import array, string
def str_to_array(value: str) -> List[str]:
"""Parses a comma separated value to an array.
Our request parameter is a str that looks like: `'item1,item2'`
>>> str_to_array('item1,item2')
['item1', 'item2']
:param value: The value to parse, e.g. 'item1,item2'
:returns: A list of values.
"""
# If your logic is more complex and the value can't be parsed, raise
# a `ParserError` in your function.
return value.split(',')
Item = string('An item.')
Items = array('An array of items.', items=Item, parser=str_to_array)
# logic.py
# HTTP POST /items items=item1%2Citem2
def create_items(items: Items):
# The comma separated string becomes a list of items when passed to the
# logic function.
print(items) # ['item1', 'item2']
Custom Type Validation¶
If you need to provide custom validation outside of that supported by the builtin
doctor types you can provide your own validate method on your type class to
perform custom validation. To do this, simply override the
validate()
method. This method should take a
single argument that is the value and perform validation on it. If it fails
validation it should raise a TypeSystemError
.
An example might be that we want to allow an object to be passed as a request parameter that doesn’t have any schema. We accept any arbitrary key/values. The only restriction is that the keys need to match a particular pattern. To do this we can add our own validate method to ensure this happens.
from doctor.errors import TypeSystemError
from doctor.types import Object
class UserSettings(Object):
description = 'An object containing user settings.'
additional_properties = True
@classmethod
def validate(cls, value):
for key in value:
if not key.startswith('user_'):
raise TypeSystemError('Key {} does not begin with `user_`'.format(key))
Module Documentation¶
Copyright © 2017, Encode OSS Ltd. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is a modified version of the typingsystem.py module in apistar. https://github.com/encode/apistar/blob/973c6485d8297c1bcef35a42221ac5107dce25d5/apistar/typesystem.py
-
class
doctor.types.
Array
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
,list
Represents a list type.
-
additional_items
= False¶ If items is a list and this is True then additional items whose types aren’t defined are allowed in the list.
-
classmethod
get_example
()[source]¶ Returns an example value for the Array type.
If an example isn’t a defined attribute on the class we return a list of 1 item containing the example value of the items attribute. If items is None we simply return a [1].
Return type: list
-
items
= None¶ The type each item should be, or a list of types where the position of the type in the list represents the type at that position in the array the item should be.
-
max_items
= None¶ The maxiimum number of items allowed in the list.
-
min_items
= 0¶ The minimum number of items allowed in the list.
-
native_type
¶ alias of
builtins.list
-
unique_items
= False¶ If True items in the array should be unique from one another.
-
-
class
doctor.types.
Boolean
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
Represents a bool type.
-
native_type
¶ alias of
builtins.bool
-
-
class
doctor.types.
Enum
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
,str
Represents a str type that must be one of any defined allowed values.
-
case_insensitive
= False¶ Indicates if the values of the enum are case insensitive or not.
-
enum
= []¶ A list of valid values.
-
lowercase_value
= False¶ If True the input value will be lowercased before validation.
-
native_type
¶ alias of
builtins.str
-
uppercase_value
= False¶ If True the input value will be uppercased before validation.
-
-
class
doctor.types.
Integer
(*args, **kwargs)[source]¶ Bases:
doctor.types._NumericType
,int
Represents an int type.
-
native_type
¶ alias of
builtins.int
-
-
doctor.types.
JSON_TYPES_TO_NATIVE
= {'array': <class 'list'>, 'boolean': <class 'bool'>, 'integer': <class 'int'>, 'number': <class 'float'>, 'object': <class 'dict'>, 'string': <class 'str'>}¶ A mapping of json types to native python types.
-
class
doctor.types.
JsonSchema
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
Represents a type loaded from a json schema.
NOTE: This class should not be used directly. Instead use
json_schema_type()
to create a new class based on this one.-
definition_key
= None¶ The key from the definitions in the schema file that the type should come from.
-
classmethod
get_example
()[source]¶ Returns an example value for the JsonSchema type.
Return type: Any
-
schema
= None¶ The loaded ResourceSchema
-
schema_file
= None¶ The full path to the schema file.
-
-
exception
doctor.types.
MissingDescriptionError
[source]¶ Bases:
ValueError
An exception raised when a type is missing a description.
-
class
doctor.types.
Number
(*args, **kwargs)[source]¶ Bases:
doctor.types._NumericType
,float
Represents a float type.
-
native_type
¶ alias of
builtins.float
-
-
class
doctor.types.
Object
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
,dict
Represents a dict type.
-
additional_properties
= True¶ If True additional properties will be allowed, otherwise they will not.
-
classmethod
get_example
()[source]¶ Returns an example value for the Dict type.
If an example isn’t a defined attribute on the class we return a dict of example values based on each property’s annotation.
Return type: dict
-
native_type
¶ alias of
builtins.dict
-
properties
= {}¶ A mapping of property name to expected type.
-
property_dependencies
= {}¶ A mapping of property name to a list of other properties it requires when the property name is present.
-
required
= []¶ A list of required properties.
-
title
= None¶ A human readable title for the object.
-
-
class
doctor.types.
String
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
,str
Represents a str type.
-
format
= None¶ Will check format of the string for date, date-time, email, time and uri.
-
max_length
= None¶ The maximum length of the string.
-
min_length
= None¶ The minimum length of the string.
-
native_type
¶ alias of
builtins.str
-
pattern
= None¶ A regex pattern that the string should match.
-
trim_whitespace
= True¶ Whether to trim whitespace on a string. Defaults to True.
-
-
class
doctor.types.
SuperType
(*args, **kwargs)[source]¶ Bases:
object
A super type all custom types must extend from.
This super type requires all subclasses define a description attribute that describes what the type represents. A ValueError will be raised if the subclass does not define a description attribute.
-
description
= None¶ The description of what the type represents.
-
example
= None¶ An example value for the type.
-
nullable
= False¶ Indicates if the value of this type is allowed to be None.
-
param_name
= None¶ An optional name of where to find the request parameter if it does not match the variable name in your logic function.
-
parser
= None¶ An optional callable to parse a request paramter before it gets validated by a type. It should accept a single value paramter and return the parsed value.
-
classmethod
validate
(value)[source]¶ Additional validation for a type.
All types will have a validate method where custom validation logic can be placed. The implementor should return nothing if the value is valid, otherwise a TypeSystemError should be raised.
Parameters: value ( Any
) – The value to be validated.
-
-
class
doctor.types.
UnionType
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
A type that can be one of any of the defined types.
The first type that does not raise a
TypeSystemError
will be used as the type for the variable.-
types
= []¶ A list of allowed types.
-
-
class
doctor.types.
_NumericType
(*args, **kwargs)[source]¶ Bases:
doctor.types.SuperType
Base class for both Number and Integer.
-
exclusive_maximum
= False¶ The maximum value should be treated as exclusive or not.
-
exclusive_minimum
= False¶ The minimum value should be treated as exclusive or not.
-
maximum
= None¶ The maximum value allowed.
-
minimum
= None¶ The minimum value allowed.
-
multiple_of
= None¶ The value is required to be a multiple of this value.
-
-
doctor.types.
array
(description, **kwargs)[source]¶ Create a
Array
type.Parameters: - description – A description of the type.
- kwargs – Can include any attribute defined in
Array
Return type: Any
-
doctor.types.
boolean
(description, **kwargs)[source]¶ Create a
Boolean
type.Parameters: - description – A description of the type.
- kwargs – Can include any attribute defined in
Boolean
Return type: Any
-
class
doctor.types.
classproperty
(fget)[source]¶ Bases:
object
A decorator that allows a class to contain a class property.
This is a function that can be executed on a non-instance but accessed via a property.
>>> class Foo(object): ... a = 1 ... @classproperty ... def b(cls): ... return cls.a + 1 ... >>> Foo.b 2
-
doctor.types.
enum
(description, **kwargs)[source]¶ Create a
Enum
type.Parameters: - description – A description of the type.
- kwargs – Can include any attribute defined in
Enum
Return type: Any
-
doctor.types.
get_types
(json_type)[source]¶ Returns the json and native python type based on the json_type input.
If json_type is a list of types it will return the first non ‘null’ value.
Parameters: json_type ( Union
[str
,List
[str
]]) – A json type or a list of json types.Return type: Tuple
[str
,str
]Returns: A tuple containing the json type and native python type.
-
doctor.types.
get_value_from_schema
(schema, definition, key, definition_key)[source]¶ Gets a value from a schema and definition.
If the value has references it will recursively attempt to resolve them.
Parameters: - schema (ResourceSchema) – The resource schema.
- definition (dict) – The definition dict from the schema.
- key (str) – The key to use to get the value from the schema.
- definition_key (str) – The name of the definition.
Returns: The value.
Raises: TypeSystemError – If the key can’t be found in the schema/definition or we can’t resolve the definition.
-
doctor.types.
integer
(description, **kwargs)[source]¶ Create a
Integer
type.Parameters: - description – A description of the type.
- kwargs – Can include any attribute defined in
Integer
Return type: Any
-
doctor.types.
json_schema_type
(schema_file, **kwargs)[source]¶ Create a
JsonSchema
type.This function will automatically load the schema and set it as an attribute of the class along with the description and example.
Parameters: - schema_file (
str
) – The full path to the json schema file to load. - kwargs – Can include any attribute defined in
JsonSchema
Return type: Type
- schema_file (
-
doctor.types.
new_type
(cls, **kwargs)[source]¶ Create a user defined type.
The new type will contain all attributes of the cls type passed in. Any attribute’s value can be overwritten using kwargs.
Parameters: kwargs – Can include any attribute defined in the provided user defined type. Return type: Any