Code Monkey home page Code Monkey logo

winter's Introduction

Winter

Build Status codecov Maintainability PyPI version Gitter

Web Framework for Python inspired by Spring Framework

Main features

  • Declarative API
  • Built around python type annotations
  • Automatic OpenAPI (swagger) documentation generation
  • Suitable for DDD
  • Handling exception without boilerplate in accordance with RFC 7807

How to use

Installation

pip install winter

Hello world

import winter

@winter.controller
class HelloWorldController:
    @winter.route_get('/hello/')
    def hello(self):
        return f'Hello, world!'

To use it with Django:

import winter_django

urlpatterns = [
    *winter_django.create_django_urls(HelloWorldController),
]

Todo list CRUD example:

from http import HTTPStatus
from typing import List
from typing import Optional

import winter
import winter.web
from dataclasses import dataclass
from winter.data.pagination import Page
from winter.data.pagination import PagePosition


@dataclass
class NewTodoDTO:
    todo: str


@dataclass
class TodoUpdateDTO:
    todo: str


@dataclass
class TodoDTO:
    todo_index: int
    todo: str


@winter.web.problem(status=HTTPStatus.NOT_FOUND, auto_handle=True)
class NotFoundException(Exception):
    def __init__(self, todo_index: int):
        self.index = todo_index


todo_list: List[str] = []


@winter.web.controller
@winter.route('todo/')
class TodoController:
    @winter.route_post('')
    @winter.request_body(argument_name='new_todo_dto')
    def create_todo(self, new_todo_dto: NewTodoDTO) -> TodoDTO:
        todo_list.append(new_todo_dto.todo)
        return self._build_todo_dto(len(todo_list) - 1)

    @winter.route_get('{todo_index}/')
    def get_todo(self, todo_index: int) -> TodoDTO:
        self._check_index(todo_index)
        return self._build_todo_dto(todo_index)

    @winter.route_get('{?q}')
    def get_todo_list(self, page_position: PagePosition, q: Optional[str] = None) -> Page[TodoDTO]:
        q = q if q is None else q.lower()
        dto_list = [
            TodoDTO(todo_index=todo_index, todo=todo)
            for todo_index, todo in enumerate(todo_list)
            if q is None or q in todo.lower()
        ]
        limit = page_position.limit
        offset = page_position.offset
        paginated_dto_list = dto_list[offset: offset + limit]
        return Page(total_count=len(dto_list), items=paginated_dto_list, position=page_position)

    @winter.route_get('{todo_index}/')
    @winter.request_body(argument_name='todo_update_dto')
    def update_todo(self, todo_index: int, todo_update_dto: TodoUpdateDTO):
        self._check_index(todo_index)
        todo_list[todo_index] = todo_update_dto.todo

    @winter.route_get('{todo_index}/')
    def delete_todo(self, todo_index: int):
        self._check_index(todo_index)
        del todo_list[todo_index]

    def _check_index(self, todo_index: int):
        if todo_index < 0 or todo_index >= len(todo_list):
            raise NotFoundException(todo_index=todo_index)

    def _build_todo_dto(self, todo_index: int):
        return TodoDTO(todo_index=todo_index, todo=todo_list[todo_index])

Extending Page class

import winter
import winter.web
from dataclasses import dataclass
from winter.data.pagination import Page
from winter.data.pagination import PagePosition
from typing import TypeVar
from typing import Generic


T = TypeVar('T')

@dataclass(frozen=True)
class CustomPage(Page, Generic[T]):
    extra_field: str  # The field will go to meta JSON response field


@winter.web.controller
class ExampleController:
    @winter.route_get('/')
    def create_todo(self, page_position: PagePosition) -> CustomPage[int]:
        return CustomPage(
            # Standard Page fields
            total_count=3,
            items=[1, 2, 3],
            position=page_position,
            # Custom fields
            extra_field=456,
        )

Exception handling

from dataclasses import dataclass
from http import HTTPStatus
from typing import List

from rest_framework.request import Request

import winter
import winter.web


# Minimalist approach. Pointed status and that this exception will be handling automatically. Expected output below:
# {'status': 404, 'type': 'urn:problem-type:todo-not-found', 'title': 'Todo not found', 'detail': 'Incorrect index: 1'}
@winter.web.problem(status=HTTPStatus.NOT_FOUND, auto_handle=True)
class TodoNotFoundException(Exception):
    def __init__(self, invalid_index: int):
        self.invalid_index = invalid_index

    def __str__(self):
        return f'Incorrect index: {self.invalid_index}'

# Extending output using dataclass. Dataclass fields will be added to response body. Expected output below:
# {'status': 404, 'type': 'urn:problem-type:todo-not-found', 'title': 'Todo not found', 'detail': '', 'invalid_index': 1}
@winter.web.problem(status=HTTPStatus.NOT_FOUND, auto_handle=True)
@dataclass
class TodoNotFoundException(Exception):
    invalid_index: int

# When we want to override global handler and customize response body. Expected output below:
# {index: 1, 'message': 'Access denied'}
@dataclass
class ErrorDTO:
    index: int
    message: str


class TodoNotFoundExceptionCustomHandler(winter.web.ExceptionHandler):
    @winter.response_status(HTTPStatus.NOT_FOUND)
    def handle(self, request: Request, exception: TodoNotFoundException) -> ErrorDTO:
        return ErrorDTO(index=exception.invalid_index, message='Access denied')


todo_list: List[str] = []


@winter.web.controller
class TodoProblemExistsController:
    @winter.route_get('global/{todo_index}/')
    def get_todo_with_global_handling(self, todo_index: int):
        raise TodoNotFoundException(invalid_index=todo_index)

    @winter.route_get('custom/{todo_index}/')
    @winter.throws(TodoNotFoundException, handler_cls=TodoNotFoundExceptionCustomHandler)
    def get_todo_with_custom_handling(self, todo_index: int):
        raise TodoNotFoundException(invalid_index=todo_index)

winter's People

Contributors

pristupa avatar mofr avatar andrey-berenda avatar mikhaillazko avatar evgenysmekalin avatar maximluzin avatar denteplykh avatar iuriisevastianov avatar

Stargazers

 avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.