streamlit-pydantic icon indicating copy to clipboard operation
streamlit-pydantic copied to clipboard

Optional[str] fields fail to render

Open bruno-f-cruz opened this issue 1 year ago • 2 comments

Describe the bug:

When attempting to run the provided test for optional fields: https://github.com/LukasMasuch/streamlit-pydantic/blob/390a45aba7bf8caccc297c335715cc141db490af/examples/optional_fields.py

The library is not able to render anything with strings it seems like. This is also true for more complex classes. The library is able to render the text if the Optional typing is removed.

Expected behaviour:

The field should be rendered as any other string field.

Steps to reproduce the issue:

  1. Checkout the most recent commit 390a45a
  2. Install
  3. Run the optional_fields.py example provided in the repository

Technical details:

  • Windows
  • Chrome

bruno-f-cruz avatar Dec 05 '23 18:12 bruno-f-cruz

This is True for any Optional field! This is mainly because on an Optional field, the field can also be null, so something like:

  • {'anyOf': [{'type': '<type>'}, {'type': 'null'}]}
  • {'anyOf': [{'$ref': '<ref>'}, {'type': 'null'}]}

Therefore, we could filter the optional properties with only null types like:

def filter_nullable(property: Dict) -> Dict:
    # if it is optional and nullable, it may be
    # - {'anyOf': [{'type': '<type>'}, {'type': 'null'}]
    # - {'anyOf': [{'$ref': '<ref>'}, {'type': 'null'}]
    # we want to remove the "null" type
    union_prop = property.get("oneOf", property.get("anyOf"))
    if union_prop is not None:
        # Remove the null type, while keeping the `$ref` and other types
        where = [i for i,d in enumerate(union_prop) if d.get('type') == "null"]
        where.reverse()
        for i in where:
            del union_prop[i]
        if len(union_prop) == 1: # it is fine, we wrap the type to the original object
            for key in union_prop[0]:
                property[key] = union_prop[0][key]

            del property["anyOf"] # now we can delete the key

    return property

and in _render_property:

    def _render_property(self, streamlit_app: Any, key: str, property: Dict) -> Any:
        # filter the case of optional and nullable
        property = schema_utils.filter_nullable(property)

KirmTwinty avatar Mar 12 '24 13:03 KirmTwinty

Hi, is there any solution how to handle optional arguments?

MaxKasper avatar Mar 19 '24 17:03 MaxKasper