---
# System prepended metadata

title: 11 Criando seu próprio framework
tags: [web, day1]

---

---
tags: web,day1
robots: noindex, nofollow
lang: pt-br
---
https://github.com/rochacbruno/python-web-api/tree/main/docs

# 11 Criando seu próprio framework

Você já deve ter ouvido falar do **Flask** ? um dos principais frameworks para desenvolvimento web com Python, agora vamos fazer um exercicio bem simples, vamos criar nosso próprio framework que vai funcionar de maneira similar ao Flask.

A primeira tarefa é escolher qual será o nome do framework, e você pode escolher o nome que você quiser!

Eu por exemplo vou dar ao meu framework o nome do meu filho `Erik` e você ai no seu ambiente pode acompanhar este exercicio dando o nome que você quiser! pode usar o nome de uma pessoa, de um animal de estimação, de um personagem do seu filme favorito.

Precisa de idéias?

- Corleone framework
- Tony framework
- Dwight framework
- SpaceGhost framework
- Banana framework
- Tapioca framework
- Farofa framework

Use sua criatividade.

Por enquanto toda a lógica do nosso programa está em `wsgi.py` vamos então criar um novo arquivo na mesma pasta.

```bash
# use o nome que quiser,
# desde que siga as regras de nomenclatura do Python
# tudo em minusculo, sem espaços.
touch erik.py
```

Agora vamos usar orientação a objetos para transformar o que tinhamos no `wsgi.py` usando apenas funções para um modelo usando orientação a objetos, portanto no arquivo `erik.py` (lembre-se que você pode usar o nome que quiser) nós vamos.

01. Criar uma classe para representar a `app` do nosso framework
00. Inicializar os objetos essenciais de um framework
    - Mapa de roteamento de URLs
    - Configuração de template
    - Ambiente do motor de templates
00. Um decorator para registar novas URLs no estilo do Flask.
00. Um método para renderizar templates.
00. A aplicação `wsgi` implementada em um método `__call__`
00. Um método `run` para executar a aplicação


Um framework web em 60 linhas de código :)


```py
import cgi
import json
import re
from wsgiref.simple_server import make_server

from jinja2 import Environment, FileSystemLoader


class Erik:  # Dê o nome que quiser :) ex: `class Banana`
    def __init__(self):
        self.url_map = []
        self.template_folder = "templates"
        self.env = Environment(loader=FileSystemLoader("templates"))

    def route(self, rule, method="GET", template=None):
        def decorator(view):
            self.url_map.append((rule, method, view, template))
            return view

        return decorator

    def render_template(self, template_name, **context):
        template = self.env.get_template(template_name)
        return template.render(**context).encode("utf-8")

    def __call__(self, environ, start_response):
        path = environ["PATH_INFO"]
        request_method = environ["REQUEST_METHOD"]
        body = b"Content Not Found"
        status = "404 Not Found"
        ctype = "text/html"

        for rule, method, view, template in self.url_map:
            match = re.match(rule, path)
            if match:
                if method != request_method:
                    continue
                view_args = match.groupdict()
                if method == "POST":
                    view_args["form"] = cgi.FieldStorage(
                        fp=environ["wsgi.input"],
                        environ=environ,
                        keep_blank_values=1,
                    )
                view_result = view(**view_args)

                if isinstance(view_result, tuple):
                    view_result, status, ctype = view_result
                else:
                    status = "200 OK"

                if template:
                    body = self.render_template(template, **view_result)
                elif (
                    isinstance(view_result, dict)
                    and ctype == "application/json"
                ):
                    body = json.dumps(view_result).encode("utf-8")
                else:
                    body = str(view_result).encode("utf-8")

        start_response(status, [("Content-type", ctype)])
        return [body]

    def run(self, host="0.0.0.0", port=8000):
        server = make_server(host, port, self)
        server.serve_forever()
```


Podemos testar com:

```py
app = Erik()

@app.route("^/$")
def foo():
    return "Hello"

@app.route("^/(?P<id>\d{1,})$")
def foo2(id):
    return f"Hello {id}", 400, "foo"


# Simular chamadas HTTP
print(
    app(
        {"PATH_INFO": "/1234", "REQUEST_METHOD": "GET"},
        lambda *args: print(args),
    )
)
print(
    app(
        {"PATH_INFO": "/", "REQUEST_METHOD": "GET"},
        lambda *args: print(args),
    )
)
```
O resultado
```
(400, [('Content-type', 'foo')])
[b'Hello 1234']
('200 OK', [('Content-type', 'text/html')])
[b'Hello']
```

Agora vamos colocar em uso em nosso blog, alteramos o arquivo `wsgi.py` para usar o nosso framework.


```py
from database import conn
from erik import Erik

app = Erik()


@app.route("^/$", template="list.template.html")
def post_list():
    posts = get_posts_from_database()
    return {"post_list": posts}


@app.route("^/(?P<id>\d{1,})$", template="post.template.html")
def post_detail(id):
    post = get_posts_from_database(post_id=id)[0]
    return {"post": post}


@app.route("^/new$", template="form.template.html")
def new_post_form():
    return {}


@app.route("^/new$", method="POST")
def new_post_add(form):
    post = {item.name: item.value for item in form.list}
    add_new_post(post)
    return "New post Created with Success!", "201 Created", "text/plain"


def get_posts_from_database(post_id=None):
    cursor = conn.cursor()
    fields = ("id", "title", "content", "author")

    if post_id:
        results = cursor.execute("SELECT * FROM post WHERE id = ?;", post_id)
    else:
        results = cursor.execute("SELECT * FROM post;")

    return [dict(zip(fields, post)) for post in results]


def add_new_post(post):
    cursor = conn.cursor()
    cursor.execute(
        """\
        INSERT INTO post (title, content, author)
        VALUES (:title, :content, :author);
        """,
        post,
    )
    conn.commit()


if __name__ == "__main__":
    app.run()
```


## Conclusão:

Criar um framework com Python é super divertido, porém tem muito mais coisas que um framework precisa fazer:

- Facilidade no registro de rotas
- Segurança e autenticação
- Sessões e Cookies
- Execução assincrona

Para não ter esse trabalho todo manualmente, vamos deixar nosso framerwork caseiro de lado e começar a conhecer as soluções prontas para Python.

Finalizamos aqui o Day 1 e no próximo daremos inicio ao aprendizado dos frameworks Django, Flask e FastAPI :)


Até o Day 2 :)