blu

A full stack React framework for Python.

class blu.ClientElement

Bases: object

A custom element to be rendered client-side. Created using the blu.client() decorator.

from blu import client
from blu.html import b, span

__client__ = True


@client
def ColorfulText(color, bold):
    colorful_span = span(style={'color': color})[
        (yield)
    ]
    if bold:
        return b[colorful_span]
    else:
        return colorful_span


ColorfulText('red', bold=True)[
    'Danger! The world said hello back.',
]
<b>
    <span style="color: red">
        Danger! The world said hello back.
    </span>
</b>
__call__(*args: Any, **kwargs: Any) ClientElement

Create a copy of self that will be rendered with the given arguments.

from blu import client
from blu.html import div

__client__ = True


@client
def Greeting(name='World'):
    return div[f'Hello, {name}!']


div[
    Greeting('Arnold'),
    Greeting(name='Hailey'),
    Greeting,
]
<div>Hello, Arnold!</div>
<div>Hello, Hailey!</div>
<div>Hello, World!</div>
Parameters:
  • args – The positional arguments to pass into the element’s render function when it is rendered.

  • kwargs – The keyword arguments to pass into the element’s render function when it is rendered, as well as an optional keyword argument key, which will be used by React to uniquely identify the element when it is rendered. Usually, you won’t need to pass in a key, but it’s required for any ClientElement that is rendered as an item in an Iterable (unless that Iterable is a str or a tuple).

Returns:

A copy of self that will be rendered by calling its render function with *args and **kwargs, with the exception that the keyword argument key will not be passed on to the render function and will instead be used by React to identify the new element. If the original had a key, it will not be retained in the copy, and the copy will have a key if and only if **kwargs contains the key argument.

Below is an example of using the key argument:

from blu import client
from blu.html import div

PEOPLE = [
    {'id': 0, 'name': 'Allison'},
    {'id': 1, 'name': 'Bill'},
    {'id': 2, 'name': 'Carrie'},
]


@client
def Greeting(name):
    return div[f'Hello, {name}!']


[Greeting(x['name'], key=x['id']) for x in PEOPLE]
<div>Hello, Allison!</div>
<div>Hello, Bill!</div>
<div>Hello, Carrie!</div>
__getitem__(children: Node) ClientElement

Create a copy of self that will render with the given children displayed where the render function uses the yield keyword.

from blu import client
from blu.html import span


@client
def RedText(text):
    return span(style={'color': 'red'})[(yield)]


RedText['Danger! This text is red.']
<span style="color: red">Danger! This text is red.</span>
Parameters:

children – Any valid blu.Node or a tuple of blu.Nodes.

Returns:

A copy of self that will render with children displayed where the element’s render function uses the yield keyword. If no children were specified, nothing will be rendered where the yield keyword is used.

Raises:

TypeError if element’s rendering function is not a Generator, i.e. doesn’t contain a yield statement.

class blu.HTMLElement

Bases: object

A React DOM component instance. Created using the blu.html module.

from blu.html import div

div(id='my-id')[
    'Hello World!',
]
<div id="my-id">Hello World!</div>
__call__(props: Props = {}, /, **kwargs: PropValue) HTMLElement

Create a copy of self with React props set based on the given keyword arguments.

from blu.html import div

div(id='my-div')
<div id="my-div"></div>
Parameters:
  • props – A mapping of prop names to prop values. This is an escape hatch for cases when the prop name can’t be represented as a keyword argument name.

  • kwargs – The new props, where the argument name is the prop’s key and the argument value is the prop’s value. This is the usual way to specify props.

Returns:

A copy of self with the same children, but with props set as follows:

  1. For any key-value pair in the props argument, the prop named by the key will be set to the value.

    For example, div(props={'my_prop_': 45}) results in the React element <div my_prop_={45} />

  2. For any keyword argument, the value will be set to a key derived from:

    • Removing the last trailing underscore (if any).

    • Replacing all other underscores with dashes.

    For example, div(my_prop_=45) results in the React element <div my-prop={45} />.

  3. If there is any conflict between the props argument and the keyword arguments, the prop that has the conflict will be set based on the props argument as described in (1). Any props that do not have conflicts will be set the same way they otherwise would, irrespective of the props that do have conflicts.

    For example, div(props={'props-only': 'props', 'shared': 'props'}, shared='kw', kw_only='kw') results in the React element <div props-only="props" shared="props" kw-only="kw" />

__getitem__(children: Node | tuple[Node, ...]) HTMLElement

Create a copy of self whose child nodes are set to the items passed in.

from blu.html import div

div['Hello World!']
<div>Hello World!</div>
Parameters:

children – A blu.Node or a tuple of blu.Nodes.

Returns:

  • If children is a blu.Node: A copy of self whose children are set to [children].

  • If children is a tuple of blu.Nodes: A copy of self whose children are set to list(children).

class blu.Key(key: Any)

Bases: object

A keyed fragment of React nodes.

from blu import Key
from blu.html import b

Key('my-id')[
    'Hello, ',
    b['World'],
    '!',
]
Hello, <b>World</b>!
Parameters:

key – A key to be used by React to uniquely identify the fragment.

Returns:

An empty fragment identified by key.

In most cases, you won’t need to use blu.Key, but it is required when rendering an item in an Iterable (unless that Iterable is a str or tuple):

from blu import Key
from blu.html import b

PEOPLE = [
    {'id': 1, name: 'Andy'},
    {'id': 2, name: 'Brittany'},
    {'id': 3, name: 'Calvin'},
]

Key(person['id'])[
    'Hello, ',
    b[person['name']],
    '!',
]
for person in PEOPLE
Hello, <b>Andy</b>!
Hello, <b>Brittany</b>!
Hello, <b>Calvin</b>!
__getitem__(children: Node | tuple[Node, ...]) Key

Create a copy of self with the given children.

from blu import Key
from blu.html import b

Key('my-id')[
    'Hello, ',
    b['World'],
    '!',
]
Hello, <b>World</b>!
Parameters:

children – A blu.Node or tuple of blu.Nodes.

Returns:

If children is a tuple, a frament whose children are list(children). Otherwise, a fragment whose children are [children].

class blu.Ref

Bases: Generic

The object returned by blu.use_ref().

from blu import client
from blu.tml import button

__client__ = True


@client
def TwoButtons():
    # assigns variable ref to an instance of Ref.
    ref = use_ref()
__getitem__(empty_slice: slice) T

Get the value currently stored in the Ref.

from blu import client
from blu.html import button

__client__ = True


@client
def TwoButtons():
    ref = use_ref('Hello')
    ref[:]  # 'Hello'
Parameters:

empty_slice – Must be an empty slice, i.e. you must put a single colon between the square brackets, without any numbers.

Returns:

The value currently stored in self.

__setitem__(empty_slice: slice, new_value: T)

Set the Ref to point to a different value.

from blu import client
from blu.tml import button

__client__ = True


@client
def TwoButtons():
    ref = use_ref('Hello')
    ref[:] = 'Goodbye'
    ref[:]  # 'Goodbye'
Parameters:
  • empty_slice – Must be an empty slice, i.e. you must put a single colon between the square brackets, without any numbers.

  • new_value – The new value that should be stored in self.

Once this method has been called, the Ref's stored value will be set to new_value.

blu.is_client bool

True if running in a web browser environment, False if running in server environment.

from blu import is_client

if is_client:
    print('Hello from your web browser!')
else:
    print('Hello from your web app server!')
type blu.ClientRenderer = collections.abc.Callable[..., blu.Node | Generator[None, Node, Node]]

A function that defines how a ClientElement is rendered. This type of function is passed into the client decorator to create a ClientElement:

from blu import client


@client
def Greeting():
    return 'Hello, World!'

For more details on how ClientRenderer functions are used, see client().

type blu.Node = ClientElement | HTMLElement | Key | Iterable[Node] | str | int | float | None

A valid child of a React element. Nodes are rendered as follows:

  • A blu.HTMLElement is rendered as described here.

    from blu.html import span
    
    span(id='my-id')['Hello, World!']
    
    <span id="my-id">Hello, World!</span>
    
  • A blu.ClientElement is rendered as the return value of its rendering function. See blu.ClientElement for more details.
    from blu import client
    from blu.html import span
    
    
    @client
    def WorldGreeting():
        return span['Hello, World!']
    
    
    WorldGreeting
    
    <span>Hello, World!<span>
    
  • A blu.Key is rendered as a keyed React fragment whose key is the same as the key passed in to the Key object’s constructor.

    from blu import Key
    
    Key(125)[
        span['Hello, World!'],
    ]
    
    <span>Hello, World!</span>
    
  • A str is rendered as an HTML text node.

    'Hello, World!'
    
    Hello, World!
    
  • A tuple is rendered as a React fragment without a key.

    ('Hello,', ' ', 'World!')
    
    Hello, World!
    
  • Any other Iterable is rendered in React as JavaScript arrays.

    ['Hello,', ' ', 'World!']
    
    Hello, World!
    
  • A int is rendered as an HTML text node.

    1
    
    1
    
  • A float is rendered as an HTML text node.

    1.0
    
    1.0
    
  • None is rendered as nothing. It is rendered in React as the JavaScript null value.

    None
    
    
    
class blu.Response(body: Node = None, status: int = 200, headers: Mapping[str, str] = {})

Bases: object

An HTTP response for a page in a web application. Return a Response from a __page__ function (see File Conventions) to set HTTP status and headers for that page.

__index__.py
def __page__():
    return Response(
        p['Hello.'],
        status=404,
        headers={
            'Cache-Control': 'no-cache',
            'Last-Modified': 'Tue, 10 Dec 2024 10:00:00 GMT',
        },
    )
Parameters:
  • body – The React node to render when displaying the page.

  • status – The status code to send in the HTTP response.

  • headers – The HTTP headers to send in the HTTP response.

exception blu.WrongEnvironmentError

Bases: Exception

Raised when an attempt is made to perform a server-only action on the client, or a client-only action on the server.

async blu.app(scope: Scope, receive: Receiver, send: Sender)

An ASGI app that runs the Blu application defined in your current Python environment’s app package (for most ASGI servers, running the server in the parent directory of your project’s app directory will put your project’s app package in the Python environment).

$ uvicorn blu:app
$ hypercorn blu:app
$ daphne blu:app
blu.client(renderer: ClientRenderer) ClientElement

Decorator that converts a rendering function into a blu.ClientElement.

from blu import client
from blu.html import span


@client
def ColoredText(color):
    return span(style={'color': color})[(yield)]
Parameters:

renderer – The function that should be used to render the ClientElement.

Returns:

An element that will be rendered client-side using renderer.

When a ClientElement is rendered, the rendering function it was created from is called with whatever arguments were passed in using ClientElement.__call__():

from blu import client


@client
def Greeting(name='World'):
    return f'Hello, {name}!'


Greeting('Gary')
Hello, Gary!

If arguments were never set using ClientElement.__call__(), then the render function will be called with no arguments:

from blu import client


@client
def Greeting(name='World'):
    return f'Hello, {name}!'


Greeting
Hello, World!

A render function may use the yield keyword (only once) to get the children set using ClientElement.__getitem__():

from blu import client
from blu.html import span


@client
def RedText():
    return span(style={'color': 'red'})[(yield)]


RedText['Danger!']
<span style='color: red'>Danger!</span>

If the element’s children were never set using ClientElement.__getitem__(), it won’t have any children:

from blu import client
from blu.html import span


@client
def RedText():
    return span(style={'color': 'red'})[(yield)]


RedText
<span style='color: red'></span>
blu.create_rare_html_element(tagname: str) HTMLElement

Create an HTML element. Usually, you will use the blu.html module to create HTML elements, but in rare cases where you cannot import the tag name you need from blu.html, you can use this function to create an HTML element from any valid tag name.

from blu import create_rare_html_element

my_element = create_rare_html_element('my_element-')

my_element(id='my-id')['Hello, World!']
<my_element->Hello, World!</my_element->
Parameters:

tagname – A valid HTML tag name.

Returns:

A blu.HTMLElement whose tag name is tagname, with no props set.

blu.server(callable: Callable[[P], R | Awaitable]) Callable[[P], Awaitable]

Create a server function that can be called from the client.

app/server_functions.py
from blu import server


@server
def read_greeting_from_file(name):
    with open('app/hello.txt', 'r') as f:
        return f.read() + name
app/hello.txt
Hello,
app/__index__.py
from blu import client
from blu.html import div

__client__ = True


def __page__():
    return MyClientElement


@client
def MyClientElement():
    return div[read_greeting_from_file('George')]
<div>Hello, George!</div>
Parameters:

callable – A Callable

Returns:

An asynchronous function that, when called in a client environment, runs callable on the server with the provided arguments, and returns the return value of callable.

When calling the returned function in a client environment, the arguments provided must be JSON-serializable and the return value must be picklable. Otherwise, the function will be unable to complete.

The function must be defined at the top level of a module in order to be accessible client-side.

Wrong!
class A:

    @server
    def func():
        return 1
Wrong!
def func_factory():

    @server
    def func():
        return 1

    return func
Right.
@server
def func():
    return 1
blu.use_effect(callback: Callable[[], None | Generator[None] | Coroutine[None, None, None] | AsyncGenerator[None, None]])

Note

This function is a hook. For more information on hooks and the rules of calling them, see Note on Hooks.

Perform set-up actions immediately after a ClientElement is rendered to the DOM and/or tear-down actions immediately before it is removed from the DOM.

from blu import client, use_effect
from blu.html import div

__client__ = True


@client
def MyClientElement():

    @use_effect
    def setup_and_teardown():
        do_some_setup()
        yield
        do_some_teardown()

    return div['Hello!']
Parameters:

callback – A non-generator function or a generator function with a single yield statement.

If callback is a generator function or async generator function, it will be run right up until the yield statement immediately after the element is initially rendered to the DOM. The rest of the function will be run immediately before the element is removed from the DOM.

Otherwise, callback will be called immediately after the element is initially rendered to the DOM.

blu.use_ref(init: T = None) Ref

Note

This function is a hook. For more information on hooks and the rules of calling them, see Note on Hooks.

Store a value that doesn’t change between renders unless explicitly set to a new value.

from blu import client, is_client, use_ref
from blu.html import button

if is_client:
    from js import alert

__client__ = True


@client
def MyElement():
    click_count_ref = use_ref(0)

    def handle_click(e):
        click_count_ref[:] = click_count_ref[:] + 1
        count = click_count_ref[:]
        alert(f'You\'ve clicked the button {count} times.')

    return button['Click me!']
Parameters:

init – Any value.

Returns:

A Ref. This will be the same Ref object on every render of the same element. On the initial render, the value stored in the Ref will be init, but you can set this to a new value at any time (see blu.Ref for how to set a Ref's value).

Note

Setting a Ref's value does not trigger a re-render.

blu.use_state(init: T = None) tuple[T, Callable[[T], None]]

Note

This function is a hook. For more information on hooks and the rules of calling them, see Note on Hooks.

Handle state management for an element.

from blu import client
from blu.html import button

__client__ = True


@client
def MyElement():
    click_count, set_click_count = use_state(0)

    def handle_click(e):
        set_click_count(click_count + 1)

    return (
        button(onClick=handle_click)[
            f'You\'ve clicked {click_count} times',
        ]
    )
Parameters:

init – An initial value for the state being managed.

Returns:

A tuple containing two items: The current value for the state being managed, and a function that takes any value and causes the element whose render function use_state was called in to re-render. On a re-render triggered by this setter, the first item in the tuple will be whatever was passed into the setter when the re-render was triggered. On a re-render not triggered by this state’s setter, the first item in the tuple will be whatever it was in the previous render. On the initial render, the first item in the tuple will be init.

Note on Hooks

Blu is a React framework, and React provides a special type of function called a hook. These are the functions you import from Blu whose names start with use. They are used for managing UI state and adding callbacks for lifecycle phases, and there are certain restrictions on how they can be used:

  1. A hook can only be called in the scope of a client element’s rendering function body, or the scope of a custom hook’s function body (a custom hook is any function whose name starts with use that calls other hooks).

  2. A client element rendering function or custom hook must call the same hooks in the same order every time the rendering function or custom hook is called. The best way to follow this rule is just to never call a hook in a conditional block or loop.

Breaking either of these rules will result in undefined behavior.

from blu import client, use_ref
from blu.html import p


# Wrong! This function is not a client element or custom hook, so
# it can't call hooks.
def some_function():
    some_ref = use_ref()


# Right. This is a client element whose rendering function calls
# the use_ref hook.
@client
def SomeClientElement():
    some_ref = use_ref()
    return p['(client element content)']


# Right. Because this function's name starts with "use", it is a
# valid custom hook and can call other hooks.
def use_some_hook():
    return use_ref()


# Wrong! Hook called in a conditional block.
@client
def SomeClientElement(some_condition):
    if some_condition:
        my_ref = use_ref()
    else:
        my_ref = None
    return p['(client element content)']


# Wrong! Hook called in a conditional block.
def use_some_hook(some_condition):
    if some_condition:
        return None
    else:
        return use_ref()


# Wrong! Hook called in a loop.
def use_some_hook(some_list):
    result = []
    for item in some_list:
        result.append(use_ref())
    return result


# Wrong! Hook called in a loop.
@client
def SomeClientElement(num_iterations):
    i = 0
    while i < num_iterations:
        my_ref = use_ref()
    return p['(client element content)']


# Wrong! use_my_custom_hook is a custom hook, so it must *not* be
# called in a conditinal block.
@client
def SomeClientElement(some_condition):
    if some_condition:
        value = use_my_custom_hook()
    else:
        value = None
    return p['(client element content)']


def use_my_custom_hook():
    return use_ref()