Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Datastar Python SDK

The datastar-py package provides a Python SDK for working with Datastar.

Datastar sends responses back to the browser using SSE. This allows the backend to send any number of events, from zero to infinity in response to a single request.

datastar-py has helpers for creating those responses, formatting the events, reading signals from the frontend, and generating the data-* HTML attributes.

The event generator can be used with any framework. There are also custom helpers included for the following frameworks:

Framework-specific helpers are kept in their own packages. e.g. datastar_py.quart Make sure to use the helpers from the package of the framework you are using.

Here is a full example using the quart framework showing many of the features available in this package.

importasynciofromdatetimeimportdatetimefromdatastar_pyimportServerSentEventGeneratorasSSE, attribute_generatorasdatafromdatastar_py.quartimportdatastar_response, read_signalsfromquartimportQuartapp=Quart(__name__)
# Import frontend library via Content Distribution Network, create targets for Server Sent Events@app.route("/")defindex():
returnf""" <html> <head> <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script> </head> <body {data.init("@get('/updates')")}> <span id="currentTime"></span><br> <span data-text="$currentTime"></span> </body> </html> """@app.route("/updates")@datastar_responseasyncdefupdates():
# Retrieve a dictionary with the current state of the signals from the frontendsignals=awaitread_signals()
# Alternate updating an element from the backend, and updating a signal from the backendwhileTrue:
yieldSSE.patch_elements(
f"""<span id="currentTime">{datetime.now().isoformat()}"""
)
awaitasyncio.sleep(1)
yieldSSE.patch_signals({"currentTime": f"{datetime.now().isoformat()}"})
awaitasyncio.sleep(1)
app.run()

Starting examples for each framework can be found in the examples directory.

Event Generation Helpers

This helper is used to generate the actual events that are sent over SSE. They are just text blobs that can be sent using any framework. These can even be used by frameworks not directly supported in this library if you set up the headers of the SSE response yourself.

Response Helpers

A datastar response consists of 0..N datastar events. There are response classes included to make this easy in all of the supported frameworks. Each framework also exposes a @datastar_response decorator that will wrap return values (including generators) into the right response class while preserving sync handlers as sync so frameworks can keep them in their threadpools.

The following examples will work across all supported frameworks when the response class is imported from the appropriate framework package. e.g. from datastar_py.quart import DatastarResponse The containing functions are not shown here, as they will differ per framework.

# per framework Response import. (Replace 'fastapi' with your framework.) e.g.:# from datastar_py.fastapi import DatastarResponsefromdatastar_pyimportServerSentEventGeneratorasSSE# 0 events, a 204@app.get("zero")defzero_event():
returnDatastarResponse()
# 1 event@app.get("one")defone_event():
returnDatastarResponse(SSE.patch_elements("<div id='mydiv'></div>"))
# 2 events@app.get("two")deftwo_event():
returnDatastarResponse([
SSE.patch_elements("<div id='mydiv'></div>"),
SSE.patch_signals({"mysignal": "myval"}),
])
# N events, a long lived stream (for all frameworks but sanic)@app.get("/updates")asyncdefupdates():
asyncdef_():
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)
returnDatastarResponse(_())
# A long lived stream for sanic@app.get("/updates")asyncdefupdates(request):
response=awaitdatastar_respond(request)
# which is just a helper for the following# response = await request.respond(DatastarResponse())whileTrue:
awaitresponse.send(SSE.patch_elements("<div id='mydiv'></div>"))
awaitasyncio.sleep(1)

Response Decorator

To make returning a DatastarResponse simpler, there is a decorator datastar_response available that automatically wraps a function result in DatastarResponse. It works on async and regular functions and generator functions. The main use case is when using a generator function, as you can avoid a second generator function inside your response function. The decorator works the same for any of the supported frameworks, and should be used under any routing decorator from the framework.

# Import the decorator from the package specific to your frameworkfromdatastar_py.sanicimportdatastar_response, ServerSentEventGeneratorasSSE@app.get('/my_route')@datastar_responseasyncdefmy_route(request):
whileTrue:
yieldSSE.patch_elements("<div id='mydiv'></div>")
awaitasyncio.sleep(1)

Signal Helpers

The current state of the datastar signals is included by default in every datastar request. A helper is included to load those signals for each framework. read_signals. The usage varies per framework so check the signature for your framework. You usually need to pass the request in.

fromdatastar_py.quartimportread_signals@app.route("/updates")asyncdefupdates():
signals=awaitread_signals()

Attribute Generation Helper

Datastar allows HTML generation to be done on the backend. datastar-py includes a helper to generate data-* attributes in your HTML with IDE completion and type checking. It can be used with many different HTML generation libraries.

fromdatastar_pyimportattribute_generatorasdata# htpybutton(data.on("click", "console.log('clicked')").debounce(1000).stop)["My Button"]
# FastHTMLButton("My Button", data.on("click", "console.log('clicked')").debounce(1000).stop)
Button(data.on("click", "console.log('clicked')").debounce(1000).stop)("My Button")
# f-stringsf"<button {data.on("click", "console.log('clicked')").debounce(1000).stop}>My Button</button>"# Jinja, but no editor completion :(<button {{data.on("click", "console.log('clicked')").debounce(1000).stop}}>MyButton</button>

When using datastar with a different alias, you can instantiate the class yourself.

fromdatastar_py.attributesimportAttributeGeneratordata=AttributeGenerator(alias="data-star-")
# htmy (htmy will transform _ into - unless the attribute starts with _, which will be stripped)data=AttributeGenerator(alias="_data-")
html.button("My Button", **data.on("click", "console.log('clicked')").debounce("1s").stop)

About

Official Datastar Python SDK.

Resources

Stars

174 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages