emmett-framework / emmett

The web framework for inventors
Other
1.05k stars 71 forks source link

Run the readme.md example Error: TypeError: 'granian._granian.RSGIHeaders' object is not subscriptable #459

Closed RuixiangS closed 1 year ago

RuixiangS commented 1 year ago

It seems like a granian bug, and it works when I use the emmett develop --interface asgicommand that works out. system: Windows 10 Pro 22H2 19045.3086 my_package version list:

absl-py              1.3.0   
anyio                3.7.0   
Appium-Python-Client 2.9.0   
argcomplete          2.1.2   
certifi              2023.5.7
charset-normalizer   3.1.0   
click                8.1.3   
colorama             0.4.6   
colorlog             6.7.0
distlib              0.3.6
emmett               2.5.3
emmett-crypto        0.3.0
emmett-rest          1.5.1
emmett-sentry        0.4.1
exceptiongroup       1.1.1
func-timeout         4.3.5
granian              0.4.2
h11                  0.14.0
idna                 3.4
pendulum             2.1.2
pip                  23.0.1
poium                1.3.2
polars               0.18.2
psycopg2-binary      2.9.6
pyDAL                17.3
pydantic             1.10.8
python-dateutil      2.8.2
python-rapidjson     1.10
pytzdata             2020.1
PyYAML               6.0
renoir               1.6.1
requests             2.31.0
sentry-sdk           1.25.0
setuptools           67.8.0
severus              1.2.1
six                  1.16.0
sniffio              1.3.0
typer                0.9.0
typing_extensions    4.6.3
urllib3              2.0.2
uvicorn              0.19.0
watchfiles           0.19.0
websockets           10.4
wheel                0.38.4

example

from emmett import App, request, response
from emmett.orm import Database, Field, Model
from emmett.tools import requires, service

class Task(Model):
    name = Field.string()
    is_completed = Field.bool(default=False)

app = App(__name__)
app.config.db.uri = "postgres://username:password@localhost/foo"
db = Database(app)
db.define_models(Task)
app.pipeline = [db.pipe]

def is_authenticated():
    print(request.headers)
    return request.headers["Api-Key"] == "foobar"

def not_authorized():
    response.status = 401
    return {"error": "not authorized"}

@app.route(methods="get")
@service.json
@requires(is_authenticated, otherwise=not_authorized)
async def todo():
    page = request.query_params.page or 1
    tasks = Task.where(lambda t: t.is_completed == False).select(paginate=(page, 20))
    return {"tasks": tasks}

My Test

import requests

url = "http://localhost:8000/todo"
headers = {"Api-Key": "foobar"}
response = requests.get(url, headers=headers)

if response.status_code == 200:
    tasks = response.json()["tasks"]
    print(tasks)
else:
    print("Error:", response.status_code, response.json())
RuixiangS commented 1 year ago

Case: Add an additional question when I use emmett develop --interface asgi I generate a new request whose headers["Api-key"]="not_foobar", otherwise condition will not work properly. Expected: otherwise return {"error": "not authorized"}

Traceback (most recent call last):
  File "D:\Anaconda\envs\emmett-frame\lib\asyncio\events.py", line 81, in _run
    self._context.run(self._callback, *self._args)
  File "D:\Anaconda\envs\emmett-frame\lib\site-packages\emmett\asgi\handlers.py", line 194, in __call__
    await asyncio.wait_for(
  File "D:\Anaconda\envs\emmett-frame\lib\asyncio\tasks.py", line 455, in wait_for
    return await fut
  File "D:\Anaconda\envs\emmett-frame\lib\site-packages\emmett\http.py", line 112, in asgi
    await self._send_body(send)
  File "D:\Anaconda\envs\emmett-frame\lib\site-packages\emmett\http.py", line 165, in _send_body
    'body': self.encoded_body,
  File "D:\Anaconda\envs\emmett-frame\lib\site-packages\emmett\http.py", line 160, in encoded_body
    return self.body.encode('utf-8')
AttributeError: 'dict' object has no attribute 'encode'

Test

import requests

url = "http://localhost:8000/todo"
headers = {"Api-Key": "not_foobar"}
response = requests.get(url, headers=headers)

if response.status_code == 200:
    tasks = response.json()["tasks"]
    print(tasks)
else:
    print("Error:", response.status_code, response.json())
gi0baro commented 1 year ago

My bad I didn't check the README example was still valid with 2.5

You can find the updated code in master