Skip to content

Commit

Permalink
Merge pull request #3 from lazy-labs/0.0.15
Browse files Browse the repository at this point in the history
0.0.15
  • Loading branch information
wvolkov authored Mar 30, 2021
2 parents 238f6c3 + 49a0b3d commit 050458c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 18 deletions.
66 changes: 52 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Object-oriented rest framework based on starlette, marshmallow and apispec.
* [Starlette] 0.12.0+
* [Marshmallow] 3.0.0rc8+
* [APISpec] 2.0.2+
* [python-multipart] 0.0.5+

## Installation

Expand All @@ -17,35 +18,53 @@ $ pip install star_resty
## Example

```python
from dataclasses import dataclass
from typing import Optional

from marshmallow import Schema, fields, post_load, ValidationError
from marshmallow import Schema, fields, ValidationError, post_load
from starlette.applications import Starlette
from starlette.datastructures import UploadFile
from starlette.responses import JSONResponse

from star_resty import Method, Operation, endpoint, json_schema, query, setup_spec
from dataclasses import dataclass

from star_resty import Method, Operation, endpoint, json_schema, json_payload, form_payload, query, setup_spec
from typing import Optional

class EchoInput(Schema):
a = fields.Int()


# Json Payload (by schema)
class JsonPayloadSchema(Schema):
a = fields.Int(required=True)
s = fields.String()


# Json Payload (by dataclass)
@dataclass
class Payload:
a: int
s: Optional[str] = None


class PayloadSchema(Schema):
a = fields.Int(required=True)
s = fields.String()
class JsonPayloadDataclass(Schema):
a=fields.Int(required=True)
s=fields.Str()

@post_load
def create_payload(self, data, **kwargs):
return Payload(**data)


# Form Payload
class FormFile(fields.Field):
def _validate(self, value):
if not isinstance(value, UploadFile):
raise ValidationError('Not a file')


class FormPayload(Schema):
id = fields.Int(required=True)
file = FormFile()


app = Starlette(debug=True)

@app.exception_handler(ValidationError)
Expand All @@ -64,13 +83,32 @@ class Echo(Method):
return query_params


@app.route('/post', methods=['POST'])
@app.route('/post/schema', methods=['POST'])
@endpoint
class PostSchema(Method):
meta = Operation(tag='default', description='post json (by schema)')

async def execute(self, item: json_payload(JsonPayloadSchema)):
return {'a': item.get('a') * 2, 's': item.get('s')}


@app.route('/post/dataclass', methods=['POST'])
@endpoint
class PostDataclass(Method):
meta = Operation(tag='default', description='post json (by dataclass)')

async def execute(self, item: json_schema(JsonPayloadDataclass, Payload)):
return {'a': item.a * 3, 's': item.s}

@app.route('/form', methods=['POST'])
@endpoint
class Post(Method):
meta = Operation(tag='default', description='post')
class PostForm(Method):
meta = Operation(tag='default', description='post form')

async def execute(self, item: json_schema(PayloadSchema, Payload)):
return {'a': item.a * 2, 's': item.s}
async def execute(self, form_data: form_payload(FormPayload)):
file_name = form_data.get('file').filename
id = form_data.get('id')
return {'message': f"file {file_name} with id {id} received"}


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ typing_extensions
marshmallow>=3.0.0rc8,<4
starlette<1
apispec<4
python-multipart

# Testing
pytest
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ def get_packages(package):
'marshmallow>=3.0.0rc8,<4',
'starlette<1',
'apispec<4',
'python-multipart'
],
version='0.0.14',
version='0.0.15',
url='https://github.com/slv0/start_resty',
license='BSD',
description='The web framework',
Expand Down
5 changes: 2 additions & 3 deletions star_resty/method/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,11 @@ def create_parser_from_data(data: Mapping) -> RequestParser:
parsers = []
async_parsers = []
for key, value in data.items():
if is_dataclass(value):
parser = getattr(value, 'parser', None)
if parser is None and is_dataclass(value):
data = {field.name: field.type for field in fields(value)}
factory = partial(DataClassParser, value)
parser = create_parser_for_dc(data, factory=factory)
else:
parser = getattr(value, 'parser', None)

if parser is None or not isinstance(parser, (Parser, RequestParser)):
continue
Expand Down
1 change: 1 addition & 0 deletions star_resty/payload/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .json import json_payload, json_schema
from .path import path, path_schema
from .query import query, query_schema
from .form import form_payload, form_schema
42 changes: 42 additions & 0 deletions star_resty/payload/form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import types
from typing import Mapping, Type, TypeVar, Union

from marshmallow import EXCLUDE, Schema
from starlette.requests import Request

from star_resty.exceptions import DecodeError
from .parser import Parser, set_parser

__all__ = ('form_schema', 'form_payload', 'FormParser')

P = TypeVar('P')


def form_schema(schema: Union[Schema, Type[Schema]], cls: P,
unknown: str = EXCLUDE) -> P:
return types.new_class('FormDataInputParams', (cls,),
exec_body=set_parser(FormParser.create(schema, unknown=unknown)))


def form_payload(schema: Union[Schema, Type[Schema]], unknown=EXCLUDE) -> Type[Mapping]:
return form_schema(schema, Mapping, unknown=unknown)


class FormParser(Parser):
__slots__ = ()

@property
def location(self):
return 'body'

@property
def media_type(self):
return 'multipart/form-data'

async def parse(self, request: Request):
try:
form_data = await request.form()
form_data = {} if not form_data else form_data
except Exception as e:
raise DecodeError('Invalid form data: %s' % (str(e))) from e
return self.schema.load(form_data, unknown=self.unknown)

0 comments on commit 050458c

Please sign in to comment.