rational 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rational-0.1.0a1/.gitignore +183 -0
- rational-0.1.0a1/CHANGELOG.md +51 -0
- rational-0.1.0a1/CONTRIBUTING.md +38 -0
- rational-0.1.0a1/LICENSE +24 -0
- rational-0.1.0a1/PKG-INFO +308 -0
- rational-0.1.0a1/README.md +249 -0
- rational-0.1.0a1/SECURITY.md +24 -0
- rational-0.1.0a1/benchmarks/run.py +231 -0
- rational-0.1.0a1/docs/api.rst +103 -0
- rational-0.1.0a1/docs/benchmarks.rst +96 -0
- rational-0.1.0a1/docs/components.rst +120 -0
- rational-0.1.0a1/docs/conf.py +32 -0
- rational-0.1.0a1/docs/crash-course-beginner.rst +218 -0
- rational-0.1.0a1/docs/crash-course-fullstack.rst +149 -0
- rational-0.1.0a1/docs/design.rst +100 -0
- rational-0.1.0a1/docs/examples/blog.rst +66 -0
- rational-0.1.0a1/docs/examples/counter.rst +47 -0
- rational-0.1.0a1/docs/examples/cv-portfolio.rst +64 -0
- rational-0.1.0a1/docs/examples/dashboard.rst +53 -0
- rational-0.1.0a1/docs/examples/hello.rst +20 -0
- rational-0.1.0a1/docs/examples/index.rst +90 -0
- rational-0.1.0a1/docs/examples/marketplace.rst +59 -0
- rational-0.1.0a1/docs/examples/media.rst +83 -0
- rational-0.1.0a1/docs/examples/social-network.rst +67 -0
- rational-0.1.0a1/docs/examples/todos.rst +62 -0
- rational-0.1.0a1/docs/examples/user-platform.rst +64 -0
- rational-0.1.0a1/docs/index.rst +97 -0
- rational-0.1.0a1/docs/projects.rst +113 -0
- rational-0.1.0a1/docs/quickstart.rst +75 -0
- rational-0.1.0a1/docs/running.rst +119 -0
- rational-0.1.0a1/docs/security.rst +40 -0
- rational-0.1.0a1/docs/services.rst +102 -0
- rational-0.1.0a1/docs/styling.rst +92 -0
- rational-0.1.0a1/docs/testing.rst +28 -0
- rational-0.1.0a1/docs/tutorial/01-first-page.rst +37 -0
- rational-0.1.0a1/docs/tutorial/02-components.rst +34 -0
- rational-0.1.0a1/docs/tutorial/03-state-and-events.rst +43 -0
- rational-0.1.0a1/docs/tutorial/04-styling.rst +40 -0
- rational-0.1.0a1/docs/tutorial/05-data-and-api.rst +58 -0
- rational-0.1.0a1/docs/tutorial/06-project-structure.rst +74 -0
- rational-0.1.0a1/docs/tutorial/07-testing.rst +35 -0
- rational-0.1.0a1/docs/tutorial/08-deploying.rst +41 -0
- rational-0.1.0a1/docs/tutorial/guestbook/app.py +19 -0
- rational-0.1.0a1/docs/tutorial/guestbook/models/__init__.py +6 -0
- rational-0.1.0a1/docs/tutorial/guestbook/models/database.py +19 -0
- rational-0.1.0a1/docs/tutorial/guestbook/models/entry.py +31 -0
- rational-0.1.0a1/docs/tutorial/guestbook/services/__init__.py +5 -0
- rational-0.1.0a1/docs/tutorial/guestbook/services/guestbook.py +30 -0
- rational-0.1.0a1/docs/tutorial/guestbook/settings.py +8 -0
- rational-0.1.0a1/docs/tutorial/guestbook/tests/conftest.py +12 -0
- rational-0.1.0a1/docs/tutorial/guestbook/tests/test_guestbook.py +36 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/__init__.py +1 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/components/__init__.py +3 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/components/entry_card.py +27 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/pages/__init__.py +8 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/pages/home.py +73 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/styles/__init__.py +5 -0
- rational-0.1.0a1/docs/tutorial/guestbook/views/styles/layout.py +6 -0
- rational-0.1.0a1/docs/tutorial/index.rst +30 -0
- rational-0.1.0a1/docs/tutorial/steps/step1.py +21 -0
- rational-0.1.0a1/docs/tutorial/steps/step2.py +45 -0
- rational-0.1.0a1/docs/tutorial/steps/step3.py +95 -0
- rational-0.1.0a1/docs/tutorial/steps/step4.py +119 -0
- rational-0.1.0a1/docs/tutorial/steps/step5.py +171 -0
- rational-0.1.0a1/examples/README.md +54 -0
- rational-0.1.0a1/examples/blog/README.md +55 -0
- rational-0.1.0a1/examples/blog/app.py +26 -0
- rational-0.1.0a1/examples/blog/models/__init__.py +19 -0
- rational-0.1.0a1/examples/blog/models/account.py +7 -0
- rational-0.1.0a1/examples/blog/models/comment.py +35 -0
- rational-0.1.0a1/examples/blog/models/database.py +19 -0
- rational-0.1.0a1/examples/blog/models/post.py +53 -0
- rational-0.1.0a1/examples/blog/services/__init__.py +6 -0
- rational-0.1.0a1/examples/blog/services/blog.py +106 -0
- rational-0.1.0a1/examples/blog/services/seed.py +53 -0
- rational-0.1.0a1/examples/blog/settings.py +10 -0
- rational-0.1.0a1/examples/blog/views/__init__.py +1 -0
- rational-0.1.0a1/examples/blog/views/components/__init__.py +7 -0
- rational-0.1.0a1/examples/blog/views/components/comments.py +45 -0
- rational-0.1.0a1/examples/blog/views/components/editor.py +61 -0
- rational-0.1.0a1/examples/blog/views/components/markup.py +25 -0
- rational-0.1.0a1/examples/blog/views/components/post_list.py +34 -0
- rational-0.1.0a1/examples/blog/views/components/shell.py +30 -0
- rational-0.1.0a1/examples/blog/views/pages/__init__.py +14 -0
- rational-0.1.0a1/examples/blog/views/pages/admin.py +141 -0
- rational-0.1.0a1/examples/blog/views/pages/feed.py +28 -0
- rational-0.1.0a1/examples/blog/views/pages/public.py +60 -0
- rational-0.1.0a1/examples/blog/views/styles/__init__.py +26 -0
- rational-0.1.0a1/examples/blog/views/styles/article.py +10 -0
- rational-0.1.0a1/examples/blog/views/styles/editor.py +4 -0
- rational-0.1.0a1/examples/blog/views/styles/layout.py +9 -0
- rational-0.1.0a1/examples/blog/views/styles/theme.py +1 -0
- rational-0.1.0a1/examples/counter/README.md +34 -0
- rational-0.1.0a1/examples/counter/app.py +16 -0
- rational-0.1.0a1/examples/counter/views/__init__.py +1 -0
- rational-0.1.0a1/examples/counter/views/components/__init__.py +4 -0
- rational-0.1.0a1/examples/counter/views/components/counter.py +27 -0
- rational-0.1.0a1/examples/counter/views/components/greeter.py +16 -0
- rational-0.1.0a1/examples/counter/views/pages/__init__.py +8 -0
- rational-0.1.0a1/examples/counter/views/pages/home.py +30 -0
- rational-0.1.0a1/examples/counter/views/styles/__init__.py +8 -0
- rational-0.1.0a1/examples/cv-portfolio/README.md +51 -0
- rational-0.1.0a1/examples/cv-portfolio/app.py +19 -0
- rational-0.1.0a1/examples/cv-portfolio/content.py +64 -0
- rational-0.1.0a1/examples/cv-portfolio/models/__init__.py +6 -0
- rational-0.1.0a1/examples/cv-portfolio/models/contact.py +38 -0
- rational-0.1.0a1/examples/cv-portfolio/models/profile.py +43 -0
- rational-0.1.0a1/examples/cv-portfolio/services/__init__.py +5 -0
- rational-0.1.0a1/examples/cv-portfolio/services/contact.py +26 -0
- rational-0.1.0a1/examples/cv-portfolio/views/__init__.py +1 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/__init__.py +8 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/contact.py +53 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/experience.py +34 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/header.py +37 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/hero.py +32 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/projects.py +52 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/section.py +9 -0
- rational-0.1.0a1/examples/cv-portfolio/views/components/skills.py +20 -0
- rational-0.1.0a1/examples/cv-portfolio/views/pages/__init__.py +8 -0
- rational-0.1.0a1/examples/cv-portfolio/views/pages/home.py +23 -0
- rational-0.1.0a1/examples/cv-portfolio/views/styles/__init__.py +20 -0
- rational-0.1.0a1/examples/cv-portfolio/views/styles/layout.py +13 -0
- rational-0.1.0a1/examples/cv-portfolio/views/styles/theme.py +1 -0
- rational-0.1.0a1/examples/cv-portfolio/views/styles/timeline.py +22 -0
- rational-0.1.0a1/examples/dashboard/README.md +45 -0
- rational-0.1.0a1/examples/dashboard/app.py +18 -0
- rational-0.1.0a1/examples/dashboard/models/__init__.py +5 -0
- rational-0.1.0a1/examples/dashboard/models/metrics.py +23 -0
- rational-0.1.0a1/examples/dashboard/services/__init__.py +5 -0
- rational-0.1.0a1/examples/dashboard/services/metrics.py +33 -0
- rational-0.1.0a1/examples/dashboard/views/__init__.py +1 -0
- rational-0.1.0a1/examples/dashboard/views/components/__init__.py +6 -0
- rational-0.1.0a1/examples/dashboard/views/components/recent_orders.py +25 -0
- rational-0.1.0a1/examples/dashboard/views/components/region_chart.py +24 -0
- rational-0.1.0a1/examples/dashboard/views/components/sidebar.py +29 -0
- rational-0.1.0a1/examples/dashboard/views/components/stat_card.py +22 -0
- rational-0.1.0a1/examples/dashboard/views/pages/__init__.py +8 -0
- rational-0.1.0a1/examples/dashboard/views/pages/overview.py +53 -0
- rational-0.1.0a1/examples/dashboard/views/styles/__init__.py +24 -0
- rational-0.1.0a1/examples/dashboard/views/styles/data.py +18 -0
- rational-0.1.0a1/examples/dashboard/views/styles/layout.py +30 -0
- rational-0.1.0a1/examples/dashboard/views/styles/theme.py +1 -0
- rational-0.1.0a1/examples/hello/README.md +17 -0
- rational-0.1.0a1/examples/hello/app.py +27 -0
- rational-0.1.0a1/examples/marketplace/README.md +58 -0
- rational-0.1.0a1/examples/marketplace/app.py +29 -0
- rational-0.1.0a1/examples/marketplace/models/__init__.py +24 -0
- rational-0.1.0a1/examples/marketplace/models/account.py +7 -0
- rational-0.1.0a1/examples/marketplace/models/database.py +19 -0
- rational-0.1.0a1/examples/marketplace/models/money.py +3 -0
- rational-0.1.0a1/examples/marketplace/models/order.py +54 -0
- rational-0.1.0a1/examples/marketplace/models/product.py +40 -0
- rational-0.1.0a1/examples/marketplace/services/__init__.py +6 -0
- rational-0.1.0a1/examples/marketplace/services/seed.py +38 -0
- rational-0.1.0a1/examples/marketplace/services/shop.py +98 -0
- rational-0.1.0a1/examples/marketplace/settings.py +9 -0
- rational-0.1.0a1/examples/marketplace/views/__init__.py +1 -0
- rational-0.1.0a1/examples/marketplace/views/components/__init__.py +6 -0
- rational-0.1.0a1/examples/marketplace/views/components/cart.py +41 -0
- rational-0.1.0a1/examples/marketplace/views/components/header.py +39 -0
- rational-0.1.0a1/examples/marketplace/views/components/product_card.py +45 -0
- rational-0.1.0a1/examples/marketplace/views/components/shell.py +9 -0
- rational-0.1.0a1/examples/marketplace/views/pages/__init__.py +10 -0
- rational-0.1.0a1/examples/marketplace/views/pages/account.py +93 -0
- rational-0.1.0a1/examples/marketplace/views/pages/catalog.py +146 -0
- rational-0.1.0a1/examples/marketplace/views/pages/checkout.py +129 -0
- rational-0.1.0a1/examples/marketplace/views/styles/__init__.py +25 -0
- rational-0.1.0a1/examples/marketplace/views/styles/cart.py +7 -0
- rational-0.1.0a1/examples/marketplace/views/styles/catalog.py +15 -0
- rational-0.1.0a1/examples/marketplace/views/styles/layout.py +8 -0
- rational-0.1.0a1/examples/marketplace/views/styles/theme.py +24 -0
- rational-0.1.0a1/examples/media/README.md +11 -0
- rational-0.1.0a1/examples/media/app.py +19 -0
- rational-0.1.0a1/examples/media/models/__init__.py +3 -0
- rational-0.1.0a1/examples/media/services/__init__.py +3 -0
- rational-0.1.0a1/examples/media/settings.py +20 -0
- rational-0.1.0a1/examples/media/static/logo.svg +269 -0
- rational-0.1.0a1/examples/media/views/__init__.py +1 -0
- rational-0.1.0a1/examples/media/views/pages/__init__.py +8 -0
- rational-0.1.0a1/examples/media/views/pages/media.py +139 -0
- rational-0.1.0a1/examples/media/views/styles/__init__.py +5 -0
- rational-0.1.0a1/examples/media/views/styles/layout.py +7 -0
- rational-0.1.0a1/examples/social-network/README.md +51 -0
- rational-0.1.0a1/examples/social-network/app.py +21 -0
- rational-0.1.0a1/examples/social-network/models/__init__.py +20 -0
- rational-0.1.0a1/examples/social-network/models/database.py +19 -0
- rational-0.1.0a1/examples/social-network/models/member.py +65 -0
- rational-0.1.0a1/examples/social-network/models/post.py +57 -0
- rational-0.1.0a1/examples/social-network/services/__init__.py +6 -0
- rational-0.1.0a1/examples/social-network/services/seed.py +54 -0
- rational-0.1.0a1/examples/social-network/services/social.py +180 -0
- rational-0.1.0a1/examples/social-network/settings.py +10 -0
- rational-0.1.0a1/examples/social-network/views/__init__.py +1 -0
- rational-0.1.0a1/examples/social-network/views/components/__init__.py +6 -0
- rational-0.1.0a1/examples/social-network/views/components/composer.py +58 -0
- rational-0.1.0a1/examples/social-network/views/components/post_card.py +85 -0
- rational-0.1.0a1/examples/social-network/views/components/shell.py +39 -0
- rational-0.1.0a1/examples/social-network/views/components/who_to_follow.py +50 -0
- rational-0.1.0a1/examples/social-network/views/pages/__init__.py +9 -0
- rational-0.1.0a1/examples/social-network/views/pages/account.py +68 -0
- rational-0.1.0a1/examples/social-network/views/pages/explore.py +28 -0
- rational-0.1.0a1/examples/social-network/views/pages/home.py +42 -0
- rational-0.1.0a1/examples/social-network/views/pages/profile.py +64 -0
- rational-0.1.0a1/examples/social-network/views/styles/__init__.py +5 -0
- rational-0.1.0a1/examples/social-network/views/styles/layout.py +9 -0
- rational-0.1.0a1/examples/todos/README.md +41 -0
- rational-0.1.0a1/examples/todos/app.py +21 -0
- rational-0.1.0a1/examples/todos/models/__init__.py +7 -0
- rational-0.1.0a1/examples/todos/models/account.py +7 -0
- rational-0.1.0a1/examples/todos/models/database.py +19 -0
- rational-0.1.0a1/examples/todos/models/todo.py +35 -0
- rational-0.1.0a1/examples/todos/services/__init__.py +5 -0
- rational-0.1.0a1/examples/todos/services/todos.py +62 -0
- rational-0.1.0a1/examples/todos/settings.py +8 -0
- rational-0.1.0a1/examples/todos/views/__init__.py +1 -0
- rational-0.1.0a1/examples/todos/views/components/__init__.py +3 -0
- rational-0.1.0a1/examples/todos/views/components/todo_item.py +41 -0
- rational-0.1.0a1/examples/todos/views/pages/__init__.py +9 -0
- rational-0.1.0a1/examples/todos/views/pages/login.py +31 -0
- rational-0.1.0a1/examples/todos/views/pages/todos.py +112 -0
- rational-0.1.0a1/examples/todos/views/styles/__init__.py +7 -0
- rational-0.1.0a1/examples/todos/views/styles/layout.py +5 -0
- rational-0.1.0a1/examples/todos/views/styles/list.py +7 -0
- rational-0.1.0a1/examples/todos/views/styles/theme.py +1 -0
- rational-0.1.0a1/examples/user-platform/README.md +52 -0
- rational-0.1.0a1/examples/user-platform/app.py +29 -0
- rational-0.1.0a1/examples/user-platform/models/__init__.py +18 -0
- rational-0.1.0a1/examples/user-platform/models/database.py +19 -0
- rational-0.1.0a1/examples/user-platform/models/forms.py +44 -0
- rational-0.1.0a1/examples/user-platform/models/user.py +40 -0
- rational-0.1.0a1/examples/user-platform/services/__init__.py +8 -0
- rational-0.1.0a1/examples/user-platform/services/accounts.py +60 -0
- rational-0.1.0a1/examples/user-platform/services/admin.py +50 -0
- rational-0.1.0a1/examples/user-platform/services/auth.py +17 -0
- rational-0.1.0a1/examples/user-platform/services/security.py +28 -0
- rational-0.1.0a1/examples/user-platform/settings.py +8 -0
- rational-0.1.0a1/examples/user-platform/views/__init__.py +1 -0
- rational-0.1.0a1/examples/user-platform/views/components/__init__.py +4 -0
- rational-0.1.0a1/examples/user-platform/views/components/account_forms.py +60 -0
- rational-0.1.0a1/examples/user-platform/views/components/shell.py +35 -0
- rational-0.1.0a1/examples/user-platform/views/pages/__init__.py +9 -0
- rational-0.1.0a1/examples/user-platform/views/pages/account.py +145 -0
- rational-0.1.0a1/examples/user-platform/views/pages/admin.py +67 -0
- rational-0.1.0a1/examples/user-platform/views/styles/__init__.py +7 -0
- rational-0.1.0a1/examples/user-platform/views/styles/layout.py +5 -0
- rational-0.1.0a1/examples/user-platform/views/styles/table.py +5 -0
- rational-0.1.0a1/examples/user-platform/views/styles/theme.py +1 -0
- rational-0.1.0a1/pyproject.toml +112 -0
- rational-0.1.0a1/src/rational/__init__.py +99 -0
- rational-0.1.0a1/src/rational/__main__.py +3 -0
- rational-0.1.0a1/src/rational/_tracking.py +50 -0
- rational-0.1.0a1/src/rational/apidocs.py +40 -0
- rational-0.1.0a1/src/rational/app.py +377 -0
- rational-0.1.0a1/src/rational/backend/__init__.py +14 -0
- rational-0.1.0a1/src/rational/backend/database.py +46 -0
- rational-0.1.0a1/src/rational/backend/services.py +174 -0
- rational-0.1.0a1/src/rational/binding.py +94 -0
- rational-0.1.0a1/src/rational/cli.py +299 -0
- rational-0.1.0a1/src/rational/component.py +167 -0
- rational-0.1.0a1/src/rational/components/__init__.py +52 -0
- rational-0.1.0a1/src/rational/components/basic.py +247 -0
- rational-0.1.0a1/src/rational/components/forms.py +253 -0
- rational-0.1.0a1/src/rational/components/layout.py +138 -0
- rational-0.1.0a1/src/rational/context.py +69 -0
- rational-0.1.0a1/src/rational/dev.py +208 -0
- rational-0.1.0a1/src/rational/document.py +54 -0
- rational-0.1.0a1/src/rational/errors.py +99 -0
- rational-0.1.0a1/src/rational/events.py +73 -0
- rational-0.1.0a1/src/rational/http.py +72 -0
- rational-0.1.0a1/src/rational/logging.py +185 -0
- rational-0.1.0a1/src/rational/pages.py +181 -0
- rational-0.1.0a1/src/rational/protocol.py +142 -0
- rational-0.1.0a1/src/rational/py.typed +0 -0
- rational-0.1.0a1/src/rational/rendering/__init__.py +5 -0
- rational-0.1.0a1/src/rational/rendering/builder.py +86 -0
- rational-0.1.0a1/src/rational/rendering/html.py +60 -0
- rational-0.1.0a1/src/rational/rendering/nodes.py +121 -0
- rational-0.1.0a1/src/rational/router.py +77 -0
- rational-0.1.0a1/src/rational/runtime.py +323 -0
- rational-0.1.0a1/src/rational/session.py +152 -0
- rational-0.1.0a1/src/rational/state.py +113 -0
- rational-0.1.0a1/src/rational/static/dev.js +53 -0
- rational-0.1.0a1/src/rational/static/runtime.js +311 -0
- rational-0.1.0a1/src/rational/styling/__init__.py +6 -0
- rational-0.1.0a1/src/rational/styling/compiler.py +301 -0
- rational-0.1.0a1/src/rational/styling/responsive.py +53 -0
- rational-0.1.0a1/src/rational/styling/style.py +228 -0
- rational-0.1.0a1/src/rational/styling/theme.py +338 -0
- rational-0.1.0a1/src/rational/testing.py +232 -0
- rational-0.1.0a1/tests/__init__.py +0 -0
- rational-0.1.0a1/tests/browser/__init__.py +0 -0
- rational-0.1.0a1/tests/browser/test_runtime.py +137 -0
- rational-0.1.0a1/tests/examples/__init__.py +0 -0
- rational-0.1.0a1/tests/examples/conftest.py +52 -0
- rational-0.1.0a1/tests/examples/test_blog.py +79 -0
- rational-0.1.0a1/tests/examples/test_cv_portfolio.py +52 -0
- rational-0.1.0a1/tests/examples/test_marketplace.py +97 -0
- rational-0.1.0a1/tests/examples/test_media.py +40 -0
- rational-0.1.0a1/tests/examples/test_small_examples.py +32 -0
- rational-0.1.0a1/tests/examples/test_social_network.py +108 -0
- rational-0.1.0a1/tests/examples/test_todos.py +65 -0
- rational-0.1.0a1/tests/examples/test_user_platform.py +94 -0
- rational-0.1.0a1/tests/test_compatibility.py +14 -0
- rational-0.1.0a1/tests/test_components.py +364 -0
- rational-0.1.0a1/tests/test_dev_and_cli.py +242 -0
- rational-0.1.0a1/tests/test_logging.py +92 -0
- rational-0.1.0a1/tests/test_protocol.py +432 -0
- rational-0.1.0a1/tests/test_rendering.py +81 -0
- rational-0.1.0a1/tests/test_styling.py +193 -0
- rational-0.1.0a1/tests/test_tutorial.py +72 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
116
|
+
.pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
121
|
+
__pypackages__/
|
|
122
|
+
|
|
123
|
+
# Celery stuff
|
|
124
|
+
celerybeat-schedule
|
|
125
|
+
celerybeat.pid
|
|
126
|
+
|
|
127
|
+
# SageMath parsed files
|
|
128
|
+
*.sage.py
|
|
129
|
+
|
|
130
|
+
# Environments
|
|
131
|
+
.env
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# PyCharm
|
|
164
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
165
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
166
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
167
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
168
|
+
#.idea/
|
|
169
|
+
|
|
170
|
+
# Ruff stuff:
|
|
171
|
+
.ruff_cache/
|
|
172
|
+
|
|
173
|
+
# PyPI configuration file
|
|
174
|
+
.pypirc
|
|
175
|
+
|
|
176
|
+
# Cursor
|
|
177
|
+
# Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
|
|
178
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
179
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
180
|
+
.cursorignore
|
|
181
|
+
.cursorindexingignore
|
|
182
|
+
# Local databases from examples
|
|
183
|
+
*.db
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/) (pre-1.0: minor versions may break the API).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- A `media` example and walkthrough covering local and remote images, multiple favicon sources,
|
|
11
|
+
internal and external links, video sources, and embedded PDF documents through `h()`.
|
|
12
|
+
- Structured request logging and telemetry through `app.logging`: request IDs, request and event
|
|
13
|
+
timings, slow-render warnings, and optional OpenTelemetry spans.
|
|
14
|
+
- A broken app no longer kills the dev server: import failures and failing startup hooks serve the
|
|
15
|
+
readable error page instead (with live reload, so the browser recovers once you save a fix).
|
|
16
|
+
`rational.cli.TargetError` replaces the `SystemExit` raised for unusable targets.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Examples and the tutorial project use a new layout: `app.py`, `settings.py`, `models/`,
|
|
20
|
+
`services/` and `views/{styles,components,pages}`. Imports are relative inside a layer and
|
|
21
|
+
absolute across layers (`from views.styles import X`).
|
|
22
|
+
|
|
23
|
+
## [0.1.0a1] - 2026-10-22
|
|
24
|
+
|
|
25
|
+
First public alpha.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Component model: `Component`, `Page`, props from annotations, `State` with component, page
|
|
29
|
+
and session scopes, dependency tracking, keyed identity, async `mount()`/`render()`.
|
|
30
|
+
- Safe node IR and deterministic HTML serializer (escaping, tag/attribute/URL validation).
|
|
31
|
+
- Typed `Style` objects compiled to hashed, deduplicated CSS classes; pseudo-states,
|
|
32
|
+
viewport breakpoints, container queries; four theme presets with light/dark/system modes.
|
|
33
|
+
- Primitives: text, headings, buttons, links, images, layout, cards, accessible form controls.
|
|
34
|
+
- HTTP event protocol v1 with DOM morphing, CSRF, session binding, payload validation,
|
|
35
|
+
rate and size limits, stale-handler resynchronization.
|
|
36
|
+
- `Service` and `@remote`: a typed JSON API under `/api` with OpenAPI docs.
|
|
37
|
+
- Async SQLAlchemy integration (`Database`, `Model`).
|
|
38
|
+
- `python app.py --dev` (supervised hot reload, live browser reload, readable error pages
|
|
39
|
+
and overlays) and `--prod` (single origin, proxy-aware); `python -m rational`.
|
|
40
|
+
- `rational.testing.TestClient`, Playwright browser tests, benchmarks, Sphinx documentation.
|
|
41
|
+
- `Router` for declaring pages, services and startup hooks in their own modules (`App.include`).
|
|
42
|
+
- Swagger UI at `/api/docs` that sends the session's CSRF token, so "Try it out" works when signed in;
|
|
43
|
+
`App(description=..., version=...)`, API tags from service docstrings.
|
|
44
|
+
- `FieldError` for business-rule errors shown under form fields; validator messages without the
|
|
45
|
+
"Value error, " prefix.
|
|
46
|
+
- `Badge` and `Avatar` primitives; `js.confirm()` to ask before a server event.
|
|
47
|
+
- `rational.testing` finds controls by accessible name (`aria-label`, then text).
|
|
48
|
+
- Examples, each laid out as a project: hello, counter, dashboard, cv-portfolio, todos, blog,
|
|
49
|
+
user-platform, marketplace, social-network.
|
|
50
|
+
- Documentation: a step-by-step tutorial (tested), example walkthroughs, a project-structure guide,
|
|
51
|
+
GitHub Pages and Read the Docs publishing.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping make web development less irrational!
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```console
|
|
8
|
+
$ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
9
|
+
$ pip install -e ".[dev,docs,browser]"
|
|
10
|
+
$ playwright install chromium # optional: real-browser tests
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Checks
|
|
14
|
+
|
|
15
|
+
Every change should pass:
|
|
16
|
+
|
|
17
|
+
```console
|
|
18
|
+
$ pytest # unit, integration, examples (+ browser tests if Chromium is present)
|
|
19
|
+
$ ruff check . && ruff format --check .
|
|
20
|
+
$ mypy # strict
|
|
21
|
+
$ python -m sphinx -W -b html docs docs/_build/html
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Guidelines
|
|
25
|
+
|
|
26
|
+
- Keep the core small. A feature that can live in user code or an optional module should.
|
|
27
|
+
- Every behaviour change needs a test; security-relevant code needs a test for the *attack*,
|
|
28
|
+
not only the happy path.
|
|
29
|
+
- Public API changes need docs (`docs/`) and a `CHANGELOG.md` entry under *Unreleased*.
|
|
30
|
+
- Performance claims need a benchmark (`benchmarks/run.py`).
|
|
31
|
+
- Write code that reads like the code around it.
|
|
32
|
+
|
|
33
|
+
## Releasing (maintainers)
|
|
34
|
+
|
|
35
|
+
1. Update `__version__` in `src/rational/__init__.py` and move *Unreleased* entries in
|
|
36
|
+
`CHANGELOG.md` under the new version.
|
|
37
|
+
2. Tag `vX.Y.Z` and push the tag. The *Release* workflow builds, checks, and publishes to PyPI
|
|
38
|
+
through trusted publishing.
|
rational-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rational
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: A Python-first, object-oriented web framework: server-rendered components, typed styles, explicit remote calls.
|
|
5
|
+
Project-URL: Homepage, https://github.com/DutraIRS/Rational-Web
|
|
6
|
+
Project-URL: Repository, https://github.com/DutraIRS/Rational-Web
|
|
7
|
+
Project-URL: Issues, https://github.com/DutraIRS/Rational-Web/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/DutraIRS/Rational-Web/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Funding, https://ko-fi.com/dutrairs
|
|
10
|
+
Author: Iago Riveiro Santos Dutra
|
|
11
|
+
License-Expression: Unlicense
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: components,css-in-python,fastapi,framework,reactive,ssr,web
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Framework :: FastAPI
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Requires-Dist: fastapi>=0.110
|
|
30
|
+
Requires-Dist: pydantic>=2.6
|
|
31
|
+
Requires-Dist: uvicorn>=0.27
|
|
32
|
+
Provides-Extra: browser
|
|
33
|
+
Requires-Dist: playwright>=1.44; extra == 'browser'
|
|
34
|
+
Provides-Extra: db
|
|
35
|
+
Requires-Dist: aiosqlite>=0.19; extra == 'db'
|
|
36
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'db'
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: aiosqlite>=0.19; extra == 'dev'
|
|
39
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
40
|
+
Requires-Dist: httpx2>=2; extra == 'dev'
|
|
41
|
+
Requires-Dist: httpx<0.28,>=0.27; extra == 'dev'
|
|
42
|
+
Requires-Dist: hypothesis>=6; extra == 'dev'
|
|
43
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
47
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
49
|
+
Provides-Extra: docs
|
|
50
|
+
Requires-Dist: furo>=2024.1; extra == 'docs'
|
|
51
|
+
Requires-Dist: sphinx-autobuild>=2024.2; extra == 'docs'
|
|
52
|
+
Requires-Dist: sphinx>=7; extra == 'docs'
|
|
53
|
+
Provides-Extra: telemetry
|
|
54
|
+
Requires-Dist: opentelemetry-api>=1.25; extra == 'telemetry'
|
|
55
|
+
Provides-Extra: testing
|
|
56
|
+
Requires-Dist: httpx2>=2; extra == 'testing'
|
|
57
|
+
Requires-Dist: httpx<0.28,>=0.27; extra == 'testing'
|
|
58
|
+
Description-Content-Type: text/markdown
|
|
59
|
+
|
|
60
|
+
# Rational Web
|
|
61
|
+
|
|
62
|
+
*Trying to make web development less irrational.*
|
|
63
|
+
|
|
64
|
+
[](https://pypi.org/project/rational/)
|
|
65
|
+
[](https://pypi.org/project/rational/)
|
|
66
|
+
[](LICENSE)
|
|
67
|
+
|
|
68
|
+
Rational Web is a Python-first, object-oriented web framework. Pages, components, services
|
|
69
|
+
and models are Python objects. HTML, CSS and JavaScript still exist, but as implementation
|
|
70
|
+
details behind one consistent, typed, inspectable API. It is built on FastAPI, Pydantic and
|
|
71
|
+
(optionally) SQLAlchemy.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from rational import App, Button, Column, Component, Page, State, Text
|
|
75
|
+
|
|
76
|
+
app = App()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Counter(Component):
|
|
80
|
+
count = State(0)
|
|
81
|
+
|
|
82
|
+
def increment(self):
|
|
83
|
+
self.count += 1
|
|
84
|
+
|
|
85
|
+
def render(self):
|
|
86
|
+
return Column(Text(f"Count: {self.count}"), Button("Increment", on_click=self.increment))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.page("/")
|
|
90
|
+
class Home(Page):
|
|
91
|
+
def render(self):
|
|
92
|
+
return Counter()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
app.run()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```console
|
|
100
|
+
$ pip install rational
|
|
101
|
+
$ python app.py --dev # hot reload, live browser reload, readable errors
|
|
102
|
+
$ python app.py --prod # production: pages at /, JSON API at /api, one origin
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
> **Status: alpha (0.1).** The architecture is implemented and tested end to end, including in
|
|
106
|
+
> a real browser, but the public API may still change before 1.0.
|
|
107
|
+
|
|
108
|
+
## Why
|
|
109
|
+
|
|
110
|
+
Full-stack Python today usually means two codebases: a Python API and a JavaScript front end,
|
|
111
|
+
glued together by hand-written fetch calls, duplicated validation and a build pipeline.
|
|
112
|
+
Rational Web keeps the whole application in Python **without pretending the browser isn't
|
|
113
|
+
there**:
|
|
114
|
+
|
|
115
|
+
- **Server-rendered first.** Every page is real HTML before any script runs. Static pages ship
|
|
116
|
+
zero JavaScript. Interactive pages load a 4 KiB (gzipped) runtime that wires events and
|
|
117
|
+
morphs updated HTML into place, preserving focus and typing.
|
|
118
|
+
- **Explicit boundaries.** Event handlers always run on the server. Only methods marked
|
|
119
|
+
`@remote` become an HTTP API. Nothing is callable from the browser by accident.
|
|
120
|
+
- **Typed boundaries.** Path parameters, event payloads and API bodies are validated from your
|
|
121
|
+
annotations by Pydantic. Invalid form input never reaches your handler and shows up next to
|
|
122
|
+
the right field. Component prop annotations support static typing; runtime checks cover required
|
|
123
|
+
and unexpected prop names.
|
|
124
|
+
- **Styles as values.** Immutable `Style` objects compile to deduplicated, hashed CSS classes,
|
|
125
|
+
with theme tokens, pseudo-states, `@media` breakpoints and `@container` queries. You get no
|
|
126
|
+
specificity wars, no class-name soup and no CSS injection.
|
|
127
|
+
- **Secure by default.** Escaped output, session-bound events, CSRF tokens, session rotation,
|
|
128
|
+
rate and size limits, and an optional nonce-based CSP.
|
|
129
|
+
|
|
130
|
+
## Features
|
|
131
|
+
|
|
132
|
+
| | |
|
|
133
|
+
|---|---|
|
|
134
|
+
| Components | Props from annotations, `State` (component/page/session scope), keyed identity, async `mount()`/`render()` |
|
|
135
|
+
| Primitives | `Text`, `Heading`, `Button`, `Link`, `Image`, `Badge`, `Avatar`, `Row`/`Column`/`Stack`/`Grid`, `Card`, `Container`, `Form`, `Input`, `Textarea`, `Select`, `Checkbox`, semantic regions, `h()` for anything else |
|
|
136
|
+
| Events | `on_click`, `on_input` (debounced), `on_change`, `on_submit`, `on_enter`, `on_keydown`, `on_blur`; sync or async; lambdas capture values server-side |
|
|
137
|
+
| Forms | Pydantic validation from handler annotations; `FieldError` for business rules; messages shown under the right field |
|
|
138
|
+
| Local interactions | `js.confirm`, `js.toggle_class`, `js.set_attr`, `js.focus`, `js.set_theme`... run with no round trip |
|
|
139
|
+
| Styling | `Style` merging with `\|`, four theme presets, light/dark/system, responsive breakpoints and container queries |
|
|
140
|
+
| Backend | `Service` + `@remote` → typed JSON API under `/api`, interactive **Swagger UI** at `/api/docs`; async SQLAlchemy |
|
|
141
|
+
| Projects | `Router` (pages and services in their own modules, with prefixes) and a composition root in `app.py` |
|
|
142
|
+
| Developer experience | `--dev` hot reload on every OS, live browser reload, error pages that point at *your* line with hints |
|
|
143
|
+
| Deployment | `--prod`, `python -m rational`, any ASGI server, mountable under a sub-path, proxy-aware |
|
|
144
|
+
| Testing | `rational.testing.TestClient` drives pages by accessible name: `page.click("Save")` |
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
Ten complete applications, each laid out like a real project (see [`examples/`](examples/)):
|
|
149
|
+
|
|
150
|
+
| Example | What you'll learn |
|
|
151
|
+
|---|---|
|
|
152
|
+
| [hello](examples/hello/) | The smallest app: pages, primitives, zero-JS output |
|
|
153
|
+
| [counter](examples/counter/) | State, keys, events, debounced input |
|
|
154
|
+
| [media](examples/media/) | Favicons, local and remote images, links, video and document embeds |
|
|
155
|
+
| [dashboard](examples/dashboard/) | Breakpoints, container queries, fluid grids, no custom CSS |
|
|
156
|
+
| [cv-portfolio](examples/cv-portfolio/) | Content as data, function components, a honeypot contact form |
|
|
157
|
+
| [todos](examples/todos/) | Databases, services, per-user isolation, the JSON API |
|
|
158
|
+
| [blog](examples/blog/) | Path parameters, safe rich text, a live-preview editor, RSS |
|
|
159
|
+
| [user-platform](examples/user-platform/) | Password hashing, authenticators, `FieldError`, roles and an admin area |
|
|
160
|
+
| [marketplace](examples/marketplace/) | A session cart shared across tabs, transactional checkout, money |
|
|
161
|
+
| [social-network](examples/social-network/) | Follows, likes and timelines with a fixed number of queries |
|
|
162
|
+
|
|
163
|
+
```console
|
|
164
|
+
$ pip install "rational[db]"
|
|
165
|
+
$ cd examples/marketplace
|
|
166
|
+
$ python app.py --dev
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Documentation
|
|
170
|
+
|
|
171
|
+
The documentation is a Sphinx site in [`docs/`](docs/):
|
|
172
|
+
|
|
173
|
+
- **Tutorial:** build a guestbook step by step, from one file to a tested, deployed project. Every
|
|
174
|
+
step is a runnable file, executed by the test suite.
|
|
175
|
+
- **Examples:** a file-by-file walkthrough of all ten examples.
|
|
176
|
+
- **Guide:** components, styling, services and the API, project structure, running and deploying,
|
|
177
|
+
security, testing.
|
|
178
|
+
- **Background and reference:** the design record, benchmarks and the full API reference.
|
|
179
|
+
|
|
180
|
+
Preview it locally, rebuilding as you edit:
|
|
181
|
+
|
|
182
|
+
```console
|
|
183
|
+
$ pip install -e ".[docs]"
|
|
184
|
+
$ sphinx-autobuild docs docs/_build/html # then open http://127.0.0.1:8000
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Publishing is ready on two hosts. The `Docs` GitHub Actions workflow builds the site with warnings
|
|
188
|
+
as errors and deploys it to GitHub Pages (enable *Settings → Pages → Source: GitHub Actions* once),
|
|
189
|
+
and `.readthedocs.yaml` configures Read the Docs.
|
|
190
|
+
|
|
191
|
+
## Benchmarks
|
|
192
|
+
|
|
193
|
+
Is it blazingly fast? It's Python, so not in the Rust or Go sense. It is built so that the
|
|
194
|
+
framework is never the bottleneck of a typical page, and so that the browser does very little
|
|
195
|
+
work. The numbers below are measured, not claimed: run `python benchmarks/run.py` to
|
|
196
|
+
reproduce them.
|
|
197
|
+
|
|
198
|
+
These are full requests through the ASGI stack in-process (routing, rendering, CSS compilation,
|
|
199
|
+
sessions, JSON) on **one CPU core**, with no network. The baseline is plain FastAPI returning
|
|
200
|
+
an equivalent *pre-built* HTML string. Measured on Python 3.14.7, Windows 11, Intel Core
|
|
201
|
+
(Tiger Lake).
|
|
202
|
+
|
|
203
|
+
| Scenario | Requests/s | p50 | p95 |
|
|
204
|
+
|---|---:|---:|---:|
|
|
205
|
+
| FastAPI baseline: prebuilt HTML string | 2,651 | 0.35 ms | 0.56 ms |
|
|
206
|
+
| **Rational: static page** | 1,942 | 0.48 ms | 0.76 ms |
|
|
207
|
+
| **Rational: interactive page** (counter) | 1,390 | 0.66 ms | 0.88 ms |
|
|
208
|
+
| **Rational: event round trip** (click → patch) | 1,390 | 0.69 ms | 0.92 ms |
|
|
209
|
+
| Rational: 100-row styled table page | 390 | 2.39 ms | 2.91 ms |
|
|
210
|
+
|
|
211
|
+
| On the wire | gzip |
|
|
212
|
+
|---|---:|
|
|
213
|
+
| `runtime.js` (interactive pages only, cached) | 4.0 KiB |
|
|
214
|
+
| Theme CSS (immutable, cached) | 1.2 KiB |
|
|
215
|
+
| A click's response | 0.2 KiB |
|
|
216
|
+
|
|
217
|
+
- A static page keeps ~73% of raw FastAPI throughput while rendering components and
|
|
218
|
+
compiling CSS.
|
|
219
|
+
- An interaction costs well under 1 ms of server time, so network latency dominates.
|
|
220
|
+
- Rendering costs about 18 µs per element, so pages with thousands of rows should paginate.
|
|
221
|
+
|
|
222
|
+
See [docs/benchmarks.rst](docs/benchmarks.rst) for the method and the micro-benchmarks.
|
|
223
|
+
|
|
224
|
+
## Manifesto
|
|
225
|
+
|
|
226
|
+
1. **Python is the interface; the web platform is the implementation.** We don't hide HTML,
|
|
227
|
+
CSS, JavaScript or HTTP. We give them one coherent, typed front door.
|
|
228
|
+
2. **The network is a boundary, not a detail.** Where code runs must always be obvious. Nothing
|
|
229
|
+
crosses the wire unless you said so.
|
|
230
|
+
3. **HTML first.** A page should be useful before a single script runs. JavaScript is an
|
|
231
|
+
enhancement, measured in kilobytes.
|
|
232
|
+
4. **Secure is the default, not a setting.** Escaping, CSRF, validation and isolation are on
|
|
233
|
+
before you know you need them.
|
|
234
|
+
5. **Values over magic.** Styles are immutable values, render output is plain data, and state
|
|
235
|
+
changes are explicit. What happens can be inspected.
|
|
236
|
+
6. **Composition over inheritance.** Small pieces combine: `Style | Style`, components in
|
|
237
|
+
components, services in services.
|
|
238
|
+
7. **Escape hatches, always.** Raw CSS, raw HTML, FastAPI routes, SQLAlchemy queries and
|
|
239
|
+
custom JavaScript are always available. The framework should never trap you.
|
|
240
|
+
8. **Measure, don't claim.** Performance, accessibility and security are tested, not
|
|
241
|
+
asserted.
|
|
242
|
+
|
|
243
|
+
## Roadmap
|
|
244
|
+
|
|
245
|
+
**0.1: foundation** *(this release)*
|
|
246
|
+
- [x] Component model, `State` scopes, dependency tracking, keyed identity
|
|
247
|
+
- [x] Safe HTML IR and serializer, typed `Style` compiler, four themes, responsive variants
|
|
248
|
+
- [x] HTTP event protocol with morph patches, CSRF, session binding, validation, rate limits
|
|
249
|
+
- [x] Services with `@remote` → typed JSON API, async SQLAlchemy integration
|
|
250
|
+
- [x] `--dev` (hot reload, live reload, readable errors) and `--prod` modes
|
|
251
|
+
- [x] `Router` for multi-file projects; Swagger UI that works with sessions and CSRF
|
|
252
|
+
- [x] Test client, Playwright browser tests, benchmarks
|
|
253
|
+
- [x] Sphinx documentation with a step-by-step tutorial and ten example projects
|
|
254
|
+
|
|
255
|
+
**0.2: interactivity**
|
|
256
|
+
- [ ] WebSocket transport (optional) and server push for collaborative pages
|
|
257
|
+
- [ ] Client-side navigation between pages, with no full reload
|
|
258
|
+
- [ ] Finer-grained server diffs (text and attribute patches)
|
|
259
|
+
- [ ] More components: `Modal`, `Tabs`, `Table`, `Toast`, `Menu`, with full keyboard support
|
|
260
|
+
- [ ] Pluggable session store (for example Redis); page runtimes remain worker-local, so
|
|
261
|
+
multi-worker deployments still need sticky routing
|
|
262
|
+
|
|
263
|
+
**0.3: productivity**
|
|
264
|
+
- [ ] `rational new` project scaffolding and an Alembic migration workflow
|
|
265
|
+
- [x] Structured logging and request telemetry (`app.logging`): request IDs, event timing,
|
|
266
|
+
slow-render tracing, standard-library logs + optional OpenTelemetry spans
|
|
267
|
+
- [ ] Streaming SSR and loading states
|
|
268
|
+
- [ ] Form helpers bound to Pydantic models
|
|
269
|
+
- [ ] Internationalization
|
|
270
|
+
- [ ] Optional Bootstrap and Tailwind adapters
|
|
271
|
+
|
|
272
|
+
**1.0: stability**
|
|
273
|
+
- [ ] Frozen public API with a deprecation policy
|
|
274
|
+
- [ ] Accessibility audit and an automated a11y suite
|
|
275
|
+
- [ ] Browser matrix in CI (Chromium, Firefox, WebKit) and load tests
|
|
276
|
+
- [ ] Independent security review
|
|
277
|
+
|
|
278
|
+
Have an idea? Open an issue. The roadmap is a conversation.
|
|
279
|
+
|
|
280
|
+
## Contributing
|
|
281
|
+
|
|
282
|
+
```console
|
|
283
|
+
git clone https://github.com/DutraIRS/Rational-Web.git
|
|
284
|
+
cd Rational-Web
|
|
285
|
+
python -m venv .venv
|
|
286
|
+
. .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
287
|
+
pip install -e ".[dev,docs,browser]"
|
|
288
|
+
pytest # browser tests run if Chromium is available
|
|
289
|
+
ruff check . && ruff format --check . && mypy
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues privately as described in
|
|
293
|
+
[SECURITY.md](SECURITY.md).
|
|
294
|
+
|
|
295
|
+
## Sponsor
|
|
296
|
+
|
|
297
|
+
Rational Web is free, public-domain software built in the open. If it saves you time, or you
|
|
298
|
+
want to see the roadmap happen sooner, please consider sponsoring:
|
|
299
|
+
|
|
300
|
+
- **Ko-fi:** https://ko-fi.com/dutrairs
|
|
301
|
+
- **Companies:** sponsorship can fund a specific roadmap item. Get in touch through GitHub.
|
|
302
|
+
|
|
303
|
+
Sponsors are thanked in the release notes. Thank you for helping keep web development
|
|
304
|
+
rational. ❤️
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
Public domain, under the [Unlicense](LICENSE).
|