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.
- bazimya-0.2.0/LICENSE +21 -0
- bazimya-0.2.0/PKG-INFO +604 -0
- bazimya-0.2.0/README.md +576 -0
- bazimya-0.2.0/bazimya/__init__.py +146 -0
- bazimya-0.2.0/bazimya/__main__.py +8 -0
- bazimya-0.2.0/bazimya/auth/__init__.py +12 -0
- bazimya-0.2.0/bazimya/auth/guard.py +251 -0
- bazimya-0.2.0/bazimya/auth/middleware.py +76 -0
- bazimya-0.2.0/bazimya/cache/__init__.py +5 -0
- bazimya-0.2.0/bazimya/cache/repository.py +206 -0
- bazimya-0.2.0/bazimya/console/__init__.py +7 -0
- bazimya-0.2.0/bazimya/console/command.py +164 -0
- bazimya-0.2.0/bazimya/console/commands/__init__.py +1 -0
- bazimya-0.2.0/bazimya/console/commands/build.py +60 -0
- bazimya-0.2.0/bazimya/console/commands/doctor.py +311 -0
- bazimya-0.2.0/bazimya/console/commands/inspect.py +302 -0
- bazimya-0.2.0/bazimya/console/commands/make.py +451 -0
- bazimya-0.2.0/bazimya/console/commands/migrate.py +210 -0
- bazimya-0.2.0/bazimya/console/commands/new.py +176 -0
- bazimya-0.2.0/bazimya/console/commands/serve.py +62 -0
- bazimya-0.2.0/bazimya/console/kernel.py +408 -0
- bazimya-0.2.0/bazimya/console/output.py +143 -0
- bazimya-0.2.0/bazimya/console/registry.py +44 -0
- bazimya-0.2.0/bazimya/database/__init__.py +22 -0
- bazimya-0.2.0/bazimya/database/connection.py +328 -0
- bazimya-0.2.0/bazimya/database/migration.py +287 -0
- bazimya-0.2.0/bazimya/database/model.py +512 -0
- bazimya-0.2.0/bazimya/database/query.py +497 -0
- bazimya-0.2.0/bazimya/database/schema.py +650 -0
- bazimya-0.2.0/bazimya/deployment/__init__.py +5 -0
- bazimya-0.2.0/bazimya/deployment/bundler.py +751 -0
- bazimya-0.2.0/bazimya/events/__init__.py +5 -0
- bazimya-0.2.0/bazimya/events/dispatcher.py +144 -0
- bazimya-0.2.0/bazimya/extensions/__init__.py +12 -0
- bazimya-0.2.0/bazimya/extensions/extension.py +198 -0
- bazimya-0.2.0/bazimya/extensions/manager.py +234 -0
- bazimya-0.2.0/bazimya/facades.py +267 -0
- bazimya-0.2.0/bazimya/filesystem/__init__.py +5 -0
- bazimya-0.2.0/bazimya/filesystem/storage.py +211 -0
- bazimya-0.2.0/bazimya/foundation/__init__.py +13 -0
- bazimya-0.2.0/bazimya/foundation/application.py +613 -0
- bazimya-0.2.0/bazimya/foundation/container.py +121 -0
- bazimya-0.2.0/bazimya/foundation/provider.py +68 -0
- bazimya-0.2.0/bazimya/hashing.py +190 -0
- bazimya-0.2.0/bazimya/http/__init__.py +58 -0
- bazimya-0.2.0/bazimya/http/controller.py +84 -0
- bazimya-0.2.0/bazimya/http/exceptions.py +100 -0
- bazimya-0.2.0/bazimya/http/form_request.py +90 -0
- bazimya-0.2.0/bazimya/http/middleware.py +94 -0
- bazimya-0.2.0/bazimya/http/request.py +435 -0
- bazimya-0.2.0/bazimya/http/response.py +232 -0
- bazimya-0.2.0/bazimya/http/route.py +204 -0
- bazimya-0.2.0/bazimya/http/router.py +433 -0
- bazimya-0.2.0/bazimya/mail/__init__.py +5 -0
- bazimya-0.2.0/bazimya/mail/mailer.py +291 -0
- bazimya-0.2.0/bazimya/notifications/__init__.py +5 -0
- bazimya-0.2.0/bazimya/notifications/notification.py +177 -0
- bazimya-0.2.0/bazimya/serving.py +251 -0
- bazimya-0.2.0/bazimya/session/__init__.py +13 -0
- bazimya-0.2.0/bazimya/session/middleware.py +222 -0
- bazimya-0.2.0/bazimya/session/store.py +353 -0
- bazimya-0.2.0/bazimya/stubs/app/.editorconfig.stub +15 -0
- bazimya-0.2.0/bazimya/stubs/app/.env.example.stub +40 -0
- bazimya-0.2.0/bazimya/stubs/app/.gitattributes.stub +10 -0
- bazimya-0.2.0/bazimya/stubs/app/.gitignore.stub +19 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Console/Commands/GreetCommand.py.stub +25 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Console/Commands/__init__.py.stub +24 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Console/Kernel.py.stub +22 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Console/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Exceptions/Handler.py.stub +28 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Exceptions/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/AuthController.py.stub +75 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/Controller.py.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/HomeController.py.stub +22 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Controllers/__init__.py.stub +36 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Kernel.py.stub +53 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/Authenticate.py.stub +10 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/EnsureEmailIsVerified.py.stub +21 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/RedirectIfAuthenticated.py.stub +7 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/TrustProxies.py.stub +25 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/VerifyCsrfToken.py.stub +16 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Middleware/__init__.py.stub +36 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/Requests/__init__.py.stub +21 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Http/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Models/User.py.stub +15 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Models/__init__.py.stub +36 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Notifications/WelcomeNotification.py.stub +28 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Notifications/__init__.py.stub +21 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Providers/AppServiceProvider.py.stub +14 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Providers/AuthServiceProvider.py.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Providers/EventServiceProvider.py.stub +15 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Providers/RouteServiceProvider.py.stub +19 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Providers/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Rules/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Services/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/Support/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/View/Components/Alert.py.stub +18 -0
- bazimya-0.2.0/bazimya/stubs/app/app/View/Components/__init__.py.stub +22 -0
- bazimya-0.2.0/bazimya/stubs/app/app/View/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/app/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/bazimya.stub +41 -0
- bazimya-0.2.0/bazimya/stubs/app/bootstrap/app.py.stub +30 -0
- bazimya-0.2.0/bazimya/stubs/app/bootstrap/cache/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/config/app.py.stub +44 -0
- bazimya-0.2.0/bazimya/stubs/app/config/auth.py.stub +28 -0
- bazimya-0.2.0/bazimya/stubs/app/config/cache.py.stub +20 -0
- bazimya-0.2.0/bazimya/stubs/app/config/cors.py.stub +16 -0
- bazimya-0.2.0/bazimya/stubs/app/config/database.py.stub +39 -0
- bazimya-0.2.0/bazimya/stubs/app/config/extensions.py.stub +17 -0
- bazimya-0.2.0/bazimya/stubs/app/config/filesystems.py.stub +27 -0
- bazimya-0.2.0/bazimya/stubs/app/config/hashing.py.stub +22 -0
- bazimya-0.2.0/bazimya/stubs/app/config/logging.py.stub +9 -0
- bazimya-0.2.0/bazimya/stubs/app/config/mail.py.stub +31 -0
- bazimya-0.2.0/bazimya/stubs/app/config/queue.py.stub +20 -0
- bazimya-0.2.0/bazimya/stubs/app/config/services.py.stub +13 -0
- bazimya-0.2.0/bazimya/stubs/app/config/session.py.stub +31 -0
- bazimya-0.2.0/bazimya/stubs/app/config/view.py.stub +12 -0
- bazimya-0.2.0/bazimya/stubs/app/database/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/database/factories/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/database/factories/UserFactory.py.stub +56 -0
- bazimya-0.2.0/bazimya/stubs/app/database/migrations/2026_01_01_000000_create_users_table.py.stub +17 -0
- bazimya-0.2.0/bazimya/stubs/app/database/seeders/DatabaseSeeder.py.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/app/extensions/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/package.json.stub +19 -0
- bazimya-0.2.0/bazimya/stubs/app/passenger_wsgi.py.stub +18 -0
- bazimya-0.2.0/bazimya/stubs/app/public/.htaccess.stub +33 -0
- bazimya-0.2.0/bazimya/stubs/app/public/css/app.css.stub +86 -0
- bazimya-0.2.0/bazimya/stubs/app/public/index.py.stub +19 -0
- bazimya-0.2.0/bazimya/stubs/app/pytest.ini.stub +9 -0
- bazimya-0.2.0/bazimya/stubs/app/requirements.txt.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/css/app.css.stub +5 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/js/app.js.stub +4 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/auth/login.baz.html.stub +23 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/auth/register.baz.html.stub +30 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/components/alert.baz.html.stub +6 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/components/input-error.baz.html.stub +8 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/404.baz.html.stub +9 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/500.baz.html.stub +9 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/errors/503.baz.html.stub +8 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/home.baz.html.stub +45 -0
- bazimya-0.2.0/bazimya/stubs/app/resources/views/layouts/app.baz.html.stub +26 -0
- bazimya-0.2.0/bazimya/stubs/app/routes/api.py.stub +12 -0
- bazimya-0.2.0/bazimya/stubs/app/routes/auth.py.stub +20 -0
- bazimya-0.2.0/bazimya/stubs/app/routes/console.py.stub +12 -0
- bazimya-0.2.0/bazimya/stubs/app/routes/web.py.stub +32 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/app/public/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/framework/cache/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/framework/sessions/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/framework/testing/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/framework/views/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/storage/logs/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Feature/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Feature/ExampleTest.py.stub +17 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Feature/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/TestCase.py.stub +21 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Unit/.gitkeep +0 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Unit/ExampleTest.py.stub +13 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/Unit/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/tests/__init__.py.stub +0 -0
- bazimya-0.2.0/bazimya/stubs/app/wsgi.py.stub +16 -0
- bazimya-0.2.0/bazimya/stubs/command.stub +18 -0
- bazimya-0.2.0/bazimya/stubs/component.stub +8 -0
- bazimya-0.2.0/bazimya/stubs/component.view.stub +3 -0
- bazimya-0.2.0/bazimya/stubs/controller.api.stub +38 -0
- bazimya-0.2.0/bazimya/stubs/controller.resource.stub +42 -0
- bazimya-0.2.0/bazimya/stubs/controller.stub +8 -0
- bazimya-0.2.0/bazimya/stubs/extension.stub +60 -0
- bazimya-0.2.0/bazimya/stubs/extension.view.stub +20 -0
- bazimya-0.2.0/bazimya/stubs/middleware.stub +13 -0
- bazimya-0.2.0/bazimya/stubs/migration.create.stub +13 -0
- bazimya-0.2.0/bazimya/stubs/migration.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/migration.table.stub +13 -0
- bazimya-0.2.0/bazimya/stubs/model.stub +16 -0
- bazimya-0.2.0/bazimya/stubs/notification.stub +17 -0
- bazimya-0.2.0/bazimya/stubs/provider.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/request.stub +19 -0
- bazimya-0.2.0/bazimya/stubs/rule.stub +11 -0
- bazimya-0.2.0/bazimya/stubs/seeder.stub +6 -0
- bazimya-0.2.0/bazimya/support/__init__.py +19 -0
- bazimya-0.2.0/bazimya/support/aliases.py +73 -0
- bazimya-0.2.0/bazimya/support/config.py +103 -0
- bazimya-0.2.0/bazimya/support/env.py +137 -0
- bazimya-0.2.0/bazimya/support/strings.py +116 -0
- bazimya-0.2.0/bazimya/testing.py +381 -0
- bazimya-0.2.0/bazimya/validation.py +340 -0
- bazimya-0.2.0/bazimya/validation_rule.py +23 -0
- bazimya-0.2.0/bazimya/view/__init__.py +14 -0
- bazimya-0.2.0/bazimya/view/compiler.py +851 -0
- bazimya-0.2.0/bazimya/view/components.py +156 -0
- bazimya-0.2.0/bazimya/view/view.py +459 -0
- bazimya-0.2.0/bazimya.egg-info/PKG-INFO +604 -0
- bazimya-0.2.0/bazimya.egg-info/SOURCES.txt +196 -0
- bazimya-0.2.0/bazimya.egg-info/dependency_links.txt +1 -0
- bazimya-0.2.0/bazimya.egg-info/entry_points.txt +2 -0
- bazimya-0.2.0/bazimya.egg-info/requires.txt +9 -0
- bazimya-0.2.0/bazimya.egg-info/top_level.txt +1 -0
- bazimya-0.2.0/pyproject.toml +47 -0
- 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.
|