bravado_core documentation¶
bravado_core is a Python library that implements the Swagger 2.0 Specification.
Client and servers alike can use bravado_core to implement these features:
- Swagger Schema ingestion and validation
- Validation and marshalling of requests and responses
- Validation and marshalling of user-defined Swagger formats
- Modelling Swagger #/definitions as Python classes or dicts
For example:
- bravado uses bravado-core to implement a fully functional Swagger client.
- pyramid_swagger uses bravado-core to seamlessly add Swagger support to Pyramid webapps.
Contents:
Configuration¶
All configuration is stored in a dict
.
from bravado_core.spec import Spec
spec_dict = json.loads(open('swagger.json', 'r').read())
config = {
'validate_requests': False,
'use_models': False,
}
swagger_spec = Spec.from_dict(spec_dict, config=config)
Config key | Type | Default | Description |
validate_swagger_spec | boolean | True | Validate the Swagger spec against
the Swagger 2.0 Specification.
|
validate_requests | boolean | True | On the client side, validates outgoing requests.
On the server side, validates incoming requests.
|
validate_responses | boolean | True | On the client side, validates incoming responses.
On the server side, validates outgoing responses.
|
use_models | boolean | True | Use python classes to represent models
instead of dicts. See Python Models.
|
formats | list of SwaggerFormat | [] | List of user-defined formats to support.
See User-Defined Formats.
|
include_missing_properties | boolean | True | Create properties with the value
None if theywere not submitted during object unmarshalling
|
Python Models¶
Models in a Swagger spec are usually defined under the path #/definitions
.
A model can refer to a primitive type or a container type such as a list
or
a dict
. In dict
form, there is an opportunity to make the interface to
access the properties of a model a little more straight forward.
Consider the following:
{
"definitions": {
"Pet": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"breed": {"type": "string"}
}
}
}
}
In python, this model easily maps to a dict
:
pet = {
"name": "Sumi",
"age": 12,
"breed": None,
}
print pet['name']
if pet['age'] < 1:
print 'What a cute puppy!'
if pet['breed'] is None:
pet['breed'] = 'mutt'
However, if the model is implemented as a Python type, dotted access to properties becomes a reality:
from bravado_core.spec import Spec
spec = Spec.from_dict(...)
Pet = spec.definitions['Pet']
pet = Pet(name='Sumi', age=12)
print pet.name
if pet.age < 1:
print 'What a cute puppy!'
if pet.breed is None:
pet.breed = 'mutt'
Configuring Models as Python Types¶
bravado-core supports models as both dicts and python types.
The feature to use python types for models is enabled by default. You can always disable it if necessary.
from bravado_core.spec import Spec
swagger_dict = {..}
spec = Spec.from_dict(swagger_dict, config={'use_models': False})
Allowing null values for properties¶
Typically, bravado-core will complain during validation if it encounters fields with null
values.
This can be problematic, especially when you’re adding Swagger support to pre-existing
APIs. In that case, declare your model properties as x-nullable
:
{
"Pet": {
"type": "object",
"properties": {
"breed": {
"type": "string",
"x-nullable": true
}
}
}
}
x-nullable
is an extension to the Swagger 2.0 spec. A nullable
attribute is being
considered for the next major
version of Swagger.
Model Discovery¶
Keep in mind that bravado-core has to do some extra legwork to figure out which
parts of your spec represent Swagger models and which parts don’t to make this
feature work automagically. With a single-file Swagger spec, this is pretty
straight forward - everything under #/definitions
is a model. However, with
more complicated specs that span multiple files and use external refs, it
becomes a bit more involved. For this reason, the discovery process for
models is best effort with a fallback to explicit annotations as follows:
Search for refs that refer to
#/definitions
in local scopeSearch for refs that refer to external definitions with pattern
<filename>#/definitions/<model name>
.swagger.json
{ "paths": { "/pet": { "get": { "responses": { "200": { "description": "A pet", "schema": { "$ref": "another_file.json#/definitions/Pet" } } } } } } }
another_file.json
{ "definitions": { "Pet": { ... } } }
Search for the
"x-model": "<model name>"
annotation to identify models that can’t be found via method 1. or 2.swagger.json
{ "paths": { "/pet": { "get": { "responses": { "200": { "description": "A pet", "schema": { "$ref": "https://my.company.com/definitions/models.json#/models/Pet" } } } } } } }
models.json (served up via
https://my.company.com/definitions/models.json
){ "models": { "Pet": { "x-model": "Pet" ... } } }
User-Defined Formats¶
Primitive types in Swagger support an optional modifier property format
as
explained in detail in the Swagger Specification.
With this feature, you can define your own domain specific formats and have
validation and marshalling to/from python/json handled transparently.
Creating a user-defined format¶
This is best explained with a simple example. Let’s create a user-defined format for CIDR notation.
In a Swagger spec, the schema-object for a CIDR would resemble:
{
"type": "string",
"format": "cidr",
"description": "IPv4 CIDR"
}
In python, we’d like CIDRs to automatically be converted to a CIDR
object
that makes them easy to work with.
class CIDR(object):
def __init__(self, cidr):
"""
:param cidr: CIDR in string form.
"""
self.cidr = cidr
def overlaps(self, other_cidr):
"""Return true if other_cidr overlaps with this cidr"""
...
def subnet_mask(self):
"""Return the subnet mask of this cidr"""
...
...
We would also like CIDRs to be validated by bravado-core whenever they are part of a HTTP request or response.
Create a bravado_core.formatter.SwaggerFormat
to define the CIDR format:
from bravado_core.formatter import SwaggerFormat
def validate_cidr(cidr_string):
if '/' not in cidr_string:
raise SwaggerValidationError('CIDR {0} is invalid'.format(cidr_string))
cidr_format = SwaggerFormat(
# name of the format as used in the Swagger spec
format='cidr',
# Callable to convert a python CIDR object to a string
to_wire=lambda cidr_object: cidr_object.cidr,
# Callable to convert a string to a python CIDR object
to_python=lambda cidr_string: CIDR(cidr_string),
# Callable to validate the cidr in string form
validate=validate_cidr
)
Configuring user-defined formats¶
Now that we have a cidr_format
, just pass it to a Spec
as part of the
config
parameter on Spec
creation.
from bravado_core.spec import Spec
spec_dict = json.loads(open('swagger.json', 'r').read())
config = {
'validate_responses': True,
'validate_requests': True,
'formats': [cidr_format],
}
spec = Spec.from_dict(spec_dict, config=config)
All validation and processing of HTTP requests and responses will now use the configured format where appropriate.
Putting it all together¶
A simple example of passing a CIDR object to a request and getting a list of CIDR objects back from the response.
{
"paths": {
"/get_overlapping_cidrs": {
"get": {
"parameters": [
{
"name": "cidr",
"in": "query",
"type": "string",
"format": "cidr"
}
],
"responses": {
"200": {
"description": "List of overlapping cidrs",
"schema": {
"type": "array",
"items": {
"type": "string",
"format": "cidr"
}
}
}
}
}
}
}
}
from bravado_core.spec import Spec
from bravado_core.response import unmarshal_response
from bravado_core.param import marshal_param
# Retrieve the swagger spec from the server and json.load() it
spec_dict = ...
# Create cidr_format add it to the config dict
config = ...
# Create a bravado_core.spec.Spec
swagger_spec = Spec.from_dict(spec_dict, config=config)
# Get the operation to invoke
op = swagger_spec.get_op_for_request('GET', '/get_overlapping_cidrs')
# Get the Param that represents the cidr query parameter
cidr_param = op.params.get('cidr')
# Create a CIDR object - to_wire() will be called on this during marshalling
cidr_object = CIDR('192.168.1.1/24')
request_dict = {}
# Marshal the cidr_object into the request_dict.
marshal_param(cidr_param, cidr_object, request_dict)
# Lots of hand-wavey stuff here - use whatever http client you have to
# send the request and receive a response
response = http_client.send(request_dict)
# Extract the list of cidrs
cidrs = unmarshal_response(response)
# Verify cidrs are CIDR objects and not strings
for cidr in cidrs:
assert type(cidr) == CIDR
Changelog¶
4.8.2 (2017-09-04)¶
- Fix marshalling of
null
values for properties withx-nullable
set totrue
- Issue #185, PR #186. Thanks Jan Baraniewski for the contribution! - Add
_asdict()
method to each model, similar to what namedtuples have - PR #188.
4.8.1 (2017-08-24)¶
- Make unmarshalling objects roughly 30% faster - PR #182.
4.8.0 (2017-07-15)¶
- Add support for Swagger spec flattening - PR #177.
- Fix handling of API calls that return non-JSON content (specifically text content) - PR #175. Thanks mostrows2 for your contribution!
- Fix error message text when trying to unmarshal an invalid model - PR #179.
4.7.3 (2017-05-05)¶
- Fix support for object composition (allOf) for data passed in the request body - PR #167. Thanks Zi Li for your contribution!
- Return the default value for an optional field missing in the response - PR #171.
4.7.2 (2017-03-23)¶
- Fix unmarshalling of null values for properties with no spec - Issue #163, PR #165.
4.7.1 (2017-03-22)¶
- Fix backward-incompatible Model API change which renames all model methods to have a single underscore infront of them. A deprecation warning has been added - Issue #160, PR #161. Thanks Adam Ever-Hadani for the contribution!
4.7.0 (2017-03-21)¶
- Added support for nullable fields in the format validator - PR #143. Thanks Adam Ever-Hadani
- Add include_missing_properties configuration - PR #152
- Consider default when unmarshalling - PR #154
- Add discriminator support - PR #128, #159. Thanks Michael Jared Lumpe for your contribution
- Make sure pre-commit hooks are installed and run when running tests - PR #155, #158
4.6.1 (2017-02-15)¶
- Fix unmarshalling empty array types - PR #148
- Removed support for Python 2.6 - PR #147
4.6.0 (2016-11-28)¶
- Security Requirement validation (for ApiKey) - PR #124
- Allow self as name for model property, adds new “create” alternate model constructor - Issue #125, PR #126.
- Allow overriding of security specs - PR #121
- Adds minimal support for responses with text/* content_type.
4.5.1 (2016-09-27)¶
- Add marshal and unmarshal methods to models - PR #113, #120.
4.5.0 (2016-09-12)¶
- Support for model composition through the allOf property - Issue #7, PR #63, #110. Thanks David Bartle for the initial contribution!
- Fix issue with header parameter values being non-string types - PR #115.
4.4.0 (2016-08-26)¶
- Adds support for security scheme definitions, mostly focusing on the “apiKey” type - PR #112.
4.3.2 (2016-08-17)¶
- Fixes around unmarshalling, x-nullable and required behavior - Issue #108, PR #109. Big thanks to Zachary Roadhouse for the report and pull request!
- Fix AttributeError when trying to unmarshal a required array param that’s not present - PR #111.
4.3.1 (2016-08-09)¶
- Check if a parameter is bool-type before assuming it’s a string - PR #107. Thanks to Nick DiRienzo for the pull request!
4.3.0 (2016-08-04)¶
- Add support for
x-nullable
- Issue #47, PR #64 and #103. Thanks to Andreas Hug for the pull request! - Fix support for vendor extensions at the path level - PR #95, #106. Thanks to Mikołaj Siedlarek for the initial pull request!
4.2.5 (2016-07-27)¶
- Add basepython python2.7 for flake8, docs, and coverage tox commands
4.2.4 (2016-07-26)¶
- coverage v4.2 was incompatible and was breaking the build. Added –append for the fix.
4.2.3 (2016-07-26)¶
- Accept tuples as a type list as well.
4.2.2 (2016-04-01)¶
- Fix marshalling of an optional array query parameter when not passed in the service call - PR #87
4.2.1 (2016-03-23)¶
- Fix optional enums in request params - Issue #77
- Fix resolving refs during validation - Issue #82
4.2.0 (2016-03-10)¶
- More robust handling of operationId which contains non-standard chars - PR #76
- Provide a client ingestible version of spec_dict with x-scope metadata removed. Accessible as Spec.client_spec_dict - Issue #78
4.1.0 (2016-03-01)¶
- Better handling of query parameters that don’t have a value - Issue #68
- Allow marshalling of objects which are subclasses of dict - PR #61
- Fix boolean query params to support case-insensetive true/false and 0/1 - Issue #70
- Support for Swagger specs in yaml format - Issue #42
- Fix validation of server side request parameters when collectionFormat=multi and item type is not string - Issue #66
- Fix unmarshaling of server side request parameters when collectionFormat=multi and cardinality is one - PR #75
4.0.1 (2016-01-11)¶
- Fix unmarshalling of an optional array query parameter when not passed in the query string.
4.0.0 (2015-11-17)¶
- Support for recursive $refs - Issue #35
- Requires swagger-spec-validator 2.0.1
- Unqualified $refs no longer supported.
Bad:
{"$ref": "User"}
Good:{"$ref": "#/definitions/User"}
- Automatic tagging of models is only supported in the root swagger spec file. If you have models defined in $ref targets that are in other files, you must manually tag them with ‘x-model’ for them to be available as python types. See Model Discovery for more info.
3.1.1 (2015-10-19)¶
- Fix the creation of operations that contain shared parameters for a given endpoint.
3.1.0 (2015-10-19)¶
- Added http
headers
tobravado_core.response.IncomingResponse
.
3.0.2 (2015-10-12)¶
- Added docs on how to use user-defined formats.
- Added docs on how to configure bravado-core.
- formats added as a config option
3.0.1 (2015-10-09)¶
- Automatically tag models in external $refs - Issue #45 - see Model Discovery for more info.
3.0.0 (2015-10-07)¶
- User-defined formats are now scoped to a Swagger spec - Issue #50 (this is a non-backwards compatible change)
- Deprecated bravado_core.request.RequestLike and renamed to bravado_core.request.IncomingRequest
- Added make docs target and updated docs (still needs a lot of work though)
2.4.1 (2015-09-30)¶
- Fixed validation of user-defined formats - Issue #48
2.4.0 (2015-08-13)¶
- Support relative ‘$ref’ external references in swagger.json
- Fix dereferencing of jsonref when given in a list
2.3.0 (2015-08-10)¶
- Raise MatchingResponseNotFound instead of SwaggerMappingError when a response can’t be matched to the Swagger schema.
2.2.0 (2015-08-06)¶
- Add reason to IncomingResponse
2.1.0 (2015-07-17)¶
- Handle user defined formats for serialization and validation.
2.0.0 (2015-07-13)¶
- Move http invocation to bravado
- Fix unicode in model docstrings
- Require swagger-spec-validator 1.0.12 to pick up bug fixes
1.1.0 (2015-06-25)¶
- Better unicode support
- Python 3 support
1.0.0-rc2 (2015-06-01)¶
- Fixed file uploads when marshaling a request
- Renamed ResponseLike to IncomingResponse
- Fixed repr of a model when it has an attr with a unicode value
1.0.0-rc1 (2015-05-26)¶
- Use basePath when matching an operation to a request
- Refactored exception hierarchy
- Added use_models config option
0.1.0 (2015-05-13)¶
- Initial release