Helpers

General utilities.

source

DEBUG

 DEBUG (iftrue:Any=True, iffalse:Any=False, k='DEBUG_BRIDGET')

Returns iftrue if debug environment variable is set, otherwise iffalse


source

BridgeCfg

 BridgeCfg ()

Core Bridget behavior settings. if True:
- auto_show (false): FastHTML objects display as HTML instead of markdown.
- auto_mount (false): components with routes are automatically mounted.
- auto_id (false): display elements get auto-generated IDs.
- bundle_cfg (BundleCfg): configuration for dynamic import of Bridget modules.
- bootstrap (false): load bridget.js on import.
- current_did (None): the ID of the current display cell.


source

BundleCfg

 BundleCfg ()

Store of arbitrary values

cprint(bridge_cfg.for_module(__name__))

Command Execution


source

run_command

 run_command (command:str, cwd:pathlib.Path|None=None, **kwargs)

Execute shell command synchronously, returns (stdout, stderr)


source

arun_command

 arun_command (command:str, cwd:pathlib.Path|None=None, **kwargs)

Async version of run_command using anyio

a,_ = await arun_command('ls')
test_eq('bridget' in a, True)
_,b = await arun_command('node notfound')
test_eq('Error: Cannot find module' in b, True)

with test_raises(FileNotFoundError):
    await arun_command('ls', cwd=Path('/not/found'))

Time Formatting


source

ts

 ts ()
ts()
'15:02:51.496'

source

ms2str

 ms2str (ts)

format timestamp as in milliseconds to readable time hours:minutes:seconds:milliseconds

ts = datetime.now().timestamp()
ts, ms2str(ts)
(1764770571.524289, '15:02:51.524289')

Id generation


source

Kounter

 Kounter ()

Counter that tracks occurrences of keys and returns incremented count

cntr = Kounter()
for _ in 'abaabbb': cntr(_)
test_eq(cntr.d, {'a': 3, 'b': 4})
test_eq(cntr('int'), 1)

id_gen

Generate unique session IDs

import random
import re
from pathlib import Path
lines = Path("static/wordlist.txt").read_text().splitlines()
words = [line.strip() for line in lines if line.isalpha()]
def modify_word(word):
    # Randomly capitalize the first or second letter
    if len(word) > 1:
        idx_to_capitalize = random.choice([0, 1])
        word = word[:idx_to_capitalize] + word[idx_to_capitalize].upper() + word[idx_to_capitalize + 1:]
    else:
        word = word.upper()  # If single letter, capitalize it
    
    # Randomly add a number (0–99) at the start or end
    if random.choice([True, False]):
        number = random.randint(0, 99)
        # if random.choice([True, False]):
        #     word = f"{number}{word}"  # Number at the start
        # else:
        word = f"{word}{number}"  # Number at the end
    
    return word

def generate_readable_id(num_words=3):
    words_part = [modify_word(random.choice(words)) for _ in range(num_words)]
    id_candidate = '-'.join(words_part)

    # Ensure it's a valid CSS identifier
    if not re.match(r"^[a-zA-Z_][\w\-]*$", id_candidate):  # Add '_' if invalid
        id_candidate = f"_{id_candidate}"
    
    return f"{id_candidate}-{random.randint(0, 9999)}"
generate_readable_id(), generate_readable_id()
('lOsing-Solution49-Own-7437', 'cLosest-Won10-hIdes-5235')

source

id_gen

 id_gen ()

Create ID generator function that produces unique session-based IDs


source

simple_id

 simple_id ()

Generate simple hex ID using random bytes

simple_id()
'bb0f5bfa4-b8ad1c79-4bc1cd7b-c96b3532'

The id_gen function creates a function that takes any object and generates an unique Id valid during the current session. Useful for creating unique element IDs in dynamic HTML content.

new_id = id_gen()
new_id(), new_id()
('bd0f6d060-acc69f5f-a6ef89f6-fc1bb445',
 'b6dcae08c-1ac05807-c47196d0-37cf4666')
int_id = id_gen()
int_id(7), int_id(7), int_id(888)
('int_1-1764770571', 'int_2-1764770571', 'int_3-1764770571')
int_id = id_gen()
int_id('asdf'), int_id('asdf'), int_id('asdf')
('asdf_1-1764770571', 'asdf_2-1764770571', 'asdf_3-1764770571')
obj_id = id_gen()
o1, o2 = object(), object()
print(obj_id(o1), obj_id(o2))

dict_id = id_gen()
print(dict_id(d1 := {'a': 1}), dict_id(d2 := {'b': 7}))

pth_id = id_gen()
print(pth_id(Path('.')), pth_id(Path()), pth_id(Path('./bin')))
object_1-1764770571 object_2-1764770571
dict_1-1764770571 dict_2-1764770571
PosixPath_1-1764770571 PosixPath_2-1764770571 PosixPath_3-1764770571

Class Patching Utilities


source

patch_cached

 patch_cached (cls, f, name:str|None=None)

Add cached method to class using functools.cache

# type: ignore

class Test:
    def __init__(self): self.d = defaultdict(list)

def a(self, n:int=1):
    self.d['a'].append(n)
    return n+1


patch_cached(Test, a)

t1 = Test()
test_eq(t1.a(), 2)
test_eq(t1.a(), t1.a())
test_eq(t1.d['a'], [1])
test_eq(t1.a(3), 4)
test_eq(t1.a(3), t1.a(3))
test_eq(t1.d['a'], [1, 3])

t2 = Test()
test_eq(t2.a(), 2)
test_eq(t2.a(), t2.a())
test_eq(t2.d['a'], [1])
test_eq(t2.a(7), 8)
test_eq(t2.a(7), t2.a(7))
test_eq(t2.d['a'], [1, 7])

source

patch_cached_property

 patch_cached_property (cls, f, name:str|None=None)

cached_property with partial support

# type: ignore

def a(self): 
    "a docs"
    self.d['a'].append('a'); return 2
def _b(self, n): 
    "b docs"
    self.d['b'].append(n); return n+n
class Test: 
    def __init__(self): self.d = defaultdict(list)

patch_cached_property(Test, a)
patch_cached_property(Test, lambda self: _b(self, 2), 'b2')
patch_cached_property(Test, partial(_b, n=3), 'b3')
patch_cached_property(Test, partial(lambda self: _b(self, 4)), 'b4')

t1 = Test()
test_eq(t1.a, 2)
test_eq(t1.a, t1.a)
test_eq(t1.d['a'], ['a'])
test_eq(t1.b2, 4)
test_eq(t1.b2, t1.b2)
test_eq(t1.d['b'], [2])
test_eq(t1.b3, 6)
test_eq(t1.b3, t1.b3)
test_eq(t1.d['b'], [2, 3])
test_eq(t1.b4, 8)
test_eq(t1.b4, t1.b4)
test_eq(t1.d['b'], [2, 3, 4])

source

cached_property

 cached_property (func)

Enhanced cached_property that preserves function attributes


source

skip

 skip (metadata:dict|None=None, **kwargs)

Convenience function to add skip=True to bridge metadata


source

bridge_metadata

 bridge_metadata (metadata:dict|None=None, **kwargs)

Add or update ‘bridge’ key in metadata dict with kwargs

test_eq(bridge_metadata(), {'bridge': {}})
test_eq(bridge_metadata(skip=True), {'bridge': {'skip': True}})
test_eq(bridge_metadata({'autoshow': True}, skip=True), {'autoshow': True, 'bridge': {'skip': True}})
test_eq(skip(), {'bridge': {'skip': True}})
test_eq(skip({'bridge': {'auto_show': True}}), {'bridge': {'skip': True, 'auto_show': True}})
test_eq(skip(auto_show=True), {'bridge': {'skip': True, 'auto_show': True}})

Function Composition

like fastcore.compose, but args are passed only to first function


source

compose_first

 compose_first (*funcs:Callable, order:Optional[Callable]=None)

Create a function that composes all functions in funcs, passing remaining *args and **kwargs to first function only. order: key function to sort funcs before composing

def add_one(x): return x + 1
def multiply_two(x): return x * 2
def add_ten(x): return x + 10

single = compose_first(add_one)
test_eq(single(5), 6)

test_eq(single, add_one)
empty = compose_first()
test_eq(empty, FC.noop)

composed = compose_first(add_one, multiply_two, add_ten)
test_eq(composed(5), 22)  # ((5+1) * 2) + 10 = 22
def tag_a(x): return f"a({x})"
def tag_b(x): return f"b({x})"
def tag_c(x): return f"c({x})"

natural = compose_first(tag_c, tag_a, tag_b)
test_eq(natural("x"), "b(a(c(x)))")

ordered = compose_first(tag_c, tag_a, tag_b, order=lambda f: f.__name__)
test_eq(ordered("x"), "c(b(a(x)))")  # Functions sorted: tag_a, tag_b, tag_c

FastHTML Utilities

Basic naked FastHTML app.


source

nb_app

 nb_app (debug=False, routes=None, middleware=None, title:str='FastHTML
         page', exception_handlers=None, on_startup=None,
         on_shutdown=None, lifespan=None, hdrs=None, ftrs=None, exts=None,
         before=None, after=None, surreal=True, htmx=True,
         default_hdrs=True, sess_cls=<class
         'starlette.middleware.sessions.SessionMiddleware'>,
         secret_key=None, session_cookie='session_', max_age=31536000,
         sess_path='/', same_site='lax', sess_https_only=False,
         sess_domain=None, key_fname='.sesskey', body_wrap=<function
         noop_body>, htmlkw=None, nb_hdrs=False, canonical=True)
app = nb_app()
test_eq(app.user_middleware, [])

source

CLog

 CLog (*o)
display(HTML(CLog('aaaa', 'bbbb')))

Display Utilities

Convenience functions and definitions just to avoid the stupid wiggly reds.


source

displaydh

 displaydh (*objs, include=None, exclude=None, metadata=None,
            transient=None, display_id=None, raw=False, clear=False,
            **kwargs)

display with display_id

Type Default Details
objs VAR_POSITIONAL
include NoneType None
exclude NoneType None
metadata NoneType None
transient NoneType None
display_id NoneType None
raw bool False
clear bool False
kwargs VAR_KEYWORD
Returns DisplayHandle type: ignore
dh = display('a', display_id=True)
# dh
'a'
dh = display('b', display_id="1234567890")
dh
'b'
<DisplayHandle display_id=1234567890>
dh = display('c')
dh
'c'

DetailsJSON

<style>
    details ul { list-style-type:none; list-style-position: outside; padding-inline-start: 22px; margin: 0px; }
</style>
<details open>
<summary>Apollo astronauts</summary>
<ul>
  <li><span>1</span>: Neil Armstrong</li>
  <li><span>2</span>: Alan Bean</li>
  <li><details>
<summary>Apollo 11</summary>
<ul>
  <li><span>1</span>: Neil Armstrong</li>
  <li><span>2</span>: Alan Bean</li>
  <li><div><span>3</span>: Buzz Aldrin</div></li>
  <li><span>4</span>: Edgar Mitchell</li>
  <li><span>5</span>: Alan Shepard</li>
</ul></li>
  <li><span>4</span>: Edgar Mitchell</li>
  <li><span>5</span>: Alan Shepard</li>
</ul>

</details>
Apollo astronauts
  • 1: Neil Armstrong
  • 2: Alan Bean
  • Apollo 11
    • 1: Neil Armstrong
    • 2: Alan Bean
    • 3: Buzz Aldrin
    • 4: Edgar Mitchell
    • 5: Alan Shepard
  • 4: Edgar Mitchell
  • 5: Alan Shepard

source

DetailsJSON

 DetailsJSON (*args, summary:str='', open:bool=True, openall:bool=False,
              skip:Sequence[str]=(), **kwargs)

Interactive collapsible JSON viewer with HTML details/summary structure


source

NameVal

 NameVal (k, v)

Render key-value pair with name and value styling


source

Val

 Val (v)

Render value with appropriate CSS class based on type

dtl = DetailsJSON({
    '1': 'Neil Armstrong',
    '2': 'Alan Bean',
    '3': 'Buzz Aldrin',
    'letters': {
        'a': 1, 
        'b': 2
        },
    '5': 'Edgar Mitchell',
    '6': 'Alan Shepard'
}, summary='Apollo astronauts')

# test_eq(val_at(dtl, '5'), 'Edgar Mitchell')
# test_eq(val_at(dtl, 'letters.a'), 1)
# cprint(to_xml(dtl))
show(dtl)
Apollo astronauts
  • 1: Neil Armstrong
  • 2: Alan Bean
  • 3: Buzz Aldrin
  • letters
    • a: 1
    • b: 2
  • 5: Edgar Mitchell
  • 6: Alan Shepard
d= {
    "idx": 1,
    "cell_type": "code",
    "source": "# cell 1\nprint('hello')",
    "id": "W1sZmlsZQ==",
    "metadata": {
        "brd": {
            "id": "717322f8-95fa-425c-839d-8b9e7d4ef921"
        }
    },
    "outputs": [
        {'output_type': 'stream', 'name': 'stdout', 'text': '1\n'},
        {'output_type': 'stream', 'name': 'stdout', 'text': '2\n'}
    ],
    "execution_count": 1
}
show(DetailsJSON(d, openall=True))
summary
  • idx: 1
  • cell_type: code
  • source: # cell 1 print('hello')
  • id: W1sZmlsZQ==
  • metadata
      brd
      • id: 717322f8-95fa-425c-839d-8b9e7d4ef921
    outputs
      0
      • output_type: stream
      • name: stdout
      • text: 1
      1
      • output_type: stream
      • name: stdout
      • text: 2
  • execution_count: 1

Convenience IPython.display.HTML subclass that accepts fastcore.xml.FT object and adds the kwargs to metadata.


source

HTML

 HTML (data=None, url=None, filename=None, metadata=None, **kwargs)

Create a text display object given raw data.

Type Default Details
data NoneType None The raw data or a URL or file to load the data from.
url NoneType None A URL to download the data from.
filename NoneType None Path to a local file to load the data from.
metadata NoneType None Dict of metadata associated to be the object when displayed
kwargs VAR_KEYWORD

Environment Detection


source

in_vscode_notebook

 in_vscode_notebook ()

Check if the code is running in VSCode


source

in_vscode

 in_vscode ()

Check if the code is running in VSCode

IN_VSCODE, in_vscode_notebook()
(True, True)

Widgets


source

find_active_widgets

 find_active_widgets ()

Find all active widget instances in memory

Brute force widget list.

import ipywidgets as W
w = W.IntSlider(value=10)
w
ww = find_active_widgets()
ww
[{'type': 'IntSlider',
  'model_id': '60abde60b31d4df19fa679c9d67e1617',
  'comm': True},
 {'type': 'Layout',
  'model_id': '92d6d2f7dfa74af4b2e650a280ac1fdf',
  'comm': True},
 {'type': 'SliderStyle',
  'model_id': '137bdd0e381c4413b0fceedf8cb7cb3b',
  'comm': True},
 {'type': 'IntSlider',
  'model_id': 'df7f5a8d2e184852be06aa0ab3357ed6',
  'comm': False},
 {'type': 'Layout',
  'model_id': '4aca39e666394da9aff161ae7b6bceab',
  'comm': False},
 {'type': 'SliderStyle',
  'model_id': 'e22322aa4357468fb0a605a13ed39580',
  'comm': False}]

source

get_kernel_widgets_comms

 get_kernel_widgets_comms ()

Get all widget comms from the kernel.

[_.comm_id for _ in get_kernel_widgets_comms()]
['92d6d2f7dfa74af4b2e650a280ac1fdf',
 '137bdd0e381c4413b0fceedf8cb7cb3b',
 '60abde60b31d4df19fa679c9d67e1617']

source

get_active_widgets_comms

 get_active_widgets_comms ()

Get “official” list of widget comms

[_.comm_id for _ in get_active_widgets_comms()]
['92d6d2f7dfa74af4b2e650a280ac1fdf',
 '137bdd0e381c4413b0fceedf8cb7cb3b',
 '60abde60b31d4df19fa679c9d67e1617']
W.Widget.close_all()
[_.comm_id for _ in get_active_widgets_comms()]
[]

source

cleanupbridget

 cleanupbridget (glbs)

Cleanup bridget environment

As VSCode sandbox the front-end, each notebook opening starts anew. If the kernel has not restarted, widgets are stale, opened (or closed but not deleted) with no counter-part in the kernel.

This helper function cleans up the environment. Not needed in Lab (the extension takes care of siwtching the environment).