tequio-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 (215) hide show
  1. tequio_core-0.1.0/.env.example +64 -0
  2. tequio_core-0.1.0/.github/workflows/ci.yml +63 -0
  3. tequio_core-0.1.0/.github/workflows/docs.yml +37 -0
  4. tequio_core-0.1.0/.github/workflows/release.yml +54 -0
  5. tequio_core-0.1.0/.gitignore +32 -0
  6. tequio_core-0.1.0/CHANGELOG.md +112 -0
  7. tequio_core-0.1.0/CODE_OF_CONDUCT.md +58 -0
  8. tequio_core-0.1.0/CONTRIBUTING.md +166 -0
  9. tequio_core-0.1.0/LICENSE +21 -0
  10. tequio_core-0.1.0/PKG-INFO +30 -0
  11. tequio_core-0.1.0/README.md +289 -0
  12. tequio_core-0.1.0/Tests/Core/CeleryApp/test_Dispatch.py +28 -0
  13. tequio_core-0.1.0/Tests/Core/CeleryApp/test_Retry.py +41 -0
  14. tequio_core-0.1.0/Tests/Core/CeleryApp/test_WorkerTaskDiscovery.py +32 -0
  15. tequio_core-0.1.0/Tests/Core/Config/test_BrokerSettings.py +30 -0
  16. tequio_core-0.1.0/Tests/Core/Console/test_CliEntrypoint.py +39 -0
  17. tequio_core-0.1.0/Tests/Core/Console/test_CliErrorBoundary.py +20 -0
  18. tequio_core-0.1.0/Tests/Core/Console/test_Console.py +138 -0
  19. tequio_core-0.1.0/Tests/Core/Console/test_DiscoveryLoud.py +43 -0
  20. tequio_core-0.1.0/Tests/Core/Console/test_FormatCommandList.py +50 -0
  21. tequio_core-0.1.0/Tests/Core/Console/test_JornalLauncher.py +39 -0
  22. tequio_core-0.1.0/Tests/Core/Console/test_LauncherCommands.py +62 -0
  23. tequio_core-0.1.0/Tests/Core/Console/test_MakeWritesToAppDir.py +132 -0
  24. tequio_core-0.1.0/Tests/Core/Console/test_NewCommands.py +57 -0
  25. tequio_core-0.1.0/Tests/Core/Console/test_QueueWorkPool.py +86 -0
  26. tequio_core-0.1.0/Tests/Core/Console/test_Scaffold.py +52 -0
  27. tequio_core-0.1.0/Tests/Core/Console/test_ScheduleRunCommand.py +85 -0
  28. tequio_core-0.1.0/Tests/Core/Cron/test_Schedule.py +99 -0
  29. tequio_core-0.1.0/Tests/Core/Database/test_Factory.py +90 -0
  30. tequio_core-0.1.0/Tests/Core/Database/test_FakerLazy.py +37 -0
  31. tequio_core-0.1.0/Tests/Core/Database/test_Filtering.py +82 -0
  32. tequio_core-0.1.0/Tests/Core/Database/test_Migrations.py +84 -0
  33. tequio_core-0.1.0/Tests/Core/Database/test_Repository.py +179 -0
  34. tequio_core-0.1.0/Tests/Core/Database/test_Seeder.py +46 -0
  35. tequio_core-0.1.0/Tests/Core/Database/test_Session.py +39 -0
  36. tequio_core-0.1.0/Tests/Core/Database/test_Transactional.py +98 -0
  37. tequio_core-0.1.0/Tests/Core/Events/test_EventDispatch.py +93 -0
  38. tequio_core-0.1.0/Tests/Core/Events/test_Observer.py +34 -0
  39. tequio_core-0.1.0/Tests/Core/Jobs/test_Jobs.py +61 -0
  40. tequio_core-0.1.0/Tests/Core/Mail/test_Mail.py +65 -0
  41. tequio_core-0.1.0/Tests/Core/Mail/test_MailDriver.py +48 -0
  42. tequio_core-0.1.0/Tests/Core/Mail/test_Mailable.py +55 -0
  43. tequio_core-0.1.0/Tests/Core/Mail/test_Mailer.py +173 -0
  44. tequio_core-0.1.0/Tests/Core/Mail/test_MailerDispatch.py +50 -0
  45. tequio_core-0.1.0/Tests/Core/Mail/test_Tasks.py +163 -0
  46. tequio_core-0.1.0/Tests/Core/Mediator/test_Mediator.py +54 -0
  47. tequio_core-0.1.0/Tests/Core/Pipeline/test_Pipeline.py +43 -0
  48. tequio_core-0.1.0/Tests/Core/Registry/test_Registry.py +126 -0
  49. tequio_core-0.1.0/Tests/Core/Translate/test_I18n.py +164 -0
  50. tequio_core-0.1.0/Tests/Core/View/test_TemplateEngine.py +115 -0
  51. tequio_core-0.1.0/Tests/Core/test_Clock.py +20 -0
  52. tequio_core-0.1.0/Tests/Core/test_Cron.py +57 -0
  53. tequio_core-0.1.0/Tests/Core/test_FrameworkIsGeneric.py +36 -0
  54. tequio_core-0.1.0/Tests/Core/test_FreeLayoutDiscovery.py +97 -0
  55. tequio_core-0.1.0/Tests/Core/test_ModelDiscovery.py +20 -0
  56. tequio_core-0.1.0/Tests/Modules/Demo/test_DemoBuildingBlocks.py +130 -0
  57. tequio_core-0.1.0/Tests/Modules/Demo/test_DemoFlows.py +156 -0
  58. tequio_core-0.1.0/Tests/conftest.py +27 -0
  59. tequio_core-0.1.0/docker-compose.yml +45 -0
  60. tequio_core-0.1.0/documentation/01-introduccion.md +120 -0
  61. tequio_core-0.1.0/documentation/02-instalacion.md +125 -0
  62. tequio_core-0.1.0/documentation/03-configuracion.md +154 -0
  63. tequio_core-0.1.0/documentation/04-estructura-directorios.md +145 -0
  64. tequio_core-0.1.0/documentation/05-ciclo-de-vida.md +150 -0
  65. tequio_core-0.1.0/documentation/06-monolito-modular.md +260 -0
  66. tequio_core-0.1.0/documentation/07-consola-jornal.md +250 -0
  67. tequio_core-0.1.0/documentation/08-base-de-datos.md +208 -0
  68. tequio_core-0.1.0/documentation/09-modelos.md +194 -0
  69. tequio_core-0.1.0/documentation/10-repositorios-y-transacciones.md +272 -0
  70. tequio_core-0.1.0/documentation/11-filtrado-y-paginacion.md +293 -0
  71. tequio_core-0.1.0/documentation/12-jobs.md +181 -0
  72. tequio_core-0.1.0/documentation/13-colas-y-tareas.md +187 -0
  73. tequio_core-0.1.0/documentation/14-programacion-cron.md +283 -0
  74. tequio_core-0.1.0/documentation/15-eventos-y-observers.md +256 -0
  75. tequio_core-0.1.0/documentation/16-mediator.md +250 -0
  76. tequio_core-0.1.0/documentation/17-pipeline.md +222 -0
  77. tequio_core-0.1.0/documentation/18-logging.md +104 -0
  78. tequio_core-0.1.0/documentation/19-errores.md +215 -0
  79. tequio_core-0.1.0/documentation/20-correo.md +373 -0
  80. tequio_core-0.1.0/documentation/README.md +85 -0
  81. tequio_core-0.1.0/documentation/stylesheets/extra.css +20 -0
  82. tequio_core-0.1.0/jornal +18 -0
  83. tequio_core-0.1.0/logs/.gitkeep +0 -0
  84. tequio_core-0.1.0/migrations/env.py +50 -0
  85. tequio_core-0.1.0/migrations/script.py.mako +27 -0
  86. tequio_core-0.1.0/migrations/versions/.gitkeep +0 -0
  87. tequio_core-0.1.0/migrations/versions/b1f4notes01_notes.py +41 -0
  88. tequio_core-0.1.0/mkdocs.yml +90 -0
  89. tequio_core-0.1.0/pyproject.toml +151 -0
  90. tequio_core-0.1.0/src/tequio/Console/Commands/__init__.py +0 -0
  91. tequio_core-0.1.0/src/tequio/Console/__init__.py +0 -0
  92. tequio_core-0.1.0/src/tequio/Core/CeleryApp/CeleryApp.py +61 -0
  93. tequio_core-0.1.0/src/tequio/Core/CeleryApp/Dispatch.py +47 -0
  94. tequio_core-0.1.0/src/tequio/Core/CeleryApp/Retry.py +67 -0
  95. tequio_core-0.1.0/src/tequio/Core/CeleryApp/__init__.py +11 -0
  96. tequio_core-0.1.0/src/tequio/Core/Clock/Clock.py +42 -0
  97. tequio_core-0.1.0/src/tequio/Core/Clock/__init__.py +11 -0
  98. tequio_core-0.1.0/src/tequio/Core/Config/Settings.py +156 -0
  99. tequio_core-0.1.0/src/tequio/Core/Config/__init__.py +5 -0
  100. tequio_core-0.1.0/src/tequio/Core/Console/Cli.py +133 -0
  101. tequio_core-0.1.0/src/tequio/Core/Console/Commands/DbCommands.py +27 -0
  102. tequio_core-0.1.0/src/tequio/Core/Console/Commands/MakeCommands.py +327 -0
  103. tequio_core-0.1.0/src/tequio/Core/Console/Commands/MigrateCommands.py +72 -0
  104. tequio_core-0.1.0/src/tequio/Core/Console/Commands/QueueWorkCommand.py +59 -0
  105. tequio_core-0.1.0/src/tequio/Core/Console/Commands/ScheduleRunCommand.py +53 -0
  106. tequio_core-0.1.0/src/tequio/Core/Console/Commands/ScheduleWorkCommand.py +43 -0
  107. tequio_core-0.1.0/src/tequio/Core/Console/Commands/SeedCommands.py +33 -0
  108. tequio_core-0.1.0/src/tequio/Core/Console/Commands/__init__.py +7 -0
  109. tequio_core-0.1.0/src/tequio/Core/Console/Console.py +182 -0
  110. tequio_core-0.1.0/src/tequio/Core/Console/Scaffold.py +114 -0
  111. tequio_core-0.1.0/src/tequio/Core/Console/__init__.py +29 -0
  112. tequio_core-0.1.0/src/tequio/Core/Cron/Cron.py +188 -0
  113. tequio_core-0.1.0/src/tequio/Core/Cron/Schedule.py +133 -0
  114. tequio_core-0.1.0/src/tequio/Core/Cron/__init__.py +50 -0
  115. tequio_core-0.1.0/src/tequio/Core/Database/Base.py +25 -0
  116. tequio_core-0.1.0/src/tequio/Core/Database/Factory.py +59 -0
  117. tequio_core-0.1.0/src/tequio/Core/Database/Faker.py +55 -0
  118. tequio_core-0.1.0/src/tequio/Core/Database/Filtering.py +101 -0
  119. tequio_core-0.1.0/src/tequio/Core/Database/Migrations.py +57 -0
  120. tequio_core-0.1.0/src/tequio/Core/Database/Repository.py +192 -0
  121. tequio_core-0.1.0/src/tequio/Core/Database/Seeder.py +32 -0
  122. tequio_core-0.1.0/src/tequio/Core/Database/Session.py +87 -0
  123. tequio_core-0.1.0/src/tequio/Core/Database/SoftDelete.py +25 -0
  124. tequio_core-0.1.0/src/tequio/Core/Database/Timestamp.py +27 -0
  125. tequio_core-0.1.0/src/tequio/Core/Database/Transactional.py +110 -0
  126. tequio_core-0.1.0/src/tequio/Core/Database/__init__.py +27 -0
  127. tequio_core-0.1.0/src/tequio/Core/Discovery.py +50 -0
  128. tequio_core-0.1.0/src/tequio/Core/Errors/Errors.py +101 -0
  129. tequio_core-0.1.0/src/tequio/Core/Errors/__init__.py +22 -0
  130. tequio_core-0.1.0/src/tequio/Core/Events/Dispatch.py +46 -0
  131. tequio_core-0.1.0/src/tequio/Core/Events/Observer.py +43 -0
  132. tequio_core-0.1.0/src/tequio/Core/Events/Tasks.py +54 -0
  133. tequio_core-0.1.0/src/tequio/Core/Events/__init__.py +19 -0
  134. tequio_core-0.1.0/src/tequio/Core/Jobs/Jobs.py +87 -0
  135. tequio_core-0.1.0/src/tequio/Core/Jobs/__init__.py +10 -0
  136. tequio_core-0.1.0/src/tequio/Core/Logging/Logging.py +88 -0
  137. tequio_core-0.1.0/src/tequio/Core/Logging/__init__.py +10 -0
  138. tequio_core-0.1.0/src/tequio/Core/Mail/Mail.py +56 -0
  139. tequio_core-0.1.0/src/tequio/Core/Mail/Mailable.py +91 -0
  140. tequio_core-0.1.0/src/tequio/Core/Mail/Mailer.py +285 -0
  141. tequio_core-0.1.0/src/tequio/Core/Mail/Tasks.py +162 -0
  142. tequio_core-0.1.0/src/tequio/Core/Mail/__init__.py +21 -0
  143. tequio_core-0.1.0/src/tequio/Core/Mediator/Mediator.py +62 -0
  144. tequio_core-0.1.0/src/tequio/Core/Mediator/__init__.py +20 -0
  145. tequio_core-0.1.0/src/tequio/Core/Pipeline/Pipeline.py +60 -0
  146. tequio_core-0.1.0/src/tequio/Core/Pipeline/__init__.py +10 -0
  147. tequio_core-0.1.0/src/tequio/Core/Registry/Registry.py +212 -0
  148. tequio_core-0.1.0/src/tequio/Core/Registry/__init__.py +29 -0
  149. tequio_core-0.1.0/src/tequio/Core/Translate/I18n.py +146 -0
  150. tequio_core-0.1.0/src/tequio/Core/Translate/__init__.py +14 -0
  151. tequio_core-0.1.0/src/tequio/Core/View/TemplateEngine.py +139 -0
  152. tequio_core-0.1.0/src/tequio/Core/View/__init__.py +12 -0
  153. tequio_core-0.1.0/src/tequio/Core/__init__.py +0 -0
  154. tequio_core-0.1.0/src/tequio/Dictionaries/__init__.py +11 -0
  155. tequio_core-0.1.0/src/tequio/Models/Note.py +22 -0
  156. tequio_core-0.1.0/src/tequio/Models/__init__.py +22 -0
  157. tequio_core-0.1.0/src/tequio/Modules/Demo/Commands.py +18 -0
  158. tequio_core-0.1.0/src/tequio/Modules/Demo/Console/Commands/ArchiveNoteCommand.py +24 -0
  159. tequio_core-0.1.0/src/tequio/Modules/Demo/Console/Commands/__init__.py +1 -0
  160. tequio_core-0.1.0/src/tequio/Modules/Demo/Console/__init__.py +1 -0
  161. tequio_core-0.1.0/src/tequio/Modules/Demo/Crons/DailyDigestCron.py +50 -0
  162. tequio_core-0.1.0/src/tequio/Modules/Demo/Crons/__init__.py +0 -0
  163. tequio_core-0.1.0/src/tequio/Modules/Demo/Events.py +23 -0
  164. tequio_core-0.1.0/src/tequio/Modules/Demo/Factories/__init__.py +0 -0
  165. tequio_core-0.1.0/src/tequio/Modules/Demo/Factories/factories.py +23 -0
  166. tequio_core-0.1.0/src/tequio/Modules/Demo/Handlers/ArchiveNoteHandler.py +33 -0
  167. tequio_core-0.1.0/src/tequio/Modules/Demo/Handlers/__init__.py +0 -0
  168. tequio_core-0.1.0/src/tequio/Modules/Demo/Jobs/ExportNotesJob.py +25 -0
  169. tequio_core-0.1.0/src/tequio/Modules/Demo/Jobs/__init__.py +0 -0
  170. tequio_core-0.1.0/src/tequio/Modules/Demo/Mail/DailyDigestMailable.py +68 -0
  171. tequio_core-0.1.0/src/tequio/Modules/Demo/Mail/__init__.py +0 -0
  172. tequio_core-0.1.0/src/tequio/Modules/Demo/Observers/LogNoteCreated.py +30 -0
  173. tequio_core-0.1.0/src/tequio/Modules/Demo/Observers/__init__.py +0 -0
  174. tequio_core-0.1.0/src/tequio/Modules/Demo/Pipes/CleanContent.py +40 -0
  175. tequio_core-0.1.0/src/tequio/Modules/Demo/Pipes/__init__.py +0 -0
  176. tequio_core-0.1.0/src/tequio/Modules/Demo/Repositories/NoteRepository.py +15 -0
  177. tequio_core-0.1.0/src/tequio/Modules/Demo/Repositories/__init__.py +0 -0
  178. tequio_core-0.1.0/src/tequio/Modules/Demo/Resources/Static/logo.png +0 -0
  179. tequio_core-0.1.0/src/tequio/Modules/Demo/Resources/Views/emails/digest.html.j2 +6 -0
  180. tequio_core-0.1.0/src/tequio/Modules/Demo/Seeders/DemoSeeder.py +26 -0
  181. tequio_core-0.1.0/src/tequio/Modules/Demo/Seeders/__init__.py +0 -0
  182. tequio_core-0.1.0/src/tequio/Modules/Demo/Services/NoteService.py +59 -0
  183. tequio_core-0.1.0/src/tequio/Modules/Demo/Services/__init__.py +0 -0
  184. tequio_core-0.1.0/src/tequio/Modules/Demo/__init__.py +19 -0
  185. tequio_core-0.1.0/src/tequio/Modules/__init__.py +0 -0
  186. tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/master.en.yml +14 -0
  187. tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/master.es.yml +15 -0
  188. tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/mastersigned.en.yml +35 -0
  189. tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/mastersigned.es.yml +41 -0
  190. tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/Footer/email_footer.html.j2 +31 -0
  191. tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/Styles/basic.html.j2 +133 -0
  192. tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/master.html.j2 +40 -0
  193. tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/mastersigned.html.j2 +35 -0
  194. tequio_core-0.1.0/src/tequio/__init__.py +0 -0
  195. tequio_core-0.1.0/src/tequio/_skeleton/.env.example.tmpl +62 -0
  196. tequio_core-0.1.0/src/tequio/_skeleton/.gitignore.tmpl +20 -0
  197. tequio_core-0.1.0/src/tequio/_skeleton/README.md.tmpl +41 -0
  198. tequio_core-0.1.0/src/tequio/_skeleton/app/Console/Commands/__init__.py.tmpl +0 -0
  199. tequio_core-0.1.0/src/tequio/_skeleton/app/Console/__init__.py.tmpl +0 -0
  200. tequio_core-0.1.0/src/tequio/_skeleton/app/Models/__init__.py.tmpl +15 -0
  201. tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/Commands/HelloCommand.py.tmpl +18 -0
  202. tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/Commands/__init__.py.tmpl +0 -0
  203. tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/__init__.py.tmpl +0 -0
  204. tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/__init__.py.tmpl +1 -0
  205. tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/__init__.py.tmpl +0 -0
  206. tequio_core-0.1.0/src/tequio/_skeleton/app/__init__.py.tmpl +0 -0
  207. tequio_core-0.1.0/src/tequio/_skeleton/docker-compose.yml.tmpl +25 -0
  208. tequio_core-0.1.0/src/tequio/_skeleton/jornal.tmpl +17 -0
  209. tequio_core-0.1.0/src/tequio/_skeleton/logs/.gitkeep.tmpl +1 -0
  210. tequio_core-0.1.0/src/tequio/_skeleton/migrations/env.py.tmpl +50 -0
  211. tequio_core-0.1.0/src/tequio/_skeleton/migrations/script.py.mako.tmpl +27 -0
  212. tequio_core-0.1.0/src/tequio/_skeleton/migrations/versions/.gitkeep.tmpl +1 -0
  213. tequio_core-0.1.0/src/tequio/_skeleton/pyproject.toml.tmpl +26 -0
  214. tequio_core-0.1.0/src/tequio/migrations/versions/b1f4notes01_notes.py +43 -0
  215. tequio_core-0.1.0/uv.lock +1397 -0
@@ -0,0 +1,64 @@
1
+ # Copia este archivo a .env y ajusta. Estos valores asumen ejecución en DOCKER.
2
+ # Si corres en el HOST (uv run), usa localhost/127.0.0.1 en las URLs.
3
+
4
+ # --- Infraestructura ---
5
+ # Por default sqlite local (zero-config, igual que el skeleton). Para otro motor
6
+ # instala el extra del driver (uv sync --extra mysql) y cambia la URL:
7
+ # DATABASE_URL=mysql+pymysql://user:password@host:3306/database
8
+ # DATABASE_URL=postgresql+psycopg://user:password@host:5432/database
9
+ DATABASE_URL=sqlite:///tequio.db
10
+
11
+ # --- Colas / broker-agnostic ---
12
+ # BROKER_URL: vacío => redis local por default. Solo se usa para lo ENCOLADO (los
13
+ # flujos síncronos no lo tocan). Cambia el transporte sin tocar código:
14
+ # Redis: redis://localhost:6379/0
15
+ # RabbitMQ: amqp://guest:guest@localhost:5672// (docker compose ya lo levanta)
16
+ BROKER_URL=
17
+ # Result backend: OPCIONAL (crons fire-and-forget). Vacío => sin backend.
18
+ RESULT_BACKEND_URL=
19
+ # Store de locks para without_overlapping (redis). Vacío => redis local por default.
20
+ LOCK_URL=
21
+ # Reintentos de tasks ante fallos TRANSITORIOS (backoff exponencial con jitter). Son los
22
+ # DEFAULTS de retry_policy(); se pueden pisar A MANO en código por-task. Solo afectan a
23
+ # tasks que OPTAN por reintentar (autoretry_for), NUNCA a crons. 0 => sin reintentos.
24
+ TASK_MAX_RETRIES=3
25
+ TASK_RETRY_BACKOFF=2 # segundos base del 1er reintento (luego se duplica)
26
+ TASK_RETRY_BACKOFF_MAX=600 # tope del backoff entre reintentos (10 min)
27
+ #
28
+ # CLOUD (EDUCATIVO / EN CONSTRUCCIÓN — basado en docs, NO probado en docker, sin
29
+ # mantenimiento garantizado estos primeros meses; solo de referencia):
30
+ # AWS SQS: BROKER_URL=sqs://<AWS_KEY>:<AWS_SECRET>@ (dep: celery[sqs])
31
+ # Azure SB: BROKER_URL=azureservicebus://<SAS>@<namespace> (dep: azure-servicebus)
32
+ # GCP Pub/Sub: BROKER_URL=gcpubsub://projects/<PROJECT_ID> (dep: celery[gcpubsub])
33
+ # ActiveMQ NO es compatible con Celery (AMQP 1.0). Usa RabbitMQ como MQ.
34
+ # Si se omite, el default es la zona del HOST. IMPORTANTE fijarla explícita: un
35
+ # server suele estar en UTC y quien lo monta puede no ser quien programa.
36
+ TIMEZONE=America/Mexico_City
37
+ APP_ENV=local
38
+ # Nombre del proyecto (el default del framework es genérico "App"; pon el tuyo).
39
+ APP_NAME="My App"
40
+ # Locale de fallback de i18n (correos, API, etc.) cuando no se pasa uno explícito.
41
+ APP_FALLBACK_LOCALE=es
42
+ # Locale de Faker para factories/seeders (datos falsos). Cualquier locale de Faker:
43
+ # es_MX, es_ES, en_US, pt_BR, ... Default: es_MX.
44
+ FAKER_LOCALE=es_MX
45
+
46
+ # --- Logging (Loguru) ---
47
+ LOG_LEVEL=INFO
48
+ # LOG_JSON=true agrega logs/app.jsonl (JSON Lines) para Loki/Grafana.
49
+ LOG_JSON=false
50
+
51
+ # --- Correo ---
52
+ # MAIL_DRIVER: cómo se manda. smtp = SMTP real | log = lo escribe en el log (dev sin
53
+ # SMTP, cross-platform) | null = no-op (lo descarta).
54
+ MAIL_DRIVER=smtp
55
+ # En local apunta a Mailpit (puertos del docker-compose).
56
+ MAILPIT_SMTP_PORT=1025
57
+ MAILPIT_UI_PORT=8025
58
+ MAIL_HOST=<host> # ej. localhost (host) o mailpit (docker)
59
+ MAIL_PORT=1025
60
+ MAIL_USERNAME=
61
+ MAIL_PASSWORD=
62
+ MAIL_ENCRYPTION= # "" sin cifrado (Mailpit) | tls (STARTTLS) | ssl (SMTPS)
63
+ MAIL_FROM_ADDRESS=no-reply@example.com
64
+ MAIL_FROM_NAME="My App"
@@ -0,0 +1,63 @@
1
+ name: CI
2
+
3
+ # Corre los MISMOS guardrails que en local (CONTRIBUTING §Guardrails) en cada push a main y en cada PR.
4
+ # tequio es worker-side: NO hay capa HTTP ni frontend, así que tampoco hay pasos de npm/Vite.
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+
10
+ # Cancela corridas viejas del mismo ref si llega un push nuevo.
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ quality:
17
+ name: Lint + Types + Imports + Tests + Build
18
+ runs-on: ubuntu-latest
19
+ # Sin .env en CI: damos un sqlite dummy explícito (DATABASE_URL ya tiene default en
20
+ # Settings, pero lo dejamos claro). Basta para importar (el engine es perezoso) y los
21
+ # tests son sin BD. Igual lo necesitan lint-imports y pytest (importan tequio.*).
22
+ env:
23
+ DATABASE_URL: "sqlite://"
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v5
27
+
28
+ - name: Instalar uv (con Python 3.14 + caché)
29
+ uses: astral-sh/setup-uv@v8.1.0
30
+ with:
31
+ python-version: "3.14"
32
+ enable-cache: true
33
+
34
+ - name: Sincronizar dependencias (incluye grupo dev; reproducible con el lock)
35
+ run: uv sync --frozen
36
+
37
+ # Los 3 guardrails + tests, en pasos separados para ver de un vistazo cuál falla.
38
+ - name: Ruff format (solo verifica)
39
+ run: uv run ruff format --check .
40
+
41
+ - name: Ruff lint
42
+ run: uv run ruff check .
43
+
44
+ - name: Mypy (estricto)
45
+ run: uv run mypy
46
+
47
+ - name: "Fronteras (import-linter: worker-side + módulos)"
48
+ run: uv run lint-imports
49
+
50
+ - name: Pytest (rápidos, sin BD)
51
+ run: uv run pytest
52
+
53
+ # --- Gate de empaquetado: el wheel se construye en limpio y se puede importar ---
54
+ - name: Build sanity (uv build construye sdist + wheel)
55
+ run: uv build
56
+
57
+ - name: Smoke de instalación (wheel limpio + import + CLI tequio)
58
+ run: |
59
+ uv venv /tmp/tequio-smoke
60
+ uv pip install --python /tmp/tequio-smoke/bin/python dist/*.whl
61
+ /tmp/tequio-smoke/bin/python -c "import tequio; print('tequio OK')"
62
+ # El script `tequio` se instaló en el PATH del venv: que al menos liste sin tronar.
63
+ /tmp/tequio-smoke/bin/tequio list
@@ -0,0 +1,37 @@
1
+ name: Docs
2
+
3
+ # Publica la guía (documentation/) como sitio MkDocs Material en GitHub Pages en cada push a main.
4
+ # Requiere (una sola vez): Settings → Pages → Source = "Deploy from a branch" → rama "gh-pages".
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ paths:
9
+ - "documentation/**"
10
+ - "mkdocs.yml"
11
+ - ".github/workflows/docs.yml"
12
+
13
+ permissions:
14
+ contents: write # gh-deploy publica el sitio en la rama gh-pages
15
+
16
+ concurrency:
17
+ group: docs-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ deploy:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v5
26
+
27
+ - name: Instalar uv (con Python 3.14 + caché)
28
+ uses: astral-sh/setup-uv@v8.1.0
29
+ with:
30
+ python-version: "3.14"
31
+ enable-cache: true
32
+
33
+ - name: Instalar dependencias de docs
34
+ run: uv sync --group docs
35
+
36
+ - name: Construir y publicar en gh-pages
37
+ run: uv run mkdocs gh-deploy --force --no-history
@@ -0,0 +1,54 @@
1
+ name: Release (PyPI)
2
+
3
+ # Publica tequio-core a PyPI cuando empujas un tag de versión (p. ej. `git tag v0.1.0 && git push --tags`).
4
+ # Usa Trusted Publishing (OIDC): NO hay tokens ni secretos — PyPI confía en este workflow.
5
+ # Requisito una sola vez: configurar el "pending publisher" en PyPI para el proyecto tequio-core.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ jobs:
11
+ release:
12
+ name: Build + Publish (Trusted Publishing OIDC)
13
+ runs-on: ubuntu-latest
14
+ # 'environment' protege el publish: puedes exigir aprobación manual en Settings → Environments.
15
+ environment: pypi
16
+ permissions:
17
+ id-token: write # OBLIGATORIO para OIDC (Trusted Publishing, sin tokens)
18
+ contents: read # con un bloque permissions explícito, GitHub recorta el resto a 'none'
19
+ env:
20
+ DATABASE_URL: "sqlite://"
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v5
24
+
25
+ - name: Instalar uv (Python 3.14 + caché)
26
+ uses: astral-sh/setup-uv@v8.1.0
27
+ with:
28
+ python-version: "3.14"
29
+ enable-cache: true
30
+
31
+ - name: Sincronizar dependencias (reproducible con el lock)
32
+ run: uv sync --frozen
33
+
34
+ # --- Los guardrails ANTES de publicar: no se publica algo roto ---
35
+ - name: Ruff format
36
+ run: uv run ruff format --check .
37
+ - name: Ruff lint
38
+ run: uv run ruff check .
39
+ - name: Mypy
40
+ run: uv run mypy
41
+ - name: Import-linter
42
+ run: uv run lint-imports
43
+ - name: Pytest
44
+ run: uv run pytest
45
+
46
+ - name: Construir sdist + wheel
47
+ run: uv build --no-sources
48
+
49
+ # uv detecta el OIDC de GitHub Actions y publica vía Trusted Publishing (sin tokens).
50
+ # (el default es --trusted-publishing automatic; no se pasa flag).
51
+ # Tip primer release: si falla el OIDC, cambia temporalmente a
52
+ # `uv publish --trusted-publishing always` para ver el error exacto.
53
+ - name: Publicar a PyPI
54
+ run: uv publish
@@ -0,0 +1,32 @@
1
+ # Entorno Python
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ *.pyo
6
+
7
+ # Caches de herramientas (se regeneran solas; NO se versionan).
8
+ .import_linter_cache/
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ .mypy_cache/
12
+ .claude/settings.local.json
13
+
14
+ # Secretos y configuración local (NUNCA al repo)
15
+ .env
16
+
17
+ # Logs (no versionamos contenido, sí la carpeta)
18
+ logs/*
19
+ !logs/.gitkeep
20
+
21
+ # BD sqlite local (p. ej. DATABASE_URL=sqlite:///tequio.db)
22
+ *.db
23
+ *.sqlite3
24
+
25
+ # Artefactos de build
26
+ dist/
27
+
28
+ # Sitio de docs generado por `mkdocs build` (se publica con gh-deploy; NO se versiona).
29
+ site/
30
+
31
+ # Artefacto runtime del beat de Celery (shelve que persiste el schedule; se regenera solo)
32
+ celerybeat-schedule*
@@ -0,0 +1,112 @@
1
+ # Changelog
2
+
3
+ Todos los cambios notables de **tequio** se documentan aquí.
4
+
5
+ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y el proyecto usa
6
+ [Versionado Semántico](https://semver.org/lang/es/). En `0.x` la API puede cambiar entre minors.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-06
11
+
12
+ **Nacimiento de tequio**: la extracción **worker-side** de [milpa](https://github.com/calcifux/milpa)
13
+ `0.4.0`. El mismo estilo y el mismo kernel reutilizable (`Core`), pero **sin la capa web** —para
14
+ servicios de Python que NO sirven páginas ni API (daemons, pipelines, monitores, ETLs, cron-jobs)
15
+ y solo necesitan **trabajo en segundo plano + base de datos + consola**, con **correo** que vuelve
16
+ al worker. Un contrato de `import-linter` garantiza que la capa web nunca se vuelva a colar al core.
17
+
18
+ Forma tradicional vs estilo milpa: la forma tradicional arrastra todo el framework web (FastAPI,
19
+ auth, pipeline de assets) aunque el servicio nunca sirva HTTP; estilo milpa el worker baja de peso
20
+ quedándose solo con lo que de verdad usa (Celery + SQLAlchemy + Typer + correo), y el guardrail de
21
+ fronteras lo mantiene así.
22
+
23
+ ### Added
24
+
25
+ #### Lo que vino de milpa 0.4.0 (worker-side)
26
+
27
+ - **Background** — `@job` (on-demand, `.dispatch()`, con `broker_guard` que traduce el broker caído
28
+ en un error accionable) y `@cron_task` (agendado, anti-overlap con lock en Redis y gate por
29
+ `APP_ENV`), separados a propósito (job ≠ cron). Celery broker-agnóstico (`BROKER_URL`).
30
+ - **Patrones estilo milpa** (OPT-IN, auto-descubribles) — `Events`/`Observers` (1:N, transporte
31
+ adaptativo: worker si hay broker, si no síncrono), `Mediator` (command bus 1:1, `@handles`/`send`)
32
+ y `Pipeline` (modelo cebolla). Patrones que un arquitecto puede sugerir, no impuestos.
33
+ - **Datos estilo Spring Data** — `Repository[Model, Id]` tipado, `@transactional`,
34
+ `current_session`, `Factory`/`Seeder` (con Faker), soft-delete y timestamps automáticos; engine
35
+ **agnóstico del motor** (se elige por `DATABASE_URL`, default `sqlite` zero-config), migraciones
36
+ **Alembic** motor-agnósticas.
37
+ - **Errores que NUNCA fallan en silencio** — el CLI rinde un `DomainError` (esperado) como mensaje
38
+ accionable + su código (sin traceback crudo) y deja el traceback completo de un error inesperado
39
+ en el log.
40
+ - **Consola estilo artisan** — kernel Typer con descubrimiento automático de comandos (Core +
41
+ generales del proyecto + módulos): `queue work`, `schedule work`/`run`, `migrate`, `db seed`,
42
+ `make …`, salida en tabla rich.
43
+ - **Logging** — Loguru configurado (stderr conciso sin fuga de valores + archivo rotativo);
44
+ `LOG_JSON=true` agrega `logs/app.jsonl` (JSON Lines) para Loki/Grafana.
45
+ - **Config tipada** — `Settings` (pydantic-settings) lee el `.env`; la infraestructura sin default
46
+ (obligatoria) falla claro si falta.
47
+ - **Reloj inyectable** (`Core/Clock`) — `SystemClock` / `FixedClock` (= `Carbon::setTestNow`) para
48
+ congelar el tiempo en tests, estilo `java.time.Clock` de Spring.
49
+
50
+ #### Lo propio de tequio
51
+
52
+ - **Correo worker-side** (`Core/Mail`) — Mailables estilo Laravel (`Mail.send` / `Mail.queue`),
53
+ drivers (`smtp`/`log`/`null`), adjuntos por bytes o archivo y logo por CID. Vuelve al worker
54
+ porque muchísimos crons y jobs terminan mandando correo (justo el caso del `DailyDigestCron`).
55
+ - **TemplateEngine e i18n de correos worker-side** (`Core/View/TemplateEngine.py`, `Core/Translate`)
56
+ — los correos se renderizan con Jinja2 y se traducen con `i18nice`, todo worker-side; `jinja2` e
57
+ `i18n` quedan permitidos por el contrato de fronteras (lo prohibido es la capa HTTP/Auth/frontend,
58
+ no el correo).
59
+ - **Cola `emails` por convención** — los correos se encolan a la cola `emails`
60
+ (`queue work --queue emails`), el equivalente del `->onQueue('emails')` de Laravel; con
61
+ `MAIL_DRIVER=log` (default dev) el MIME se vuelca al log sin SMTP, y el `docker-compose.yml` trae
62
+ Mailpit para verlos en una UI.
63
+ - **`queue work --pool`** — opción para elegir el pool de Celery (`prefork`/`solo`/`threads`/
64
+ `gevent`); en **Windows**, si se omite, cae a `solo` automáticamente (el prefork de billiard no es
65
+ confiable ahí).
66
+ - **Encarpetado libre con discovery recursivo** — el discovery importa **todo el árbol** de cada
67
+ módulo (`import_submodules(..., recursive=True)`), así organizas tu app como quieras: la pieza se
68
+ descubre mientras lleve su decorador o herede de su base. La única convención con peso es
69
+ `Console/Commands/`. El guardrail `test_FreeLayoutDiscovery` fija esta libertad.
70
+ - **El beat agenda los `@cron_task`** — `collect_beat_schedule()` (en el `Registry`) **fusiona** dos
71
+ fuentes para el calendario del beat: (1) cada `@cron_task(schedule=…)` auto-descubierto —su
72
+ expresión cron convertida a `celery.schedules.crontab`— y (2) los `beat_schedule` declarados en
73
+ `Console/Kernel.py` (vía declarativa, **con precedencia** en colisiones de nombre). Así
74
+ `schedule work` (el beat) agenda los `@cron_task` **sin** escribir un `Kernel.py`. El conversor
75
+ cron-string → `crontab` exige **exactamente 5 campos**; si no, falla con un error claro (no agenda
76
+ mal en silencio). Los gates de ejecución (anti-overlap y `environments`) siguen viviendo en
77
+ `@cron_task` al ejecutar; el beat solo agenda. (Decisión consciente: este comportamiento **diverge
78
+ de milpa**, que solo agendaba lo declarado en `Console/Kernel.py`.)
79
+ - **Demo `Notes`** (`tequio new --demo`) — módulo de referencia corrible (notas, worker-side) que
80
+ ejercita TODO el stack: `@job`, `@cron_task` (un digest diario que **manda correo**),
81
+ eventos→observer (1:N), Mediator (1:1), Pipeline de limpieza y Mailables (i18n de correos), más
82
+ seeders/factories con Faker. Corre sobre SQLite sin levantar infraestructura. La nota es
83
+ deliberadamente mínima (`title`/`body`/`archived`): tequio no tiene Auth ni usuarios (eso vive en
84
+ milpa), así que la nota no tiene dueño.
85
+ - **Scaffolder `tequio new <app> [--demo]`** — genera un proyecto listo para correr desde un
86
+ skeleton embebido (estilo `laravel new`); el paquete se llama `tequio-core` en PyPI pero el import
87
+ y el comando siguen siendo `tequio`. `--demo` materializa el módulo Notes; sin él, un módulo
88
+ `Hello` mínimo.
89
+ - **Tres contratos `import-linter`** — (1) `forbidden`: `tequio.Core` **NO** depende de la capa web
90
+ (fastapi/starlette/uvicorn/slowapi/httpx/itsdangerous/pwdlib/jwt) —el contrato distintivo de
91
+ tequio—; (2) `forbidden`: el shared kernel (`Core`/`Models`/`Dictionaries`) no importa
92
+ `Modules`; (3) `independence`: los módulos no se importan entre sí.
93
+ - **Documentación completa** (mkdocs) — guía estilo Laravel en español: instalación, configuración,
94
+ ciclo de vida (del **worker**, sin request), monolito modular, consola (`jornal`), base de datos,
95
+ background (jobs, colas, cron + el reloj), los patrones estilo milpa, correo, errores de dominio y
96
+ logging. Cada feature contrasta la *forma tradicional* vs *estilo milpa* y se demuestra ejecutable
97
+ en el módulo Demo.
98
+
99
+ ### Excluido (vive en milpa)
100
+
101
+ - **Http/FastAPI, Auth** (RBAC/ABAC, JWT/sesión, Passport), **Views/Vite** (frontend,
102
+ microfrontends, PWA) e **i18n de la UI**. tequio **sí** trae correo (Mailables + i18n de correos)
103
+ porque vuelve al worker. Si tu servicio necesita algo de lo excluido, usa
104
+ [milpa](https://github.com/calcifux/milpa).
105
+
106
+ ### Notas
107
+
108
+ - Todo es **síncrono** (SQLAlchemy + Celery). Los tests corren **sin base de datos** (fakes +
109
+ monkeypatch) y el toolchain de gates es ruff + mypy strict + import-linter + pytest.
110
+
111
+ [Unreleased]: https://github.com/calcifux/tequio-core/compare/v0.1.0...HEAD
112
+ [0.1.0]: https://github.com/calcifux/tequio-core/releases/tag/v0.1.0
@@ -0,0 +1,58 @@
1
+ # Código de Conducta
2
+
3
+ ## Nuestro compromiso
4
+
5
+ Como miembros, contribuyentes y mantenedores, nos comprometemos a hacer de la
6
+ participación en **tequio** una experiencia libre de acoso,
7
+ acogedora y respetuosa para todas las personas, sin importar su origen o
8
+ identidad. Nos comprometemos a actuar e interactuar de formas que construyan una
9
+ comunidad abierta, amable, diversa y sana.
10
+
11
+ ## Nuestros estándares
12
+
13
+ Ejemplos de comportamiento que ayudan a crear un ambiente positivo:
14
+
15
+ - Mostrar empatía y amabilidad hacia los demás
16
+ - Respetar opiniones, puntos de vista y experiencias distintas
17
+ - Dar y aceptar con gracia retroalimentación constructiva
18
+ - Asumir la responsabilidad, disculparse con quienes se vean afectados por
19
+ nuestros errores y aprender de ellos
20
+ - Enfocarse en lo que es mejor para la comunidad en su conjunto
21
+
22
+ El acoso, las conductas abusivas o irrespetuosas, los ataques personales o
23
+ políticos, publicar información privada de terceros sin su consentimiento y
24
+ cualquier otra conducta que una persona razonable consideraría inapropiada en un
25
+ entorno profesional **no se toleran**.
26
+
27
+ ## Estándar adoptado
28
+
29
+ Este proyecto adopta el **[Contributor Covenant, versión 2.1](https://www.contributor-covenant.org/es/version/2/1/code_of_conduct/)**.
30
+ El texto completo —con los estándares detallados y las cuatro guías de aplicación
31
+ (Corrección → Advertencia → Expulsión temporal → Expulsión permanente)— aplica a
32
+ este proyecto. Por favor léelo ahí.
33
+
34
+ ## Alcance
35
+
36
+ Este Código de Conducta aplica en todos los espacios del proyecto (issues, pull
37
+ requests, discusiones, código) y cuando una persona representa oficialmente al
38
+ proyecto en espacios públicos.
39
+
40
+ ## Aplicación
41
+
42
+ Las personas mantenedoras son responsables de aclarar y hacer cumplir estos
43
+ estándares, y pueden eliminar, editar o rechazar contribuciones que no se
44
+ alineen con este Código de Conducta.
45
+
46
+ Las instancias de comportamiento abusivo, de acoso o de cualquier otra forma
47
+ inaceptable pueden reportarse **en privado** a la persona mantenedora
48
+ (`@calcifux`) vía GitHub (abrir un *private security advisory* o contactarla
49
+ directamente).
50
+ <!-- TODO: agrega un email de contacto dedicado si prefieres uno en vez de GitHub. -->
51
+ Todas las quejas serán revisadas e investigadas de forma rápida y justa. Las
52
+ personas mantenedoras están obligadas a respetar la privacidad y la seguridad de
53
+ quien reporta.
54
+
55
+ ## Atribución
56
+
57
+ Este Código de Conducta es una adaptación del
58
+ [Contributor Covenant](https://www.contributor-covenant.org/es/), versión 2.1.
@@ -0,0 +1,166 @@
1
+ # Contribuir a tequio
2
+
3
+ ¡Gracias por tu interés en contribuir! Este documento explica cómo levantar el proyecto
4
+ en local, las convenciones que seguimos y cómo enviar un Pull Request con las mejores
5
+ probabilidades de merge.
6
+
7
+ > Si vas a tocar un subsistema, lee primero su página en
8
+ > [`documentation/`](documentation/README.md): ahí está el "cómo funciona".
9
+
10
+ ## ¿Qué es tequio (y qué no)?
11
+
12
+ tequio es la **extracción worker-side** de [milpa](https://github.com/calcifux/milpa):
13
+ el núcleo de trabajo en segundo plano (jobs, crons, eventos/observers, Mediator, Pipeline,
14
+ BD con SQLAlchemy + Alembic, logging y CLI). **No** trae capa HTTP, Mail, Auth,
15
+ Views/Vite ni i18n: si tu servicio sirve páginas o API, eso vive en
16
+ [milpa](https://github.com/calcifux/milpa), no aquí.
17
+
18
+ ## Inicio rápido
19
+
20
+ ```bash
21
+ git clone https://github.com/calcifux/tequio-core.git
22
+ cd tequio-core
23
+ uv sync # crea el venv y resuelve deps (incluye las de dev)
24
+ cp .env.example .env # ajusta lo que necesites (sqlite ya viene por default)
25
+ uv run pytest # si la suite pasa, ya puedes desarrollar
26
+ ```
27
+
28
+ Con `uv`, antepón `uv run` a los comandos; con el venv activo, omítelo. El launcher de
29
+ consola en la raíz es `jornal` (el mismo nombre que en milpa, a propósito).
30
+
31
+ > **¿Quieres ver algo funcionando ya?** Corre el demo (sqlite, sin infra de BD):
32
+ > ```bash
33
+ > uv run python jornal migrate make -m inicial && uv run python jornal migrate run
34
+ > uv run python jornal db seed # puebla notas con Faker (DemoSeeder)
35
+ > uv run python jornal queue work # arranca el worker (procesa los @job)
36
+ > ```
37
+ > Notes worker-side: jobs/crons/observers que escriben al log, Mediator, Pipeline,
38
+ > seeder con Faker. Para procesar la cola y el beat necesitas un broker (Redis); los
39
+ > flujos síncronos no lo tocan.
40
+
41
+ ## Requisitos
42
+
43
+ - **Python 3.14+**
44
+ - **[uv](https://docs.astral.sh/uv/)** (recomendado) como gestor de entorno y deps
45
+ - **Docker NO** es necesario para los tests: son unitarios y **sin base de datos** (usan
46
+ fakes y monkeypatch, no servicios vivos). Para correr la cola/beat de verdad hace falta
47
+ un broker (Redis), no para contribuir/testear.
48
+ - **Git** con auth SSH o HTTPS a GitHub
49
+
50
+ ## Estructura del repo
51
+
52
+ ```
53
+ src/tequio/
54
+ Core/ ← EL FRAMEWORK (kernel genérico worker-side, reutilizable)
55
+ Models/ ← modelos SQLAlchemy compartidos (auto-discovery)
56
+ Dictionaries/ ← constantes de dominio compartidas
57
+ Modules/ ← los módulos del proyecto (independientes entre sí; Demo es el de referencia)
58
+ Console/ ← commands GENERALES del proyecto (opcional)
59
+ Tests/ ← espeja src/tequio/ 1:1; tests unitarios sin BD
60
+ documentation/ ← guía estilo Laravel (sitio MkDocs Material)
61
+ jornal ← entrypoint de consola en la raíz (el "artisan")
62
+ ```
63
+
64
+ Detalle en [documentation/04-estructura-directorios.md](documentation/04-estructura-directorios.md).
65
+
66
+ ## Las dos fronteras (no las rompas)
67
+
68
+ `import-linter` las fuerza en CI (ver `[tool.importlinter]` en `pyproject.toml`):
69
+
70
+ 1. **tequio es worker-side**: `tequio.Core` **no** importa la capa web ni mail
71
+ (`fastapi`, `starlette`, `uvicorn`, `jinja2`, `jwt`, `i18n`, …). Ese contrato evita
72
+ que la capa HTTP/auth de milpa se vuelva a colar al núcleo.
73
+ 2. **El kernel no depende de los módulos**: `tequio.Core` / `tequio.Models` /
74
+ `tequio.Dictionaries` no importan `tequio.Modules`.
75
+ 3. **Los módulos son independientes entre sí**: `tequio.Modules.A` no importa
76
+ `tequio.Modules.B`.
77
+
78
+ Si dos módulos necesitan compartir algo, ese algo sube al kernel compartido. Ver
79
+ [documentation/06-monolito-modular.md](documentation/06-monolito-modular.md).
80
+
81
+ ## Ramas y commits
82
+
83
+ - `main` siempre verde. No hagas push directo; abre un PR.
84
+ - Ramas de feature: `feat/cron-lock-timeout`, `fix/queue-retry-backoff`, `docs/install-guide`.
85
+ - [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`,
86
+ `refactor:`, `test:`, `ci:`, `chore:`.
87
+
88
+ ## Estilo de código
89
+
90
+ - Identificadores en **inglés**; comentarios y docstrings en **español**, explicando el
91
+ *porqué* (no abreviar).
92
+ - Sin emojis en código/comentarios/docstrings.
93
+ - Tipos completos: `mypy` corre en modo **estricto** sobre `src/tequio` y `Tests/Core`.
94
+ - Un archivo por clase/responsabilidad (estilo Laravel/PascalCase en `src/tequio`).
95
+ - Formato e imports los maneja **Ruff** (no formatees a mano); cada archivo declara
96
+ `from __future__ import annotations` (lo exige y autofixea Ruff).
97
+
98
+ ## Guardrails (lo que valida CI)
99
+
100
+ Antes de abrir el PR, corre todo y déjalo verde. Son los **3 guardrails** (lint, tipos,
101
+ fronteras) + **tests** + **build**:
102
+
103
+ ```bash
104
+ uv run ruff format . # formato | --check solo verifica (modo CI)
105
+ uv run ruff check . # lint | --fix arregla lo auto-arreglable
106
+ uv run mypy # tipos (estricto)
107
+ uv run lint-imports # fronteras (worker-side + módulos)
108
+ uv run pytest # tests (rápidos, sin BD)
109
+ uv build # empaqueta sdist + wheel (no se publica algo que no construye)
110
+ ```
111
+
112
+ Todo de una:
113
+
114
+ ```bash
115
+ uv run ruff format --check . && uv run ruff check . && uv run mypy && uv run lint-imports && uv run pytest && uv build
116
+ ```
117
+
118
+ ## Contribuir al Core
119
+
120
+ tequio es un **framework**: las contribuciones van al **kernel** (`src/tequio/Core`), a sus
121
+ tests (`Tests/Core`) y a la documentación. **No** se trata de crear módulos de negocio
122
+ nuevos — eso lo hace cada proyecto que *usa* tequio, en su propio repo (con `tequio new`).
123
+ `src/tequio/Modules/Demo` es solo el módulo de **referencia/demo** (Notes worker-side):
124
+ tócalo únicamente para demostrar una capacidad del Core, no para meter features de dominio.
125
+
126
+ Al tocar el Core:
127
+
128
+ 1. **Mantén el Core genérico.** Nada de dominio ni de un proyecto en particular (ni
129
+ nombres, ni reglas de negocio): el kernel debe servir igual a cualquier servicio
130
+ worker-side. Si dudas si algo es "del framework" o "de un proyecto", probablemente no
131
+ va en Core.
132
+ 2. **Respeta las fronteras**: `tequio.Core` no importa la capa web/mail ni los módulos (el
133
+ discovery es dinámico, no por imports estáticos). Lo valida `lint-imports`.
134
+ 3. **Espeja la estructura por capas**: cada subsistema vive en `src/tequio/Core/<Subsistema>/`
135
+ con un `__init__.py` que expone su API pública.
136
+ 4. **Agrega tests** en `Tests/Core/...` (sin BD) y, si cambia comportamiento público,
137
+ **documéntalo** en `documentation/`.
138
+
139
+ Para entender un subsistema antes de tocarlo, lee su página en
140
+ [documentation/](documentation/README.md).
141
+
142
+ ## Tests
143
+
144
+ - Espeja `src/tequio/` en `Tests/` (misma ruta). Tests **unitarios y sin BD**: usa fakes y
145
+ monkeypatch, no levantes Redis/Postgres.
146
+ - Corre uno solo: `uv run pytest Tests/Core/Cron/test_Schedule.py -x`.
147
+
148
+ ## Checklist del PR
149
+
150
+ - [ ] `ruff format --check .` y `ruff check .` pasan
151
+ - [ ] `mypy` pasa (estricto)
152
+ - [ ] `lint-imports` pasa (no rompiste las fronteras)
153
+ - [ ] `pytest` pasa
154
+ - [ ] `uv build` construye sdist + wheel
155
+ - [ ] Subject en Conventional Commits
156
+ - [ ] Sin datos personales, secretos ni credenciales
157
+ - [ ] Si cambió comportamiento público, actualizaste `documentation/`
158
+
159
+ ## Código de Conducta
160
+
161
+ Este proyecto adopta el [Código de Conducta](CODE_OF_CONDUCT.md) (Contributor Covenant
162
+ 2.1). Al participar, aceptas cumplirlo.
163
+
164
+ ## Licencia
165
+
166
+ Al contribuir aceptas que tu aporte queda bajo la [Licencia MIT](LICENSE).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 @Calcifux (Carlos Guillermo Reyes Ramiro)
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,30 @@
1
+ Metadata-Version: 2.4
2
+ Name: tequio-core
3
+ Version: 0.1.0
4
+ Summary: Núcleo de trabajo en segundo plano estilo milpa: jobs, crons, eventos y BD (Celery + SQLAlchemy + Typer) sin capa HTTP.
5
+ Author: @Calcifux (Carlos Guillermo Reyes Ramiro)
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.14
9
+ Requires-Dist: alembic>=1.18.4
10
+ Requires-Dist: celery[redis]>=5.6.3
11
+ Requires-Dist: croniter>=6.2.2
12
+ Requires-Dist: i18nice[yaml]>=0.16
13
+ Requires-Dist: jinja2>=3.1.4
14
+ Requires-Dist: loguru>=0.7
15
+ Requires-Dist: pydantic-settings>=2.14.1
16
+ Requires-Dist: redis>=6.4.0
17
+ Requires-Dist: rich>=15.0.0
18
+ Requires-Dist: sqlalchemy-easy-softdelete>=0.9.0
19
+ Requires-Dist: sqlalchemy>=2.0.50
20
+ Requires-Dist: typer>=0.26.2
21
+ Requires-Dist: tzdata>=2026.2
22
+ Requires-Dist: tzlocal>=5.3.1
23
+ Provides-Extra: mssql
24
+ Requires-Dist: pyodbc>=5.2; extra == 'mssql'
25
+ Provides-Extra: mysql
26
+ Requires-Dist: pymysql>=1.2.0; extra == 'mysql'
27
+ Provides-Extra: oracle
28
+ Requires-Dist: oracledb>=2.5; extra == 'oracle'
29
+ Provides-Extra: postgres
30
+ Requires-Dist: psycopg[binary]>=3.2; extra == 'postgres'