ludic


Nameludic JSON
Version 0.5.2 PyPI version JSON
download
home_pageNone
SummaryLightweight framework for building dynamic HTML pages in pure Python.
upload_time2024-08-12 07:01:00
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseNone
keywords async html htmx templating web
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <p align="center">
    <img width="600px" src="https://raw.githubusercontent.com/getludic/ludic/main/docs/assets/ludic.png" alt="Ludic Logo">
</p>

[![test](https://github.com/getludic/ludic/actions/workflows/test.yaml/badge.svg)](https://github.com/getludic/ludic/actions) [![codecov](https://codecov.io/gh/getludic/ludic/graph/badge.svg?token=BBDNJWHMGX)](https://codecov.io/gh/getludic/ludic) [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-312/) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![Discord Server](https://img.shields.io/badge/discord-ludic-black)](https://discord.gg/4Y5fSQUS)

**Documentation**: https://getludic.dev/docs/

---

*"I've just composed my first `PageLayout` component and I have no words!"*

– Igor Davydenko

---

Ludic is a lightweight framework for building HTML pages with a component approach similar to [React](https://react.dev/). It is built to be used together with [htmx.org](https://htmx.org/) so that developers don't need to write almost any JavaScript to create dynamic web services. Its potential can be leveraged together with its web framework which is a wrapper around the powerful [Starlette](https://www.starlette.io/) framework. It is built with the latest Python 3.12 features heavily incorporating typing.

> [!IMPORTANT]
> The framework is in a very early development/experimental stage. There are a lot of half-functioning features at the moment. Contributions are welcome to help out with the progress!

## Features

- Seamless **&lt;/&gt; htmx** integration for rapid web development in **pure Python**
- **Type-Guided components** utilizing Python's typing system
- Uses the power of **Starlette** and **Async** for high-performance web development
- Build HTML with the ease and power of Python **f-strings**
- Add CSS styling to your components with **Themes**
- Create simple, responsive layouts adopted from the **Every Layout Book**

## Comparison

Here is a table comparing Ludic to other similar tools:

| Feature                     | Ludic       | FastUI      | Reflex      |
|-----------------------------|-------------|-------------|-------------|
| HTML rendering              | Server Side | Client Side | Client Side |
| Uses a template engine      | No          | No          | No          |
| UI interactivity            | [</> htmx](https://htmx.org)* | [React](https://react.dev/) | [React](https://react.dev/) |
| Backend framework           | [Starlette](https://www.starlette.io), [Django](https://www.djangoproject.com/)*  | [FastAPI](https://fastapi.tiangolo.com) | [FastAPI](https://fastapi.tiangolo.com) |
| Client-Server Communication | [HTML + REST](https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/) | [JSON + REST](https://github.com/pydantic/FastUI?tab=readme-ov-file#the-principle-long-version) | [WebSockets](https://reflex.dev/blog/2024-03-21-reflex-architecture/) |

<sup>(*) HTMX as well as Starlette or Django are optional dependencies for Ludic, it does not enforce any frontend or backend frameworks. At it's core, Ludic only generates HTML and allows registering CSS.</sup>

## Motivation

This framework allows HTML generation in Python while utilizing Python's typing system. Our goal is to enable the creation of dynamic web applications with reusable components, all while offering a greater level of type safety than raw HTML.

**Key Ideas:**

- **Type-Guided HTML**: Catch potential HTML structural errors at development time thanks to type hints. The framework enforces stricter rules than standard HTML, promoting well-structured and maintainable code.
- **Composable Components**: Define reusable, dynamic HTML components in pure Python. This aligns with modern web development practices, emphasizing modularity.

### Type-Guided HTML

Here is an example of how Python's type system can be leveraged to enforce HTML structure:

```python
br("Hello, World!")        # type error (<br> can't have children)
br()                       # ok

html(body(...))            # type error (first child must be a <head>)
html(head(...), body(...)) # ok

div("Test", href="test")   # type error (unknown attribute)
a("Test", href="...")      # ok
```

### Composable Components

Instead of using only basic HTML elements, it is possible to create modular components with the support of Python's type system. Let's take a look at an example:

```python
Table(
    TableHead("Id", "Name"),
    TableRow("1", "John"),
    TableRow("2", "Jane"),
    TableRow("3", "Bob"),
)
```

This structure can be type-checked thanks to Python's rich type system. Additionally, this `Table` component could have **dynamic properties** like sorting or filtering.

## Requirements

Python 3.12+

## Installation

```
pip install "ludic[full]"
```

Similar to Starlette, you'll also want to install an [ASGI](https://asgi.readthedocs.io/en/latest/) server:

```
pip install uvicorn
```

You can also use a basic cookiecutter template to get quickly started:

```
cookiecutter gh:getludic/template
```

## Full Example

**components.py**:

```python
from typing import override

from ludic import Attrs, Component
from ludic.html import a

class LinkAttrs(Attrs):
    to: str

class Link(Component[str, LinkAttrs]):
    classes = ["link"]

    @override
    def render(self) -> a:
        return a(
            *self.children,
            href=self.attrs["to"],
            style={"color": self.theme.colors.primary},
        )
```

Now you can use it like this:

```python
link = Link("Hello, World!", to="/home")
```

**web.py**:

```python
from ludic.web import LudicApp
from ludic.html import b, p

from .components import Link

app = LudicApp()

@app.get("/")
async def homepage() -> p:
    return p(f"Hello {b("Stranger")}! Click {Link("here", to="https://example.com")}!")
```

To run the application:

```python
uvicorn web:app
```

### Integrations

Here is a list of integrations and a link to the guide on how to get started:

* [Starlette](https://getludic.dev/docs/web-framework)
* [Django](https://getludic.dev/docs/integrations#django)

### More Examples

For more complex usage incorporating all capabilities of the framework, please visit the examples on [the web](https://getludic.dev/examples) or go to the folder with examples [on GitHub](https://github.com/getludic/ludic/tree/master/examples/).

## Contributing

Any contributions to the framework are warmly welcome! Your help will make it a better resource for the community. If you're ready to contribute, read the [contribution guide](https://github.com/getludic/ludic/tree/master/CONTRIBUTING.md).

* [GitHub Issues](https://github.com/getludic/ludic/issues) - If you encounter a bug, please report it here.
* [GitHub Discussions](https://github.com/getludic/ludic/discussions) - To request a new feature, this is the best place to initiate the discussion.
* [Discord](https://discord.gg/4Y5fSQUS) - Join our Discord server for support, sharing ideas, and receiving assistance.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "ludic",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": "Pavel Ded\u00edk <dedikx@gmail.com>",
    "keywords": "async, html, htmx, templating, web",
    "author": null,
    "author_email": "Pavel Ded\u00edk <dedikx@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/21/a8/18a4654559082092bac766b38a7cc33b5037e53305f872f93f75f2cbf80b/ludic-0.5.2.tar.gz",
    "platform": null,
    "description": "<p align=\"center\">\n    <img width=\"600px\" src=\"https://raw.githubusercontent.com/getludic/ludic/main/docs/assets/ludic.png\" alt=\"Ludic Logo\">\n</p>\n\n[![test](https://github.com/getludic/ludic/actions/workflows/test.yaml/badge.svg)](https://github.com/getludic/ludic/actions) [![codecov](https://codecov.io/gh/getludic/ludic/graph/badge.svg?token=BBDNJWHMGX)](https://codecov.io/gh/getludic/ludic) [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-312/) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![Discord Server](https://img.shields.io/badge/discord-ludic-black)](https://discord.gg/4Y5fSQUS)\n\n**Documentation**: https://getludic.dev/docs/\n\n---\n\n*\"I've just composed my first `PageLayout` component and I have no words!\"*\n\n\u2013 Igor Davydenko\n\n---\n\nLudic is a lightweight framework for building HTML pages with a component approach similar to [React](https://react.dev/). It is built to be used together with [htmx.org](https://htmx.org/) so that developers don't need to write almost any JavaScript to create dynamic web services. Its potential can be leveraged together with its web framework which is a wrapper around the powerful [Starlette](https://www.starlette.io/) framework. It is built with the latest Python 3.12 features heavily incorporating typing.\n\n> [!IMPORTANT]\n> The framework is in a very early development/experimental stage. There are a lot of half-functioning features at the moment. Contributions are welcome to help out with the progress!\n\n## Features\n\n- Seamless **&lt;/&gt; htmx** integration for rapid web development in **pure Python**\n- **Type-Guided components** utilizing Python's typing system\n- Uses the power of **Starlette** and **Async** for high-performance web development\n- Build HTML with the ease and power of Python **f-strings**\n- Add CSS styling to your components with **Themes**\n- Create simple, responsive layouts adopted from the **Every Layout Book**\n\n## Comparison\n\nHere is a table comparing Ludic to other similar tools:\n\n| Feature                     | Ludic       | FastUI      | Reflex      |\n|-----------------------------|-------------|-------------|-------------|\n| HTML rendering              | Server Side | Client Side | Client Side |\n| Uses a template engine      | No          | No          | No          |\n| UI interactivity            | [</> htmx](https://htmx.org)* | [React](https://react.dev/) | [React](https://react.dev/) |\n| Backend framework           | [Starlette](https://www.starlette.io), [Django](https://www.djangoproject.com/)*  | [FastAPI](https://fastapi.tiangolo.com) | [FastAPI](https://fastapi.tiangolo.com) |\n| Client-Server Communication | [HTML + REST](https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/) | [JSON + REST](https://github.com/pydantic/FastUI?tab=readme-ov-file#the-principle-long-version) | [WebSockets](https://reflex.dev/blog/2024-03-21-reflex-architecture/) |\n\n<sup>(*) HTMX as well as Starlette or Django are optional dependencies for Ludic, it does not enforce any frontend or backend frameworks. At it's core, Ludic only generates HTML and allows registering CSS.</sup>\n\n## Motivation\n\nThis framework allows HTML generation in Python while utilizing Python's typing system. Our goal is to enable the creation of dynamic web applications with reusable components, all while offering a greater level of type safety than raw HTML.\n\n**Key Ideas:**\n\n- **Type-Guided HTML**: Catch potential HTML structural errors at development time thanks to type hints. The framework enforces stricter rules than standard HTML, promoting well-structured and maintainable code.\n- **Composable Components**: Define reusable, dynamic HTML components in pure Python. This aligns with modern web development practices, emphasizing modularity.\n\n### Type-Guided HTML\n\nHere is an example of how Python's type system can be leveraged to enforce HTML structure:\n\n```python\nbr(\"Hello, World!\")        # type error (<br> can't have children)\nbr()                       # ok\n\nhtml(body(...))            # type error (first child must be a <head>)\nhtml(head(...), body(...)) # ok\n\ndiv(\"Test\", href=\"test\")   # type error (unknown attribute)\na(\"Test\", href=\"...\")      # ok\n```\n\n### Composable Components\n\nInstead of using only basic HTML elements, it is possible to create modular components with the support of Python's type system. Let's take a look at an example:\n\n```python\nTable(\n    TableHead(\"Id\", \"Name\"),\n    TableRow(\"1\", \"John\"),\n    TableRow(\"2\", \"Jane\"),\n    TableRow(\"3\", \"Bob\"),\n)\n```\n\nThis structure can be type-checked thanks to Python's rich type system. Additionally, this `Table` component could have **dynamic properties** like sorting or filtering.\n\n## Requirements\n\nPython 3.12+\n\n## Installation\n\n```\npip install \"ludic[full]\"\n```\n\nSimilar to Starlette, you'll also want to install an [ASGI](https://asgi.readthedocs.io/en/latest/) server:\n\n```\npip install uvicorn\n```\n\nYou can also use a basic cookiecutter template to get quickly started:\n\n```\ncookiecutter gh:getludic/template\n```\n\n## Full Example\n\n**components.py**:\n\n```python\nfrom typing import override\n\nfrom ludic import Attrs, Component\nfrom ludic.html import a\n\nclass LinkAttrs(Attrs):\n    to: str\n\nclass Link(Component[str, LinkAttrs]):\n    classes = [\"link\"]\n\n    @override\n    def render(self) -> a:\n        return a(\n            *self.children,\n            href=self.attrs[\"to\"],\n            style={\"color\": self.theme.colors.primary},\n        )\n```\n\nNow you can use it like this:\n\n```python\nlink = Link(\"Hello, World!\", to=\"/home\")\n```\n\n**web.py**:\n\n```python\nfrom ludic.web import LudicApp\nfrom ludic.html import b, p\n\nfrom .components import Link\n\napp = LudicApp()\n\n@app.get(\"/\")\nasync def homepage() -> p:\n    return p(f\"Hello {b(\"Stranger\")}! Click {Link(\"here\", to=\"https://example.com\")}!\")\n```\n\nTo run the application:\n\n```python\nuvicorn web:app\n```\n\n### Integrations\n\nHere is a list of integrations and a link to the guide on how to get started:\n\n* [Starlette](https://getludic.dev/docs/web-framework)\n* [Django](https://getludic.dev/docs/integrations#django)\n\n### More Examples\n\nFor more complex usage incorporating all capabilities of the framework, please visit the examples on [the web](https://getludic.dev/examples) or go to the folder with examples [on GitHub](https://github.com/getludic/ludic/tree/master/examples/).\n\n## Contributing\n\nAny contributions to the framework are warmly welcome! Your help will make it a better resource for the community. If you're ready to contribute, read the [contribution guide](https://github.com/getludic/ludic/tree/master/CONTRIBUTING.md).\n\n* [GitHub Issues](https://github.com/getludic/ludic/issues) - If you encounter a bug, please report it here.\n* [GitHub Discussions](https://github.com/getludic/ludic/discussions) - To request a new feature, this is the best place to initiate the discussion.\n* [Discord](https://discord.gg/4Y5fSQUS) - Join our Discord server for support, sharing ideas, and receiving assistance.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Lightweight framework for building dynamic HTML pages in pure Python.",
    "version": "0.5.2",
    "project_urls": null,
    "split_keywords": [
        "async",
        " html",
        " htmx",
        " templating",
        " web"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "74cb734533b0ead919bd7934ec80396fcd9fe3e8151686444d5065d55d542dc4",
                "md5": "0c39521ae3cc507e3def42ed86872d83",
                "sha256": "fc0a74d281391447cf5efd750a6141d15a6f56cf2c0b9ce27fd0f9e66752fd03"
            },
            "downloads": -1,
            "filename": "ludic-0.5.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0c39521ae3cc507e3def42ed86872d83",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 63226,
            "upload_time": "2024-08-12T07:00:58",
            "upload_time_iso_8601": "2024-08-12T07:00:58.094546Z",
            "url": "https://files.pythonhosted.org/packages/74/cb/734533b0ead919bd7934ec80396fcd9fe3e8151686444d5065d55d542dc4/ludic-0.5.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "21a818a4654559082092bac766b38a7cc33b5037e53305f872f93f75f2cbf80b",
                "md5": "1cd3ad0a2fb9c5b84527ba005c4873a9",
                "sha256": "9c2edd977161adf6bff8278f308cc7c3b08bbf5ba2cd136ec29969c8bf33e9db"
            },
            "downloads": -1,
            "filename": "ludic-0.5.2.tar.gz",
            "has_sig": false,
            "md5_digest": "1cd3ad0a2fb9c5b84527ba005c4873a9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 555187,
            "upload_time": "2024-08-12T07:01:00",
            "upload_time_iso_8601": "2024-08-12T07:01:00.781353Z",
            "url": "https://files.pythonhosted.org/packages/21/a8/18a4654559082092bac766b38a7cc33b5037e53305f872f93f75f2cbf80b/ludic-0.5.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-12 07:01:00",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "ludic"
}
        
Elapsed time: 1.37639s