bazimya 0.2.0__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.
Files changed (198) hide show
  1. bazimya-0.2.0/LICENSE +21 -0
  2. bazimya-0.2.0/PKG-INFO +604 -0
  3. bazimya-0.2.0/README.md +576 -0
  4. bazimya-0.2.0/bazimya/__init__.py +146 -0
  5. bazimya-0.2.0/bazimya/__main__.py +8 -0
  6. bazimya-0.2.0/bazimya/auth/__init__.py +12 -0
  7. bazimya-0.2.0/bazimya/auth/guard.py +251 -0
  8. bazimya-0.2.0/bazimya/auth/middleware.py +76 -0
  9. bazimya-0.2.0/bazimya/cache/__init__.py +5 -0
  10. bazimya-0.2.0/bazimya/cache/repository.py +206 -0
  11. bazimya-0.2.0/bazimya/console/__init__.py +7 -0
  12. bazimya-0.2.0/bazimya/console/command.py +164 -0
  13. bazimya-0.2.0/bazimya/console/commands/__init__.py +1 -0
  14. bazimya-0.2.0/bazimya/console/commands/build.py +60 -0
  15. bazimya-0.2.0/bazimya/console/commands/doctor.py +311 -0
  16. bazimya-0.2.0/bazimya/console/commands/inspect.py +302 -0
  17. bazimya-0.2.0/bazimya/console/commands/make.py +451 -0
  18. bazimya-0.2.0/bazimya/console/commands/migrate.py +210 -0
  19. bazimya-0.2.0/bazimya/console/commands/new.py +176 -0
  20. bazimya-0.2.0/bazimya/console/commands/serve.py +62 -0
  21. bazimya-0.2.0/bazimya/console/kernel.py +408 -0
  22. bazimya-0.2.0/bazimya/console/output.py +143 -0
  23. bazimya-0.2.0/bazimya/console/registry.py +44 -0
  24. bazimya-0.2.0/bazimya/database/__init__.py +22 -0
  25. bazimya-0.2.0/bazimya/database/connection.py +328 -0
  26. bazimya-0.2.0/bazimya/database/migration.py +287 -0
  27. bazimya-0.2.0/bazimya/database/model.py +512 -0
  28. bazimya-0.2.0/bazimya/database/query.py +497 -0
  29. bazimya-0.2.0/bazimya/database/schema.py +650 -0
  30. bazimya-0.2.0/bazimya/deployment/__init__.py +5 -0
  31. bazimya-0.2.0/bazimya/deployment/bundler.py +751 -0
  32. bazimya-0.2.0/bazimya/events/__init__.py +5 -0
  33. bazimya-0.2.0/bazimya/events/dispatcher.py +144 -0
  34. bazimya-0.2.0/bazimya/extensions/__init__.py +12 -0
  35. bazimya-0.2.0/bazimya/extensions/extension.py +198 -0
  36. bazimya-0.2.0/bazimya/extensions/manager.py +234 -0
  37. bazimya-0.2.0/bazimya/facades.py +267 -0
  38. bazimya-0.2.0/bazimya/filesystem/__init__.py +5 -0
  39. bazimya-0.2.0/bazimya/filesystem/storage.py +211 -0
  40. bazimya-0.2.0/bazimya/foundation/__init__.py +13 -0
  41. bazimya-0.2.0/bazimya/foundation/application.py +613 -0
  42. bazimya-0.2.0/bazimya/foundation/container.py +121 -0
  43. bazimya-0.2.0/bazimya/foundation/provider.py +68 -0
  44. bazimya-0.2.0/bazimya/hashing.py +190 -0
  45. bazimya-0.2.0/bazimya/http/__init__.py +58 -0
  46. bazimya-0.2.0/bazimya/http/controller.py +84 -0
  47. bazimya-0.2.0/bazimya/http/exceptions.py +100 -0
  48. bazimya-0.2.0/bazimya/http/form_request.py +90 -0
  49. bazimya-0.2.0/bazimya/http/middleware.py +94 -0
  50. bazimya-0.2.0/bazimya/http/request.py +435 -0
  51. bazimya-0.2.0/bazimya/http/response.py +232 -0
  52. bazimya-0.2.0/bazimya/http/route.py +204 -0
  53. bazimya-0.2.0/bazimya/http/router.py +433 -0
  54. bazimya-0.2.0/bazimya/mail/__init__.py +5 -0
  55. bazimya-0.2.0/bazimya/mail/mailer.py +291 -0
  56. bazimya-0.2.0/bazimya/notifications/__init__.py +5 -0
  57. bazimya-0.2.0/bazimya/notifications/notification.py +177 -0
  58. bazimya-0.2.0/bazimya/serving.py +251 -0
  59. bazimya-0.2.0/bazimya/session/__init__.py +13 -0
  60. bazimya-0.2.0/bazimya/session/middleware.py +222 -0
  61. bazimya-0.2.0/bazimya/session/store.py +353 -0
  62. bazimya-0.2.0/bazimya/stubs/app/.editorconfig.stub +15 -0
  63. bazimya-0.2.0/bazimya/stubs/app/.env.example.stub +40 -0
  64. bazimya-0.2.0/bazimya/stubs/app/.gitattributes.stub +10 -0
  65. bazimya-0.2.0/bazimya/stubs/app/.gitignore.stub +19 -0
  66. bazimya-0.2.0/bazimya/stubs/app/app/Console/Commands/GreetCommand.py.stub +25 -0
  67. bazimya-0.2.0/bazimya/stubs/app/app/Console/Commands/__init__.py.stub +24 -0
  68. bazimya-0.2.0/bazimya/stubs/app/app/Console/Kernel.py.stub +22 -0
  69. bazimya-0.2.0/bazimya/stubs/app/app/Console/__init__.py.stub +0 -0
  70. bazimya-0.2.0/bazimya/stubs/app/app/Exceptions/Handler.py.stub +28 -0
  71. bazimya-0.2.0/bazimya/stubs/app/app/Exceptions/__init__.py.stub +0 -0
  72. bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/AuthController.py.stub +75 -0
  73. bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/Controller.py.stub +11 -0
  74. bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/HomeController.py.stub +22 -0
  75. bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/__init__.py.stub +36 -0
  76. bazimya-0.2.0/bazimya/stubs/app/app/Http/Kernel.py.stub +53 -0
  77. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/Authenticate.py.stub +10 -0
  78. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/EnsureEmailIsVerified.py.stub +21 -0
  79. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/RedirectIfAuthenticated.py.stub +7 -0
  80. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/TrustProxies.py.stub +25 -0
  81. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/VerifyCsrfToken.py.stub +16 -0
  82. bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/__init__.py.stub +36 -0
  83. bazimya-0.2.0/bazimya/stubs/app/app/Http/Requests/__init__.py.stub +21 -0
  84. bazimya-0.2.0/bazimya/stubs/app/app/Http/__init__.py.stub +0 -0
  85. bazimya-0.2.0/bazimya/stubs/app/app/Models/User.py.stub +15 -0
  86. bazimya-0.2.0/bazimya/stubs/app/app/Models/__init__.py.stub +36 -0
  87. bazimya-0.2.0/bazimya/stubs/app/app/Notifications/WelcomeNotification.py.stub +28 -0
  88. bazimya-0.2.0/bazimya/stubs/app/app/Notifications/__init__.py.stub +21 -0
  89. bazimya-0.2.0/bazimya/stubs/app/app/Providers/AppServiceProvider.py.stub +14 -0
  90. bazimya-0.2.0/bazimya/stubs/app/app/Providers/AuthServiceProvider.py.stub +11 -0
  91. bazimya-0.2.0/bazimya/stubs/app/app/Providers/EventServiceProvider.py.stub +15 -0
  92. bazimya-0.2.0/bazimya/stubs/app/app/Providers/RouteServiceProvider.py.stub +19 -0
  93. bazimya-0.2.0/bazimya/stubs/app/app/Providers/__init__.py.stub +0 -0
  94. bazimya-0.2.0/bazimya/stubs/app/app/Rules/__init__.py.stub +0 -0
  95. bazimya-0.2.0/bazimya/stubs/app/app/Services/__init__.py.stub +0 -0
  96. bazimya-0.2.0/bazimya/stubs/app/app/Support/__init__.py.stub +0 -0
  97. bazimya-0.2.0/bazimya/stubs/app/app/View/Components/Alert.py.stub +18 -0
  98. bazimya-0.2.0/bazimya/stubs/app/app/View/Components/__init__.py.stub +22 -0
  99. bazimya-0.2.0/bazimya/stubs/app/app/View/__init__.py.stub +0 -0
  100. bazimya-0.2.0/bazimya/stubs/app/app/__init__.py.stub +0 -0
  101. bazimya-0.2.0/bazimya/stubs/app/bazimya.stub +41 -0
  102. bazimya-0.2.0/bazimya/stubs/app/bootstrap/app.py.stub +30 -0
  103. bazimya-0.2.0/bazimya/stubs/app/bootstrap/cache/.gitkeep +0 -0
  104. bazimya-0.2.0/bazimya/stubs/app/config/app.py.stub +44 -0
  105. bazimya-0.2.0/bazimya/stubs/app/config/auth.py.stub +28 -0
  106. bazimya-0.2.0/bazimya/stubs/app/config/cache.py.stub +20 -0
  107. bazimya-0.2.0/bazimya/stubs/app/config/cors.py.stub +16 -0
  108. bazimya-0.2.0/bazimya/stubs/app/config/database.py.stub +39 -0
  109. bazimya-0.2.0/bazimya/stubs/app/config/extensions.py.stub +17 -0
  110. bazimya-0.2.0/bazimya/stubs/app/config/filesystems.py.stub +27 -0
  111. bazimya-0.2.0/bazimya/stubs/app/config/hashing.py.stub +22 -0
  112. bazimya-0.2.0/bazimya/stubs/app/config/logging.py.stub +9 -0
  113. bazimya-0.2.0/bazimya/stubs/app/config/mail.py.stub +31 -0
  114. bazimya-0.2.0/bazimya/stubs/app/config/queue.py.stub +20 -0
  115. bazimya-0.2.0/bazimya/stubs/app/config/services.py.stub +13 -0
  116. bazimya-0.2.0/bazimya/stubs/app/config/session.py.stub +31 -0
  117. bazimya-0.2.0/bazimya/stubs/app/config/view.py.stub +12 -0
  118. bazimya-0.2.0/bazimya/stubs/app/database/.gitkeep +0 -0
  119. bazimya-0.2.0/bazimya/stubs/app/database/factories/.gitkeep +0 -0
  120. bazimya-0.2.0/bazimya/stubs/app/database/factories/UserFactory.py.stub +56 -0
  121. bazimya-0.2.0/bazimya/stubs/app/database/migrations/2026_01_01_000000_create_users_table.py.stub +17 -0
  122. bazimya-0.2.0/bazimya/stubs/app/database/seeders/DatabaseSeeder.py.stub +11 -0
  123. bazimya-0.2.0/bazimya/stubs/app/extensions/.gitkeep +0 -0
  124. bazimya-0.2.0/bazimya/stubs/app/package.json.stub +19 -0
  125. bazimya-0.2.0/bazimya/stubs/app/passenger_wsgi.py.stub +18 -0
  126. bazimya-0.2.0/bazimya/stubs/app/public/.htaccess.stub +33 -0
  127. bazimya-0.2.0/bazimya/stubs/app/public/css/app.css.stub +86 -0
  128. bazimya-0.2.0/bazimya/stubs/app/public/index.py.stub +19 -0
  129. bazimya-0.2.0/bazimya/stubs/app/pytest.ini.stub +9 -0
  130. bazimya-0.2.0/bazimya/stubs/app/requirements.txt.stub +11 -0
  131. bazimya-0.2.0/bazimya/stubs/app/resources/css/app.css.stub +5 -0
  132. bazimya-0.2.0/bazimya/stubs/app/resources/js/app.js.stub +4 -0
  133. bazimya-0.2.0/bazimya/stubs/app/resources/views/auth/login.baz.html.stub +23 -0
  134. bazimya-0.2.0/bazimya/stubs/app/resources/views/auth/register.baz.html.stub +30 -0
  135. bazimya-0.2.0/bazimya/stubs/app/resources/views/components/alert.baz.html.stub +6 -0
  136. bazimya-0.2.0/bazimya/stubs/app/resources/views/components/input-error.baz.html.stub +8 -0
  137. bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/404.baz.html.stub +9 -0
  138. bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/500.baz.html.stub +9 -0
  139. bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/503.baz.html.stub +8 -0
  140. bazimya-0.2.0/bazimya/stubs/app/resources/views/home.baz.html.stub +45 -0
  141. bazimya-0.2.0/bazimya/stubs/app/resources/views/layouts/app.baz.html.stub +26 -0
  142. bazimya-0.2.0/bazimya/stubs/app/routes/api.py.stub +12 -0
  143. bazimya-0.2.0/bazimya/stubs/app/routes/auth.py.stub +20 -0
  144. bazimya-0.2.0/bazimya/stubs/app/routes/console.py.stub +12 -0
  145. bazimya-0.2.0/bazimya/stubs/app/routes/web.py.stub +32 -0
  146. bazimya-0.2.0/bazimya/stubs/app/storage/app/public/.gitkeep +0 -0
  147. bazimya-0.2.0/bazimya/stubs/app/storage/framework/cache/.gitkeep +0 -0
  148. bazimya-0.2.0/bazimya/stubs/app/storage/framework/sessions/.gitkeep +0 -0
  149. bazimya-0.2.0/bazimya/stubs/app/storage/framework/testing/.gitkeep +0 -0
  150. bazimya-0.2.0/bazimya/stubs/app/storage/framework/views/.gitkeep +0 -0
  151. bazimya-0.2.0/bazimya/stubs/app/storage/logs/.gitkeep +0 -0
  152. bazimya-0.2.0/bazimya/stubs/app/tests/Feature/.gitkeep +0 -0
  153. bazimya-0.2.0/bazimya/stubs/app/tests/Feature/ExampleTest.py.stub +17 -0
  154. bazimya-0.2.0/bazimya/stubs/app/tests/Feature/__init__.py.stub +0 -0
  155. bazimya-0.2.0/bazimya/stubs/app/tests/TestCase.py.stub +21 -0
  156. bazimya-0.2.0/bazimya/stubs/app/tests/Unit/.gitkeep +0 -0
  157. bazimya-0.2.0/bazimya/stubs/app/tests/Unit/ExampleTest.py.stub +13 -0
  158. bazimya-0.2.0/bazimya/stubs/app/tests/Unit/__init__.py.stub +0 -0
  159. bazimya-0.2.0/bazimya/stubs/app/tests/__init__.py.stub +0 -0
  160. bazimya-0.2.0/bazimya/stubs/app/wsgi.py.stub +16 -0
  161. bazimya-0.2.0/bazimya/stubs/command.stub +18 -0
  162. bazimya-0.2.0/bazimya/stubs/component.stub +8 -0
  163. bazimya-0.2.0/bazimya/stubs/component.view.stub +3 -0
  164. bazimya-0.2.0/bazimya/stubs/controller.api.stub +38 -0
  165. bazimya-0.2.0/bazimya/stubs/controller.resource.stub +42 -0
  166. bazimya-0.2.0/bazimya/stubs/controller.stub +8 -0
  167. bazimya-0.2.0/bazimya/stubs/extension.stub +60 -0
  168. bazimya-0.2.0/bazimya/stubs/extension.view.stub +20 -0
  169. bazimya-0.2.0/bazimya/stubs/middleware.stub +13 -0
  170. bazimya-0.2.0/bazimya/stubs/migration.create.stub +13 -0
  171. bazimya-0.2.0/bazimya/stubs/migration.stub +11 -0
  172. bazimya-0.2.0/bazimya/stubs/migration.table.stub +13 -0
  173. bazimya-0.2.0/bazimya/stubs/model.stub +16 -0
  174. bazimya-0.2.0/bazimya/stubs/notification.stub +17 -0
  175. bazimya-0.2.0/bazimya/stubs/provider.stub +11 -0
  176. bazimya-0.2.0/bazimya/stubs/request.stub +19 -0
  177. bazimya-0.2.0/bazimya/stubs/rule.stub +11 -0
  178. bazimya-0.2.0/bazimya/stubs/seeder.stub +6 -0
  179. bazimya-0.2.0/bazimya/support/__init__.py +19 -0
  180. bazimya-0.2.0/bazimya/support/aliases.py +73 -0
  181. bazimya-0.2.0/bazimya/support/config.py +103 -0
  182. bazimya-0.2.0/bazimya/support/env.py +137 -0
  183. bazimya-0.2.0/bazimya/support/strings.py +116 -0
  184. bazimya-0.2.0/bazimya/testing.py +381 -0
  185. bazimya-0.2.0/bazimya/validation.py +340 -0
  186. bazimya-0.2.0/bazimya/validation_rule.py +23 -0
  187. bazimya-0.2.0/bazimya/view/__init__.py +14 -0
  188. bazimya-0.2.0/bazimya/view/compiler.py +851 -0
  189. bazimya-0.2.0/bazimya/view/components.py +156 -0
  190. bazimya-0.2.0/bazimya/view/view.py +459 -0
  191. bazimya-0.2.0/bazimya.egg-info/PKG-INFO +604 -0
  192. bazimya-0.2.0/bazimya.egg-info/SOURCES.txt +196 -0
  193. bazimya-0.2.0/bazimya.egg-info/dependency_links.txt +1 -0
  194. bazimya-0.2.0/bazimya.egg-info/entry_points.txt +2 -0
  195. bazimya-0.2.0/bazimya.egg-info/requires.txt +9 -0
  196. bazimya-0.2.0/bazimya.egg-info/top_level.txt +1 -0
  197. bazimya-0.2.0/pyproject.toml +47 -0
  198. bazimya-0.2.0/setup.cfg +4 -0
bazimya-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bazimya contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bazimya-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,604 @@
1
+ Metadata-Version: 2.4
2
+ Name: bazimya
3
+ Version: 0.2.0
4
+ Summary: A Python web framework with Laravel's structure.
5
+ Author-email: James Mark <ericnyirimana31@gmail.com>
6
+ Maintainer-email: James Mark <ericnyirimana31@gmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/bzsaph/bazimya
9
+ Project-URL: Repository, https://github.com/bzsaph/bazimya
10
+ Project-URL: Issues, https://github.com/bzsaph/bazimya/issues
11
+ Project-URL: Changelog, https://github.com/bzsaph/bazimya/releases
12
+ Keywords: framework,web,mvc,laravel,wsgi
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: mysql
22
+ Requires-Dist: PyMySQL>=1.1; extra == "mysql"
23
+ Provides-Extra: postgres
24
+ Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
25
+ Provides-Extra: server
26
+ Requires-Dist: gunicorn>=21.2; extra == "server"
27
+ Dynamic: license-file
28
+
29
+ # Bazimya
30
+
31
+ **Laravel's structure. Python's language.**
32
+
33
+ Same folders, same file names, same vocabulary — `app/Http/Controllers`,
34
+ `routes/web`, `config/app`, `app/Console/Kernel`, `database/migrations`. The
35
+ only differences are that the files end in `.py` instead of `.php`, templates
36
+ are `.baz.html` instead of `.blade.php`, and the CLI is called `bazimya`
37
+ instead of `artisan`.
38
+
39
+ ```python
40
+ # routes/web.py
41
+ from bazimya import Route
42
+ from app.Http.Controllers import PostController
43
+
44
+ Route.get('/posts', [PostController, 'index']).name('posts.index')
45
+ Route.resource('posts', PostController)
46
+ ```
47
+
48
+ ```python
49
+ # app/Http/Controllers/PostController.py
50
+ from app.Models import Post
51
+
52
+ from .Controller import Controller
53
+
54
+
55
+ class PostController(Controller):
56
+ def index(self, request):
57
+ return self.view('posts.index', posts=Post.where('published', 1).latest().get())
58
+ ```
59
+
60
+ ```html
61
+ <!-- resources/views/posts/index.baz.html -->
62
+ @extends('layouts.app')
63
+
64
+ @section('content')
65
+ @forelse post in posts
66
+ <article>{{ post.title }}</article>
67
+ @empty
68
+ <p>Nothing here yet.</p>
69
+ @endforelse
70
+ @endsection
71
+ ```
72
+
73
+ **Status: 0.2.0.** Working and tested end to end, but pre-1.0 and still moving.
74
+ Not recommended for production yet.
75
+
76
+ ---
77
+
78
+ ## Requirements
79
+
80
+ - Python 3.8+
81
+ - Node 16+ *(optional — only for the `npx bazimya` wrapper)*
82
+
83
+ Nothing else. The framework runs on the standard library alone, which is what
84
+ makes it deployable to shared hosting where you cannot run `pip`.
85
+
86
+ ## Install
87
+
88
+ ```bash
89
+ npm install -g bazimya # or: pip install bazimya
90
+ bazimya new blog
91
+ cd blog
92
+ bazimya migrate
93
+ bazimya serve
94
+ ```
95
+
96
+ Open <http://127.0.0.1:8000>.
97
+
98
+ Inside a project you can use any of these — they all run the same code:
99
+
100
+ ```bash
101
+ bazimya serve # global install
102
+ npx bazimya serve # npm, no install
103
+ npm run serve # the script in package.json
104
+ python bazimya serve # no Node at all
105
+ ```
106
+
107
+ ## Coming from Laravel
108
+
109
+ | Laravel | Bazimya |
110
+ | --- | --- |
111
+ | `artisan` | `bazimya` |
112
+ | `app/Http/Controllers/PostController.php` | `app/Http/Controllers/PostController.py` |
113
+ | `app/Http/Middleware/`, `app/Http/Requests/` | same |
114
+ | `app/Models/`, `app/Providers/`, `app/Rules/` | same |
115
+ | `app/Notifications/`, `app/View/Components/` | same |
116
+ | `app/Services/`, `app/Support/` | same |
117
+ | `app/Console/Kernel.php`, `app/Console/Commands/` | same, `.py` |
118
+ | `app/Exceptions/Handler.php` | `app/Exceptions/Handler.py` |
119
+ | `routes/web.php`, `api.php`, `auth.php`, `console.php` | same, `.py` |
120
+ | `config/` app, auth, cache, cors, database, filesystems, hashing, logging, mail, queue, services, session, view | same, `.py` |
121
+ | `resources/views/components/` | same, `.baz.html` |
122
+ | `tests/TestCase.php`, `tests/Feature`, `tests/Unit` | same, `.py` |
123
+ | `database/factories/UserFactory.php` | same, `.py` |
124
+ | `phpunit.xml` | `pytest.ini` |
125
+ | `resources/views/home.blade.php` | `resources/views/home.baz.html` |
126
+ | `bootstrap/app.php`, `public/`, `storage/` | same |
127
+ | `Route::get(...)` | `Route.get(...)` |
128
+ | `User::find($id)` | `User.find(id)` |
129
+ | `$user->name` | `user.name` |
130
+ | `composer install` | nothing to install |
131
+
132
+ **Method names work either way.** Bazimya's own methods are snake_case, but
133
+ every Laravel camelCase spelling resolves to the same call, so ported code
134
+ runs unchanged:
135
+
136
+ ```python
137
+ User.where('active', 1).orderBy('name').firstOrFail() # Laravel's spelling
138
+ User.where('active', 1).order_by('name').first_or_fail() # identical
139
+ ```
140
+
141
+ Python has no `::`, so `Route::get` becomes `Route.get` — one character. That
142
+ is the largest syntactic difference in the framework.
143
+
144
+ ## Layout
145
+
146
+ ```
147
+ blog/
148
+ ├── app/
149
+ │ ├── Console/
150
+ │ │ ├── Commands/
151
+ │ │ └── Kernel.py your CLI commands
152
+ │ ├── Exceptions/Handler.py
153
+ │ ├── Http/
154
+ │ │ ├── Controllers/
155
+ │ │ ├── Middleware/ Authenticate, VerifyCsrfToken, TrustProxies…
156
+ │ │ ├── Requests/ form requests
157
+ │ │ └── Kernel.py middleware stacks, groups, aliases
158
+ │ ├── Models/
159
+ │ ├── Notifications/
160
+ │ ├── Providers/ App, Auth, Event, Route
161
+ │ ├── Rules/ custom validation rules
162
+ │ ├── Services/
163
+ │ ├── Support/
164
+ │ └── View/Components/ <x-alert> and friends
165
+ ├── bootstrap/app.py builds the application
166
+ ├── config/ 13 files, same names as Laravel's
167
+ ├── database/
168
+ │ ├── factories/ migrations/ seeders/
169
+ ├── extensions/ drop-in packages
170
+ ├── public/ document root; index.py is the CGI fallback
171
+ ├── resources/
172
+ │ ├── css/ js/
173
+ │ └── views/ *.baz.html, incl. components/ and auth/
174
+ ├── routes/ web.py, api.py, auth.py, console.py
175
+ ├── storage/ sessions, cache, compiled views, logs
176
+ ├── tests/ TestCase.py, Feature/, Unit/
177
+ ├── .env
178
+ ├── bazimya the CLI (Laravel's artisan)
179
+ ├── passenger_wsgi.py cPanel entry point
180
+ └── wsgi.py gunicorn entry point
181
+ ```
182
+
183
+ Controllers, models, middleware, requests and commands each live in a file
184
+ named after the class, and are importable straight away — nothing to register:
185
+
186
+ ```python
187
+ from app.Http.Controllers import PostController
188
+ from app.Models import Post
189
+ ```
190
+
191
+ ## Routing
192
+
193
+ ```python
194
+ from bazimya import Route
195
+
196
+ Route.get('/', [HomeController, 'index']).name('home')
197
+ Route.post('/posts', [PostController, 'store'])
198
+ Route.get('/posts/{id}', [PostController, 'show']).where_number('id')
199
+ Route.get('/posts/{slug?}', [PostController, 'show']) # optional
200
+ Route.resource('posts', PostController) # the seven RESTful routes
201
+ Route.api_resource('posts', PostController) # minus create/edit
202
+
203
+ Route.middleware('auth').prefix('admin').name('admin.').group(lambda: [
204
+ Route.get('/dashboard', [AdminController, 'index']).name('dashboard'),
205
+ ])
206
+ ```
207
+
208
+ Return a `Response`, a string, or a dict/list — dicts become JSON
209
+ automatically. Route parameters arrive as named arguments:
210
+
211
+ ```python
212
+ def show(self, request, id):
213
+ return {'id': id}
214
+ ```
215
+
216
+ `routes/api.py` is loaded under `/api` with the `api` middleware group, exactly
217
+ as Laravel's `RouteServiceProvider` does it.
218
+
219
+ ### Middleware
220
+
221
+ ```python
222
+ from bazimya import Middleware, Response
223
+
224
+
225
+ class EnsureToken(Middleware):
226
+ def handle(self, request, next):
227
+ if request.bearer_token() != 'secret':
228
+ return Response.json({'error': 'Unauthorised'}, 401)
229
+
230
+ return next(request)
231
+ ```
232
+
233
+ Register it in `app/Http/Kernel.py` under `middleware`, `middleware_groups` or
234
+ `middleware_aliases`, then use it by name: `.middleware('auth')`.
235
+
236
+ ## Views
237
+
238
+ Templates live in `resources/views` and end in `.baz.html`. The syntax is
239
+ Blade's; the expressions are Python.
240
+
241
+ ```html
242
+ @extends('layouts.app')
243
+
244
+ @section('title', 'Posts')
245
+
246
+ @section('content')
247
+ <h1>{{ title }}</h1>
248
+
249
+ @if len(posts) > 10
250
+ <p>Quite a lot.</p>
251
+ @elseif len(posts) > 0
252
+ <p>A few.</p>
253
+ @else
254
+ <p>None.</p>
255
+ @endif
256
+
257
+ @foreach post in posts
258
+ <article>{{ post.title }} — {{ post['author'] }}</article>
259
+ @endforeach
260
+
261
+ @forelse comment in comments
262
+ <li>{{ comment.body }}</li>
263
+ @empty
264
+ <li>No comments.</li>
265
+ @endforelse
266
+
267
+ @include('partials.footer', {'year': 2026})
268
+ @endsection
269
+ ```
270
+
271
+ `{{ }}` escapes, `{!! !!}` does not, `{{-- --}}` is a comment. Also available:
272
+ `@unless`, `@isset`, `@while`, `@each`, `@yield`, `@show`, `@parent`,
273
+ `@verbatim`, `@csrf`, `@method('PUT')`, `@json(data)`, `@dump(x)`, `@route`,
274
+ `@asset`, `@config`, and `@python … @endpython` for a block of real Python.
275
+
276
+ Templates compile to Python and are cached in `storage/framework/views`.
277
+
278
+ Writing `{{ $title }}` or `post->title` out of habit gives you a message
279
+ saying so, with the Python spelling, rather than a syntax error.
280
+
281
+ ### Components
282
+
283
+ `<x-alert>` works as it does in Blade — a template in
284
+ `resources/views/components/`, and optionally a class in
285
+ `app/View/Components/` that prepares its data.
286
+
287
+ ```html
288
+ <x-alert type="error" :count="len(errors)">
289
+ Something went wrong.
290
+ </x-alert>
291
+
292
+ <x-input-error field="email" />
293
+ ```
294
+
295
+ ```python
296
+ # app/View/Components/Alert.py
297
+ class Alert(Component):
298
+ def __init__(self, type='info'):
299
+ self.type = type
300
+
301
+ def classes(self):
302
+ return 'alert alert-' + self.type
303
+ ```
304
+
305
+ Attributes become constructor arguments; `:name="..."` evaluates a Python
306
+ expression in the surrounding template's scope; the inner content arrives as
307
+ `slot`. A template with no class works on its own.
308
+
309
+ ```bash
310
+ bazimya make:component Badge # class + template
311
+ bazimya make:component Badge --view-only
312
+ ```
313
+
314
+ ## Database
315
+
316
+ SQLite by default — the file is created on first use, so there is nothing to
317
+ install. Change `DB_CONNECTION` in `.env` for MySQL (`pip install PyMySQL`) or
318
+ Postgres (`pip install psycopg2-binary`).
319
+
320
+ **Query builder:**
321
+
322
+ ```python
323
+ from bazimya import DB
324
+
325
+ DB.table('users').where('active', 1).order_by('name').limit(10).get()
326
+ DB.table('users').where('age', '>=', 18).count()
327
+ DB.table('users').insert({'name': 'James', 'email': 'j@example.com'})
328
+ DB.table('users').where('id', 3).update({'name': 'James M.'})
329
+ ```
330
+
331
+ Values are always bound. Column and table names are validated as identifiers
332
+ rather than interpolated, so a column name arriving from request data cannot
333
+ become SQL.
334
+
335
+ **Models:**
336
+
337
+ ```python
338
+ from bazimya import Model
339
+
340
+
341
+ class User(Model):
342
+ table = 'users'
343
+ fillable = ['name', 'email']
344
+ hidden = ['password']
345
+ casts = {'active': bool}
346
+
347
+
348
+ user = User.create({'name': 'James', 'email': 'j@example.com'})
349
+ user = User.find(1)
350
+ user.name = 'James M.'
351
+ user.save()
352
+
353
+ User.where('active', 1).order_by('name').get()
354
+ User.find_or_fail(3)
355
+ User.first_or_create({'email': 'a@b.c'}, {'name': 'Ada'})
356
+ User.paginate(page=2, per_page=15)
357
+ ```
358
+
359
+ **Migrations** use a schema builder:
360
+
361
+ ```python
362
+ from bazimya import Migration, Schema
363
+
364
+
365
+ class CreatePostsTable(Migration):
366
+ def up(self):
367
+ with Schema.create('posts') as table:
368
+ table.id()
369
+ table.string('title')
370
+ table.text('body').nullable()
371
+ table.foreign_id('user_id').references('id').on('users').on_delete('cascade')
372
+ table.boolean('published').default(False)
373
+ table.timestamps()
374
+
375
+ def down(self):
376
+ Schema.drop_if_exists('posts')
377
+ ```
378
+
379
+ ```bash
380
+ bazimya migrate
381
+ bazimya migrate:rollback --step=2
382
+ bazimya migrate:fresh --seed
383
+ bazimya migrate:status
384
+ ```
385
+
386
+ ## Validation
387
+
388
+ ```python
389
+ data = self.validate(request, {
390
+ 'title': 'required|max:255',
391
+ 'email': 'required|email|unique:users,email',
392
+ 'age': 'nullable|integer|min:18',
393
+ })
394
+ ```
395
+
396
+ A failure raises `ValidationException`, which becomes a 422 with the messages
397
+ attached. Rules can also live in a form request (`app/Http/Requests`) or a rule
398
+ class (`app/Rules`):
399
+
400
+ ```python
401
+ class StorePostRequest(FormRequest):
402
+ def authorize(self, request):
403
+ return True
404
+
405
+ def rules(self):
406
+ return {'title': 'required|max:255'}
407
+
408
+
409
+ def store(self, request):
410
+ data = StorePostRequest.validate(request)
411
+ ```
412
+
413
+ ## Extensions
414
+
415
+ An extension is a package under `extensions/` that brings its own routes,
416
+ controllers, commands, middleware, views and migrations. Drop the directory in
417
+ and it is live — there is nothing to register.
418
+
419
+ ```bash
420
+ bazimya make:extension Blog
421
+ ```
422
+
423
+ ```python
424
+ # extensions/Blog/__init__.py
425
+ from bazimya import Extension, Route
426
+
427
+ extension = Extension('Blog', version='1.0.0', prefix='blog')
428
+
429
+
430
+ class PostController(extension.Controller):
431
+ def index(self, request):
432
+ return self.view('blog::index', posts=[])
433
+
434
+
435
+ @extension.routes
436
+ def routes():
437
+ Route.get('/', [PostController, 'index']).name('blog.index')
438
+
439
+
440
+ @extension.command('blog:publish', 'Publish scheduled posts')
441
+ def publish(args, options):
442
+ return 'Published.'
443
+ ```
444
+
445
+ Its views are addressed as `blog::index` and can extend the application's own
446
+ layouts. Override any of them by creating
447
+ `resources/views/vendor/blog/index.baz.html`. Its migrations are picked up by
448
+ `bazimya migrate`; its commands appear in `bazimya list`.
449
+
450
+ Application routes are registered before extension routes, so your own route
451
+ always wins on a shared URI.
452
+
453
+ ## Deploying
454
+
455
+ ```bash
456
+ bazimya build # VPS: gunicorn + nginx
457
+ bazimya build --shared # shared hosting: cPanel
458
+ ```
459
+
460
+ Both produce a `build/` directory that is ready to upload, and a `DEPLOY.md`
461
+ inside it with the steps for that target. The build:
462
+
463
+ - vendors the framework into `vendor/`, so the server needs no `pip`;
464
+ - compiles every template ahead of time, so `storage/` can be read-only;
465
+ - rewrites `.env` with `APP_ENV=production`, `APP_DEBUG=false` and every
466
+ secret blanked;
467
+ - writes `.htaccess` files that keep `app/`, `config/`, `storage/`,
468
+ `database/`, `extensions/` and `vendor/` out of the web root.
469
+
470
+ **Shared hosting (cPanel).** If the host has **Setup Python App**, point it at
471
+ `passenger_wsgi.py` with entry point `application`. If it does not, the app
472
+ still runs: `public/index.py` serves it over CGI. Neither needs SSH.
473
+
474
+ Since shared hosting has no shell, run `bazimya migrate` locally and upload
475
+ `database/database.sqlite`, or point it at your MySQL database and migrate
476
+ against that.
477
+
478
+ **VPS.** The build includes `nginx.conf.example` and `bazimya.service.example`.
479
+
480
+ ```bash
481
+ gunicorn wsgi:application --workers 3 --bind 127.0.0.1:8000
482
+ ```
483
+
484
+ `bazimya doctor` checks an environment and reports what will bite — writable
485
+ directories, debug left on in production, a SQLite file inside `public/`, a
486
+ missing template cache.
487
+
488
+ ## CLI
489
+
490
+ ```
491
+ bazimya new <name> Create an application
492
+ bazimya serve [--host] [--port] Development server, with auto-reload
493
+ bazimya build [--shared] [--zip] Build for deployment
494
+ bazimya doctor Check the environment
495
+ bazimya tinker REPL with the app booted
496
+
497
+ bazimya make:controller <Name> [--resource] [--api] [--model=Post]
498
+ bazimya make:model <Name> [-m] [-c]
499
+ bazimya make:migration <name> [--create=table] [--table=table]
500
+ bazimya make:middleware <Name>
501
+ bazimya make:request <Name>
502
+ bazimya make:rule <Name>
503
+ bazimya make:notification <Name>
504
+ bazimya make:component <Name> [--view-only]
505
+ bazimya make:provider <Name>
506
+ bazimya make:seeder <Name>
507
+ bazimya make:command <Name> [--command=my:name]
508
+ bazimya make:extension <Name> [--prefix=blog]
509
+
510
+ bazimya migrate [--pretend] [--seed]
511
+ bazimya migrate:rollback [--step=1]
512
+ bazimya migrate:fresh [--seed]
513
+ bazimya migrate:status
514
+ bazimya db:seed [--class=DatabaseSeeder]
515
+
516
+ bazimya route:list [--method] [--path] [--name]
517
+ bazimya extension:list [--commands]
518
+ bazimya view:cache
519
+ bazimya cache:clear
520
+ ```
521
+
522
+ ## Authentication, sessions and CSRF
523
+
524
+ `bazimya new` scaffolds a working login, registration and logout flow —
525
+ `app/Http/Controllers/AuthController.py`, `routes/auth.py` and the two views.
526
+
527
+ ```python
528
+ from bazimya import Auth, Hash
529
+
530
+ if Auth.attempt({'email': email, 'password': password}):
531
+ return redirect('/dashboard')
532
+ ```
533
+
534
+ Passwords are hashed with scrypt, falling back to pbkdf2 where the host's
535
+ OpenSSL lacks it. Sessions are files under `storage/framework/sessions`, with
536
+ the id signed by `APP_KEY` so a forged cookie is rejected before anything is
537
+ read from disk. `VerifyCsrfToken` is in the `web` group, so `@csrf` in a form
538
+ is checked, and a state-changing POST without it gets a 419.
539
+
540
+ Protect routes with the aliases in `app/Http/Kernel.py`:
541
+
542
+ ```python
543
+ Route.middleware('auth').group(lambda: [
544
+ Route.get('/dashboard', [DashboardController, 'index']),
545
+ ])
546
+ ```
547
+
548
+ `storage/framework/sessions` must be **writable** on the server. If it is not,
549
+ every request gets a fresh token and every form POST returns 419 — Bazimya
550
+ writes a warning to stderr when that happens, and `bazimya doctor` checks it.
551
+
552
+ ## Testing
553
+
554
+ ```python
555
+ from tests.TestCase import TestCase
556
+
557
+
558
+ class PostTest(TestCase):
559
+ def test_a_post_can_be_created(self):
560
+ self.acting_as(user)
561
+
562
+ response = self.post('/posts', {'title': 'Hello'})
563
+
564
+ response.assert_redirect('/posts')
565
+ self.assert_database_has('posts', {'title': 'Hello'})
566
+ ```
567
+
568
+ Each test gets a fresh in-memory database with the migrations applied, plus
569
+ in-memory sessions, cache and mail — nothing touches your real data. Requests
570
+ go straight through the WSGI app, so there is no server to start.
571
+
572
+ ```bash
573
+ python -m unittest discover -s tests -p "*Test.py" -t .
574
+ pytest # pytest.ini is scaffolded
575
+ ```
576
+
577
+ ## Also included
578
+
579
+ `Cache` (file/array), `Storage` (local disks, with path traversal refused),
580
+ `Mail` (log/smtp/array via stdlib `smtplib`), `Notification` classes over mail,
581
+ database and log channels, an `Event` dispatcher, and `<x-component>` view
582
+ components with optional backing classes in `app/View/Components`.
583
+
584
+ ## What is not here yet
585
+
586
+ Being explicit, so nothing is discovered the hard way:
587
+
588
+ - **No queue worker.** `config/queue.py` exists but `sync` is the only driver:
589
+ jobs run in the request that dispatched them. Use cron for out-of-band work:
590
+ `* * * * * cd /path/to/app && python bazimya your:command`.
591
+ - **No broadcasting.** No websockets, no `routes/channels.py`.
592
+ - **No relationships on models.** `has_many` / `belongs_to` are not
593
+ implemented; use the query builder for joins.
594
+ - **No authorisation policies or gates.** `AuthServiceProvider` has a
595
+ `policies` dict, but nothing consumes it yet.
596
+ - **No password reset or email verification.** The `verified` middleware
597
+ exists and checks an `email_verified_at` column; nothing sends the email.
598
+ - **No rate limiting** on login or anywhere else.
599
+ - **No asset pipeline.** `resources/css` and `resources/js` are yours to point
600
+ a build at; `public/` is served as-is.
601
+
602
+ ## Licence
603
+
604
+ MIT.