Metadata-Version: 2.4
Name: pyramid-marshmallow
Version: 0.8.2
Summary: Validate request and response data with Marshmallow and optionally generate an OpenAPI spec.
Author-email: Theron Luhn <theron@luhn.com>
Project-URL: Homepage, https://github.com/luhn/pyramid-marshmallow
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Pyramid
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Programming Language :: Python
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyramid>=1.7
Requires-Dist: marshmallow~=3.0
Requires-Dist: zope.interface>=3.8.0
Provides-Extra: openapi
Requires-Dist: apispec<7,>=3.0; extra == "openapi"
Requires-Dist: PyYAML<7,>=5.4; extra == "openapi"
Requires-Dist: hupper~=1.12; extra == "openapi"
Requires-Dist: waitress~=3.0; extra == "openapi"
Provides-Extra: testing
Requires-Dist: pytest~=8.3; extra == "testing"
Requires-Dist: webtest~=2.0; extra == "testing"
Provides-Extra: linting
Requires-Dist: ruff~=0.15.12; extra == "linting"
Dynamic: license-file

# pyramid-marshmallow

pyramid-marshmallow is a simple Pyramid plugin that allows you to validate and marshal a JSON HTTP request or response using [Marshmallow](http://marshmallow.readthedocs.io/) schemas.
You can then leverage this to automatically generate an OpenAPI specification for your API.

> **Version 0.5 and greater requires Marshmallow 3.x.  For Marshmallow 2.x, use version 0.4.**

## Basic usage

Install the project with `pip install pyramid-marshmallow`.

Activate it by adding `config.include('pyramid_marshmallow')` into your config function or adding `pyramid.includes = pyramid_marshmallow` into your ini file.

To validate incoming data, set `validate` to a Marshmallow schema in your `view_config`.
The request body is parsed as JSON then passed through the schema's `load` function.
You can access the processed data with `request.data`.

```python
from marshmallow import Schema, String


class HelloSchema(Schema):
    name = String()


@view_config(
    context=Root,
    name='hello',
    request_method='post',
    validate=HelloSchema(),
)
def hello(context, request):
    return Response(body='Hello, {}'.format(
        request.data['name']
    ))
```

For GET requests, the URL parameters are passed into the schema.
Value lists are not currently supported.

Setting `marshal` in your `view_config` will run the view output through marshmallow (i.e. `Schema.dump`) before going to the renderer.
You probably will want to set the renderer to `json`.

```
@view_config(
    context=Root,
    name='hello',
    request_method='get',
    marshal=HelloSchema(),
    renderer='json',
)
def hello(context, request):
    name = fetch_name()
    return {
        'name': name,
    }
```

`validate` and `marshal` operate independently, so can be used separately or together.

As a convenience, you can pass in a dictionary to `validate` or `marshal` and pyramid-marshmallow will turn it into a schema for you.

```python
@view_config(
    context=Root,
    name='hello',
    request_method='post',
    validate={
        'name': String(),
    },
)
```

You can also get a schema made from a dictionary by using Marshmallow's `Schema.from_dict` classmethod.
This can be useful for `Nested` fields.


### Error handling

pyramid-marshmallow passes through exceptions from marshmallow.
So errors during validation will raise a `marshmallow.exceptions.ValidationError` exception.
([Documentation](https://marshmallow.readthedocs.io/en/stable/api_reference.html#marshmallow.exceptions.ValidationError))

You may want to attach a view to this exception to expose the error messages to the user.

```python
@view_config(
    context=ValidationError,
    renderer='json',
)
def validation_error(context, request):
    request.response.status = 401  # HTTP Bad Request
    return {
        'errors': context.normalized_messages(),
    }
```

## OpenAPI

By adding validation and marshalling to your views, we have the opportunity to utilize that data to generate documentation.
pyramid-marshmallow includes an utility that uses [apispec](https://apispec.readthedocs.io/en/stable/) to generate an [OpenAPI](https://swagger.io/resources/open-api/) specification for your application.

First, you'll need to install some extra dependencies.

```bash
pip install pyramid-marshmallow[openapi]
```

Before you generate the OpenAPI spec, you'll need to add `openapi.title` (your application name), `openapi.version` (your application version), and `openapi.openapi_version` (the OpenAPI Specification version) to the Pyramid settings.
If you use paste-style configuration, this might look like this:

```
[app:main]
use = egg:myproject

openapi.title = My Project
openapi.version = 1.0
openapi.openapi_version = 3.1.1
```

You can generate your spec by calling the `generate-spec` command with your Pyramid application as the first argument, formatted as `[module]:[name]`.

```bash
generate-spec myproject:app
```

You can suffix with a pair of parentheses (`myproject:app()`) and the attribute will be invoke as a function and the result used as the Pyramid application.

If you configure your application via an ini file (Paste), you can also use that.

```bash
generate-spec --ini development.ini
```

This will output the spec to stdout as JSON.
You can set the `--output` flag to output the results to a file.

You can set `--format yaml` to output the spec as YAML instead or `--format html` to output the spec as an HTML file, powered by [ReDoc](https://github.com/Redocly/redoc).

### Local Server

You can also serve your spec locally using `serve-spec`.

```
serve-spec myproject:app
```

If you then navigate to `localhost:8080` in your browser, you'll see the spec rendered with [ReDoc](https://github.com/Redocly/redoc).

`serve-spec` has largely the same arguments as `generate-spec`.
You can control the binding with the `--host` and `--port` arguments.
Adding the `--watch` flag will automatically reload the server when a file changes.

### Documenting Your API

Documentation will be autogenerated from the structure of your Pyramid app and your `validate` and `marshal` declarations.
You can document schema fields by setting the `description` property.

```python
class Hello(Schema):
    name = String(required=True, description='Your first and last name.')
```

The first line of a view callable's docstring will be used as the `summary` property.
The following lines will be used for the `description`.


Documentation for the endpoint will be pulled from the view callable's docstring.
The first line becomes the `summary` and the remaining lines become the `description`.

You can also a line with three hyphens followed by a YAML object.
This will be merged with the auto-generated [operation object](https://swagger.io/specification/#operationObject) and can be used to manually augment the spec.

```python
@view_config(
    context=WidgetResource,
    method='post',
    validate=WidgetSchema(),
)
def create_widget(context, request):
    """
    Create new widget

    Creates a new widget with an attached plumbus.

    ---
    responses:
        201:
            description: Indicates the widget was successfully created.
    """
    widget.make()
    return HTTPCreated()
```

You can also pass a dictionary as the `api_spec` property to `Configurator.add_view` or `@view_config`, which will be merged with the spec in the same way.
This can be advantageous if a single function services multiple views.

```python
@view_config(
    context=WidgetResource,
    method='post',
    validate=WidgetSchema(),
    api_spec={
        'summary': 'Create widget',
    },
)
@view_config(
    context=WidgetResource,
    method='put',
    validate=WidgetSchema(),
    api_spec={
        'summary': 'Update widget',
    },
)
def create_widget(context, request):
    """
    Create/update new widget

    ---
    responses:
        201:
            description: Indicates the widget was successfully created.
    """
    widget.create_or_update()
    return HTTPCreated()
```

## URL Traversal

If you're using Pyramid's URL traversal, the generated spec may be mostly empty.
This is because pyramid-marshmallow has no way of knowing where in the resource tree a resource is.
You can denote this by setting the `__path__` property on each resource.

```python
class Widget(Resource):
    __path__ = '/widget'
```

Views attached to this resource will then be added to the spec.

You can add parameters to your path via the `__params__` property.
You can also tag all attached views via `__tag__`.
Once you define a tag in one resource, you can use it elsewhere by setting `__tag__` to the tag name.

```python
class Widget(Resource):
    __path__ = '/widget/{widgetId}'
    __params__ = [{
        'name': 'widgetId',
        'schema': {
            'type': 'integer',
        },
    }]
    __tag__ = {
        'name': 'widgets',
        'description': 'Endpoints for managing a widget.',
    }
```

## Mergefile

You likely will wish to augment your API spec with a description, additional components, and more.
This can be achieved with a mergefile.
Write a YAML file with the parts of the spec you wish to augment and pass the filename as the `--merge` flag with `generate-spec`.
You can also reference a package resource in the format `[package]:[path]`.
The file will be loaded and merged into the generated spec.
You can use the `--merge` flag multiple times to merge in multiple files.
You can also add a merge file by adding the path to the `openapi.merge` setting in your Pyramid application.
Multiple mergefiles can be separated with a comma.

## Zones

It may be that not all endpoints are made available to all users.
For example, you may have all endpoints available internally but only select ones available publicly.
You would then want separate API docs for internal users versus external users.
This can be achieved using zones.

Tag each endpoint by setting `api_zone` in `Configuration.add_view` or `@view_config`.
By default, all endpoints regardless of zone will be added to the spec.
Set the `--zone` flag in `generate-spec` to only put endpoints assigned to that zone in the spec.

## Prior Art

[pyramid-apispec](https://pypi.org/project/pyramid-apispec/) allows you to augment view callable docstrings with OpenAPI definitions and can reference Marshmallow schemas with the apispec Marshmallow plugin.
It does not support validating input and marshalling output.
Schemas and routes must be manually declared.

[Cornice](https://cornice.readthedocs.io/en/latest/schema.html#using-marshmallow) supports validation with Marshmallow schemas, however only on Cornice resources, not arbitrary Pyramid endpoints.
It does not support auto-generating OpenAPI documentation.
