Skip to content

Commit

Permalink
Fixed type issues and left detailed comments for unresolved issues
Browse files Browse the repository at this point in the history
  • Loading branch information
ManiMozaffar committed May 3, 2024
1 parent b180bf4 commit 06888e7
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 35 deletions.
2 changes: 1 addition & 1 deletion demo/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ path = "src/__init__.py"
allow-direct-references = true

[project]
name = 'src'
name = 'demo'
description = 'FastUI demo project'
authors = [{ name = 'Samuel Colvin', email = '[email protected]' }]
classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion demo/src/auth_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def from_request_opt(cls, authorization: Annotated[str, Header()] = '') -> Self


class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj: Any) -> Any: # type: ignore
def default(self, obj: Any) -> Any:
if isinstance(obj, datetime):
return obj.isoformat()
else:
Expand Down
5 changes: 3 additions & 2 deletions demo/src/components_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from fastui import AnyComponent, FastUI
from fastui import components as c
from fastui.events import GoToEvent, PageEvent
from pydantic_core import Url

from .shared import demo_page

Expand Down Expand Up @@ -222,7 +223,7 @@ class Delivery(BaseModel):
components=[
c.Heading(text='Iframe', level=2),
c.Markdown(text='`Iframe` can be used to embed external content.'),
c.Iframe(src='https://pydantic.dev', width='100%', height=400), # type: ignore
c.Iframe(src=Url('https://pydantic.dev'), width='100%', height=400),
],
class_name='border-top mt-3 pt-1',
),
Expand Down Expand Up @@ -260,7 +261,7 @@ class Delivery(BaseModel):
c.Heading(text='Video', level=2),
c.Paragraph(text='A video component.'),
c.Video(
sources=['https://www.w3schools.com/html/mov_bbb.mp4'], # type: ignore
sources=[Url('https://www.w3schools.com/html/mov_bbb.mp4')],
autoplay=False,
controls=True,
loop=False,
Expand Down
2 changes: 1 addition & 1 deletion demo/src/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async def search_view(request: Request, q: str) -> SelectSearchResponse:
for co in data:
regions[co['region']].append({'value': co['cca3'], 'label': co['name']['common']})
options = [{'label': k, 'options': v} for k, v in regions.items()]
return SelectSearchResponse(options=options) # type: ignore
return SelectSearchResponse(options=options) # type: ignore (needs to be SelectOptions type)


FormKind: TypeAlias = Literal['login', 'select', 'big']
Expand Down
14 changes: 9 additions & 5 deletions demo/src/sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ async def run_openai():
from time import perf_counter

from openai import AsyncOpenAI
from openai.types.chat import ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam

messages = [
{'role': 'system', 'content': 'please response in markdown only.'},
{'role': 'user', 'content': 'What is SSE? Please include a javascript code example.'},
ChatCompletionSystemMessageParam({'role': 'system', 'content': 'please response in markdown only.'}),
ChatCompletionUserMessageParam(
{'role': 'user', 'content': 'What is SSE? Please include a javascript code example.'}
),
]
chunks = await AsyncOpenAI().chat.completions.create(
model='gpt-4',
messages=messages, # type: ignore
messages=messages,
stream=True,
) # type: ignore
)

last = None
result_chunks = []
Expand All @@ -48,7 +51,8 @@ async def run_openai():
t = now - last
else:
t = 0
text: str = chunk.choices[0].delta.content
text = chunk.choices[0].delta.content
assert text is None
print(repr(text), t)
if text is not None:
result_chunks.append((t, text))
Expand Down
15 changes: 11 additions & 4 deletions demo/src/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def cities_view(page: int = 1, country: str | None = None) -> list[AnyComponent]
DisplayLookup(field='population', table_width_percent=33),
],
),
c.Pagination(page=page, page_size=page_size, total=len(cities)), # type: ignore
c.Pagination(page=page, page_size=page_size, total=len(cities), page_query_param='page'),
title='Cities',
)

Expand Down Expand Up @@ -145,17 +145,24 @@ def tabs() -> list[AnyComponent]:

@router.get('/users/{id}/', response_model=FastUI, response_model_exclude_none=True)
def user_profile(id: int) -> list[AnyComponent]:
user: User | None = users[id - 1] if id <= len(users) else None
user = users[id - 1] if id <= len(users) else None
if user is None:
return demo_page(
*tabs(),
c.Text(text='User not found.'),
title='User not found',
)

return demo_page(
*tabs(),
c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
c.Details(
data=user, # type: ignore
data=user,
fields=[
DisplayLookup(field='name'),
DisplayLookup(field='dob', mode=DisplayMode.date),
DisplayLookup(field='enabled'),
],
),
title=user.name, # type: ignore
title=user.name,
)
4 changes: 2 additions & 2 deletions src/python-fastui/fastui/json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def json_schema_array_to_fields(
items_schema, required = deference_json_schema(items_schema, defs, required)
for field_name in 'search_url', 'placeholder', 'description':
if value := schema.get(field_name):
items_schema[field_name] = value # type: ignore
items_schema[field_name] = value # type: ignore (Could not assign item in TypedDict)
if field := special_string_field(items_schema, loc_to_name(loc), title, required, True):
yield field
return
Expand Down Expand Up @@ -318,7 +318,7 @@ def deference_json_schema(

# copy everything except `anyOf` across to the new schema
# TODO is this right?
for key, value in schema.items(): # type: ignore
for key, value in schema.items(): # type: ignore (schema type is union of unknwon and JsonSchemaAny)
if key not in {'anyOf'}:
not_null_schema[key] = value # type: ignore

Expand Down
7 changes: 5 additions & 2 deletions src/python-fastui/tests/test_auth_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fastapi import FastAPI

from fastui.auth import AuthError, GitHubAuthProvider, GitHubEmail
from fastui.auth.github import EXCHANGE_CACHE
from fastui.auth.github import EXCHANGE_CACHE, GitHubExchange
from pydantic import SecretStr


Expand Down Expand Up @@ -215,7 +215,10 @@ async def test_exchange_cached_purge(fake_github_app: FastAPI, httpx_client: htt
assert len(EXCHANGE_CACHE) == 1

# manually add an old entry
EXCHANGE_CACHE._data['old'] = (datetime(2020, 1, 1), 'old_token') # type: ignore
EXCHANGE_CACHE._data['old'] = (
datetime(2020, 1, 1),
GitHubExchange(access_token='old_token', token_type='bearer', scope=[]),
)
assert len(EXCHANGE_CACHE) == 2

await github_auth_provider.exchange_code('good')
Expand Down
4 changes: 2 additions & 2 deletions src/python-fastui/tests/test_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_root_model():

def test_root_model_single():
# fixed by validator
m = FastUI(root=components.Text(text='hello world')) # type: ignore
m = FastUI(root=[components.Text(text='hello world')])
assert m.model_dump(by_alias=True, exclude_none=True) == [
{
'text': 'hello world',
Expand All @@ -55,7 +55,7 @@ def test_root_model_single():


def test_iframe():
iframe = components.Iframe(src='https://www.example.com', srcdoc='<p>hello world</p>', sandbox='allow-scripts') # type: ignore
iframe = components.Iframe(src=Url('https://www.example.com'), srcdoc='<p>hello world</p>', sandbox='allow-scripts')
assert iframe.model_dump(by_alias=True, exclude_none=True) == {
'src': Url('https://www.example.com'),
'type': 'Iframe',
Expand Down
51 changes: 36 additions & 15 deletions src/python-fastui/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ async def test_simple_form_submit():

request = FakeRequest([('name', 'bar'), ('size', '123')])

m = await form_dep.dependency(request) # type: ignore
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert isinstance(m, SimpleForm)
assert m.model_dump() == {'name': 'bar', 'size': 123}

Expand All @@ -107,7 +108,8 @@ async def test_simple_form_submit_repeat():
request = FakeRequest([('name', 'bar'), ('size', '123'), ('size', '456')])

with pytest.raises(HTTPException) as exc_info:
await form_dep.dependency(request) # type: ignore
assert form_dep.dependency is not None
await form_dep.dependency(request)

# insert_assert(exc_info.value.detail)
assert exc_info.value.detail == {
Expand Down Expand Up @@ -157,8 +159,8 @@ async def test_w_nested_form_submit():
form_dep = fastui_form(FormWithNested)

request = FakeRequest([('name', 'bar'), ('nested.x', '123')])

m = await form_dep.dependency(request) # type: ignore
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert isinstance(m, FormWithNested)
assert m.model_dump() == {'name': 'bar', 'nested': {'x': 123}}

Expand Down Expand Up @@ -192,7 +194,9 @@ async def test_file_submit():
file = UploadFile(BytesIO(b'foobar'), size=6, filename='testing.txt')
request = FakeRequest([('profile_pic', file)])

m = await fastui_form(FormWithFile).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFile)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'profile_pic': file}


Expand All @@ -202,7 +206,9 @@ async def test_file_submit_repeat():
request = FakeRequest([('profile_pic', file1), ('profile_pic', file2)])

with pytest.raises(HTTPException) as exc_info:
await fastui_form(FormWithFile).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFile)
assert form_dep.dependency is not None
await form_dep.dependency(request)

# insert_assert(exc_info.value.detail)
assert exc_info.value.detail == {
Expand Down Expand Up @@ -241,15 +247,19 @@ async def test_file_constrained_submit():
file = UploadFile(BytesIO(b'foobar'), size=16_000, headers=headers)
request = FakeRequest([('profile_pic', file)])

m = await fastui_form(FormWithFileConstraint).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFileConstraint)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'profile_pic': file}


async def test_file_constrained_submit_filename():
file = UploadFile(BytesIO(b'foobar'), size=16_000, filename='image.png')
request = FakeRequest([('profile_pic', file)])

m = await fastui_form(FormWithFileConstraint).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFileConstraint)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'profile_pic': file}


Expand All @@ -259,7 +269,9 @@ async def test_file_constrained_submit_too_big():
request = FakeRequest([('profile_pic', file)])

with pytest.raises(HTTPException) as exc_info:
await fastui_form(FormWithFileConstraint).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFileConstraint)
assert form_dep.dependency is not None
await form_dep.dependency(request)

# insert_assert(exc_info.value.detail)
assert exc_info.value.detail == {
Expand All @@ -279,7 +291,9 @@ async def test_file_constrained_submit_wrong_type():
request = FakeRequest([('profile_pic', file)])

with pytest.raises(HTTPException) as exc_info:
await fastui_form(FormWithFileConstraint).dependency(request) # type: ignore
form_dep = fastui_form(FormWithFileConstraint)
assert form_dep.dependency is not None
await form_dep.dependency(request)

# insert_assert(exc_info.value.detail)
assert exc_info.value.detail == {
Expand Down Expand Up @@ -325,7 +339,9 @@ async def test_multiple_files_single():
file = UploadFile(BytesIO(b'foobar'), size=16_000, filename='image.png')
request = FakeRequest([('files', file)])

m = await fastui_form(FormMultipleFiles).dependency(request) # type: ignore
form_dep = fastui_form(FormMultipleFiles)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'files': [file]}


Expand All @@ -334,7 +350,9 @@ async def test_multiple_files_multiple():
file2 = UploadFile(BytesIO(b'foobar'), size=6, filename='image2.png')
request = FakeRequest([('files', file1), ('files', file2)])

m = await fastui_form(FormMultipleFiles).dependency(request) # type: ignore
form_dep = fastui_form(FormMultipleFiles)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'files': [file1, file2]}


Expand Down Expand Up @@ -381,7 +399,9 @@ def test_fixed_tuple():
async def test_fixed_tuple_submit():
request = FakeRequest([('foo.0', 'bar'), ('foo.1', '123'), ('foo.2', '456')])

m = await fastui_form(FixedTuple).dependency(request) # type: ignore
form_dep = fastui_form(FixedTuple)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'foo': ('bar', 123, 456)}


Expand Down Expand Up @@ -427,8 +447,9 @@ def test_fixed_tuple_nested():

async def test_fixed_tuple_nested_submit():
request = FakeRequest([('bar.foo.0', 'bar'), ('bar.foo.1', '123'), ('bar.foo.2', '456')])

m = await fastui_form(NestedTuple).dependency(request) # type: ignore
form_dep = fastui_form(NestedTuple)
assert form_dep.dependency is not None
m = await form_dep.dependency(request)
assert m.model_dump() == {'bar': {'foo': ('bar', 123, 456)}}


Expand Down

0 comments on commit 06888e7

Please sign in to comment.