splent-cli 1.29.0__tar.gz → 1.29.1__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 (201) hide show
  1. {splent_cli-1.29.0/src/splent_cli.egg-info → splent_cli-1.29.1}/PKG-INFO +1 -1
  2. {splent_cli-1.29.0 → splent_cli-1.29.1}/pyproject.toml +1 -1
  3. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_release.py +96 -1
  4. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_release.py +70 -41
  5. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace.py +69 -1
  6. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release.py +54 -4
  7. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/toml_edit.py +48 -0
  8. {splent_cli-1.29.0 → splent_cli-1.29.1/src/splent_cli.egg-info}/PKG-INFO +1 -1
  9. {splent_cli-1.29.0 → splent_cli-1.29.1}/LICENSE +0 -0
  10. {splent_cli-1.29.0 → splent_cli-1.29.1}/README.md +0 -0
  11. {splent_cli-1.29.0 → splent_cli-1.29.1}/setup.cfg +0 -0
  12. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/__init__.py +0 -0
  13. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/__main__.py +0 -0
  14. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/cli.py +0 -0
  15. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/__init__.py +0 -0
  16. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/__init__.py +0 -0
  17. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  18. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  19. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  20. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  21. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_size.py +0 -0
  22. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_status.py +0 -0
  23. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  24. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  25. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/__init__.py +0 -0
  26. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_contracts.py +0 -0
  27. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_deps.py +0 -0
  28. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_docker.py +0 -0
  29. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_env.py +0 -0
  30. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_features.py +0 -0
  31. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_github.py +0 -0
  32. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_infra.py +0 -0
  33. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_product.py +0 -0
  34. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_pypi.py +0 -0
  35. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  36. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_releases.py +0 -0
  37. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/__init__.py +0 -0
  38. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_build.py +0 -0
  39. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_log.py +0 -0
  40. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  41. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/command/__init__.py +0 -0
  42. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/command/command_create.py +0 -0
  43. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/coverage.py +0 -0
  44. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_console.py +0 -0
  45. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_dump.py +0 -0
  46. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_migrate.py +0 -0
  47. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_reset.py +0 -0
  48. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_restore.py +0 -0
  49. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_rollback.py +0 -0
  50. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_seed.py +0 -0
  51. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_status.py +0 -0
  52. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  53. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/doctor.py +0 -0
  54. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_list.py +0 -0
  55. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_set.py +0 -0
  56. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_show.py +0 -0
  57. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/export/__init__.py +0 -0
  58. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/export/export_puml.py +0 -0
  59. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_add.py +0 -0
  60. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  61. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  62. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  63. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  64. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  65. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  66. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_create.py +0 -0
  67. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  68. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  69. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  70. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  71. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_env.py +0 -0
  72. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  73. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_git.py +0 -0
  74. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  75. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  76. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  77. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  78. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_info.py +0 -0
  79. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  80. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_install.py +0 -0
  81. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_list.py +0 -0
  82. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_order.py +0 -0
  83. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  84. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  85. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  86. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  87. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  88. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  89. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  90. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_search.py +0 -0
  91. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_status.py +0 -0
  92. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  93. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_test.py +0 -0
  94. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  95. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  96. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  97. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  98. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  99. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/lint.py +0 -0
  100. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/locust.py +0 -0
  101. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/login.py +0 -0
  102. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/logout.py +0 -0
  103. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  104. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
  105. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  106. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/__init__.py +0 -0
  107. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  108. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_build.py +0 -0
  109. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_clean.py +0 -0
  110. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_commands.py +0 -0
  111. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_config.py +0 -0
  112. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_configure.py +0 -0
  113. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_console.py +0 -0
  114. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_containers.py +0 -0
  115. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_create.py +0 -0
  116. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_deploy.py +0 -0
  117. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_derive.py +0 -0
  118. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_deselect.py +0 -0
  119. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_down.py +0 -0
  120. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_drift.py +0 -0
  121. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_env.py +0 -0
  122. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_list.py +0 -0
  123. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_logs.py +0 -0
  124. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_missing.py +0 -0
  125. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_port.py +0 -0
  126. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_resolve.py +0 -0
  127. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_restart.py +0 -0
  128. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_routes.py +0 -0
  129. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_run.py +0 -0
  130. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_select.py +0 -0
  131. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_shell.py +0 -0
  132. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_signals.py +0 -0
  133. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  134. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_test.py +0 -0
  135. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_up.py +0 -0
  136. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_validate.py +0 -0
  137. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/__init__.py +0 -0
  138. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/release_core.py +0 -0
  139. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/release_resume.py +0 -0
  140. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/selenium.py +0 -0
  141. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/__init__.py +0 -0
  142. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  143. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  144. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  145. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  146. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_create.py +0 -0
  147. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  148. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_features.py +0 -0
  149. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  150. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_info.py +0 -0
  151. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_list.py +0 -0
  152. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  153. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  154. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  155. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  156. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  157. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/tokens_setup.py +0 -0
  158. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  159. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/version.py +0 -0
  160. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/__init__.py +0 -0
  161. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/compose.py +0 -0
  162. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/context.py +0 -0
  163. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/credentials.py +0 -0
  164. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/image_registry.py +0 -0
  165. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/index_refresh.py +0 -0
  166. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace_api.py +0 -0
  167. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace_url.py +0 -0
  168. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/preflight.py +0 -0
  169. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/pypi_quota.py +0 -0
  170. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/registry.py +0 -0
  171. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release_dry_run.py +0 -0
  172. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release_gate.py +0 -0
  173. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/spl_store.py +0 -0
  174. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/__init__.py +0 -0
  175. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/archetype.py +0 -0
  176. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/cache_utils.py +0 -0
  177. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/command_loader.py +0 -0
  178. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/contract_freshness.py +0 -0
  179. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/db_client.py +0 -0
  180. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/db_utils.py +0 -0
  181. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/decorators.py +0 -0
  182. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/dynamic_imports.py +0 -0
  183. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/feature_installer.py +0 -0
  184. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/feature_utils.py +0 -0
  185. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/forge.py +0 -0
  186. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/git_url.py +0 -0
  187. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/integrity.py +0 -0
  188. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/io_utils.py +0 -0
  189. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/lifecycle.py +0 -0
  190. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/manifest.py +0 -0
  191. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/path_utils.py +0 -0
  192. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/proc.py +0 -0
  193. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/redact.py +0 -0
  194. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/refinement_facts.py +0 -0
  195. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/template_drift.py +0 -0
  196. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/upgrade_plan.py +0 -0
  197. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/SOURCES.txt +0 -0
  198. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  199. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/entry_points.txt +0 -0
  200. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/requires.txt +0 -0
  201. {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.29.0
3
+ Version: 1.29.1
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.29.0"
7
+ version = "1.29.1"
8
8
  description = "SPLENT-CLI is a CLI to be able to work on your development more easily."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -3,6 +3,7 @@ feature:release — Release a feature: bump version, tag, publish to the git for
3
3
  """
4
4
 
5
5
  import ast
6
+ import json
6
7
  import os
7
8
  import re
8
9
  import shutil
@@ -363,6 +364,83 @@ def _extract_commands(commands_path: Path) -> list[str]:
363
364
  return sorted(set(re.findall(r"""@click\.command\s*\(\s*['"]([^'"]+)['"]""", text)))
364
365
 
365
366
 
367
+ def _call_name(node) -> str:
368
+ """``click.command`` for the decorator call ``@click.command(...)``."""
369
+ func = node.func if isinstance(node, ast.Call) else node
370
+ parts = []
371
+ while isinstance(func, ast.Attribute):
372
+ parts.append(func.attr)
373
+ func = func.value
374
+ if isinstance(func, ast.Name):
375
+ parts.append(func.id)
376
+ return ".".join(reversed(parts))
377
+
378
+
379
+ def _literal(node):
380
+ try:
381
+ return ast.literal_eval(node)
382
+ except (ValueError, SyntaxError, TypeError):
383
+ return None
384
+
385
+
386
+ def _first_line(text) -> str:
387
+ for line in (text or "").strip().splitlines():
388
+ if line.strip():
389
+ return line.strip()
390
+ return ""
391
+
392
+
393
+ def _extract_command_help(commands_path: Path) -> dict:
394
+ """What each command a feature adds is for, and how it is called.
395
+
396
+ ``{name: {"usage": "EMAIL ROLE [OPTIONS]", "help": "..."}}``, read from
397
+ the source without importing it: the arguments in the order they are
398
+ declared, in click's uppercase, ``[OPTIONS]`` when there are any, and the
399
+ first line of ``short_help``, ``help`` or the docstring. The marketplace
400
+ shows it next to each command, which used to be a bare name.
401
+ """
402
+ if not commands_path.exists():
403
+ return {}
404
+ try:
405
+ tree = ast.parse(commands_path.read_text())
406
+ except SyntaxError:
407
+ return {}
408
+ found = {}
409
+ for node in ast.walk(tree):
410
+ if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
411
+ continue
412
+ name, help_text, usage, has_options = None, "", [], False
413
+ for deco in node.decorator_list:
414
+ if not isinstance(deco, ast.Call):
415
+ continue
416
+ kind = _call_name(deco)
417
+ if kind.endswith("command") and deco.args:
418
+ name = _literal(deco.args[0])
419
+ kwargs = {k.arg: _literal(k.value) for k in deco.keywords}
420
+ help_text = kwargs.get("short_help") or kwargs.get("help") or ""
421
+ elif kind.endswith("argument") and deco.args:
422
+ arg = _literal(deco.args[0])
423
+ if not isinstance(arg, str):
424
+ continue
425
+ kwargs = {k.arg: _literal(k.value) for k in deco.keywords}
426
+ metavar = arg.upper().replace("-", "_")
427
+ if kwargs.get("nargs") == -1:
428
+ metavar += "..."
429
+ optional = kwargs.get("required") is False or "default" in kwargs
430
+ usage.append(f"[{metavar}]" if optional else metavar)
431
+ elif kind.endswith("option"):
432
+ has_options = True
433
+ if not isinstance(name, str):
434
+ continue
435
+ if has_options:
436
+ usage.append("[OPTIONS]")
437
+ found[name] = {
438
+ "usage": " ".join(usage),
439
+ "help": _first_line(help_text) or _first_line(ast.get_docstring(node)),
440
+ }
441
+ return dict(sorted(found.items()))
442
+
443
+
366
444
  def _feature_sources(src_dir: Path):
367
445
  """The feature's own Python files, without its tests.
368
446
 
@@ -693,6 +771,7 @@ def infer_contract(
693
771
  templates = _extract_templates(src_dir)
694
772
  template_hook_slots = _extract_template_hook_slots(src_dir)
695
773
  commands = _extract_commands(src_dir / "commands.py")
774
+ commands_help = _extract_command_help(src_dir / "commands.py")
696
775
  docker = _extract_docker(feature_root)
697
776
  docker_contract = _extract_docker_contract(feature_root)
698
777
  req_features, env_vars = _scan_dependencies(src_dir, feature_name)
@@ -751,6 +830,7 @@ def infer_contract(
751
830
  "blueprints": blueprints,
752
831
  "models": models,
753
832
  "commands": commands,
833
+ "commands_help": commands_help,
754
834
  "hooks": hooks,
755
835
  "services": services,
756
836
  "signals": signals_provided,
@@ -945,7 +1025,8 @@ def write_contract(pyproject_path: str, contract: dict, feature_name: str) -> No
945
1025
  f"signals = {_toml_value(contract.get('signals', []))}\n"
946
1026
  f"translations = {_toml_value(contract.get('translations', []))}\n"
947
1027
  f"docker = {_toml_value(contract['docker'])}\n"
948
- "\n"
1028
+ + _commands_help_block(contract.get("commands_help") or {})
1029
+ + "\n"
949
1030
  "[tool.splent.contract.requires]\n"
950
1031
  f"features = {_toml_value(contract['requires_features'])}\n"
951
1032
  + (
@@ -989,6 +1070,20 @@ def write_contract(pyproject_path: str, contract: dict, feature_name: str) -> No
989
1070
  path.write_text(text + contract_block + refinement_block)
990
1071
 
991
1072
 
1073
+ def _commands_help_block(commands_help: dict) -> str:
1074
+ """The ``[tool.splent.contract.provides.commands_help]`` table, or nothing."""
1075
+ if not commands_help:
1076
+ return ""
1077
+ lines = ["\n[tool.splent.contract.provides.commands_help]\n"]
1078
+ for name, entry in commands_help.items():
1079
+ key = name if re.fullmatch(r"[A-Za-z0-9_-]+", name) else json.dumps(name)
1080
+ lines.append(
1081
+ f"{key} = {{ usage = {json.dumps(entry.get('usage', ''))}, "
1082
+ f"help = {json.dumps(entry.get('help', ''))} }}\n"
1083
+ )
1084
+ return "".join(lines)
1085
+
1086
+
992
1087
  # ── Compile assets ────────────────────────────────────────────────────
993
1088
 
994
1089
 
@@ -2,8 +2,11 @@
2
2
  product:release, a product release: version bump, tag, forge, PyPI and the image.
3
3
  """
4
4
 
5
+ import contextlib
6
+ import json
5
7
  import os
6
8
  import subprocess
9
+ import tempfile
7
10
 
8
11
  import click
9
12
  from splent_cli.commands.product.product_build import (
@@ -63,7 +66,32 @@ def release_targets(product: str, version: str, product_path: str, org: str):
63
66
  return image_registry.image_targets(product, version, org, registries, mirrors)
64
67
 
65
68
 
66
- def _login(target) -> bool:
69
+ #: Docker Hub answers to these names in a client's configuration.
70
+ _DOCKER_HUB_HOSTS = ("index.docker.io", "registry-1.docker.io", "docker.io")
71
+
72
+
73
+ @contextlib.contextmanager
74
+ def registry_session(hosts=()):
75
+ """A docker client configuration of its own for one release's logins and pushes.
76
+
77
+ The host's configuration may hand credentials to a helper, the macOS
78
+ keychain under Docker Desktop, and that helper refuses to store a login
79
+ for a host the keychain already holds a git password for ("The specified
80
+ item already exists in the keychain"), which stopped the marketplace's
81
+ first release. A configuration of its own also keeps the release tokens
82
+ off the machine: it is deleted when the release is done.
83
+ """
84
+ # An empty credsStore is not enough: the docker client then detects the
85
+ # platform's helper again. An empty helper per host is what makes it keep
86
+ # the login in this file.
87
+ helpers = {host: "" for host in (*hosts, *_DOCKER_HUB_HOSTS) if host}
88
+ with tempfile.TemporaryDirectory(prefix="splent-docker-") as config_dir:
89
+ with open(os.path.join(config_dir, "config.json"), "w") as handle:
90
+ json.dump({"auths": {}, "credHelpers": helpers}, handle)
91
+ yield {**os.environ, "DOCKER_CONFIG": config_dir}
92
+
93
+
94
+ def _login(target, env=None) -> bool:
67
95
  user, secret = target.credentials()
68
96
  if not user or not secret:
69
97
  return False
@@ -71,7 +99,7 @@ def _login(target) -> bool:
71
99
  if target.host:
72
100
  command.insert(2, target.host)
73
101
  try:
74
- run(command, input=secret, capture=True)
102
+ run(command, input=secret, capture=True, env=env)
75
103
  except subprocess.CalledProcessError:
76
104
  return False
77
105
  return True
@@ -105,51 +133,52 @@ def release_docker_image(product: str, version: str, docker_dir: str, targets):
105
133
  )
106
134
  raise SystemExit(1)
107
135
 
108
- usable = []
109
- for target in targets:
110
- click.echo(f" docker logging into {target.label}...")
111
- if _login(target):
112
- usable.append(target)
113
- continue
114
- if target.mirror:
115
- click.secho(
116
- f" docker the {target.label} mirror is left out, its login failed "
117
- f"or {' / '.join(target.credential_names())} are not set",
118
- fg="yellow",
119
- )
120
- continue
121
- user_env, secret_env = target.credential_names()
122
- click.secho(
123
- f" error: could not log into {target.label}. Check {user_env} and "
124
- f"{secret_env}.",
125
- fg="red",
126
- )
127
- raise SystemExit(1)
128
-
129
- refs = [ref for target in usable for ref in target.refs]
130
- click.echo(" docker building image...")
131
- try:
132
- build_production_image(product, str(context.workspace()), docker_dir, refs)
133
- except subprocess.CalledProcessError:
134
- click.secho(" error: Docker image build failed.", fg="red")
135
- raise SystemExit(1)
136
-
137
- for target in usable:
138
- click.echo(f" docker pushing to {target.label}...")
139
- try:
140
- for ref in target.refs:
141
- run(["docker", "push", ref])
142
- except subprocess.CalledProcessError:
136
+ with registry_session([t.host for t in targets]) as docker_env:
137
+ usable = []
138
+ for target in targets:
139
+ click.echo(f" docker logging into {target.label}...")
140
+ if _login(target, docker_env):
141
+ usable.append(target)
142
+ continue
143
143
  if target.mirror:
144
144
  click.secho(
145
- f" docker the push to the {target.label} mirror failed and is "
146
- "left out",
145
+ f" docker the {target.label} mirror is left out, its login "
146
+ f"failed or {' / '.join(target.credential_names())} are not set",
147
147
  fg="yellow",
148
148
  )
149
149
  continue
150
- click.secho(f" error: the push to {target.label} failed.", fg="red")
150
+ user_env, secret_env = target.credential_names()
151
+ click.secho(
152
+ f" error: could not log into {target.label}. Check {user_env} and "
153
+ f"{secret_env}.",
154
+ fg="red",
155
+ )
156
+ raise SystemExit(1)
157
+
158
+ refs = [ref for target in usable for ref in target.refs]
159
+ click.echo(" docker building image...")
160
+ try:
161
+ build_production_image(product, str(context.workspace()), docker_dir, refs)
162
+ except subprocess.CalledProcessError:
163
+ click.secho(" error: Docker image build failed.", fg="red")
151
164
  raise SystemExit(1)
152
- click.echo(f" docker {', '.join(target.refs)}")
165
+
166
+ for target in usable:
167
+ click.echo(f" docker pushing to {target.label}...")
168
+ try:
169
+ for ref in target.refs:
170
+ run(["docker", "push", ref], env=docker_env)
171
+ except subprocess.CalledProcessError:
172
+ if target.mirror:
173
+ click.secho(
174
+ f" docker the push to the {target.label} mirror failed "
175
+ "and is left out",
176
+ fg="yellow",
177
+ )
178
+ continue
179
+ click.secho(f" error: the push to {target.label} failed.", fg="red")
180
+ raise SystemExit(1)
181
+ click.echo(f" docker {', '.join(target.refs)}")
153
182
 
154
183
  click.echo(" docker push complete")
155
184
 
@@ -19,7 +19,8 @@ Layout of the index document (schema 1)::
19
19
  }
20
20
 
21
21
  Feature entries carry the full contract (provides / requires / extensible /
22
- docker), the derived archetype, presentation metadata (description,
22
+ docker), the settings the feature declares under ``[tool.splent.config]``
23
+ (``config``), the derived archetype, presentation metadata (description,
23
24
  category, tags), distribution state (latest tag, PyPI presence) and the
24
25
  computed reverse dependencies (``used_by``).
25
26
  """
@@ -36,6 +37,11 @@ import click
36
37
  INDEX_SCHEMA = 1
37
38
  FEATURE_PREFIX = "splent_feature_"
38
39
 
40
+ # Where a feature declares the variables it reads, and the value a setting
41
+ # holds when only the deployment can supply it (a secret).
42
+ CONFIG_TABLE = ("tool", "splent", "config")
43
+ REQUIRED_VALUE = "<SET>"
44
+
39
45
  # Feeds feature:search / feature:info when no explicit path is given.
40
46
  INDEX_URL_ENV = "SPLENT_INDEX_URL"
41
47
 
@@ -72,6 +78,67 @@ def index_cache_path(workspace: str) -> Path:
72
78
  # ── Feature entries ───────────────────────────────────────────────────
73
79
 
74
80
 
81
+ # The value types product:env writes into an env file. A table or a date
82
+ # under [tool.splent.config] never reaches the environment, so it is no
83
+ # setting a product can change.
84
+ _CONFIG_TYPES = (bool, int, float, str, list)
85
+
86
+
87
+ def _config_text(value) -> str:
88
+ """A declared value as the environment receives it from ``product:env``.
89
+
90
+ Booleans are ``true``/``false``, numbers and strings their plain text, and
91
+ a list its items joined by commas.
92
+ """
93
+ if isinstance(value, bool):
94
+ return "true" if value else "false"
95
+ if isinstance(value, list):
96
+ return ",".join(_config_text(item) for item in value)
97
+ return str(value)
98
+
99
+
100
+ def config_declarations(pyproject_text: str, data: dict | None = None) -> list[dict]:
101
+ """The settings a feature declares under ``[tool.splent.config]``.
102
+
103
+ One ``{"key", "default", "required", "help"}`` per key, in the order they
104
+ are declared (tomllib keeps it), for every key whose value reaches the
105
+ environment. ``default`` is the value as text (see :func:`_config_text`).
106
+ A value of ``<SET>`` means only the deployment can supply it, so it gives
107
+ ``required`` true and an empty ``default``. ``help`` is the comment above
108
+ the key, or trailing its value (see :func:`toml_edit.key_comments`), and
109
+ ``""`` when there is none.
110
+
111
+ The values come from tomllib (*data*, parsed from *pyproject_text* when
112
+ not given) and only the comments from the text, so an unusual layout
113
+ costs the help and never a key or a value.
114
+ """
115
+ from splent_cli.utils.toml_edit import TomlEditError, key_comments
116
+
117
+ if data is None:
118
+ data = tomllib.loads(pyproject_text)
119
+ declared = data.get("tool", {}).get("splent", {}).get("config", {})
120
+ if not isinstance(declared, dict) or not declared:
121
+ return []
122
+ try:
123
+ comments = key_comments(pyproject_text, CONFIG_TABLE)
124
+ except TomlEditError:
125
+ comments = {}
126
+ settings = []
127
+ for key, value in declared.items():
128
+ if not isinstance(value, _CONFIG_TYPES):
129
+ continue
130
+ required = isinstance(value, str) and value.strip() == REQUIRED_VALUE
131
+ settings.append(
132
+ {
133
+ "key": key,
134
+ "default": "" if required else _config_text(value),
135
+ "required": required,
136
+ "help": comments.get(key, ""),
137
+ }
138
+ )
139
+ return settings
140
+
141
+
75
142
  def feature_entry_from_pyproject(
76
143
  pyproject_text: str,
77
144
  *,
@@ -109,6 +176,7 @@ def feature_entry_from_pyproject(
109
176
  "provides": contract.get("provides", {}),
110
177
  "requires": contract.get("requires", {}),
111
178
  "extensible": contract.get("extensible", {}),
179
+ "config": config_declarations(pyproject_text, data),
112
180
  "docker": contract.get("docker") or None,
113
181
  "refinement": splent.get("refinement") or None,
114
182
  "cli_version": splent.get("cli_version"),
@@ -203,11 +203,33 @@ def require_release_version(version: str | None) -> None:
203
203
  raise SystemExit(1)
204
204
 
205
205
 
206
- def commit_locally(cwd: str, version: str, subject: str = "bump version") -> None:
207
- """Stage all changes and commit. Nothing leaves the machine here.
206
+ def untracked_files(cwd: str) -> set[str]:
207
+ """Paths git does not track and does not ignore, relative to ``cwd``."""
208
+ r = subprocess.run(
209
+ ["git", "ls-files", "--others", "--exclude-standard"],
210
+ capture_output=True,
211
+ text=True,
212
+ cwd=cwd,
213
+ )
214
+ return {line for line in r.stdout.splitlines() if line.strip()}
215
+
216
+
217
+ def commit_locally(
218
+ cwd: str,
219
+ version: str,
220
+ subject: str = "bump version",
221
+ leave_out: set[str] | None = None,
222
+ ) -> None:
223
+ """Stage the release's changes and commit. Nothing leaves the machine here.
208
224
 
209
225
  A local commit is reversible, so it happens before the first irreversible
210
226
  step and is pushed only once the un-undoable channel has accepted.
227
+
228
+ ``leave_out`` are the untracked files that were already there before the
229
+ release started. They are not staged: the marketplace's first release
230
+ committed a derive log, the staged UVL model and the contracts snapshot,
231
+ all of them build output the developer never meant to version, and the
232
+ server then refused to update over its own copies of them.
211
233
  """
212
234
  r = subprocess.run(
213
235
  ["git", "status", "--porcelain"],
@@ -219,10 +241,34 @@ def commit_locally(cwd: str, version: str, subject: str = "bump version") -> Non
219
241
  click.echo(" commit working tree clean, nothing to commit")
220
242
  return
221
243
 
244
+ leave_out = set(leave_out or ())
245
+ if leave_out:
246
+ click.secho(
247
+ f" commit leaving {len(leave_out)} file(s) out that were untracked "
248
+ "before the release: "
249
+ + ", ".join(sorted(leave_out)[:5])
250
+ + (" ..." if len(leave_out) > 5 else ""),
251
+ fg="bright_black",
252
+ )
222
253
  try:
223
254
  subprocess.run(
224
- ["git", "add", "-A"], cwd=cwd, check=True, capture_output=True, text=True
255
+ ["git", "add", "-u"], cwd=cwd, check=True, capture_output=True, text=True
256
+ )
257
+ created = sorted(untracked_files(cwd) - leave_out)
258
+ if created:
259
+ subprocess.run(
260
+ ["git", "add", "--", *created],
261
+ cwd=cwd,
262
+ check=True,
263
+ capture_output=True,
264
+ text=True,
265
+ )
266
+ staged = subprocess.run(
267
+ ["git", "diff", "--cached", "--quiet"], cwd=cwd, capture_output=True
225
268
  )
269
+ if staged.returncode == 0:
270
+ click.echo(" commit nothing the release changed, nothing to commit")
271
+ return
226
272
  subprocess.run(
227
273
  ["git", "commit", "-m", f"chore: {subject} to {version}"],
228
274
  cwd=cwd,
@@ -1598,12 +1644,16 @@ def run_release_pipeline(
1598
1644
  # ── Everything below this line changes the world ──────────────────
1599
1645
  click.echo()
1600
1646
 
1647
+ # Files nobody versioned before the release started are the developer's
1648
+ # business (a log, a local note), not the release's.
1649
+ left_alone = untracked_files(path)
1650
+
1601
1651
  update_version(pyproject, normalized)
1602
1652
 
1603
1653
  if pre_commit_hook:
1604
1654
  pre_commit_hook(path, normalized)
1605
1655
 
1606
- commit_locally(path, tag, subject=f"release {name}")
1656
+ commit_locally(path, tag, subject=f"release {name}", leave_out=left_alone)
1607
1657
 
1608
1658
  # Where this version is already published. From the first entry on, any
1609
1659
  # failure leaves the channels out of step, and is reported as such.
@@ -32,6 +32,10 @@ nothing is written. The same line by line idea is what
32
32
  ``upgrade_plan.replace_feature_pins`` does for ``feature:upgrade``; this module
33
33
  covers the edits that insert and remove entries, keys and tables as well.
34
34
 
35
+ :func:`key_comments` reads the same scan the other way round. It returns
36
+ the comment written above each key of a table, which is how a feature
37
+ documents its settings, so the marketplace can show it.
38
+
35
39
  No third party round-trip library is needed. tomlkit would keep the
36
40
  formatting too, but it is one more dependency every CLI install and image
37
41
  has to carry, and replacing a list through it still loses the comments
@@ -742,6 +746,50 @@ def edit_toml(text: str, data: dict) -> str:
742
746
  return result
743
747
 
744
748
 
749
+ def key_comments(text: str, table: tuple[str, ...]) -> dict[str, str]:
750
+ """The comment that documents each key written directly in *table*.
751
+
752
+ A key's comment is the block of ``#`` lines right above it, each line
753
+ without its leading ``#`` and joined into one paragraph. When there is no
754
+ block, it is the comment trailing the key's value instead. A blank line
755
+ ends a block, so a comment separated from the key by one documents
756
+ something else, and so does one above the table header. A key with
757
+ neither maps to ``""``. Keys of sub-tables and of arrays of tables are
758
+ left out.
759
+
760
+ Raises :class:`TomlEditError` for a file the scanner cannot read.
761
+ """
762
+ doc = _Scanner(text).scan()
763
+ # By TOML's grammar, what lies between the end of one statement and the
764
+ # start of the next is only blank lines and comments.
765
+ ends = sorted(
766
+ [t.header_end for t in doc.tables[1:]] + [kv.line_end for kv in doc.kvs]
767
+ )
768
+ depth = len(table) + 1
769
+ out: dict[str, str] = {}
770
+ for kv in doc.kvs:
771
+ if kv.in_aot or len(kv.path) != depth or kv.path[:-1] != table:
772
+ continue
773
+ gap_start = max((end for end in ends if end <= kv.line_start), default=0)
774
+ block: list[str] = []
775
+ for line in text[gap_start : kv.line_start].splitlines():
776
+ stripped = line.lstrip("\ufeff").strip()
777
+ block = (
778
+ [*block, _comment_text(stripped)] if stripped.startswith("#") else []
779
+ )
780
+ comment = " ".join(part for part in block if part)
781
+ if not comment:
782
+ tail = text[kv.value_end : kv.line_end].strip()
783
+ comment = _comment_text(tail) if tail.startswith("#") else ""
784
+ out[kv.path[-1]] = comment
785
+ return out
786
+
787
+
788
+ def _comment_text(line: str) -> str:
789
+ """A comment line without its leading ``#`` marks and spaces."""
790
+ return line.lstrip("#").strip()
791
+
792
+
745
793
  def write_toml(path: str | os.PathLike, data: dict) -> None:
746
794
  """Write *data* to the TOML file at *path*, keeping its comments and layout.
747
795
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.29.0
3
+ Version: 1.29.1
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
File without changes
File without changes
File without changes