deta / space-cli

Deta Space CLI
https://deta.space
MIT License
31 stars 5 forks source link

Runtime.ExitError on root path after using `space push` #134

Closed diamond-fish closed 1 year ago

diamond-fish commented 1 year ago

I'm following this tutorial , but after space push I get the error below even though it works fine locally (via uvicorn main:app --reload) and deploys without errors.

https://fastapitutorial-1-j0837537.deta.app/

Error Type:
Runtime.ExitError

Error Message:
RequestId: c937c6ec-3304-4dad-8561-73e8116addfe Error: Runtime exited with error: exit status 1

Logs:
time='2023-07-25T23:00:52Z' level=info msg='serving logs listener on sandbox.localdomain:1234' agent=logsApiAgent
TELEMETRY   Name: telemetry-extension   State: Already subscribed   Types: [Function]
Traceback (most recent call last):
File '/opt/python/bin/uvicorn', line 5, in 
from uvicorn.main import main
File '/opt/python/uvicorn/__init__.py', line 1, in 
from uvicorn.config import Config
File '/opt/python/uvicorn/config.py', line 1, in 
import asyncio
File '/var/lang/lib/python3.9/asyncio/__init__.py', line 8, in 
from .base_events import *
File '/var/lang/lib/python3.9/asyncio/base_events.py', line 45, in 
from . import staggered
File '/var/lang/lib/python3.9/asyncio/staggered.py', line 6, in 
import typing
File '/opt/python/typing.py', line 1359, in 
class Callable(extra=collections_abc.Callable, metaclass=CallableMeta):
File '/opt/python/typing.py', line 1007, in __new__
self._abc_registry = extra._abc_registry
AttributeError: type object 'Callable' has no attribute '_abc_registry'

main.py

from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel

class Item(BaseModel):
  name: str
  price: Annotated[float, Query(gt=0)]

app = FastAPI()

@app.get('/')
def get_root():
  return { 'message': 'Hello World' }

@app.post('/items/')
def create_item(item: Item) -> Item:
  return item

requirements.txt

fastapi
pydantic
typing
uvicorn[standard]

Spacefile

# Spacefile Docs: https://go.deta.dev/docs/spacefile/v0
v: 0
micros:
  - name: fastapi-tutorial
    src: ./
    engine: python3.9
    primary: true
    run: uvicorn main:app
    dev: uvicorn main:app --reload
    public_routes:
      - '/*'
abdelhai commented 1 year ago

remove typing from your reqs?

diamond-fish commented 1 year ago

Removing typing worked, until I continued with the tutorial and it broke again :(. I wish Deta was more predictable -- I cannot tell when pushing a code will break the API. Here's the current code and I can't get Deta to work even though it works in other cloud Python services:

from enum import Enum
from typing import Annotated, Union
from fastapi import FastAPI, Query
from pydantic import BaseModel

class Item(BaseModel):
  description: Union[str, None] = None
  name: str
  price: Annotated[float, Query(gt=0)]
  tax: Union[float, None] = None

class ModelName(str, Enum):
  ALEXNET = 'alexnet'
  LENET = 'lenet'
  RESNET = 'resnet'

class BaseUser(BaseModel):
  email: Union[str, None] = None
  full_name: Union[str, None] = None
  username: str

class UserIn(BaseUser):
  password: str

app = FastAPI()

fake_items_db = [
  { 'item_name': 'Foo' },
  { 'item_name': 'Bar' },
  { 'item_name': 'Baz' },
]

@app.get('/')
def get_root():
  return { 'message': 'Hello World' }

@app.get('/items/')
def get_items(skip: int = 0, limit: int = 10):
  return fake_items_db[skip : skip + limit]

@app.get('/items/{item_id}')
def get_item(item_id: int, is_flag: Annotated[Union[bool, None], Query(alias='flag')] = None):
  response = { 'item_id': item_id }
  if is_flag:
    response['is_flag'] = is_flag
  return response

@app.get('/models/{model_name}')
def get_model(model_name: ModelName):
  response = {}
  match model_name:
    case 'alexnet':
      response = { 'model_name': model_name }
    case 'lenet':
      response = { 'model_name': model_name }
    case 'resnet':
      response = { 'model_name': model_name }
  return response

@app.post('/items/')
def create_item(item: Item) -> Item:
  return item

@app.post('/user/', response_model_exclude_unset=True)
def create_user(user: UserIn) -> BaseUser:
  return user

How do you get the code above to work when deployed?

diamond-fish commented 1 year ago

I found out the root cause: I was using Python 3.10 pattern (match/case), but Deta currently runs on Python 3.9.