# stretchable
[](https://pypi.org/project/stretchable/)
[](https://www.python.org)
[](https://github.com/mortencombat/stretchable/blob/main/LICENSE)
[](https://github.com/mortencombat/stretchable/issues)
[](https://github.com/mortencombat/stretchable/actions/workflows/build-publish.yml)
[](https://stretchable.readthedocs.io/en/latest/?badge=stable)
[](https://github.com/mortencombat/stretchable/actions/workflows/test.yml)
[](https://github.com/mortencombat/stretchable/actions/workflows/test.yml)
**stretchable** is a layout library for Python that enables context-agnostic layout operations using CSS Block, [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) and [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/). Possible uses include UI layouts, page layouts for reports, complex plotting layouts, etc.
It implements Python bindings for [Taffy](https://github.com/dioxuslabs/taffy), an implementation of **CSS Block**, **Flexbox** and **CSS Grid** layout algorithms written in [Rust](https://www.rust-lang.org/). It was originally based on [Stretch](https://vislyhq.github.io/stretch/) (hence the name), but has since migrated to use Taffy. It is multi-platform and there are distributions available for Windows, Linux and macOS.
## Getting Started
**stretchable** is a Python package [hosted on PyPI](https://pypi.org/project/stretchable/). It can be installed using [pip](https://pip.pypa.io/en/stable/):
```console
python -m pip install stretchable
```
Building a tree of nodes and calculating the layout is as simple as:
```python
from stretchable import Edge, Node
from stretchable.style import AUTO, PCT
# Build node tree
root = Node(
margin=20,
size=(500, 300),
).add(
Node(border=5, size=(50 * PCT, AUTO)),
Node(key="child", padding=10 * PCT, size=50 * PCT),
)
# Compute layout
root.compute_layout()
# Get the second of the child nodes
child_node = root.find("/child")
content_box = child_node.get_box(Edge.CONTENT)
print(content_box)
# Box(x=300.0, y=50.0, width=150.0, height=50.0)
```
For more information and details, see the [documentation](https://stretchable.readthedocs.io/).
## Contributing
Contributions are welcomed. Please open an issue to clarify/plan implementation details prior to starting the work.
### Building
Install Rust with [rustup](https://rustup.rs/) and use `maturin develop` for development and `maturin build [--release]` to build.
### Documentation
To build documentation use `make html` (in `docs/` folder) or, to use live reloading: `sphinx-autobuild docs/source docs/build/html`
NOTE: Sometimes, you may need to run `make clean html` (in `docs/` folder) to ensure that all changes are included in the built html.
### Testing
Install test dependencies and invoke `pytest`. Note that there are ~900 tests, the majority of which are run using Selenium with the Chrome WebDriver, and the complete test suite can take ~30 minutes to complete. Use `pytest --lf` to only run the last-failed tests.
To run basic tests and ensure compatibility with a specific Python version, use Docker and the provided `Dockerfile`: `docker build . [--build-arg pyver=3.11]` (defaults to lowest supported Python version, currently 3.8).
## License
This work is released under the MIT license. A copy of the license is provided in the [LICENSE](https://github.com/mortencombat/stretchable/blob/main/LICENSE) file.
Raw data
{
"_id": null,
"home_page": null,
"name": "stretchable",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "flexbox, grid, block, stretch, css, layout",
"author": "Kenneth Trelborg Vestergaard",
"author_email": null,
"download_url": null,
"platform": null,
"description": "# stretchable\r\n\r\n[](https://pypi.org/project/stretchable/)\r\n[](https://www.python.org)\r\n[](https://github.com/mortencombat/stretchable/blob/main/LICENSE)\r\n[](https://github.com/mortencombat/stretchable/issues)\r\n[](https://github.com/mortencombat/stretchable/actions/workflows/build-publish.yml)\r\n[](https://stretchable.readthedocs.io/en/latest/?badge=stable)\r\n[](https://github.com/mortencombat/stretchable/actions/workflows/test.yml)\r\n[](https://github.com/mortencombat/stretchable/actions/workflows/test.yml)\r\n\r\n**stretchable** is a layout library for Python that enables context-agnostic layout operations using CSS Block, [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) and [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/). Possible uses include UI layouts, page layouts for reports, complex plotting layouts, etc.\r\n\r\nIt implements Python bindings for [Taffy](https://github.com/dioxuslabs/taffy), an implementation of **CSS Block**, **Flexbox** and **CSS Grid** layout algorithms written in [Rust](https://www.rust-lang.org/). It was originally based on [Stretch](https://vislyhq.github.io/stretch/) (hence the name), but has since migrated to use Taffy. It is multi-platform and there are distributions available for Windows, Linux and macOS.\r\n\r\n## Getting Started\r\n\r\n**stretchable** is a Python package [hosted on PyPI](https://pypi.org/project/stretchable/). It can be installed using [pip](https://pip.pypa.io/en/stable/):\r\n\r\n```console\r\npython -m pip install stretchable\r\n```\r\n\r\nBuilding a tree of nodes and calculating the layout is as simple as:\r\n\r\n```python\r\nfrom stretchable import Edge, Node\r\nfrom stretchable.style import AUTO, PCT\r\n\r\n# Build node tree\r\nroot = Node(\r\n margin=20,\r\n size=(500, 300),\r\n).add(\r\n Node(border=5, size=(50 * PCT, AUTO)),\r\n Node(key=\"child\", padding=10 * PCT, size=50 * PCT),\r\n)\r\n\r\n# Compute layout\r\nroot.compute_layout()\r\n\r\n# Get the second of the child nodes\r\nchild_node = root.find(\"/child\")\r\ncontent_box = child_node.get_box(Edge.CONTENT)\r\nprint(content_box)\r\n# Box(x=300.0, y=50.0, width=150.0, height=50.0)\r\n\r\n```\r\n\r\nFor more information and details, see the [documentation](https://stretchable.readthedocs.io/).\r\n\r\n## Contributing\r\n\r\nContributions are welcomed. Please open an issue to clarify/plan implementation details prior to starting the work.\r\n\r\n### Building\r\n\r\nInstall Rust with [rustup](https://rustup.rs/) and use `maturin develop` for development and `maturin build [--release]` to build.\r\n\r\n### Documentation\r\n\r\nTo build documentation use `make html` (in `docs/` folder) or, to use live reloading: `sphinx-autobuild docs/source docs/build/html`\r\n\r\nNOTE: Sometimes, you may need to run `make clean html` (in `docs/` folder) to ensure that all changes are included in the built html.\r\n\r\n### Testing\r\n\r\nInstall test dependencies and invoke `pytest`. Note that there are ~900 tests, the majority of which are run using Selenium with the Chrome WebDriver, and the complete test suite can take ~30 minutes to complete. Use `pytest --lf` to only run the last-failed tests.\r\n\r\nTo run basic tests and ensure compatibility with a specific Python version, use Docker and the provided `Dockerfile`: `docker build . [--build-arg pyver=3.11]` (defaults to lowest supported Python version, currently 3.8).\r\n\r\n## License\r\n\r\nThis work is released under the MIT license. A copy of the license is provided in the [LICENSE](https://github.com/mortencombat/stretchable/blob/main/LICENSE) file.\r\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Layout library for Python (based on Taffy, a rust-powered implementation of CSS Grid/Flexbox)",
"version": "1.1.7",
"project_urls": {
"Documentation": "https://stretchable.readthedocs.io/en/latest/",
"Homepage": "https://stretchable.readthedocs.io/en/latest/",
"Source": "https://github.com/mortencombat/stretchable",
"Tracker": "https://github.com/mortencombat/stretchable/issues"
},
"split_keywords": [
"flexbox",
" grid",
" block",
" stretch",
" css",
" layout"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "224b8676828e4e5bc31d769f4fdd11a6b207e3acc1de1d8cc1ecda812aebbdc0",
"md5": "5a8a4834a377aa6c6db666aa0648ab6a",
"sha256": "70eaf75a402ad170dd947f05d2cf32096745c6fbfa944c9177e1c84f9f5aa0bb"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-macosx_10_12_x86_64.whl",
"has_sig": false,
"md5_digest": "5a8a4834a377aa6c6db666aa0648ab6a",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 461329,
"upload_time": "2025-01-26T15:42:31",
"upload_time_iso_8601": "2025-01-26T15:42:31.687993Z",
"url": "https://files.pythonhosted.org/packages/22/4b/8676828e4e5bc31d769f4fdd11a6b207e3acc1de1d8cc1ecda812aebbdc0/stretchable-1.1.7-cp38-abi3-macosx_10_12_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ad089cb0ad9ac4efa4e61f47e0a4ff92e49bfdde1c5ad6bf313c2589f1fb56d3",
"md5": "0151d800a5f63636ba7bde9a93ce13ba",
"sha256": "262db09cb216f788d88611459d1112ce5a38d72f35571dd4215eef00b44d3bfb"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "0151d800a5f63636ba7bde9a93ce13ba",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 434312,
"upload_time": "2025-01-26T15:42:34",
"upload_time_iso_8601": "2025-01-26T15:42:34.099355Z",
"url": "https://files.pythonhosted.org/packages/ad/08/9cb0ad9ac4efa4e61f47e0a4ff92e49bfdde1c5ad6bf313c2589f1fb56d3/stretchable-1.1.7-cp38-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c520b2a7e39c0580631fc7556ab3dc05c09bccc552752b0163d1e2e0a6acb493",
"md5": "404a11a7d2ee30752453c9acecf50f0e",
"sha256": "3981fa85667d49896b5f7cb126d50eade0f3c5b01309179d45e57a1b9b45576c"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"has_sig": false,
"md5_digest": "404a11a7d2ee30752453c9acecf50f0e",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 473398,
"upload_time": "2025-01-26T15:42:36",
"upload_time_iso_8601": "2025-01-26T15:42:36.294549Z",
"url": "https://files.pythonhosted.org/packages/c5/20/b2a7e39c0580631fc7556ab3dc05c09bccc552752b0163d1e2e0a6acb493/stretchable-1.1.7-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "e0601031ef1397b37440f58cd6596775535ce9dad7d731712f8ef03e269795cd",
"md5": "e30ef3bf812d7d059843db6c81e9f2c2",
"sha256": "754455bc4b032df51387eb48ea52aaab84049afa3fa05af51e7da7d9f683ee5c"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "e30ef3bf812d7d059843db6c81e9f2c2",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 502036,
"upload_time": "2025-01-26T15:42:38",
"upload_time_iso_8601": "2025-01-26T15:42:38.687015Z",
"url": "https://files.pythonhosted.org/packages/e0/60/1031ef1397b37440f58cd6596775535ce9dad7d731712f8ef03e269795cd/stretchable-1.1.7-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "9bd8cb317bc50e580720be568b31d59c6d8788d15a88850acbd0f54bc98b28cf",
"md5": "95fe3c6ce1ab586bbe79bebd456597bc",
"sha256": "63c3f5f770c09d6970723763c84ae448b36c262d3a5e0693d9e8e246a99a191b"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-musllinux_1_2_aarch64.whl",
"has_sig": false,
"md5_digest": "95fe3c6ce1ab586bbe79bebd456597bc",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 642056,
"upload_time": "2025-01-26T15:42:40",
"upload_time_iso_8601": "2025-01-26T15:42:40.222000Z",
"url": "https://files.pythonhosted.org/packages/9b/d8/cb317bc50e580720be568b31d59c6d8788d15a88850acbd0f54bc98b28cf/stretchable-1.1.7-cp38-abi3-musllinux_1_2_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7e59f817a2a13987051a4d4c67bb7427698711a4e0b53e3a9bf2bef9132d8c17",
"md5": "116c2056ba09e4b27f7f616e963e5e11",
"sha256": "30c2a6fc33576918edd14089f528666dcf674cc859b857d7c31b95221dae05c3"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-musllinux_1_2_x86_64.whl",
"has_sig": false,
"md5_digest": "116c2056ba09e4b27f7f616e963e5e11",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 659132,
"upload_time": "2025-01-26T15:42:42",
"upload_time_iso_8601": "2025-01-26T15:42:42.283984Z",
"url": "https://files.pythonhosted.org/packages/7e/59/f817a2a13987051a4d4c67bb7427698711a4e0b53e3a9bf2bef9132d8c17/stretchable-1.1.7-cp38-abi3-musllinux_1_2_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "47e35274d641eca26e452fa5176fa5b4f629fd7de2596295d59a50edf0e50893",
"md5": "c4bddce8f770ccabec629700fd10262a",
"sha256": "f7a851ef6f9e350d8719a2ba2627f3cdbb5802ac12f00948e8b271de859fbe04"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-win32.whl",
"has_sig": false,
"md5_digest": "c4bddce8f770ccabec629700fd10262a",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 322065,
"upload_time": "2025-01-26T15:42:43",
"upload_time_iso_8601": "2025-01-26T15:42:43.891722Z",
"url": "https://files.pythonhosted.org/packages/47/e3/5274d641eca26e452fa5176fa5b4f629fd7de2596295d59a50edf0e50893/stretchable-1.1.7-cp38-abi3-win32.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7f13a0837818168593323d1c2a146f273f08b986ebf8abba73b45272a1919a31",
"md5": "8a7dbd6d8df290ee6b768a96aa560424",
"sha256": "7608739adcd3db4ac7323d1da8b5630329f6152b57b53834323d9084b2afb076"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-win_amd64.whl",
"has_sig": false,
"md5_digest": "8a7dbd6d8df290ee6b768a96aa560424",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 332537,
"upload_time": "2025-01-26T15:42:45",
"upload_time_iso_8601": "2025-01-26T15:42:45.217078Z",
"url": "https://files.pythonhosted.org/packages/7f/13/a0837818168593323d1c2a146f273f08b986ebf8abba73b45272a1919a31/stretchable-1.1.7-cp38-abi3-win_amd64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "78d0cca81cfaffeaaf3ec06770aa3e5da416c088b5c6c32bddd95ba91027524e",
"md5": "68d5feefe82340429bb1fcf4a6f8c8cf",
"sha256": "fd919beff3bc158633abe50423fa4bd35fba2d081a80f7858bf0a810af9d9706"
},
"downloads": -1,
"filename": "stretchable-1.1.7-cp38-abi3-win_arm64.whl",
"has_sig": false,
"md5_digest": "68d5feefe82340429bb1fcf4a6f8c8cf",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 309649,
"upload_time": "2025-01-26T15:42:47",
"upload_time_iso_8601": "2025-01-26T15:42:47.266542Z",
"url": "https://files.pythonhosted.org/packages/78/d0/cca81cfaffeaaf3ec06770aa3e5da416c088b5c6c32bddd95ba91027524e/stretchable-1.1.7-cp38-abi3-win_arm64.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-26 15:42:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mortencombat",
"github_project": "stretchable",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "stretchable"
}