tesorotools-python 0.1.10__tar.gz → 0.1.11__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 (170) hide show
  1. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/PKG-INFO +1 -1
  2. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/pyproject.toml +4 -1
  3. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/matrix.py +36 -9
  4. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/plotly_backend.py +16 -11
  5. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/waterfall.py +46 -29
  6. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/lint.py +53 -12
  7. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/markdown.py +43 -0
  8. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/builder.py +481 -118
  9. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/house.py +13 -3
  10. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/lint.py +594 -36
  11. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/markdown.py +29 -5
  12. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/skill/SKILL.md +78 -34
  13. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/matplotlib.py +10 -1
  14. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/__init__.py +4 -0
  15. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/app.py +19 -2
  16. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/brand.py +77 -5
  17. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/components.py +24 -7
  18. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/theme.py +8 -0
  19. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/.gitignore +0 -0
  20. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/__init__.py +0 -0
  21. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/_build_context.py +0 -0
  22. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/_registry.py +0 -0
  23. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/__init__.py +0 -0
  24. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/_common.py +0 -0
  25. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/_dates.py +0 -0
  26. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/bar_line.py +0 -0
  27. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/barh_plot.py +0 -0
  28. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/box_plot.py +0 -0
  29. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/compact.py +0 -0
  30. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/intraday_plot.py +0 -0
  31. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/line_plot.py +0 -0
  32. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/sankey.py +0 -0
  33. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/scatter.py +0 -0
  34. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/sharing.py +0 -0
  35. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/shock_plot.py +0 -0
  36. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/stacked.py +0 -0
  37. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/tikz_backend.py +0 -0
  38. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/type_curve.py +0 -0
  39. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/artists/vector_plot.py +0 -0
  40. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/README.md +0 -0
  41. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
  42. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
  43. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
  44. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
  45. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
  46. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
  47. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
  48. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
  49. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/fonts/README.md +0 -0
  50. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/nota_template.dotx +0 -0
  51. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/plots.yaml +0 -0
  52. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/template.docx +0 -0
  53. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/assets/tesoro.mplstyle +0 -0
  54. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/data_sources/__init__.py +0 -0
  55. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/data_sources/debug.py +0 -0
  56. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/database/__init__.py +0 -0
  57. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/database/local.py +0 -0
  58. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/database/push.py +0 -0
  59. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/database/shared.py +0 -0
  60. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/dependencies/__init__.py +0 -0
  61. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/dependencies/node.py +0 -0
  62. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/dependencies/resolution.py +0 -0
  63. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/driver.py +0 -0
  64. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/__init__.py +0 -0
  65. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/cli.py +0 -0
  66. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/scaffold.py +0 -0
  67. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/article/main.tex.tmpl +0 -0
  68. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/article/sections/01-introduccion.tex +0 -0
  69. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/article/sections/02-analisis.tex +0 -0
  70. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/article/sections/03-conclusiones.tex +0 -0
  71. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/beamer/main.tex.tmpl +0 -0
  72. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/beamer/slides/01-contexto.tex +0 -0
  73. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/beamer/slides/02-analisis.tex +0 -0
  74. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/beamer/slides/03-conclusiones.tex +0 -0
  75. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/beamer/style/beamerthemeTesoro.sty +0 -0
  76. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/book/appendices/a-fuentes.tex +0 -0
  77. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/book/chapters/01-introduccion.tex +0 -0
  78. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/book/chapters/02-analisis.tex +0 -0
  79. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/book/frontmatter/resumen.tex +0 -0
  80. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/book/main.tex.tmpl +0 -0
  81. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/README.md.tmpl +0 -0
  82. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/dot-gitignore +0 -0
  83. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/dot-vscode/settings.json +0 -0
  84. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/figures/dot-gitkeep +0 -0
  85. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/fonts/README.md +0 -0
  86. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/latexmkrc +0 -0
  87. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/refs.bib +0 -0
  88. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/style/tesoro.sty +0 -0
  89. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/style/tesorobrand.sty.tmpl +0 -0
  90. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/latex/templates/common/style/tesorolocal.sty.tmpl +0 -0
  91. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/manifest.py +0 -0
  92. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/__init__.py +0 -0
  93. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/builder.py +0 -0
  94. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/charts.py +0 -0
  95. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/cli.py +0 -0
  96. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/inline.py +0 -0
  97. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/issues.py +0 -0
  98. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/pdf.py +0 -0
  99. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/skeleton.md +0 -0
  100. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/skill/SKILL.md +0 -0
  101. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/skill/reglas.md +0 -0
  102. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/note/template.py +0 -0
  103. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/offsets/__init__.py +0 -0
  104. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/offsets/offsets.py +0 -0
  105. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/offsets/outliers.py +0 -0
  106. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/orchestration.py +0 -0
  107. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/palette.py +0 -0
  108. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/pipeline/__init__.py +0 -0
  109. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/pipeline/diagnose.py +0 -0
  110. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/pipeline/engine.py +0 -0
  111. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/pipeline/rules.py +0 -0
  112. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/__init__.py +0 -0
  113. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/charts.py +0 -0
  114. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/cli.py +0 -0
  115. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/fit.py +0 -0
  116. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/glossary.py +0 -0
  117. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/native.py +0 -0
  118. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/pdf.py +0 -0
  119. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/sections.py +0 -0
  120. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/skeleton.md +0 -0
  121. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/skill/reglas.md +0 -0
  122. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/presentation/template.py +0 -0
  123. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/__init__.py +0 -0
  124. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/base.py +0 -0
  125. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/bde.py +0 -0
  126. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/ecb.py +0 -0
  127. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/fred.py +0 -0
  128. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/imf_irfcl.py +0 -0
  129. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/providers/lseg.py +0 -0
  130. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/py.typed +0 -0
  131. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/__init__.py +0 -0
  132. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/__init__.py +0 -0
  133. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/content.py +0 -0
  134. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/images.py +0 -0
  135. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/section.py +0 -0
  136. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/subtitle.py +0 -0
  137. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/table.py +0 -0
  138. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/text.py +0 -0
  139. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/content/title.py +0 -0
  140. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/document.py +0 -0
  141. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/render/report.py +0 -0
  142. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/scaffold.py +0 -0
  143. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/testing/__init__.py +0 -0
  144. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/testing/compare.py +0 -0
  145. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/__init__.py +0 -0
  146. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/config.py +0 -0
  147. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/format.py +0 -0
  148. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/globals.py +0 -0
  149. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/series.py +0 -0
  150. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/shortcuts.py +0 -0
  151. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/utils/template.py +0 -0
  152. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/cli.py +0 -0
  153. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/formatting.py +0 -0
  154. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/launcher.py +0 -0
  155. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/palette.py +0 -0
  156. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/period.py +0 -0
  157. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/plotting.py +0 -0
  158. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/scaffold.py +0 -0
  159. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/Dockerfile.tmpl +0 -0
  160. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/README.md.tmpl +0 -0
  161. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/dot-dockerignore.tmpl +0 -0
  162. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/dot-gitignore.tmpl +0 -0
  163. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/dot-streamlit/config.toml.tmpl +0 -0
  164. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/pyproject.toml.tmpl +0 -0
  165. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/src/$package/__init__.py.tmpl +0 -0
  166. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/src/$package/cli.py.tmpl +0 -0
  167. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/src/$package/main.py.tmpl +0 -0
  168. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/src/$package/sections.py.tmpl +0 -0
  169. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/tests/__init__.py.tmpl +0 -0
  170. {tesorotools_python-0.1.10 → tesorotools_python-0.1.11}/src/tesorotools/web/templates/tests/test_app.py.tmpl +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tesorotools-python
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Requires-Python: >=3.13
5
5
  Requires-Dist: babel>=2.17
6
6
  Requires-Dist: matplotlib>=3.10
@@ -45,7 +45,10 @@ tesorotools-presentacion = "tesorotools.presentation.cli:main"
45
45
  [dependency-groups]
46
46
  dev = [
47
47
  "ruff>=0.8",
48
- "pyright>=1.1",
48
+ # Con `[nodejs]` node llega como rueda de PyPI; sin él pyright lo baja con
49
+ # nodeenv en cada job, y nodeenv 1.10 se rompió el 03/11/2026 con una
50
+ # versión `-alpha` de node.
51
+ "pyright[nodejs]>=1.1",
49
52
  "pre-commit>=4.0",
50
53
  "pandas-stubs>=2.2",
51
54
  "coverage>=7.0",
@@ -17,8 +17,10 @@ column per column id -- the same wide shape
17
17
  :class:`GroupedBarChart` consumes -- so ``rows`` selects and
18
18
  labels the index and ``cols`` selects and labels the columns.
19
19
  Cell shading uses a sequential colormap normalised over the
20
- selected values; text colour flips to stay legible on dark
21
- cells. ``NaN`` cells are left blank (no fill, no text), which
20
+ selected values or, when they have both signs, a diverging one
21
+ centred on zero (:data:`DIVERGING`), so -0,6 reads as strongly as
22
+ 0,6 instead of almost white; text colour flips to stay legible on
23
+ dark cells. ``NaN`` cells are left blank (no fill, no text), which
22
24
  covers triangular or sparse matrices.
23
25
 
24
26
  Styling constants come from ``PLOT_CONFIG['matrix']``.
@@ -34,11 +36,12 @@ import matplotlib.pyplot as plt
34
36
  import numpy as np
35
37
  import pandas as pd
36
38
  from matplotlib.axes import Axes
37
- from matplotlib.colors import Colormap, Normalize
39
+ from matplotlib.colors import Colormap, LinearSegmentedColormap, Normalize
38
40
  from matplotlib.figure import Figure
39
41
  from matplotlib.patches import Rectangle
40
42
  from yaml.nodes import MappingNode
41
43
 
44
+ from tesorotools import palette
42
45
  from tesorotools.artists._common import (
43
46
  FigureLifecycle,
44
47
  FIG_CONFIG,
@@ -51,6 +54,12 @@ from tesorotools.utils.template import TemplateLoader
51
54
  __all__ = ["MatrixChart"]
52
55
 
53
56
  MATRIX_CONFIG: dict[str, Any] = PLOT_CONFIG["matrix"]
57
+ #: The scale of a matrix with figures of both signs: red under zero,
58
+ #: white at zero and blue over it, in the house colours.
59
+ DIVERGING_STOPS = (f"#{palette.RED}", "#FFFFFF", f"#{palette.BLUE}")
60
+ DIVERGING: Colormap = LinearSegmentedColormap.from_list(
61
+ "tesoro_diverging", list(DIVERGING_STOPS)
62
+ )
54
63
 
55
64
 
56
65
  def _text_color(rgba: tuple[float, float, float, float]) -> str:
@@ -91,10 +100,12 @@ class MatrixChart(FigureLifecycle):
91
100
  :class:`Format` for the big per-cell value.
92
101
  cmap
93
102
  Matplotlib colormap name for the shading (default
94
- ``PLOT_CONFIG['matrix']['cmap']``).
103
+ ``PLOT_CONFIG['matrix']['cmap']`` or, with values of both
104
+ signs, :data:`DIVERGING`).
95
105
  vmin, vmax
96
106
  Value range mapped to the colormap ends. Default to
97
- the min / max of the selected values.
107
+ the min / max of the selected values (with both signs,
108
+ symmetric around zero).
98
109
  xlabel, ylabel
99
110
  Axis titles (the column / row variable names).
100
111
  figsize
@@ -142,6 +153,8 @@ class MatrixChart(FigureLifecycle):
142
153
  self.scale = scale
143
154
  self.fmt = fmt or Format()
144
155
  self.cmap = cmap or MATRIX_CONFIG["cmap"]
156
+ #: The colormap was not chosen: values of both signs diverge.
157
+ self.default_cmap = cmap is None
145
158
  self.vmin = vmin
146
159
  self.vmax = vmax
147
160
  self.xlabel = xlabel
@@ -230,10 +243,7 @@ class MatrixChart(FigureLifecycle):
230
243
 
231
244
  values_arr = matrix.to_numpy(dtype=float)
232
245
  valid = values_arr[~np.isnan(values_arr)]
233
- vmin = self.vmin if self.vmin is not None else float(valid.min())
234
- vmax = self.vmax if self.vmax is not None else float(valid.max())
235
- norm = Normalize(vmin=vmin, vmax=vmax)
236
- cmap: Colormap = mpl.colormaps[self.cmap]
246
+ norm, cmap = self.shading(valid)
237
247
 
238
248
  fig_kw = dict(FIG_CONFIG)
239
249
  fig_kw["figsize"] = self.figsize if self.figsize is not None else (6, 6)
@@ -276,6 +286,23 @@ class MatrixChart(FigureLifecycle):
276
286
  self._style_axes(ax, row_ids, col_ids)
277
287
  return fig, ax
278
288
 
289
+ def shading(
290
+ self, valid: np.ndarray[Any, np.dtype[np.float64]]
291
+ ) -> tuple[Normalize, Colormap]:
292
+ """The colour scale of the cells with *valid* values.
293
+
294
+ With values of both signs, and no ``cmap``, ``vmin`` or ``vmax``
295
+ given, :data:`DIVERGING` centred on zero: a sequential scale
296
+ would paint -0,6 almost white, like a zero.
297
+ """
298
+ vmin = self.vmin if self.vmin is not None else float(valid.min())
299
+ vmax = self.vmax if self.vmax is not None else float(valid.max())
300
+ both = vmin < 0 < vmax and self.vmin is None and self.vmax is None
301
+ if self.default_cmap and both:
302
+ edge = max(-vmin, vmax)
303
+ return Normalize(vmin=-edge, vmax=edge), DIVERGING
304
+ return Normalize(vmin=vmin, vmax=vmax), mpl.colormaps[self.cmap]
305
+
279
306
  def _annotate_cell(
280
307
  self,
281
308
  ax: Axes,
@@ -46,7 +46,6 @@ from __future__ import annotations
46
46
 
47
47
  from typing import TYPE_CHECKING, Any
48
48
 
49
- import matplotlib as mpl
50
49
  import matplotlib.colors as mcolors
51
50
  import matplotlib.pyplot as plt
52
51
  import numpy as np
@@ -763,10 +762,8 @@ def waterfall_to_figure(chart: Waterfall) -> go.Figure:
763
762
  dec_color = WATERFALL_CONFIG.get("decrease_color", "C9")
764
763
  colors: list[str] = []
765
764
  for i in range(n):
766
- if i == 0:
767
- colors.append(_color(start_color))
768
- elif i == n - 1 or is_total[i]:
769
- colors.append(_color(end_color))
765
+ if is_total[i]:
766
+ colors.append(_color(start_color if i == 0 else end_color))
770
767
  else:
771
768
  base = inc_color if values[i] >= 0 else dec_color
772
769
  colors.append(_color(base, light_alpha))
@@ -919,17 +916,25 @@ def grouped_bar_to_figure(chart: GroupedBarChart) -> go.Figure:
919
916
 
920
917
  def matrix_to_figure(chart: MatrixChart) -> go.Figure:
921
918
  """Interactive ``Figure`` from a :class:`MatrixChart` (heatmap + labels)."""
922
- from tesorotools.artists.matrix import _text_color
919
+ from tesorotools.artists.matrix import (
920
+ DIVERGING,
921
+ DIVERGING_STOPS,
922
+ _text_color,
923
+ )
923
924
 
924
925
  row_ids = list(chart.rows)
925
926
  col_ids = list(chart.cols)
926
927
  matrix = chart.data.loc[row_ids, col_ids] * chart.scale
927
928
  values = matrix.to_numpy(dtype=float)
928
929
  valid = values[~np.isnan(values)]
929
- vmin = chart.vmin if chart.vmin is not None else float(valid.min())
930
- vmax = chart.vmax if chart.vmax is not None else float(valid.max())
931
- norm = mcolors.Normalize(vmin=vmin, vmax=vmax)
932
- cmap = mpl.colormaps[chart.cmap]
930
+ norm, cmap = chart.shading(valid)
931
+ vmin, vmax = norm.vmin, norm.vmax
932
+ # the diverging scale of values of both signs, as in the PNG
933
+ colorscale: str | list[list[Any]] = (
934
+ [[n / 2, stop] for n, stop in enumerate(DIVERGING_STOPS)]
935
+ if cmap is DIVERGING
936
+ else "Blues"
937
+ )
933
938
 
934
939
  row_labels = [chart.rows[r] for r in row_ids]
935
940
  col_labels = [chart.cols[c] for c in col_ids]
@@ -954,7 +959,7 @@ def matrix_to_figure(chart: MatrixChart) -> go.Figure:
954
959
  z=values,
955
960
  x=col_labels,
956
961
  y=row_labels,
957
- colorscale="Blues",
962
+ colorscale=colorscale,
958
963
  zmin=vmin,
959
964
  zmax=vmax,
960
965
  showscale=False,
@@ -25,6 +25,7 @@ import pandas as pd
25
25
  from matplotlib.axes import Axes
26
26
  from matplotlib.colors import to_rgba
27
27
  from matplotlib.figure import Figure
28
+ from matplotlib.text import Annotation
28
29
  from yaml.nodes import MappingNode
29
30
 
30
31
  from tesorotools.artists._common import (
@@ -47,6 +48,31 @@ __all__ = ["Waterfall"]
47
48
 
48
49
  WATERFALL_CONFIG: dict[str, Any] = PLOT_CONFIG.get("waterfall", {})
49
50
 
51
+ # points of air between two value labels for them not to read as one
52
+ _LABEL_GAP_PT = 2.0
53
+
54
+
55
+ def _drop_crowded(
56
+ fig: Figure, labels: list[Annotation], is_total: list[bool]
57
+ ) -> None:
58
+ """Remove the deltas' value labels if any two neighbours collide.
59
+
60
+ Measured on the drawn figure: all of them go or none, so the
61
+ chart never labels some steps and not others; the totals stay.
62
+ """
63
+ if len(labels) < 2:
64
+ return
65
+ fig.draw_without_rendering()
66
+ gap = _LABEL_GAP_PT * fig.dpi / 72
67
+ boxes = [label.get_window_extent() for label in labels]
68
+ if any(
69
+ a.x1 + gap > b.x0 and a.y0 < b.y1 and b.y0 < a.y1
70
+ for a, b in zip(boxes, boxes[1:])
71
+ ):
72
+ for label, total in zip(labels, is_total):
73
+ if not total:
74
+ label.remove()
75
+
50
76
 
51
77
  class Waterfall(FigureLifecycle):
52
78
  """Waterfall / bridge chart with the tesorotools visual style.
@@ -205,12 +231,12 @@ class Waterfall(FigureLifecycle):
205
231
  inc_color = WATERFALL_CONFIG.get("increase_color", "C0")
206
232
  dec_color = WATERFALL_CONFIG.get("decrease_color", "C9")
207
233
  light_alpha = float(WATERFALL_CONFIG.get("light_alpha", 0.35))
234
+ # the colour follows what the bar is, not where it sits: a first or
235
+ # last bar left out of ``totals`` is a delta like the rest
208
236
  colors: list[Any] = []
209
237
  for i in range(n):
210
- if i == 0:
211
- colors.append(to_rgba(start_color))
212
- elif i == n - 1 or is_total[i]:
213
- colors.append(to_rgba(end_color))
238
+ if is_total[i]:
239
+ colors.append(to_rgba(start_color if i == 0 else end_color))
214
240
  else:
215
241
  base = inc_color if values[i] >= 0 else dec_color
216
242
  colors.append(to_rgba(base, light_alpha))
@@ -243,19 +269,11 @@ class Waterfall(FigureLifecycle):
243
269
  zorder=0,
244
270
  )
245
271
 
246
- if self.annotate:
247
- # at 7.4 cm the per-delta labels collide horizontally, so compact
248
- # charts label only the opening/closing totals (the bars and the
249
- # y-axis already convey the steps).
250
- self._annotate_bars(
251
- ax,
252
- x,
253
- tops,
254
- values,
255
- is_total,
256
- font_pt,
257
- only_totals=self.compact,
258
- )
272
+ labels = (
273
+ self._annotate_bars(ax, x, tops, values, is_total, font_pt)
274
+ if self.annotate
275
+ else []
276
+ )
259
277
 
260
278
  style_spines(
261
279
  ax,
@@ -286,6 +304,11 @@ class Waterfall(FigureLifecycle):
286
304
  top = float(tops.max())
287
305
  ax.set_ylim(bottom, top + (top - bottom) * headroom)
288
306
 
307
+ if self.compact:
308
+ # a compact chart at 7.4 cm has no room for every label; one as
309
+ # wide as a slide does, so the deltas go only if they collide
310
+ _drop_crowded(fig, labels, is_total)
311
+
289
312
  if self.plot_size is not None and not self.compact:
290
313
  adjust_figure_for_plot_size(fig, ax, self.plot_size)
291
314
 
@@ -299,22 +322,14 @@ class Waterfall(FigureLifecycle):
299
322
  values: np.ndarray[tuple[int], np.dtype[np.float64]],
300
323
  is_total: list[bool],
301
324
  font_pt: float | None,
302
- *,
303
- only_totals: bool = False,
304
- ) -> None:
305
- """Write each bar's value just above its top (totals in bold).
306
-
307
- With ``only_totals`` set, the floating deltas are left
308
- unlabelled -- used by compact charts where the per-delta
309
- labels would overlap.
310
- """
325
+ ) -> list[Annotation]:
326
+ """Write each bar's value just above its top (totals in bold)."""
327
+ labels: list[Annotation] = []
311
328
  for i in range(len(x)):
312
- if only_totals and not is_total[i]:
313
- continue
314
329
  text = format_annotation(
315
330
  float(values[i]), self.fmt.decimals, self.fmt.units
316
331
  )
317
- ax.annotate( # type: ignore[reportUnknownMemberType]
332
+ label = ax.annotate( # type: ignore[reportUnknownMemberType]
318
333
  text,
319
334
  xy=(float(x[i]), float(tops[i])),
320
335
  xytext=(0, 3),
@@ -324,6 +339,8 @@ class Waterfall(FigureLifecycle):
324
339
  fontsize=font_pt,
325
340
  fontweight="bold" if is_total[i] else "normal",
326
341
  )
342
+ labels.append(label)
343
+ return labels
327
344
 
328
345
  def save(
329
346
  self,
@@ -86,6 +86,8 @@ FOREIGN_WORDS: tuple[str, ...] = (
86
86
  "default",
87
87
  "tapering",
88
88
  "carry trade",
89
+ "home bias",
90
+ "unit-linked",
89
91
  "benchmark",
90
92
  "trading",
91
93
  "outlook",
@@ -108,12 +110,14 @@ _WORD_BEFORE = re.compile(r"(\w+)\s+$")
108
110
  _BEFORE_NUMBER = re.compile(
109
111
  r"(?:gráfico|tabla|apartado|sección|capítulo|anexo|nivel|punto|fase"
110
112
  r"|pilar|artículo|nota|paso|grupo|tramo|versión|n\.º|nº"
113
+ r"|alternativa|opción|escenario|propuesta|etapa|hito|eje|objetivo"
114
+ r"|medida|caso|tema"
111
115
  r"|art\.|arts\.|núm\.|pág\.|págs\.|p\.|pp\.|cap\.|apdo\.|vol\.)\s*$",
112
116
  re.I,
113
117
  )
114
118
  _UNIT_AFTER = re.compile(
115
- r"\s*(?:%|por\s+ciento\b|pb\b|pp\b|puntos?\b|millones?\b|mil\b|miles\b"
116
- r"|billones?\b|billions?\b"
119
+ r"\s*(?:%|por\s+cien(?:to)?\b|pb\b|pp\b|puntos?\b|millones?\b|mil\b"
120
+ r"|miles\b|billones?\b|billions?\b|bn\b|tn\b"
117
121
  r"|euros?\b|dólares?\b|€|\$|°|veces\b|x\b|h\b|horas?\b|de\s+(?:"
118
122
  + _MONTHS.lower()
119
123
  + r")\b)",
@@ -145,10 +149,10 @@ def _real_decimal(text: str, match: re.Match[str]) -> bool:
145
149
  def _month_context(text: str, match: re.Match[str]) -> bool:
146
150
  """«de Mayo» outside a proper name such as «Plaza de Mayo».
147
151
 
148
- Only «de» can join a proper name; «en Octubre» is always a fault,
149
- also after a capitalised word or in a title.
152
+ Only «de» can join a proper name; «en Octubre» or «desde Octubre» is
153
+ always a fault, also after a capitalised word or in a title.
150
154
  """
151
- if not match.group(0).lower().startswith("de"):
155
+ if match.group(0).split()[0].lower() != "de":
152
156
  return True
153
157
  word = _WORD_BEFORE.search(text[: match.start()])
154
158
  return word is None or not word.group(1)[0].isupper()
@@ -187,6 +191,8 @@ def _small_number(text: str, match: re.Match[str]) -> bool:
187
191
  before, after = text[: match.start()], text[match.end() :]
188
192
  if _BEFORE_NUMBER.search(before) or _UNIT_AFTER.match(after):
189
193
  return False
194
+ if _quoted(text, match.start()): # a name or a quote: «peso: 2»
195
+ return False
190
196
  if _NAME_AFTER.match(after) or _RANGE_AFTER.match(after):
191
197
  return False
192
198
  if _RANGE_BEFORE.search(before):
@@ -195,8 +201,14 @@ def _small_number(text: str, match: re.Match[str]) -> bool:
195
201
 
196
202
 
197
203
  def _empty_adjective(text: str, match: re.Match[str]) -> bool:
198
- if match.group(0).lower().startswith("hist"):
204
+ word = match.group(0).lower()
205
+ if word.startswith("hist"):
199
206
  return not _HISTORIC.search(text[: match.start()])
207
+ if word.startswith("significativ"):
208
+ # the statistical term, not an intensifier
209
+ return not re.search(
210
+ r"estadísticamente\s+$", text[: match.start()], re.I
211
+ )
200
212
  return True
201
213
 
202
214
 
@@ -283,7 +295,7 @@ _RULES: tuple[_Rule, ...] = (
283
295
  "todo",
284
296
  ),
285
297
  _r(
286
- r"\bpor ciento\b",
298
+ r"\bpor cien(?:to)?\b(?!\s+(?:de|del)\b)",
287
299
  "porcentaje",
288
300
  "escribe «%» con espacio fijo",
289
301
  scope="prosa",
@@ -346,7 +358,8 @@ _RULES: tuple[_Rule, ...] = (
346
358
  flags=re.I,
347
359
  ),
348
360
  _r(
349
- rf"\b(?:de|en|el|del|\d+) (?:{_MONTHS})\b",
361
+ rf"\b(?:de|en|el|del|desde|hasta|entre|hacia|para|y|\d+)"
362
+ rf" (?:{_MONTHS})\b",
350
363
  "mes",
351
364
  "los meses y los días de la semana van en minúscula",
352
365
  _month_context,
@@ -391,6 +404,27 @@ _RULES: tuple[_Rule, ...] = (
391
404
  scope="prosa",
392
405
  flags=re.I,
393
406
  ),
407
+ _r(
408
+ r"\bse\s+desplom\w*|\bdesplom(?:e|es|ó|aron)\b",
409
+ "registro",
410
+ "registro coloquial: «cayó un 5 %», «registró la mayor caída desde...»",
411
+ scope="prosa",
412
+ flags=re.I,
413
+ ),
414
+ _r(
415
+ r"\bse\s+dispar(?:a|an|ó|aron|aba|aban)\b",
416
+ "registro",
417
+ "registro coloquial: «subió con fuerza», «aumentó un 5 %»",
418
+ scope="prosa",
419
+ flags=re.I,
420
+ ),
421
+ _r(
422
+ r"\btir[oó]n(?:es)?\b",
423
+ "registro",
424
+ "registro coloquial: «aumento», «repunte»",
425
+ scope="prosa",
426
+ flags=re.I,
427
+ ),
394
428
  _r(
395
429
  r"\ba trozos\b",
396
430
  "registro",
@@ -406,7 +440,8 @@ _RULES: tuple[_Rule, ...] = (
406
440
  flags=re.I,
407
441
  ),
408
442
  _r(
409
- r"\b(?:burbuja|sobrevalora\w*|excesiv[oa]s?|insostenibles?)\b",
443
+ r"\b(?:burbuja|sobrevalora\w*|excesiv[oa]s?|insostenibles?"
444
+ r"|preocupantes?|alarmantes?)\b",
410
445
  "juicio",
411
446
  "sin juicios subjetivos: descríbelo con indicadores («valoraciones"
412
447
  " elevadas en perspectiva histórica») o atribúyelo a quien lo dice",
@@ -423,7 +458,8 @@ _RULES: tuple[_Rule, ...] = (
423
458
  ),
424
459
  _r(
425
460
  r"\b(?:enorme|enormes|histórico|histórica|históricos|históricas|muy"
426
- r"|extraordinari[oa]s?|espectacular(?:es)?|dramátic[oa]s?)\b",
461
+ r"|extraordinari[oa]s?|espectacular(?:es)?|dramátic[oa]s?|realmente"
462
+ r"|claramente|significativ[oa]s?|significativamente|notablemente)\b",
427
463
  "precision",
428
464
  "adjetivo o intensificador que no añade información: úsalo solo si"
429
465
  " una cifra lo respalda",
@@ -439,7 +475,8 @@ _RULES: tuple[_Rule, ...] = (
439
475
  flags=re.I,
440
476
  ),
441
477
  _r(
442
- r"\b(?:el|la|los|las|del)\s+(?:Presidente|Presidenta|Ministro|Ministra"
478
+ r"\b(?:[Ee]l|[Ll]a|[Ll]os|[Ll]as|[Dd]el)\s+(?:Presidente|Presidenta"
479
+ r"|Ministro|Ministra"
443
480
  r"|Gobernador|Gobernadora|Vicepresidente|Vicepresidenta|Consejero"
444
481
  r"|Consejera|Bolsa|Euro|Dólar)\b",
445
482
  "mayusculas",
@@ -547,7 +584,11 @@ _KNOWN_ACRONYMS = {
547
584
  "CNH",
548
585
  "RMB",
549
586
  }
550
- _ACRONYM = re.compile(r"\b[A-ZÁÉÍÓÚÑ]{2,6}\b")
587
+ #: An acronym: in capitals («CNMV») or with a lowercase letter inside
588
+ #: («AIReF», «BdE»).
589
+ _ACRONYM = re.compile(
590
+ r"\b(?:[A-ZÁÉÍÓÚÑ]{2,6}|[A-Z]{2,4}[a-z][A-Z]{1,2}|[A-Z][a-z][A-Z])\b"
591
+ )
551
592
 
552
593
 
553
594
  def _scope_texts(text: str, scope: str) -> Iterator[_Rule]:
@@ -386,12 +386,55 @@ def _hash_comments(text: str, line: int) -> None:
386
386
  )
387
387
 
388
388
 
389
+ #: A decimal comma («3,5», «1.234,5»), not a date («2024-01-01,2024»).
390
+ _DECIMAL_COMMA = re.compile(
391
+ r"(?<![\d\-])(?:\d{1,3}(?:\.\d{3})+|\d+),\d+(?![\d\-:])"
392
+ )
393
+ #: A bracket, or a comma that separates the elements of a flow list.
394
+ _SEPARATOR = re.compile(r"[\[\]{}]|,(?!\d)|(?<!\d),")
395
+
396
+
397
+ def _decimal_commas(text: str, line: int) -> None:
398
+ """Reject «3,5» inside a flow list or map (``[...]``, ``{...}``):
399
+ YAML splits it at the comma, into «3» and «5»."""
400
+ depth = 0
401
+ for offset, raw in enumerate(text.splitlines()):
402
+ # quoted text keeps its length, so positions match *raw*
403
+ bare = _QUOTED.sub(lambda m: "'" + "_" * (len(m[0]) - 2) + "'", raw)
404
+ marks = [(m.start(), m[0]) for m in _SEPARATOR.finditer(bare)]
405
+ for match in _DECIMAL_COMMA.finditer(bare):
406
+ before = [(n, c) for n, c in marks if n < match.start()]
407
+ opened = depth + sum(
408
+ 1 if c in "[{" else -1 for _, c in before if c != ","
409
+ )
410
+ if opened <= 0:
411
+ continue
412
+ start = before[-1][0] + 1 if before else 0
413
+ end = next((n for n, _ in marks if n >= match.end()), len(raw))
414
+ element = re.sub(r"^[\w-]+:\s+", "", raw[start:end].strip())
415
+ if re.fullmatch(r"[-+]?[\d.]+(?:,\d+)+", element):
416
+ plain = element.replace(".", "").replace(",", ".")
417
+ hint = (
418
+ f"los decimales van con punto ({plain}); si son varias"
419
+ " cifras, sepáralas con coma y espacio"
420
+ )
421
+ else:
422
+ hint = f'entrecomíllalo ("{element}")'
423
+ raise NoteSyntaxError(
424
+ line + offset,
425
+ f"«{element}» lleva coma decimal y, entre [...] o {{...}},"
426
+ f" YAML lo parte en dos por la coma: {hint}",
427
+ )
428
+ depth += sum(1 if c in "[{" else -1 for _, c in marks if c != ",")
429
+
430
+
389
431
  def _yaml(text: str, line: int) -> Any:
390
432
  """``yaml.safe_load`` with the errors in Spanish and with their line.
391
433
 
392
434
  *line* is the line of the source where *text* starts.
393
435
  """
394
436
  _hash_comments(text, line)
437
+ _decimal_commas(text, line)
395
438
  try:
396
439
  return yaml.safe_load(_windows_paths(text))
397
440
  except yaml.YAMLError as exc: