blu¶
A full stack React framework for Python.
- class blu.ClientElement¶
Bases:
objectA 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
selfthat 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 akey, but it’s required for anyClientElementthat is rendered as an item in anIterable(unless thatIterableis astror atuple).
- Returns:
A copy of
selfthat will be rendered by calling its render function with*argsand**kwargs, with the exception that the keyword argumentkeywill 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**kwargscontains thekeyargument.
Below is an example of using the
keyargument: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
selfthat will render with the given children displayed where the render function uses theyieldkeyword.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:
- Returns:
A copy of
selfthat will render withchildrendisplayed where the element’s render function uses theyieldkeyword. If no children were specified, nothing will be rendered where theyieldkeyword is used.- Raises:
TypeErrorif element’s rendering function is not aGenerator, i.e. doesn’t contain ayieldstatement.
- class blu.HTMLElement¶
Bases:
objectA React DOM component instance. Created using the
blu.htmlmodule.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
selfwith 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
selfwith the same children, but with props set as follows:For any key-value pair in the
propsargument, 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} />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} />.If there is any conflict between the
propsargument and the keyword arguments, the prop that has the conflict will be set based on thepropsargument 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
selfwhose child nodes are set to the items passed in.from blu.html import div div['Hello World!']
<div>Hello World!</div>
- class blu.Key(key: Any)¶
Bases:
objectA 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 anIterable(unless thatIterableis astrortuple):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>!
- class blu.Ref¶
Bases:
GenericThe 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
Refto 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 tonew_value.
- blu.is_client bool¶
Trueif running in a web browser environment,Falseif 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
ClientElementis rendered. This type of function is passed into theclientdecorator to create aClientElement:from blu import client @client def Greeting(): return 'Hello, World!'
For more details on how
ClientRendererfunctions are used, seeclient().
- 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.HTMLElementis rendered as described here.from blu.html import span span(id='my-id')['Hello, World!']
<span id="my-id">Hello, World!</span>
- A
blu.ClientElementis rendered as the return value of its rendering function. Seeblu.ClientElementfor more details. from blu import client from blu.html import span @client def WorldGreeting(): return span['Hello, World!'] WorldGreeting
<span>Hello, World!<span>
- A
A
blu.Keyis rendered as a keyed React fragment whose key is the same as the key passed in to theKeyobject’s constructor.from blu import Key Key(125)[ span['Hello, World!'], ]
<span>Hello, World!</span>
A
stris rendered as an HTML text node.'Hello, World!'Hello, World!
A
tupleis rendered as a React fragment without a key.('Hello,', ' ', 'World!')
Hello, World!
Any other
Iterableis rendered in React as JavaScript arrays.['Hello,', ' ', 'World!']
Hello, World!
A
intis rendered as an HTML text node.11
A
floatis rendered as an HTML text node.1.01.0
Noneis rendered as nothing. It is rendered in React as the JavaScriptnullvalue.None
- class blu.Response(body: Node = None, status: int = 200, headers: Mapping[str, str] = {})¶
Bases:
objectAn HTTP response for a page in a web application. Return a
Responsefrom 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:
ExceptionRaised 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
apppackage (for most ASGI servers, running the server in the parent directory of your project’sappdirectory will put your project’sapppackage 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
ClientElementis rendered, the rendering function it was created from is called with whatever arguments were passed in usingClientElement.__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.htmlmodule to create HTML elements, but in rare cases where you cannot import the tag name you need fromblu.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.HTMLElementwhose tag name istagname, 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
callableon the server with the provided arguments, and returns the return value ofcallable.
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
ClientElementis 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
yieldstatement.
If
callbackis a generator function or async generator function, it will be run right up until theyieldstatement 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,
callbackwill 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 sameRefobject on every render of the same element. On the initial render, the value stored in theRefwill beinit, but you can set this to a new value at any time (seeblu.Reffor how to set aRef'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_statewas 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 beinit.
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:
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
usethat calls other hooks).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()