kstlib 3.2.0__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. {kstlib-3.2.0/src/kstlib.egg-info → kstlib-3.3.0}/PKG-INFO +1 -1
  2. kstlib-3.3.0/src/kstlib/_shared/logging_helpers.py +40 -0
  3. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cache/strategies.py +4 -10
  4. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/rapi/call.py +52 -14
  5. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/config/sops.py +3 -13
  6. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/database.py +2 -8
  7. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/kstlib.conf.yml +4 -0
  8. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/logging/manager.py +11 -2
  9. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/meta.py +1 -1
  10. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/rapi/client.py +59 -57
  11. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/rapi/config.py +14 -16
  12. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/chain.py +5 -9
  13. {kstlib-3.2.0 → kstlib-3.3.0/src/kstlib.egg-info}/PKG-INFO +1 -1
  14. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib.egg-info/SOURCES.txt +1 -0
  15. {kstlib-3.2.0 → kstlib-3.3.0}/LICENSE.md +0 -0
  16. {kstlib-3.2.0 → kstlib-3.3.0}/MANIFEST.in +0 -0
  17. {kstlib-3.2.0 → kstlib-3.3.0}/README.md +0 -0
  18. {kstlib-3.2.0 → kstlib-3.3.0}/pyproject.toml +0 -0
  19. {kstlib-3.2.0 → kstlib-3.3.0}/setup.cfg +0 -0
  20. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/__init__.py +0 -0
  21. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/__main__.py +0 -0
  22. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/_shared/__init__.py +0 -0
  23. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/_shared/jinja.py +0 -0
  24. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/_shared/redaction.py +0 -0
  25. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/__init__.py +0 -0
  26. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/channels/__init__.py +0 -0
  27. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/channels/base.py +0 -0
  28. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/channels/email.py +0 -0
  29. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/channels/slack.py +0 -0
  30. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/exceptions.py +0 -0
  31. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/manager.py +0 -0
  32. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/models.py +0 -0
  33. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/alerts/throttle.py +0 -0
  34. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/__init__.py +0 -0
  35. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/callback.py +0 -0
  36. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/check.py +0 -0
  37. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/config.py +0 -0
  38. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/errors.py +0 -0
  39. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/models.py +0 -0
  40. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/providers/__init__.py +0 -0
  41. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/providers/base.py +0 -0
  42. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/providers/oauth2.py +0 -0
  43. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/providers/oidc.py +0 -0
  44. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/session.py +0 -0
  45. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/auth/token.py +0 -0
  46. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cache/__init__.py +0 -0
  47. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cache/decorator.py +0 -0
  48. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/__init__.py +0 -0
  49. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/app.py +0 -0
  50. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/__init__.py +0 -0
  51. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/__init__.py +0 -0
  52. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/check.py +0 -0
  53. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/common.py +0 -0
  54. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/login.py +0 -0
  55. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/logout.py +0 -0
  56. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/providers.py +0 -0
  57. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/status.py +0 -0
  58. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/token.py +0 -0
  59. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/auth/whoami.py +0 -0
  60. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/config.py +0 -0
  61. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/__init__.py +0 -0
  62. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/attach.py +0 -0
  63. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/common.py +0 -0
  64. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
  65. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/logs.py +0 -0
  66. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/start.py +0 -0
  67. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/status.py +0 -0
  68. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/ops/stop.py +0 -0
  69. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
  70. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/rapi/list.py +0 -0
  71. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/rapi/show.py +0 -0
  72. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
  73. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/common.py +0 -0
  74. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
  75. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
  76. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
  77. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/commands/secrets/shred.py +0 -0
  78. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/cli/common.py +0 -0
  79. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/config/__init__.py +0 -0
  80. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/config/exceptions.py +0 -0
  81. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/config/export.py +0 -0
  82. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/config/loader.py +0 -0
  83. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/__init__.py +0 -0
  84. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/aiosqlcipher.py +0 -0
  85. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/cipher.py +0 -0
  86. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/exceptions.py +0 -0
  87. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/db/pool.py +0 -0
  88. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/helpers/__init__.py +0 -0
  89. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/helpers/exceptions.py +0 -0
  90. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/helpers/time_trigger.py +0 -0
  91. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/limits.py +0 -0
  92. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/logging/__init__.py +0 -0
  93. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/__init__.py +0 -0
  94. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/_helpers.py +0 -0
  95. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/builder.py +0 -0
  96. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/collector.py +0 -0
  97. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/exceptions.py +0 -0
  98. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/filesystem.py +0 -0
  99. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/throttle.py +0 -0
  100. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transport.py +0 -0
  101. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transports/__init__.py +0 -0
  102. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transports/gmail.py +0 -0
  103. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transports/resend.py +0 -0
  104. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transports/ses.py +0 -0
  105. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/mail/transports/smtp.py +0 -0
  106. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/metrics/__init__.py +0 -0
  107. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/metrics/decorators.py +0 -0
  108. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/metrics/exceptions.py +0 -0
  109. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/__init__.py +0 -0
  110. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/_styles.py +0 -0
  111. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/cell.py +0 -0
  112. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/config.py +0 -0
  113. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/delivery.py +0 -0
  114. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/exceptions.py +0 -0
  115. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/image.py +0 -0
  116. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/kv.py +0 -0
  117. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/list.py +0 -0
  118. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/metric.py +0 -0
  119. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/monitoring.py +0 -0
  120. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/renderer.py +0 -0
  121. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/service.py +0 -0
  122. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/table.py +0 -0
  123. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/monitoring/types.py +0 -0
  124. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/__init__.py +0 -0
  125. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/base.py +0 -0
  126. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/container.py +0 -0
  127. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/exceptions.py +0 -0
  128. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/manager.py +0 -0
  129. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/models.py +0 -0
  130. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/tmux.py +0 -0
  131. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ops/validators.py +0 -0
  132. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/__init__.py +0 -0
  133. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/base.py +0 -0
  134. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/exceptions.py +0 -0
  135. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/models.py +0 -0
  136. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/runner.py +0 -0
  137. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/__init__.py +0 -0
  138. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/_base.py +0 -0
  139. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/_helpers.py +0 -0
  140. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/callable.py +0 -0
  141. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/python.py +0 -0
  142. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/steps/shell.py +0 -0
  143. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/pipeline/validators.py +0 -0
  144. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/py.typed +0 -0
  145. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/rapi/__init__.py +0 -0
  146. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/rapi/credentials.py +0 -0
  147. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/rapi/exceptions.py +0 -0
  148. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/__init__.py +0 -0
  149. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/circuit_breaker.py +0 -0
  150. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/exceptions.py +0 -0
  151. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/heartbeat.py +0 -0
  152. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/rate_limiter.py +0 -0
  153. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/shutdown.py +0 -0
  154. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/resilience/watchdog.py +0 -0
  155. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/__init__.py +0 -0
  156. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/exceptions.py +0 -0
  157. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/models.py +0 -0
  158. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/__init__.py +0 -0
  159. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/base.py +0 -0
  160. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/environment.py +0 -0
  161. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/keyring.py +0 -0
  162. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/kms.py +0 -0
  163. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/kwargs.py +0 -0
  164. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/providers/sops.py +0 -0
  165. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/resolver.py +0 -0
  166. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secrets/sensitive.py +0 -0
  167. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secure/__init__.py +0 -0
  168. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secure/fs.py +0 -0
  169. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secure/passwords.py +0 -0
  170. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/secure/permissions.py +0 -0
  171. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ssl.py +0 -0
  172. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/__init__.py +0 -0
  173. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/config.py +0 -0
  174. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/exceptions.py +0 -0
  175. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/primitives.py +0 -0
  176. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/transform/validators.py +0 -0
  177. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ui/__init__.py +0 -0
  178. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ui/exceptions.py +0 -0
  179. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ui/panels.py +0 -0
  180. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ui/spinner.py +0 -0
  181. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/ui/tables.py +0 -0
  182. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/__init__.py +0 -0
  183. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/dict.py +0 -0
  184. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/formatting.py +0 -0
  185. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/http_trace.py +0 -0
  186. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/lazy.py +0 -0
  187. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/secure_delete.py +0 -0
  188. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/serialization.py +0 -0
  189. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/text.py +0 -0
  190. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/utils/validators.py +0 -0
  191. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/websocket/__init__.py +0 -0
  192. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/websocket/exceptions.py +0 -0
  193. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/websocket/manager.py +0 -0
  194. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib/websocket/models.py +0 -0
  195. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib.egg-info/dependency_links.txt +0 -0
  196. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib.egg-info/entry_points.txt +0 -0
  197. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib.egg-info/requires.txt +0 -0
  198. {kstlib-3.2.0 → kstlib-3.3.0}/src/kstlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kstlib
3
- Version: 3.2.0
3
+ Version: 3.3.0
4
4
  Summary: Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)
5
5
  Author-email: Michel TRUONG <michel.truong@gmail.com>
6
6
  Maintainer-email: Michel TRUONG <michel.truong@gmail.com>
@@ -0,0 +1,40 @@
1
+ """Shared TRACE-level logging helper (private to kstlib internals).
2
+
3
+ Consolidates the ``_log_trace`` helper that was previously duplicated across
4
+ several sub-packages into a single shared emitter. Modules pass their own
5
+ logger so emission stays attributed to the calling module's name.
6
+
7
+ This module must never import ``kstlib.logging`` at top level: see
8
+ :func:`log_trace` for the circular-import rationale.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ import logging
17
+
18
+ __all__ = ["log_trace"]
19
+
20
+
21
+ def log_trace(logger: logging.Logger, msg: str, *args: object) -> None:
22
+ """Emit a TRACE-level (custom level 5) record on the given logger.
23
+
24
+ Args:
25
+ logger: Target standard-library logger to emit the record on.
26
+ msg: Log message, optionally containing ``%``-style placeholders.
27
+ *args: Positional arguments interpolated into ``msg`` lazily by the
28
+ logging framework (only when the record is actually emitted).
29
+
30
+ Note:
31
+ ``TRACE_LEVEL`` is imported lazily inside the function to avoid the
32
+ circular import chain
33
+ ``kstlib.logging.manager -> kstlib.config -> kstlib.config.sops``.
34
+ Importing ``kstlib.logging`` at module top level would resurrect that
35
+ cycle as soon as a cascade module (e.g. ``kstlib.config.sops``) imports
36
+ this helper.
37
+ """
38
+ from kstlib.logging import TRACE_LEVEL
39
+
40
+ logger.log(TRACE_LEVEL, msg, *args)
@@ -29,19 +29,13 @@ from collections.abc import Callable
29
29
  from pathlib import Path
30
30
  from typing import Any, TypeVar, cast
31
31
 
32
+ from kstlib._shared.logging_helpers import log_trace
32
33
  from kstlib.limits import CacheLimits, get_cache_limits
33
34
  from kstlib.utils.formatting import format_bytes
34
35
 
35
36
  logger = logging.getLogger(__name__)
36
37
 
37
38
 
38
- def _log_trace(msg: str, *args: object) -> None:
39
- """Log at TRACE level (custom level 5, below DEBUG)."""
40
- from kstlib.logging import TRACE_LEVEL
41
-
42
- logger.log(TRACE_LEVEL, msg, *args)
43
-
44
-
45
39
  _CACHE_FORMAT_VERSION = "kstlib:file-cache:v1"
46
40
  _SUPPORTED_SERIALIZERS: set[str] = {"json", "pickle", "auto"}
47
41
  _PICKLE_SAFE_BUILTINS: set[str] = {
@@ -190,7 +184,7 @@ class TTLCacheStrategy(CacheStrategy):
190
184
  if key not in self._cache:
191
185
  # The key is the SHA256 hash from make_key; logging it is safe
192
186
  # by construction (never the function args themselves).
193
- _log_trace("[CACHE] TTL miss: key=%s", key)
187
+ log_trace(logger, "[CACHE] TTL miss: key=%s", key)
194
188
  return None
195
189
 
196
190
  value, expiry = self._cache[key]
@@ -198,10 +192,10 @@ class TTLCacheStrategy(CacheStrategy):
198
192
  # Check expiration
199
193
  if time.time() > expiry:
200
194
  del self._cache[key]
201
- _log_trace("[CACHE] TTL miss (expired): key=%s", key)
195
+ log_trace(logger, "[CACHE] TTL miss (expired): key=%s", key)
202
196
  return None
203
197
 
204
- _log_trace("[CACHE] TTL hit: key=%s", key)
198
+ log_trace(logger, "[CACHE] TTL hit: key=%s", key)
205
199
  return value
206
200
 
207
201
  def set(self, key: str, value: Any) -> None:
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import re
6
7
  from typing import TYPE_CHECKING, Annotated, Any, cast
7
8
 
8
9
  import typer
@@ -530,19 +531,44 @@ def _emit_extracted_value(
530
531
  print(content)
531
532
 
532
533
 
533
- def _resolve_show_extracted(response: RapiResponse, key: str) -> tuple[Any, str | None]:
534
+ def _split_extracted_keys(spec: str) -> list[str]:
535
+ """Split a ``--show-extracted`` spec on commas/whitespace, dropping empties.
536
+
537
+ Examples:
538
+ >>> _split_extracted_keys("v1,v2")
539
+ ['v1', 'v2']
540
+ >>> _split_extracted_keys("v1 v2")
541
+ ['v1', 'v2']
542
+ >>> _split_extracted_keys("v1, ,v2")
543
+ ['v1', 'v2']
544
+ >>> _split_extracted_keys("")
545
+ []
546
+
547
+ """
548
+ return [key for key in re.split(r"[,\s]+", spec) if key]
549
+
550
+
551
+ def _resolve_show_extracted(response: RapiResponse, spec: str) -> tuple[Any, str | None]:
534
552
  """Resolve the ``--show-extracted`` value and its empty-policy hint.
535
553
 
536
- The policy frontier is declared vs not declared: an endpoint without an
537
- ``extract:`` directive or an unknown key is a usage failure (exit 1 via
538
- the hint), while a declared key holding an empty collection is a
539
- legitimate result. A declared key that evaluated to None (expression
540
- matched nothing) also fails, mirroring ``--pick``. Hints name keys only,
541
- never extracted values.
554
+ The failure semantics follow the output form:
555
+
556
+ - No ``spec`` (bare flag): the whole ``extracted`` mapping is returned as a
557
+ dict (exit 0; a missing ``extract:`` directive still fails).
558
+ - A single key: the raw value is returned and a None value fails (exit 1),
559
+ mirroring ``--pick``. This keeps backward compatibility with v3.2.0
560
+ scripts that read one key.
561
+ - Several keys (comma/space-separated): a JSON object subset is returned
562
+ (exit 0). A declared key whose expression matched nothing appears as
563
+ ``null``; only an unknown key fails.
564
+
565
+ An endpoint without an ``extract:`` directive, or an unknown key, is a
566
+ usage failure (exit 1 via the hint). Hints name keys only, never values.
542
567
 
543
568
  Args:
544
569
  response: The API response whose ``extracted`` mapping is read.
545
- key: The requested key, or an empty string to select all keys.
570
+ spec: The requested keys (comma/space-separated), or an empty string
571
+ to select all keys.
546
572
 
547
573
  Returns:
548
574
  Tuple of (value to emit, empty hint). The hint is consumed by
@@ -552,12 +578,22 @@ def _resolve_show_extracted(response: RapiResponse, key: str) -> tuple[Any, str
552
578
  extracted: Mapping[str, Any] = response.extracted
553
579
  if not extracted:
554
580
  return None, "No extract: directive declared for this endpoint."
555
- if not key:
581
+ keys = _split_extracted_keys(spec)
582
+ if not keys:
556
583
  return dict(extracted), None
557
- if key not in extracted:
584
+ if len(keys) == 1:
585
+ key = keys[0]
586
+ if key not in extracted:
587
+ available = ", ".join(sorted(extracted))
588
+ return None, f"No extracted key '{key}'. Available: {available}."
589
+ return extracted.get(key), f"Extracted key '{key}' matched nothing."
590
+ missing = [key for key in keys if key not in extracted]
591
+ if missing:
558
592
  available = ", ".join(sorted(extracted))
559
- return None, f"No extracted key '{key}'. Available: {available}."
560
- return extracted.get(key), f"Extracted key '{key}' matched nothing."
593
+ noun = "key" if len(missing) == 1 else "keys"
594
+ names = ", ".join(f"'{key}'" for key in missing)
595
+ return None, f"No extracted {noun} {names}. Available: {available}."
596
+ return {key: extracted[key] for key in keys}, None
561
597
 
562
598
 
563
599
  def _handle_extraction_output(
@@ -811,10 +847,12 @@ def call(
811
847
  str | None,
812
848
  typer.Option(
813
849
  "--show-extracted",
814
- metavar="[KEY]",
850
+ metavar="[KEY[,KEY...]]",
815
851
  help=(
816
852
  "Print values declared by the endpoint extract: directive. "
817
- "With KEY, print that key only; without, print all extracted keys as JSON."
853
+ "One KEY prints that value; several comma/space-separated keys "
854
+ "print a JSON object subset; without a value, print all keys as JSON. "
855
+ 'Quote keys with spaces ("v1 v2") or join with commas (v1,v2) so the shell does not split them.'
818
856
  ),
819
857
  ),
820
858
  ] = None,
@@ -28,6 +28,7 @@ import subprocess
28
28
  from collections import OrderedDict
29
29
  from typing import Any
30
30
 
31
+ from kstlib._shared.logging_helpers import log_trace
31
32
  from kstlib.config.exceptions import ConfigSopsError, ConfigSopsNotAvailableError
32
33
  from kstlib.limits import (
33
34
  DEFAULT_MAX_SOPS_CACHE_ENTRIES,
@@ -37,17 +38,6 @@ from kstlib.limits import (
37
38
  logger = logging.getLogger(__name__)
38
39
 
39
40
 
40
- def _log_trace(msg: str, *args: object) -> None:
41
- """Log at TRACE level (custom level 5, below DEBUG).
42
-
43
- Uses a lazy import to avoid the circular import chain
44
- ``kstlib.logging.manager -> kstlib.config -> kstlib.config.sops``.
45
- """
46
- from kstlib.logging import TRACE_LEVEL
47
-
48
- logger.log(TRACE_LEVEL, msg, *args)
49
-
50
-
51
41
  SOPS_FILE_PATTERNS: tuple[str, ...] = (
52
42
  ".sops.yml",
53
43
  ".sops.yaml",
@@ -217,7 +207,7 @@ class SopsDecryptor:
217
207
  cached = self._cache.get(resolved)
218
208
  if cached and cached[0] == mtime:
219
209
  self._cache.move_to_end(resolved)
220
- _log_trace("SOPS cache hit for: %s", path.name)
210
+ log_trace(logger, "SOPS cache hit for: %s", path.name)
221
211
  return cached[1]
222
212
 
223
213
  # Security: reject absolute/relative paths to prevent binary override via config
@@ -269,7 +259,7 @@ class SopsDecryptor:
269
259
  else:
270
260
  removed = self._cache.pop(path.resolve(), None)
271
261
  if removed:
272
- _log_trace("SOPS cache entry removed: %s", path.name)
262
+ log_trace(logger, "SOPS cache entry removed: %s", path.name)
273
263
 
274
264
  @property
275
265
  def cache_size(self) -> int:
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING, Any, cast
17
17
 
18
18
  from typing_extensions import Self
19
19
 
20
+ from kstlib._shared.logging_helpers import log_trace
20
21
  from kstlib.db.exceptions import TransactionError
21
22
  from kstlib.db.pool import ConnectionPool, PoolStats
22
23
 
@@ -35,13 +36,6 @@ log = logging.getLogger(__name__)
35
36
  _SQL_TRACE_TRUNCATE = 200
36
37
 
37
38
 
38
- def _log_trace(msg: str, *args: object) -> None:
39
- """Log at TRACE level (custom level 5, below DEBUG)."""
40
- from kstlib.logging import TRACE_LEVEL
41
-
42
- log.log(TRACE_LEVEL, msg, *args)
43
-
44
-
45
39
  def _truncate_sql(sql: str, limit: int = _SQL_TRACE_TRUNCATE) -> str:
46
40
  """Return ``sql`` truncated to ``limit`` characters, single-line."""
47
41
  flat = " ".join(sql.split())
@@ -254,7 +248,7 @@ class AsyncDatabase:
254
248
  # Sanitization invariant : log the truncated SQL only, NEVER the
255
249
  # parameters tuple (would leak PII / credentials in WHERE/INSERT
256
250
  # values). Truncation prevents giant DDL from drowning the trace.
257
- _log_trace("[DB] Execute: %s (params=%s)", _truncate_sql(sql), "yes" if parameters else "no")
251
+ log_trace(log, "[DB] Execute: %s (params=%s)", _truncate_sql(sql), "yes" if parameters else "no")
258
252
  pool = self._ensure_pool()
259
253
  async with pool.connection() as conn:
260
254
  if parameters:
@@ -175,6 +175,10 @@ logger:
175
175
  format: "::: PID %(process)d / TID %(thread)d ::: %(message)s"
176
176
  show_path: true
177
177
  tracebacks_show_locals: true
178
+ # Console routing: stdout (default) | stderr. Route to stderr when the CLI
179
+ # emits data/JSON on stdout, so logs do not corrupt the piped output
180
+ # (Unix data vs diagnostics separation). Invalid values raise ConfigError.
181
+ stream: stdout
178
182
 
179
183
  # File handler settings
180
184
  # Two configuration styles are supported:
@@ -49,7 +49,7 @@ from rich.logging import RichHandler
49
49
  from rich.theme import Theme
50
50
  from rich.traceback import Traceback
51
51
 
52
- from kstlib.config import get_config
52
+ from kstlib.config import ConfigError, get_config
53
53
 
54
54
  # =============================================================================
55
55
  # HARDCODED LIMITS (Deep Defense)
@@ -253,6 +253,7 @@ FALLBACK_DEFAULTS = {
253
253
  "format": "::: PID %(process)d / TID %(thread)d ::: %(message)s",
254
254
  "show_path": True,
255
255
  "tracebacks_show_locals": False,
256
+ "stream": "stdout",
256
257
  },
257
258
  "file": {
258
259
  "level": "DEBUG",
@@ -340,7 +341,15 @@ class LogManager(logging.Logger):
340
341
  # Setup console and theme
341
342
  self.width = shutil.get_terminal_size(fallback=(120, 30)).columns
342
343
  theme = self._create_theme()
343
- self.console = Console(theme=theme, width=self.width)
344
+ # Route console output to stdout (default) or stderr. Routing to stderr
345
+ # keeps stdout clean for CLIs that emit data/JSON there (Unix data vs
346
+ # diagnostics separation). Fail fast on a typo so a misconfigured value
347
+ # never silently falls back to stdout and corrupts piped data.
348
+ stream = self._config.console.get("stream", "stdout")
349
+ if stream not in ("stdout", "stderr"):
350
+ raise ConfigError(f"Invalid console.stream {stream!r}: expected 'stdout' or 'stderr'")
351
+ _internal_log.debug("[INIT] console stream resolved to %r", stream)
352
+ self.console = Console(theme=theme, width=self.width, stderr=(stream == "stderr"))
344
353
 
345
354
  # Setup handlers
346
355
  self._setup_handlers()
@@ -39,7 +39,7 @@ __logo__ = (
39
39
  )
40
40
 
41
41
  __app_name__ = "kstlib"
42
- __version__ = "3.2.0"
42
+ __version__ = "3.3.0"
43
43
  __description__ = (
44
44
  "Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)"
45
45
  )