Skip to content

Commit 3e62956

Browse files
committed
More compat
1 parent 31d6c8b commit 3e62956

File tree

5 files changed

+47
-31
lines changed

5 files changed

+47
-31
lines changed

src/python-fastui/fastui/components/__init__.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ def __get_pydantic_json_schema__(
154154
) -> _t.Any:
155155
# until https://github.com/pydantic/pydantic/issues/8413 is fixed
156156
json_schema = handler(core_schema)
157-
json_schema['required'].append('level')
157+
schema_def = handler.resolve_ref_schema(json_schema)
158+
schema_def['required'].append('level')
158159
return json_schema
159160

160161

@@ -309,7 +310,8 @@ def __get_pydantic_json_schema__(
309310
) -> _t.Any:
310311
# until https://github.com/pydantic/pydantic/issues/8413 is fixed
311312
json_schema = handler(core_schema)
312-
json_schema.setdefault('required', []).extend(['startLinks', 'endLinks'])
313+
schema_def = handler.resolve_ref_schema(json_schema)
314+
schema_def.setdefault('required', []).extend(['startLinks', 'endLinks'])
313315
return json_schema
314316

315317

@@ -523,7 +525,8 @@ def __get_pydantic_json_schema__(
523525
) -> _t.Any:
524526
# add `children` to the schema so it can be used in the client
525527
json_schema = handler(core_schema)
526-
json_schema['properties']['children'] = {'tsType': 'ReactNode'}
528+
schema_def = handler.resolve_ref_schema(json_schema)
529+
schema_def['properties']['children'] = {'tsType': 'ReactNode'}
527530
return json_schema
528531

529532

src/python-fastui/fastui/json_schema.py

+35-20
Original file line numberDiff line numberDiff line change
@@ -160,21 +160,26 @@ def json_schema_obj_to_fields(
160160
def json_schema_any_to_fields(
161161
schema: JsonSchemaAny, loc: SchemeLocation, title: _t.List[str], required: bool, defs: JsonSchemaDefs
162162
) -> _t.Iterable[FormField]:
163-
schema, required = deference_json_schema(schema, defs, required)
164-
title = title + [schema.get('title') or loc_to_title(loc)]
165-
166-
if schema_is_field(schema):
167-
yield json_schema_field_to_field(schema, loc, title, required)
168-
elif schema_is_array(schema):
169-
yield from json_schema_array_to_fields(schema, loc, title, required, defs)
163+
dereferenced, required = deference_json_schema(schema, defs, required)
164+
title = title + [schema.get('title', dereferenced.get('title', loc_to_title(loc)))]
165+
description = schema.get('description', dereferenced.get('description'))
166+
167+
if schema_is_field(dereferenced):
168+
yield json_schema_field_to_field(dereferenced, loc, title, description, required)
169+
elif schema_is_array(dereferenced):
170+
yield from json_schema_array_to_fields(dereferenced, loc, title, description, required, defs)
170171
else:
171-
assert schema_is_object(schema), f'Unexpected schema type {schema}'
172+
assert schema_is_object(dereferenced), f'Unexpected schema type {dereferenced}'
172173

173-
yield from json_schema_obj_to_fields(schema, loc, title, defs)
174+
yield from json_schema_obj_to_fields(dereferenced, loc, title, defs)
174175

175176

176177
def json_schema_field_to_field(
177-
schema: JsonSchemaField, loc: SchemeLocation, title: _t.List[str], required: bool
178+
schema: JsonSchemaField,
179+
loc: SchemeLocation,
180+
title: _t.List[str],
181+
description: _t.Union[str, None],
182+
required: bool,
178183
) -> FormField:
179184
name = loc_to_name(loc)
180185
if schema['type'] == 'boolean':
@@ -183,10 +188,10 @@ def json_schema_field_to_field(
183188
title=title,
184189
required=required,
185190
initial=schema.get('default'),
186-
description=schema.get('description'),
191+
description=description,
187192
mode=schema.get('mode', 'checkbox'),
188193
)
189-
elif field := special_string_field(schema, name, title, required, False):
194+
elif field := special_string_field(schema, name, title, description, required, False):
190195
return field
191196
else:
192197
return FormFieldInput(
@@ -206,15 +211,20 @@ def loc_to_title(loc: SchemeLocation) -> str:
206211

207212

208213
def json_schema_array_to_fields(
209-
schema: JsonSchemaArray, loc: SchemeLocation, title: _t.List[str], required: bool, defs: JsonSchemaDefs
214+
schema: JsonSchemaArray,
215+
loc: SchemeLocation,
216+
title: _t.List[str],
217+
description: _t.Union[str, None],
218+
required: bool,
219+
defs: JsonSchemaDefs,
210220
) -> _t.Iterable[FormField]:
211221
items_schema = schema.get('items')
212222
if items_schema:
213223
items_schema, required = deference_json_schema(items_schema, defs, required)
214-
for field_name in 'search_url', 'placeholder', 'description':
224+
for field_name in 'search_url', 'placeholder':
215225
if value := schema.get(field_name):
216226
items_schema[field_name] = value # type: ignore
217-
if field := special_string_field(items_schema, loc_to_name(loc), title, required, True):
227+
if field := special_string_field(items_schema, loc_to_name(loc), title, description, required, True):
218228
yield field
219229
return
220230

@@ -236,7 +246,12 @@ def json_schema_array_to_fields(
236246

237247

238248
def special_string_field(
239-
schema: JsonSchemaConcrete, name: str, title: _t.List[str], required: bool, multiple: bool
249+
schema: JsonSchemaConcrete,
250+
name: str,
251+
title: _t.List[str],
252+
description: _t.Union[str, None],
253+
required: bool,
254+
multiple: bool,
240255
) -> _t.Union[FormField, None]:
241256
if schema['type'] == 'string':
242257
if schema.get('format') == 'binary':
@@ -246,7 +261,7 @@ def special_string_field(
246261
required=required,
247262
multiple=multiple,
248263
accept=schema.get('accept'),
249-
description=schema.get('description'),
264+
description=description,
250265
)
251266
elif schema.get('format') == 'textarea':
252267
return FormFieldTextarea(
@@ -257,7 +272,7 @@ def special_string_field(
257272
cols=schema.get('cols'),
258273
placeholder=schema.get('placeholder'),
259274
initial=schema.get('initial'),
260-
description=schema.get('description'),
275+
description=description,
261276
autocomplete=schema.get('autocomplete'),
262277
)
263278
elif enum := schema.get('enum'):
@@ -270,7 +285,7 @@ def special_string_field(
270285
multiple=multiple,
271286
options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum],
272287
initial=schema.get('default'),
273-
description=schema.get('description'),
288+
description=description,
274289
autocomplete=schema.get('autocomplete'),
275290
)
276291
elif search_url := schema.get('search_url'):
@@ -282,7 +297,7 @@ def special_string_field(
282297
required=required,
283298
multiple=multiple,
284299
initial=schema.get('initial'),
285-
description=schema.get('description'),
300+
description=description,
286301
)
287302

288303

src/python-fastui/requirements/pyproject.txt

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# This file is autogenerated by pip-compile with Python 3.11
2+
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
55
# pip-compile --constraint=src/python-fastui/requirements/lint.txt --extra=fastapi --output-file=src/python-fastui/requirements/pyproject.txt --strip-extras src/python-fastui/pyproject.toml
@@ -18,20 +18,19 @@ idna==3.6
1818
# via
1919
# anyio
2020
# email-validator
21-
pydantic==2.6.1
21+
pydantic==2.10.6
2222
# via
2323
# fastapi
2424
# fastui (src/python-fastui/pyproject.toml)
25-
# pydantic
26-
pydantic-core==2.16.2
25+
pydantic-core==2.27.2
2726
# via pydantic
2827
python-multipart==0.0.7
2928
# via fastui (src/python-fastui/pyproject.toml)
3029
sniffio==1.3.0
3130
# via anyio
3231
starlette==0.36.3
3332
# via fastapi
34-
typing-extensions==4.9.0
33+
typing-extensions==4.12.2
3534
# via
3635
# fastapi
3736
# pydantic

src/python-fastui/tests/test_components.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
that's just testing pydantic!
66
"""
77
from fastui import FastUI, components
8-
from pydantic_core import Url
8+
from pydantic import HttpUrl
99

1010

1111
def test_div_text():
@@ -56,7 +56,7 @@ def test_root_model_single():
5656
def test_iframe():
5757
iframe = components.Iframe(src='https://www.example.com', srcdoc='<p>hello world</p>', sandbox='allow-scripts')
5858
assert iframe.model_dump(by_alias=True, exclude_none=True) == {
59-
'src': Url('https://www.example.com'),
59+
'src': HttpUrl('https://www.example.com'),
6060
'type': 'Iframe',
6161
'srcdoc': '<p>hello world</p>',
6262
'sandbox': 'allow-scripts',

src/python-fastui/tests/test_forms.py

-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,6 @@ class FormSelectMultiple(BaseModel):
485485

486486
def test_form_description_leakage():
487487
m = components.ModelForm(model=FormSelectMultiple, submit_url='/foobar/')
488-
489488
assert m.model_dump(by_alias=True, exclude_none=True) == {
490489
'formFields': [
491490
{

0 commit comments

Comments
 (0)