fasthtml icon indicating copy to clipboard operation
fasthtml copied to clipboard

[BUG] Route handlers convert falsy values (0, False) to empty strings

Open civvic opened this issue 1 year ago • 0 comments

Describe the bug FastHTML route handlers convert falsy values to empty strings when rendering responses. Specifically:

  • The integer 0 is converted to '' (empty string) instead of '0'
  • The boolean False is converted to '' instead of 'False'

This behavior is inconsistent with how other primitive values are handled, where integers like 1 correctly convert to '1' and True correctly converts to 'True'.

Minimal Reproducible Example

from fasthtml.core import FastHTML, Client

app, cli, rt = (app := FastHTML()), Client(app), app.route

@rt('/zero')
def get(): return 0

@rt('/one')
def get(): return 1

@rt('/true')
def get(): return True

@rt('/false')
def get(): return False

assert cli.get('/zero').text == '0'  # Fails: returns ''
assert cli.get('/one').text == '1'   # Passes
assert cli.get('/true').text == 'True'  # Passes
assert cli.get('/false').text == 'False'  # Fails: returns ''

Expected behavior

  • Route handlers returning 0 should render as '0' in the response
  • Route handlers returning False should render as 'False' in the response

This would be consistent with how other primitive values are handled, where:

  • 1 renders as '1'
  • True renders as 'True'

Environment Information

  • fasthtml version: 0.9.0
  • fastcore version: 1.7.19
  • fastlite version: 0.0.13

Confirmation

  • [x] I have read the FAQ (https://docs.fastht.ml/explains/faq.html)
  • [x] I have provided a minimal reproducible example
  • [X] I have included the versions of fastlite, fastcore, and fasthtml
  • [x] I understand that this is a volunteer open source project with no commercial support.

Additional context The current behavior makes it impossible to distinguish between an empty response and a response containing 0 or False.

Similar but unrelated bug #567, this one caused by line 188 ...v not in (False, None, '')... in to_xml.

This happens because the initial check if not resp: resp=() in _resp() treats these valid values as falsy conditions, preventing them from reaching the string conversion at the end of the function.

I don't know if this is intended, but i can imagine cases where 0 can be a valid response. At least, it think it should be deocumented so we can intercept this cases and return strings.

The above examples are contrived, but consider this simple component with a readout that never shows the 0 (running in a notebook):

import dataclasses as DC
from fasthtml.components import Label, Input, Text

_n = '\n'

@DC.dataclass
class IntSlider:
    value:int=-1; min:int=-1; max:int=1; step:int=1
    def __ft__(self):
        return Div()(
            Label(_for='value')('Value'), _n,
            Input(type='range', name='value', **DC.asdict(self),
                hx_post=f"/value", hx_trigger='input changed', hx_target='next text', hx_swap='textContent'),
            Text(style='inline')(self.value), _n
        )

rp = IntSlider()

@rt('/value')
def changed(value:int):
    rp.value = value
    return value

show(rp)

Screenshots Screenshot 2024-11-10 at 3 26 07 PM Screenshot 2024-11-10 at 3 26 13 PM Screenshot 2024-11-10 at 3 26 19 PM

civvic avatar Nov 10 '24 14:11 civvic