spry-core 0.1.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 (93) hide show
  1. spry_core-0.1.0/LICENSE +21 -0
  2. spry_core-0.1.0/PKG-INFO +426 -0
  3. spry_core-0.1.0/README.md +386 -0
  4. spry_core-0.1.0/pyproject.toml +57 -0
  5. spry_core-0.1.0/setup.cfg +4 -0
  6. spry_core-0.1.0/src/spry/__init__.py +95 -0
  7. spry_core-0.1.0/src/spry/app.py +741 -0
  8. spry_core-0.1.0/src/spry/auth.py +264 -0
  9. spry_core-0.1.0/src/spry/cli.py +428 -0
  10. spry_core-0.1.0/src/spry/config.py +167 -0
  11. spry_core-0.1.0/src/spry/controllers.py +97 -0
  12. spry_core-0.1.0/src/spry/cors.py +57 -0
  13. spry_core-0.1.0/src/spry/csrf.py +46 -0
  14. spry_core-0.1.0/src/spry/db/__init__.py +11 -0
  15. spry_core-0.1.0/src/spry/db/backend.py +129 -0
  16. spry_core-0.1.0/src/spry/db/backends/__init__.py +50 -0
  17. spry_core-0.1.0/src/spry/db/backends/mariadb.py +7 -0
  18. spry_core-0.1.0/src/spry/db/backends/mysql.py +72 -0
  19. spry_core-0.1.0/src/spry/db/backends/postgres.py +86 -0
  20. spry_core-0.1.0/src/spry/db/backends/sqlite.py +62 -0
  21. spry_core-0.1.0/src/spry/db/backends/sqlserver.py +91 -0
  22. spry_core-0.1.0/src/spry/db/column_type.py +9 -0
  23. spry_core-0.1.0/src/spry/db/url.py +55 -0
  24. spry_core-0.1.0/src/spry/debug.py +134 -0
  25. spry_core-0.1.0/src/spry/di.py +172 -0
  26. spry_core-0.1.0/src/spry/events.py +48 -0
  27. spry_core-0.1.0/src/spry/http.py +308 -0
  28. spry_core-0.1.0/src/spry/i18n.py +73 -0
  29. spry_core-0.1.0/src/spry/middleware.py +22 -0
  30. spry_core-0.1.0/src/spry/openapi.py +288 -0
  31. spry_core-0.1.0/src/spry/orm.py +737 -0
  32. spry_core-0.1.0/src/spry/results.py +26 -0
  33. spry_core-0.1.0/src/spry/routing.py +149 -0
  34. spry_core-0.1.0/src/spry/scaffold.py +72 -0
  35. spry_core-0.1.0/src/spry/session.py +122 -0
  36. spry_core-0.1.0/src/spry/tasks.py +63 -0
  37. spry_core-0.1.0/src/spry/templates/api/Dockerfile.tmpl +30 -0
  38. spry_core-0.1.0/src/spry/templates/api/README.md.tmpl +29 -0
  39. spry_core-0.1.0/src/spry/templates/api/appsettings.json.tmpl +9 -0
  40. spry_core-0.1.0/src/spry/templates/api/docker-compose.yml.tmpl +17 -0
  41. spry_core-0.1.0/src/spry/templates/api/main.py.tmpl +8 -0
  42. spry_core-0.1.0/src/spry/templates/api/pyproject.toml.tmpl +15 -0
  43. spry_core-0.1.0/src/spry/templates/api/src/__PACKAGE_NAME__/__init__.py.tmpl +3 -0
  44. spry_core-0.1.0/src/spry/templates/api/src/__PACKAGE_NAME__/app.py.tmpl +25 -0
  45. spry_core-0.1.0/src/spry/templates/api/src/__PACKAGE_NAME__/controllers.py.tmpl +44 -0
  46. spry_core-0.1.0/src/spry/templates/api/src/__PACKAGE_NAME__/data.py.tmpl +19 -0
  47. spry_core-0.1.0/src/spry/templates/api/src/__PACKAGE_NAME__/seed.py.tmpl +9 -0
  48. spry_core-0.1.0/src/spry/templates/mvc/Dockerfile.tmpl +30 -0
  49. spry_core-0.1.0/src/spry/templates/mvc/README.md.tmpl +109 -0
  50. spry_core-0.1.0/src/spry/templates/mvc/appsettings.json.tmpl +13 -0
  51. spry_core-0.1.0/src/spry/templates/mvc/docker-compose.yml.tmpl +17 -0
  52. spry_core-0.1.0/src/spry/templates/mvc/main.py.tmpl +8 -0
  53. spry_core-0.1.0/src/spry/templates/mvc/pyproject.toml.tmpl +15 -0
  54. spry_core-0.1.0/src/spry/templates/mvc/src/__PACKAGE_NAME__/__init__.py.tmpl +3 -0
  55. spry_core-0.1.0/src/spry/templates/mvc/src/__PACKAGE_NAME__/app.py.tmpl +36 -0
  56. spry_core-0.1.0/src/spry/templates/mvc/src/__PACKAGE_NAME__/controllers.py.tmpl +130 -0
  57. spry_core-0.1.0/src/spry/templates/mvc/src/__PACKAGE_NAME__/data.py.tmpl +25 -0
  58. spry_core-0.1.0/src/spry/templates/mvc/src/__PACKAGE_NAME__/seed.py.tmpl +19 -0
  59. spry_core-0.1.0/src/spry/templates/mvc/static/site.css.tmpl +352 -0
  60. spry_core-0.1.0/src/spry/templates/mvc/views/account/login.html.tmpl +22 -0
  61. spry_core-0.1.0/src/spry/templates/mvc/views/home/_todo_card.html.tmpl +19 -0
  62. spry_core-0.1.0/src/spry/templates/mvc/views/home/index.html.tmpl +93 -0
  63. spry_core-0.1.0/src/spry/templates/mvc/views/shared/_alert.html.tmpl +1 -0
  64. spry_core-0.1.0/src/spry/templates/mvc/views/shared/_empty_state.html.tmpl +7 -0
  65. spry_core-0.1.0/src/spry/templates/mvc/views/shared/_layout.html.tmpl +13 -0
  66. spry_core-0.1.0/src/spry/testing.py +168 -0
  67. spry_core-0.1.0/src/spry/throttling.py +66 -0
  68. spry_core-0.1.0/src/spry/validation.py +160 -0
  69. spry_core-0.1.0/src/spry/validators.py +122 -0
  70. spry_core-0.1.0/src/spry/views.py +582 -0
  71. spry_core-0.1.0/src/spry_core.egg-info/PKG-INFO +426 -0
  72. spry_core-0.1.0/src/spry_core.egg-info/SOURCES.txt +91 -0
  73. spry_core-0.1.0/src/spry_core.egg-info/dependency_links.txt +1 -0
  74. spry_core-0.1.0/src/spry_core.egg-info/entry_points.txt +2 -0
  75. spry_core-0.1.0/src/spry_core.egg-info/requires.txt +21 -0
  76. spry_core-0.1.0/src/spry_core.egg-info/top_level.txt +1 -0
  77. spry_core-0.1.0/tests/test_application.py +109 -0
  78. spry_core-0.1.0/tests/test_auth.py +71 -0
  79. spry_core-0.1.0/tests/test_auth_advanced.py +109 -0
  80. spry_core-0.1.0/tests/test_auto_discovery.py +86 -0
  81. spry_core-0.1.0/tests/test_cli.py +41 -0
  82. spry_core-0.1.0/tests/test_config.py +81 -0
  83. spry_core-0.1.0/tests/test_db_backends.py +117 -0
  84. spry_core-0.1.0/tests/test_di.py +67 -0
  85. spry_core-0.1.0/tests/test_http.py +139 -0
  86. spry_core-0.1.0/tests/test_openapi.py +85 -0
  87. spry_core-0.1.0/tests/test_orm.py +67 -0
  88. spry_core-0.1.0/tests/test_routing.py +92 -0
  89. spry_core-0.1.0/tests/test_scaffold.py +23 -0
  90. spry_core-0.1.0/tests/test_security_middleware.py +148 -0
  91. spry_core-0.1.0/tests/test_validation.py +195 -0
  92. spry_core-0.1.0/tests/test_views.py +44 -0
  93. spry_core-0.1.0/tests/test_views_engine.py +171 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Spry 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.
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.4
2
+ Name: spry-core
3
+ Version: 0.1.0
4
+ Summary: Opinionated Python web framework with an EF Core-inspired ORM
5
+ Author-email: Reni Dantas <reni@spry.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://spry.dev
8
+ Project-URL: Repository, https://github.com/anomalyco/spry
9
+ Project-URL: Documentation, https://spry.dev/docs
10
+ Project-URL: Changelog, https://github.com/anomalyco/spry/blob/main/CHANGELOG.md
11
+ Keywords: web,framework,orm,api,mvc,wsgi,asgi,aspnet-core-inspired,python-web
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
20
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: postgres
25
+ Requires-Dist: psycopg2-binary; extra == "postgres"
26
+ Provides-Extra: mysql
27
+ Requires-Dist: pymysql; extra == "mysql"
28
+ Provides-Extra: mariadb
29
+ Requires-Dist: pymysql; extra == "mariadb"
30
+ Provides-Extra: sqlserver
31
+ Requires-Dist: pyodbc; extra == "sqlserver"
32
+ Provides-Extra: jinja2
33
+ Requires-Dist: jinja2>=3.0; extra == "jinja2"
34
+ Provides-Extra: all
35
+ Requires-Dist: psycopg2-binary; extra == "all"
36
+ Requires-Dist: pymysql; extra == "all"
37
+ Requires-Dist: pyodbc; extra == "all"
38
+ Requires-Dist: jinja2>=3.0; extra == "all"
39
+ Dynamic: license-file
40
+
41
+ # Spry
42
+
43
+ Spry é um framework Python opinado para quem quer sair do boilerplate rápido sem cair em muita magia.
44
+
45
+ Ele pega algumas ideias do ASP.NET Core e adapta para um fluxo mais pythonic:
46
+
47
+ - `AppBuilder` para bootstrap, configuração e DI
48
+ - Descoberta automática de controllers no pacote da aplicação
49
+ - `ControllerBase` para API e `Controller` para MVC
50
+ - `DbContext` e `DbSet` inspirados no EF Core
51
+ - Middleware por pipeline
52
+ - Validação de payload com resposta `422`
53
+ - Suporte WSGI e ASGI no mesmo app
54
+ - Scaffold de projeto com templates `api` e `mvc`
55
+ - CLI para `new`, `run`, `watch`, `migrate` e `seed`
56
+
57
+ ## Requirements
58
+
59
+ - Python `3.11+`
60
+ - `pip`
61
+
62
+ ## Quick start
63
+
64
+ Instale o framework localmente:
65
+
66
+ ```bash
67
+ pip install -e .
68
+ ```
69
+
70
+ Crie uma API:
71
+
72
+ ```bash
73
+ spry new taskboard
74
+ cd taskboard
75
+ spry run --app taskboard.app:create_app
76
+ ```
77
+
78
+ Crie um projeto MVC:
79
+
80
+ ```bash
81
+ spry new backoffice --template mvc
82
+ cd backoffice
83
+ spry run --app backoffice.app:create_app
84
+ ```
85
+
86
+ ### Hot reload
87
+
88
+ ```bash
89
+ spry watch --app taskboard.app:create_app
90
+ ```
91
+
92
+ ## First manual app
93
+
94
+ O menor exemplo útil com Spry hoje:
95
+
96
+ ```python
97
+ from dataclasses import dataclass
98
+
99
+ from spry import AppBuilder, ControllerBase, DbContext, controller, dbset, get, key, post
100
+
101
+
102
+ @dataclass(slots=True)
103
+ class Todo:
104
+ id: int | None = key()
105
+ title: str = ""
106
+ done: bool = False
107
+
108
+
109
+ class AppDbContext(DbContext):
110
+ todos = dbset(Todo)
111
+
112
+
113
+ @controller("/todos")
114
+ class TodosController(ControllerBase):
115
+ def __init__(self, db: AppDbContext) -> None:
116
+ self.db = db
117
+
118
+ @get("/")
119
+ def list(self):
120
+ return self.db.todos.all()
121
+
122
+ @post("/")
123
+ def create(self, todo: Todo):
124
+ self.db.todos.add(todo)
125
+ self.db.save_changes()
126
+ return self.created(f"/todos/{todo.id}", todo)
127
+
128
+
129
+ builder = AppBuilder()
130
+ builder.add_db_context(AppDbContext)
131
+ app = builder.build()
132
+ app.run()
133
+ ```
134
+
135
+ Você não precisa registrar controllers manualmente. O `AppBuilder` descobre automaticamente classes decoradas com `@controller` no pacote da aplicação.
136
+
137
+ ## API vs MVC
138
+
139
+ Use `ControllerBase` quando:
140
+
141
+ - O retorno principal é JSON
142
+ - O app é uma API
143
+ - Você quer helpers como `self.created()`, `self.not_found()` e `self.no_content()`
144
+
145
+ Use `Controller` quando:
146
+
147
+ - O app serve HTML
148
+ - Você quer `self.view(...)`, `self.partial_view(...)` e `self.redirect(...)`
149
+ - O projeto segue MVC server-side
150
+
151
+ ## Creating a project
152
+
153
+ ### Templates
154
+
155
+ ```
156
+ spry new taskboard # template api (padrão)
157
+ spry new backoffice --template mvc
158
+ spry new inventory --output ./projetos
159
+ ```
160
+
161
+ Template `api`:
162
+
163
+ - `main.py` — entrypoint para desenvolvimento
164
+ - `appsettings.json` — host, porta e configuração de banco
165
+ - `src/<app>/app.py` — composição do `AppBuilder`
166
+ - `src/<app>/controllers.py` — controllers HTTP
167
+ - `src/<app>/data.py` — entidades e `DbContext`
168
+ - `src/<app>/seed.py` — carga inicial de dados
169
+
170
+ Template `mvc`:
171
+
172
+ - Tudo do template `api`
173
+ - `views/` — layouts, páginas e partials
174
+ - `static/site.css` — estilos da interface
175
+
176
+ ### Conventions the framework assumes
177
+
178
+ - Controllers são classes decoradas com `@controller`
179
+ - A descoberta automática olha para o pacote da aplicação
180
+ - `DbContext` é tipicamente registrado com `builder.add_db_context(...)`
181
+ - Para MVC, views ficam em arquivos dentro de `views/`
182
+ - Middlewares devem ser pequenos e focados em preocupações transversais
183
+
184
+ ## CLI reference
185
+
186
+ ```
187
+ spry new <nome> [--template api|mvc] [--output <pasta>]
188
+ spry run --app modulo:factory [--host 127.0.0.1] [--port 8000]
189
+ spry watch --app modulo:factory [--path extra]
190
+ spry migrate add <nome> --context modulo:DbContext [--output migrations]
191
+ spry migrate apply --database app.db [--input migrations]
192
+ spry seed --entry modulo:funcao [--context modulo:DbContext] [--database app.db]
193
+ ```
194
+
195
+ ## Database, migrations and seed
196
+
197
+ Gerar SQL inicial a partir do `DbContext`:
198
+
199
+ ```bash
200
+ spry migrate add initial --context taskboard.data:AppDbContext
201
+ ```
202
+
203
+ Aplicar migrações:
204
+
205
+ ```bash
206
+ spry migrate apply --database taskboard.db
207
+ ```
208
+
209
+ Executar seed:
210
+
211
+ ```bash
212
+ spry seed --entry taskboard.seed:seed --context taskboard.data:AppDbContext --database taskboard.db
213
+ ```
214
+
215
+ Fluxo completo local:
216
+
217
+ ```bash
218
+ spry migrate add initial --context taskboard.data:AppDbContext
219
+ spry migrate apply --database taskboard.db
220
+ spry seed --entry taskboard.seed:seed --context taskboard.data:AppDbContext --database taskboard.db
221
+ spry run --app taskboard.app:create_app
222
+ ```
223
+
224
+ ## Production
225
+
226
+ ### WSGI server (recommended)
227
+
228
+ A `Application` do Spry é um callable WSGI compatível com qualquer servidor WSGI.
229
+
230
+ ```bash
231
+ # Gunicorn
232
+ pip install gunicorn
233
+ gunicorn taskboard.app:create_app -w 4 -b 0.0.0.0:8000
234
+
235
+ # Waitress (Windows-friendly)
236
+ pip install waitress
237
+ waitress-serve taskboard.app:create_app
238
+ ```
239
+
240
+ ### ASGI server
241
+
242
+ Para ambientes que requerem async, Spry também é um callable ASGI válido.
243
+
244
+ ```bash
245
+ # Uvicorn
246
+ pip install uvicorn
247
+ uvicorn taskboard.app:create_app --host 0.0.0.0 --port 8000 --workers 4
248
+
249
+ # Hypercorn
250
+ pip install hypercorn
251
+ hypercorn taskboard.app:create_app --bind 0.0.0.0:8000 --workers 4
252
+ ```
253
+
254
+ ### Health check
255
+
256
+ Toda aplicação Spry expõe automaticamente `GET /health`:
257
+
258
+ ```bash
259
+ curl http://localhost:8000/health
260
+ # {"status":"ok","version":"0.1.0","uptime_seconds":42}
261
+ ```
262
+
263
+ ### CORS
264
+
265
+ Para consumir a API de um browser SPA, configure CORS:
266
+
267
+ ```python
268
+ builder.add_cors(origins=["https://meuapp.com"])
269
+ # ou para desenvolvimento:
270
+ builder.add_cors(origins=["*"], credentials=False)
271
+ ```
272
+
273
+ ### Security
274
+
275
+ **Secret key:** A configuração `auth.secret_key` é obrigatória em produção. Não use o valor padrão:
276
+
277
+ ```json
278
+ {
279
+ "auth": {
280
+ "secret_key": "substitua-por-uma-chave-forte-aqui",
281
+ "cookie_name": "meuapp_auth"
282
+ }
283
+ }
284
+ ```
285
+
286
+ **Request body limit:** O padrão é 10 MB. Ajuste conforme necessário:
287
+
288
+ ```python
289
+ builder.set_max_body_size(50 * 1024 * 1024) # 50 MB
290
+ ```
291
+
292
+ **Debug mode:** Em produção, desative o debug para não vazar stack traces:
293
+
294
+ ```json
295
+ { "server": { "debug": false } }
296
+ ```
297
+
298
+ Ou programaticamente:
299
+
300
+ ```python
301
+ builder.set_debug(False)
302
+ ```
303
+
304
+ ### Environment config
305
+
306
+ O Spry carrega `appsettings.json` e sobrescreve com variáveis de ambiente prefixadas com `APP__`:
307
+
308
+ ```bash
309
+ APP__database__url=postgresql://usuario:senha@host/db spry run --app app:create_app
310
+ ```
311
+
312
+ ## Troubleshooting
313
+
314
+ ### ModuleNotFoundError ao rodar um projeto gerado
315
+
316
+ Normalmente acontece por um destes motivos:
317
+
318
+ - Você está rodando fora da pasta do projeto e o `PYTHONPATH` não inclui o `src` correto
319
+ - O `--app` não bate com o nome do pacote gerado
320
+
321
+ Exemplo correto:
322
+
323
+ ```bash
324
+ spry run --app taskboard.app:create_app
325
+ ```
326
+
327
+ Se estiver trabalhando com o framework e o app lado a lado:
328
+
329
+ ```powershell
330
+ $env:PYTHONPATH="$PSScriptRoot\..\src;$PSScriptRoot\taskboard\src"
331
+ python -m spry.cli run --app taskboard.app:create_app
332
+ ```
333
+
334
+ ### Controller não responde rota
335
+
336
+ Checklist:
337
+
338
+ - A classe tem `@controller("/prefixo")`
339
+ - O método tem `@get`, `@post`, `@put`, `@patch` ou `@delete`
340
+ - O controller está dentro do pacote da aplicação
341
+ - A rota chamada bate com o prefixo + método
342
+
343
+ ### Payload retorna 422
344
+
345
+ Isso significa que o binding do payload para a dataclass falhou.
346
+
347
+ Cheque:
348
+
349
+ - Campos obrigatórios ausentes
350
+ - Tipos inválidos
351
+ - Nomes de propriedades divergentes do DTO esperado
352
+
353
+ ### MVC não encontra view
354
+
355
+ Cheque:
356
+
357
+ - Se `builder.add_views(...)` foi chamado
358
+ - Se os arquivos existem dentro da pasta `views/`
359
+ - Se o nome passado em `self.view("home/index")` bate com `views/home/index.html`
360
+
361
+ ## Contributing and branch strategy
362
+
363
+ Contribuições são bem-vindas! Leia o [`CONTRIBUTING.md`](CONTRIBUTING.md) para setup, estilo de código e processo de PR.
364
+
365
+ ### Branch naming
366
+
367
+ | Branch | Base | Merge para | Descrição |
368
+ |--------|------|------------|-----------|
369
+ | `feat/*` | `main` | `main` via PR | Nova funcionalidade |
370
+ | `fix/*` | `main` | `main` via PR | Correção de bug |
371
+ | `docs/*` | `main` | `main` via PR | Documentação |
372
+ | `chore/*` | `main` | `main` via PR | Manutenção (CI, dependências) |
373
+
374
+ ### Release flow
375
+
376
+ 1. Crie uma branch `release/vX.Y.Z` a partir de `main`
377
+ 2. Ajuste versão e changelog
378
+ 3. Merge para `main` e crie uma tag `vX.Y.Z`
379
+ 4. O GitHub Release é criado a partir da tag
380
+
381
+ ### CI
382
+
383
+ O workflow de CI roda em todos os PRs para `main` com Python 3.11, 3.12 e 3.13 em Linux, Windows e macOS.
384
+
385
+ ## Repository structure
386
+
387
+ - `src/spry` — núcleo do framework
388
+ - `src/spry/templates/api` — template de API
389
+ - `src/spry/templates/mvc` — template MVC server-side
390
+ - `examples/taskboard` — exemplo de API usando o framework
391
+ - `docs` — site de documentação do framework
392
+ - `tests` — suite de testes
393
+
394
+ ## Developing locally
395
+
396
+ Instale em modo editável:
397
+
398
+ ```bash
399
+ pip install -e .
400
+ pip install -e ".[all]" # dependências opcionais
401
+ ```
402
+
403
+ ### Rodando os exemplos
404
+
405
+ ```bash
406
+ pip install -e .
407
+ cd examples/taskboard
408
+ spy run --app taskboard.app:create_app
409
+ ```
410
+
411
+ ### Rodando os testes
412
+
413
+ ```bash
414
+ python -m unittest discover -s tests
415
+ ```
416
+
417
+ ### Documentação local
418
+
419
+ ```bash
420
+ cd docs
421
+ spry run --app spry_docs.app:create_app --host 127.0.0.1 --port 8010
422
+ ```
423
+
424
+ ## Documentation site
425
+
426
+ O site de documentação fica em `docs/` e cobre guias mais visuais e organizados por assunto.