denckring 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (657) hide show
  1. denckring-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +37 -0
  2. denckring-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  3. denckring-0.1.0/.github/ISSUE_TEMPLATE/new_procedure.yml +49 -0
  4. denckring-0.1.0/.github/dependabot.yml +39 -0
  5. denckring-0.1.0/.github/pull_request_template.md +18 -0
  6. denckring-0.1.0/.github/workflows/ci.yml +293 -0
  7. denckring-0.1.0/.github/workflows/release.yml +175 -0
  8. denckring-0.1.0/.gitignore +39 -0
  9. denckring-0.1.0/.pre-commit-config.yaml +31 -0
  10. denckring-0.1.0/.python-version +1 -0
  11. denckring-0.1.0/CHANGELOG.md +1202 -0
  12. denckring-0.1.0/CITATION.cff +29 -0
  13. denckring-0.1.0/CODE_OF_CONDUCT.md +34 -0
  14. denckring-0.1.0/CONTRIBUTING.md +116 -0
  15. denckring-0.1.0/LICENSE +202 -0
  16. denckring-0.1.0/LICENSE-DATA +56 -0
  17. denckring-0.1.0/Makefile +151 -0
  18. denckring-0.1.0/NOTICE +16 -0
  19. denckring-0.1.0/PKG-INFO +335 -0
  20. denckring-0.1.0/README.md +291 -0
  21. denckring-0.1.0/SECURITY.md +30 -0
  22. denckring-0.1.0/docs/adr/0001-english-api-multilingual-content.md +22 -0
  23. denckring-0.1.0/docs/adr/0002-check-mandatory-apply-optional.md +28 -0
  24. denckring-0.1.0/docs/adr/0003-catalogue-yaml-not-sqlite.md +19 -0
  25. denckring-0.1.0/docs/adr/0004-language-packs-as-capabilities.md +24 -0
  26. denckring-0.1.0/docs/adr/0005-continuous-score.md +20 -0
  27. denckring-0.1.0/docs/adr/0006-dual-licence.md +25 -0
  28. denckring-0.1.0/docs/adr/0007-one-module-per-procedure.md +21 -0
  29. denckring-0.1.0/docs/adr/0008-catalogue-inside-package.md +19 -0
  30. denckring-0.1.0/docs/adr/0009-folding-is-a-parameter.md +30 -0
  31. denckring-0.1.0/docs/adr/0010-packs-via-entry-points.md +29 -0
  32. denckring-0.1.0/docs/adr/0011-checkability.md +44 -0
  33. denckring-0.1.0/docs/adr/0012-syllable-capabilities.md +35 -0
  34. denckring-0.1.0/docs/adr/0013-data-as-a-separate-distribution.md +32 -0
  35. denckring-0.1.0/docs/adr/0014-metre-as-satisfiability.md +40 -0
  36. denckring-0.1.0/docs/adr/0015-lexicon-capabilities.md +37 -0
  37. denckring-0.1.0/docs/adr/0016-one-pack-per-language.md +30 -0
  38. denckring-0.1.0/docs/adr/0017-devices-ship-with-their-procedures.md +43 -0
  39. denckring-0.1.0/docs/adr/0018-attestation.md +43 -0
  40. denckring-0.1.0/docs/adr/0019-figures.md +45 -0
  41. denckring-0.1.0/docs/adr/0020-corpora-are-not-shipped.md +39 -0
  42. denckring-0.1.0/docs/adr/0021-the-transferable-part.md +42 -0
  43. denckring-0.1.0/docs/adr/0022-german-moves-to-a-default.md +31 -0
  44. denckring-0.1.0/docs/adr/0023-the-german-lexicon.md +49 -0
  45. denckring-0.1.0/docs/adr/0024-apache-two-for-the-code.md +54 -0
  46. denckring-0.1.0/docs/adr/0025-the-apply-spine.md +115 -0
  47. denckring-0.1.0/docs/adr/0026-the-production-surface.md +144 -0
  48. denckring-0.1.0/docs/adr/0027-candidates-carry-their-scores.md +107 -0
  49. denckring-0.1.0/docs/adr/0028-the-graded-lexicon.md +154 -0
  50. denckring-0.1.0/docs/adr/0029-the-choices-the-catalogue-promised.md +213 -0
  51. denckring-0.1.0/docs/adr/0030-german-pronunciations.md +194 -0
  52. denckring-0.1.0/docs/adr/0031-devices-are-address-spaces.md +151 -0
  53. denckring-0.1.0/docs/adr/0032-the-french-lexicon.md +204 -0
  54. denckring-0.1.0/docs/adr/0033-the-layer-split.md +117 -0
  55. denckring-0.1.0/docs/adr/0034-french-prosody.md +221 -0
  56. denckring-0.1.0/docs/adr/0035-orthographic-and-phonetic-readings.md +298 -0
  57. denckring-0.1.0/docs/adr/0036-the-elision-seam.md +109 -0
  58. denckring-0.1.0/docs/adr/0037-the-calculator-word.md +128 -0
  59. denckring-0.1.0/docs/adr/0038-german-frequency.md +147 -0
  60. denckring-0.1.0/docs/api/errors.md +35 -0
  61. denckring-0.1.0/docs/api/functions.md +15 -0
  62. denckring-0.1.0/docs/api/index.md +78 -0
  63. denckring-0.1.0/docs/api/packs.md +23 -0
  64. denckring-0.1.0/docs/api/types.md +19 -0
  65. denckring-0.1.0/mkdocs.yml +134 -0
  66. denckring-0.1.0/pyproject.toml +170 -0
  67. denckring-0.1.0/scripts/build_docs.py +263 -0
  68. denckring-0.1.0/scripts/build_overview_gif.py +124 -0
  69. denckring-0.1.0/scripts/new_procedure.py +81 -0
  70. denckring-0.1.0/scripts/rings_from_text.py +82 -0
  71. denckring-0.1.0/scripts/templates/catalogue_row.yaml.tmpl +12 -0
  72. denckring-0.1.0/scripts/templates/golden.yaml.tmpl +13 -0
  73. denckring-0.1.0/scripts/templates/procedure.py.tmpl +25 -0
  74. denckring-0.1.0/scripts/templates/strategy.py.tmpl +12 -0
  75. denckring-0.1.0/scripts/templates/test.py.tmpl +9 -0
  76. denckring-0.1.0/skills/denckring/SKILL.md +65 -0
  77. denckring-0.1.0/src/denckring/__init__.py +65 -0
  78. denckring-0.1.0/src/denckring/cli.py +359 -0
  79. denckring-0.1.0/src/denckring/core/__init__.py +0 -0
  80. denckring-0.1.0/src/denckring/core/arca.py +94 -0
  81. denckring-0.1.0/src/denckring/core/base.py +447 -0
  82. denckring-0.1.0/src/denckring/core/calculator.py +78 -0
  83. denckring-0.1.0/src/denckring/core/catalogue.py +46 -0
  84. denckring-0.1.0/src/denckring/core/corpus.py +126 -0
  85. denckring-0.1.0/src/denckring/core/describe.py +250 -0
  86. denckring-0.1.0/src/denckring/core/device.py +670 -0
  87. denckring-0.1.0/src/denckring/core/errors.py +516 -0
  88. denckring-0.1.0/src/denckring/core/pasigraph.py +88 -0
  89. denckring-0.1.0/src/denckring/core/prosody.py +395 -0
  90. denckring-0.1.0/src/denckring/core/protocol.py +282 -0
  91. denckring-0.1.0/src/denckring/core/registry.py +55 -0
  92. denckring-0.1.0/src/denckring/core/source_compare.py +233 -0
  93. denckring-0.1.0/src/denckring/core/text.py +187 -0
  94. denckring-0.1.0/src/denckring/data/catalogue.yaml +3539 -0
  95. denckring-0.1.0/src/denckring/data/devices/harsdoerffer_1651.yaml +337 -0
  96. denckring-0.1.0/src/denckring/data/devices/poesieautomat_2000.yaml +602 -0
  97. denckring-0.1.0/src/denckring/data/figures/kircher_ars_magna.yaml +22 -0
  98. denckring-0.1.0/src/denckring/data/figures/llull_ternary.yaml +80 -0
  99. denckring-0.1.0/src/denckring/eval/__init__.py +0 -0
  100. denckring-0.1.0/src/denckring/eval/fixtures/golden/abecedarian.yaml +32 -0
  101. denckring-0.1.0/src/denckring/eval/fixtures/golden/acrostic.yaml +46 -0
  102. denckring-0.1.0/src/denckring/eval/fixtures/golden/alcaic_stanza.yaml +46 -0
  103. denckring-0.1.0/src/denckring/eval/fixtures/golden/alexandrine.yaml +37 -0
  104. denckring-0.1.0/src/denckring/eval/fixtures/golden/alliterative_verse.yaml +41 -0
  105. denckring-0.1.0/src/denckring/eval/fixtures/golden/alphabetical_sentence.yaml +13 -0
  106. denckring-0.1.0/src/denckring/eval/fixtures/golden/anagram.yaml +60 -0
  107. denckring-0.1.0/src/denckring/eval/fixtures/golden/anaphora.yaml +18 -0
  108. denckring-0.1.0/src/denckring/eval/fixtures/golden/antigram.yaml +13 -0
  109. denckring-0.1.0/src/denckring/eval/fixtures/golden/arca_musarithmica.yaml +61 -0
  110. denckring-0.1.0/src/denckring/eval/fixtures/golden/assonance_constraint.yaml +41 -0
  111. denckring-0.1.0/src/denckring/eval/fixtures/golden/ballade.yaml +169 -0
  112. denckring-0.1.0/src/denckring/eval/fixtures/golden/beau_present.yaml +25 -0
  113. denckring-0.1.0/src/denckring/eval/fixtures/golden/belle_absente.yaml +19 -0
  114. denckring-0.1.0/src/denckring/eval/fixtures/golden/bivocalic.yaml +19 -0
  115. denckring-0.1.0/src/denckring/eval/fixtures/golden/blank_verse.yaml +48 -0
  116. denckring-0.1.0/src/denckring/eval/fixtures/golden/boustrophedon.yaml +53 -0
  117. denckring-0.1.0/src/denckring/eval/fixtures/golden/buchstabwechsel.yaml +24 -0
  118. denckring-0.1.0/src/denckring/eval/fixtures/golden/calculator_word.yaml +103 -0
  119. denckring-0.1.0/src/denckring/eval/fixtures/golden/cent_mille_milliards.yaml +103 -0
  120. denckring-0.1.0/src/denckring/eval/fixtures/golden/charade.yaml +36 -0
  121. denckring-0.1.0/src/denckring/eval/fixtures/golden/chronogram.yaml +13 -0
  122. denckring-0.1.0/src/denckring/eval/fixtures/golden/cinquain.yaml +56 -0
  123. denckring-0.1.0/src/denckring/eval/fixtures/golden/clerihew.yaml +51 -0
  124. denckring-0.1.0/src/denckring/eval/fixtures/golden/column_reading.yaml +45 -0
  125. denckring-0.1.0/src/denckring/eval/fixtures/golden/consonantal_lipogram.yaml +13 -0
  126. denckring-0.1.0/src/denckring/eval/fixtures/golden/curtal_sonnet.yaml +83 -0
  127. denckring-0.1.0/src/denckring/eval/fixtures/golden/cut_up.yaml +13 -0
  128. denckring-0.1.0/src/denckring/eval/fixtures/golden/dactylic_hexameter.yaml +29 -0
  129. denckring-0.1.0/src/denckring/eval/fixtures/golden/definitional_expansion.yaml +51 -0
  130. denckring-0.1.0/src/denckring/eval/fixtures/golden/definitional_literature.yaml +58 -0
  131. denckring-0.1.0/src/denckring/eval/fixtures/golden/denckring.yaml +19 -0
  132. denckring-0.1.0/src/denckring/eval/fixtures/golden/diastic.yaml +33 -0
  133. denckring-0.1.0/src/denckring/eval/fixtures/golden/double_acrostic.yaml +17 -0
  134. denckring-0.1.0/src/denckring/eval/fixtures/golden/double_dactyl.yaml +50 -0
  135. denckring-0.1.0/src/denckring/eval/fixtures/golden/elegiac_couplet.yaml +32 -0
  136. denckring-0.1.0/src/denckring/eval/fixtures/golden/englyn.yaml +69 -0
  137. denckring-0.1.0/src/denckring/eval/fixtures/golden/eodermdrome.yaml +13 -0
  138. denckring-0.1.0/src/denckring/eval/fixtures/golden/epistrophe.yaml +17 -0
  139. denckring-0.1.0/src/denckring/eval/fixtures/golden/every_nth_word.yaml +13 -0
  140. denckring-0.1.0/src/denckring/eval/fixtures/golden/fold_in.yaml +57 -0
  141. denckring-0.1.0/src/denckring/eval/fixtures/golden/ghazal.yaml +59 -0
  142. denckring-0.1.0/src/denckring/eval/fixtures/golden/haibun.yaml +48 -0
  143. denckring-0.1.0/src/denckring/eval/fixtures/golden/haiku.yaml +46 -0
  144. denckring-0.1.0/src/denckring/eval/fixtures/golden/haikuization.yaml +41 -0
  145. denckring-0.1.0/src/denckring/eval/fixtures/golden/hemeling.yaml +86 -0
  146. denckring-0.1.0/src/denckring/eval/fixtures/golden/hendecasyllable.yaml +31 -0
  147. denckring-0.1.0/src/denckring/eval/fixtures/golden/heroic_couplet.yaml +40 -0
  148. denckring-0.1.0/src/denckring/eval/fixtures/golden/heterogram.yaml +25 -0
  149. denckring-0.1.0/src/denckring/eval/fixtures/golden/homoconsonantism.yaml +25 -0
  150. denckring-0.1.0/src/denckring/eval/fixtures/golden/homoteleuton.yaml +27 -0
  151. denckring-0.1.0/src/denckring/eval/fixtures/golden/homovocalism.yaml +25 -0
  152. denckring-0.1.0/src/denckring/eval/fixtures/golden/iambic_pentameter.yaml +44 -0
  153. denckring-0.1.0/src/denckring/eval/fixtures/golden/ideenwuerfeln.yaml +44 -0
  154. denckring-0.1.0/src/denckring/eval/fixtures/golden/kangaroo_word.yaml +52 -0
  155. denckring-0.1.0/src/denckring/eval/fixtures/golden/larding.yaml +25 -0
  156. denckring-0.1.0/src/denckring/eval/fixtures/golden/letter_bank.yaml +13 -0
  157. denckring-0.1.0/src/denckring/eval/fixtures/golden/limerick.yaml +56 -0
  158. denckring-0.1.0/src/denckring/eval/fixtures/golden/lipogram.yaml +44 -0
  159. denckring-0.1.0/src/denckring/eval/fixtures/golden/lipogrammatic_translation.yaml +25 -0
  160. denckring-0.1.0/src/denckring/eval/fixtures/golden/liponym.yaml +13 -0
  161. denckring-0.1.0/src/denckring/eval/fixtures/golden/llull_figure.yaml +28 -0
  162. denckring-0.1.0/src/denckring/eval/fixtures/golden/mathews_algorithm.yaml +61 -0
  163. denckring-0.1.0/src/denckring/eval/fixtures/golden/melting_text.yaml +13 -0
  164. denckring-0.1.0/src/denckring/eval/fixtures/golden/mesostic.yaml +49 -0
  165. denckring-0.1.0/src/denckring/eval/fixtures/golden/monoconsonantal.yaml +21 -0
  166. denckring-0.1.0/src/denckring/eval/fixtures/golden/monosyllabic_prose.yaml +31 -0
  167. denckring-0.1.0/src/denckring/eval/fixtures/golden/multiple_constraint.yaml +33 -0
  168. denckring-0.1.0/src/denckring/eval/fixtures/golden/n_plus_7.yaml +69 -0
  169. denckring-0.1.0/src/denckring/eval/fixtures/golden/ottava_rima.yaml +68 -0
  170. denckring-0.1.0/src/denckring/eval/fixtures/golden/palindrome.yaml +31 -0
  171. denckring-0.1.0/src/denckring/eval/fixtures/golden/pangram.yaml +30 -0
  172. denckring-0.1.0/src/denckring/eval/fixtures/golden/pangrammatic_lipogram.yaml +27 -0
  173. denckring-0.1.0/src/denckring/eval/fixtures/golden/pangrammatic_window.yaml +24 -0
  174. denckring-0.1.0/src/denckring/eval/fixtures/golden/pantoum.yaml +29 -0
  175. denckring-0.1.0/src/denckring/eval/fixtures/golden/paragram.yaml +13 -0
  176. denckring-0.1.0/src/denckring/eval/fixtures/golden/pasigraphy.yaml +34 -0
  177. denckring-0.1.0/src/denckring/eval/fixtures/golden/petrarchan_sonnet.yaml +72 -0
  178. denckring-0.1.0/src/denckring/eval/fixtures/golden/poesie_automat.yaml +78 -0
  179. denckring-0.1.0/src/denckring/eval/fixtures/golden/prisoners_constraint.yaml +31 -0
  180. denckring-0.1.0/src/denckring/eval/fixtures/golden/proteus_verse.yaml +68 -0
  181. denckring-0.1.0/src/denckring/eval/fixtures/golden/quenina.yaml +60 -0
  182. denckring-0.1.0/src/denckring/eval/fixtures/golden/recombination.yaml +13 -0
  183. denckring-0.1.0/src/denckring/eval/fixtures/golden/renga.yaml +55 -0
  184. denckring-0.1.0/src/denckring/eval/fixtures/golden/reverse_snowball.yaml +25 -0
  185. denckring-0.1.0/src/denckring/eval/fixtures/golden/rhyme_royal.yaml +49 -0
  186. denckring-0.1.0/src/denckring/eval/fixtures/golden/rhyme_scheme.yaml +68 -0
  187. denckring-0.1.0/src/denckring/eval/fixtures/golden/rondeau.yaml +106 -0
  188. denckring-0.1.0/src/denckring/eval/fixtures/golden/s_plus_7.yaml +72 -0
  189. denckring-0.1.0/src/denckring/eval/fixtures/golden/sapphic_stanza.yaml +45 -0
  190. denckring-0.1.0/src/denckring/eval/fixtures/golden/sator_square.yaml +23 -0
  191. denckring-0.1.0/src/denckring/eval/fixtures/golden/semordnilap.yaml +43 -0
  192. denckring-0.1.0/src/denckring/eval/fixtures/golden/senryu.yaml +46 -0
  193. denckring-0.1.0/src/denckring/eval/fixtures/golden/sentence_length_constraint.yaml +13 -0
  194. denckring-0.1.0/src/denckring/eval/fixtures/golden/serial_lipogram.yaml +129 -0
  195. denckring-0.1.0/src/denckring/eval/fixtures/golden/sestina.yaml +61 -0
  196. denckring-0.1.0/src/denckring/eval/fixtures/golden/shakespearean_sonnet.yaml +72 -0
  197. denckring-0.1.0/src/denckring/eval/fixtures/golden/single_sentence.yaml +13 -0
  198. denckring-0.1.0/src/denckring/eval/fixtures/golden/slenderizing.yaml +51 -0
  199. denckring-0.1.0/src/denckring/eval/fixtures/golden/snowball.yaml +25 -0
  200. denckring-0.1.0/src/denckring/eval/fixtures/golden/snowball_sentence.yaml +13 -0
  201. denckring-0.1.0/src/denckring/eval/fixtures/golden/sonnet.yaml +98 -0
  202. denckring-0.1.0/src/denckring/eval/fixtures/golden/spenserian_stanza.yaml +74 -0
  203. denckring-0.1.0/src/denckring/eval/fixtures/golden/spoonerism.yaml +43 -0
  204. denckring-0.1.0/src/denckring/eval/fixtures/golden/supervocalic.yaml +29 -0
  205. denckring-0.1.0/src/denckring/eval/fixtures/golden/syllable_count.yaml +49 -0
  206. denckring-0.1.0/src/denckring/eval/fixtures/golden/tanka.yaml +52 -0
  207. denckring-0.1.0/src/denckring/eval/fixtures/golden/tautogram.yaml +39 -0
  208. denckring-0.1.0/src/denckring/eval/fixtures/golden/tautonym.yaml +13 -0
  209. denckring-0.1.0/src/denckring/eval/fixtures/golden/telestich.yaml +35 -0
  210. denckring-0.1.0/src/denckring/eval/fixtures/golden/terza_rima.yaml +62 -0
  211. denckring-0.1.0/src/denckring/eval/fixtures/golden/text_folding.yaml +73 -0
  212. denckring-0.1.0/src/denckring/eval/fixtures/golden/tmesis.yaml +43 -0
  213. denckring-0.1.0/src/denckring/eval/fixtures/golden/transposal.yaml +13 -0
  214. denckring-0.1.0/src/denckring/eval/fixtures/golden/triolet.yaml +71 -0
  215. denckring-0.1.0/src/denckring/eval/fixtures/golden/trochaic_tetrameter.yaml +30 -0
  216. denckring-0.1.0/src/denckring/eval/fixtures/golden/univocalic.yaml +33 -0
  217. denckring-0.1.0/src/denckring/eval/fixtures/golden/univocalic_translation.yaml +25 -0
  218. denckring-0.1.0/src/denckring/eval/fixtures/golden/villanelle.yaml +100 -0
  219. denckring-0.1.0/src/denckring/eval/fixtures/golden/wechselsatz.yaml +13 -0
  220. denckring-0.1.0/src/denckring/eval/fixtures/golden/word_ladder.yaml +57 -0
  221. denckring-0.1.0/src/denckring/eval/fixtures/golden/word_square.yaml +57 -0
  222. denckring-0.1.0/src/denckring/eval/harness.py +181 -0
  223. denckring-0.1.0/src/denckring/lang/__init__.py +92 -0
  224. denckring-0.1.0/src/denckring/lang/base.py +228 -0
  225. denckring-0.1.0/src/denckring/lang/de.py +80 -0
  226. denckring-0.1.0/src/denckring/lang/en.py +65 -0
  227. denckring-0.1.0/src/denckring/lang/fr.py +53 -0
  228. denckring-0.1.0/src/denckring/mcp/__init__.py +1 -0
  229. denckring-0.1.0/src/denckring/mcp/server.py +141 -0
  230. denckring-0.1.0/src/denckring/procedures/__init__.py +0 -0
  231. denckring-0.1.0/src/denckring/procedures/abecedarian.py +87 -0
  232. denckring-0.1.0/src/denckring/procedures/acrostic.py +87 -0
  233. denckring-0.1.0/src/denckring/procedures/alcaic_stanza.py +48 -0
  234. denckring-0.1.0/src/denckring/procedures/alexandrine.py +32 -0
  235. denckring-0.1.0/src/denckring/procedures/alliterative_verse.py +71 -0
  236. denckring-0.1.0/src/denckring/procedures/alphabetical_sentence.py +45 -0
  237. denckring-0.1.0/src/denckring/procedures/anagram.py +334 -0
  238. denckring-0.1.0/src/denckring/procedures/anaphora.py +46 -0
  239. denckring-0.1.0/src/denckring/procedures/antigram.py +53 -0
  240. denckring-0.1.0/src/denckring/procedures/arca_musarithmica.py +146 -0
  241. denckring-0.1.0/src/denckring/procedures/assonance_constraint.py +97 -0
  242. denckring-0.1.0/src/denckring/procedures/ballade.py +116 -0
  243. denckring-0.1.0/src/denckring/procedures/beau_present.py +58 -0
  244. denckring-0.1.0/src/denckring/procedures/belle_absente.py +94 -0
  245. denckring-0.1.0/src/denckring/procedures/bivocalic.py +78 -0
  246. denckring-0.1.0/src/denckring/procedures/blank_verse.py +59 -0
  247. denckring-0.1.0/src/denckring/procedures/boustrophedon.py +111 -0
  248. denckring-0.1.0/src/denckring/procedures/buchstabwechsel.py +121 -0
  249. denckring-0.1.0/src/denckring/procedures/calculator_word.py +266 -0
  250. denckring-0.1.0/src/denckring/procedures/cent_mille_milliards.py +135 -0
  251. denckring-0.1.0/src/denckring/procedures/charade.py +68 -0
  252. denckring-0.1.0/src/denckring/procedures/chronogram.py +62 -0
  253. denckring-0.1.0/src/denckring/procedures/cinquain.py +30 -0
  254. denckring-0.1.0/src/denckring/procedures/clerihew.py +43 -0
  255. denckring-0.1.0/src/denckring/procedures/column_reading.py +103 -0
  256. denckring-0.1.0/src/denckring/procedures/consonantal_lipogram.py +52 -0
  257. denckring-0.1.0/src/denckring/procedures/curtal_sonnet.py +40 -0
  258. denckring-0.1.0/src/denckring/procedures/cut_up.py +73 -0
  259. denckring-0.1.0/src/denckring/procedures/dactylic_hexameter.py +73 -0
  260. denckring-0.1.0/src/denckring/procedures/definitional_expansion.py +176 -0
  261. denckring-0.1.0/src/denckring/procedures/definitional_literature.py +194 -0
  262. denckring-0.1.0/src/denckring/procedures/denckring.py +167 -0
  263. denckring-0.1.0/src/denckring/procedures/diastic.py +129 -0
  264. denckring-0.1.0/src/denckring/procedures/double_acrostic.py +80 -0
  265. denckring-0.1.0/src/denckring/procedures/double_dactyl.py +77 -0
  266. denckring-0.1.0/src/denckring/procedures/elegiac_couplet.py +75 -0
  267. denckring-0.1.0/src/denckring/procedures/englyn.py +50 -0
  268. denckring-0.1.0/src/denckring/procedures/eodermdrome.py +78 -0
  269. denckring-0.1.0/src/denckring/procedures/epistrophe.py +46 -0
  270. denckring-0.1.0/src/denckring/procedures/every_nth_word.py +77 -0
  271. denckring-0.1.0/src/denckring/procedures/fold_in.py +157 -0
  272. denckring-0.1.0/src/denckring/procedures/ghazal.py +131 -0
  273. denckring-0.1.0/src/denckring/procedures/haibun.py +114 -0
  274. denckring-0.1.0/src/denckring/procedures/haiku.py +30 -0
  275. denckring-0.1.0/src/denckring/procedures/haikuization.py +113 -0
  276. denckring-0.1.0/src/denckring/procedures/hemeling.py +135 -0
  277. denckring-0.1.0/src/denckring/procedures/hendecasyllable.py +32 -0
  278. denckring-0.1.0/src/denckring/procedures/heroic_couplet.py +56 -0
  279. denckring-0.1.0/src/denckring/procedures/heterogram.py +62 -0
  280. denckring-0.1.0/src/denckring/procedures/homoconsonantism.py +47 -0
  281. denckring-0.1.0/src/denckring/procedures/homoteleuton.py +68 -0
  282. denckring-0.1.0/src/denckring/procedures/homovocalism.py +44 -0
  283. denckring-0.1.0/src/denckring/procedures/iambic_pentameter.py +42 -0
  284. denckring-0.1.0/src/denckring/procedures/ideenwuerfeln.py +186 -0
  285. denckring-0.1.0/src/denckring/procedures/kangaroo_word.py +100 -0
  286. denckring-0.1.0/src/denckring/procedures/larding.py +161 -0
  287. denckring-0.1.0/src/denckring/procedures/letter_bank.py +57 -0
  288. denckring-0.1.0/src/denckring/procedures/limerick.py +40 -0
  289. denckring-0.1.0/src/denckring/procedures/lipogram.py +62 -0
  290. denckring-0.1.0/src/denckring/procedures/lipogrammatic_translation.py +63 -0
  291. denckring-0.1.0/src/denckring/procedures/liponym.py +51 -0
  292. denckring-0.1.0/src/denckring/procedures/llull_figure.py +158 -0
  293. denckring-0.1.0/src/denckring/procedures/mathews_algorithm.py +144 -0
  294. denckring-0.1.0/src/denckring/procedures/melting_text.py +73 -0
  295. denckring-0.1.0/src/denckring/procedures/mesostic.py +118 -0
  296. denckring-0.1.0/src/denckring/procedures/monoconsonantal.py +71 -0
  297. denckring-0.1.0/src/denckring/procedures/monosyllabic_prose.py +50 -0
  298. denckring-0.1.0/src/denckring/procedures/multiple_constraint.py +131 -0
  299. denckring-0.1.0/src/denckring/procedures/n_plus_7.py +301 -0
  300. denckring-0.1.0/src/denckring/procedures/ottava_rima.py +39 -0
  301. denckring-0.1.0/src/denckring/procedures/palindrome.py +57 -0
  302. denckring-0.1.0/src/denckring/procedures/pangram.py +64 -0
  303. denckring-0.1.0/src/denckring/procedures/pangrammatic_lipogram.py +79 -0
  304. denckring-0.1.0/src/denckring/procedures/pangrammatic_window.py +78 -0
  305. denckring-0.1.0/src/denckring/procedures/pantoum.py +66 -0
  306. denckring-0.1.0/src/denckring/procedures/paragram.py +177 -0
  307. denckring-0.1.0/src/denckring/procedures/pasigraphy.py +158 -0
  308. denckring-0.1.0/src/denckring/procedures/petrarchan_sonnet.py +41 -0
  309. denckring-0.1.0/src/denckring/procedures/poesie_automat.py +166 -0
  310. denckring-0.1.0/src/denckring/procedures/prisoners_constraint.py +48 -0
  311. denckring-0.1.0/src/denckring/procedures/proteus_verse.py +314 -0
  312. denckring-0.1.0/src/denckring/procedures/quenina.py +158 -0
  313. denckring-0.1.0/src/denckring/procedures/recombination.py +106 -0
  314. denckring-0.1.0/src/denckring/procedures/renga.py +96 -0
  315. denckring-0.1.0/src/denckring/procedures/reverse_snowball.py +37 -0
  316. denckring-0.1.0/src/denckring/procedures/rhyme_royal.py +41 -0
  317. denckring-0.1.0/src/denckring/procedures/rhyme_scheme.py +156 -0
  318. denckring-0.1.0/src/denckring/procedures/rondeau.py +89 -0
  319. denckring-0.1.0/src/denckring/procedures/s_plus_7.py +51 -0
  320. denckring-0.1.0/src/denckring/procedures/sapphic_stanza.py +52 -0
  321. denckring-0.1.0/src/denckring/procedures/sator_square.py +109 -0
  322. denckring-0.1.0/src/denckring/procedures/semordnilap.py +60 -0
  323. denckring-0.1.0/src/denckring/procedures/senryu.py +30 -0
  324. denckring-0.1.0/src/denckring/procedures/sentence_length_constraint.py +51 -0
  325. denckring-0.1.0/src/denckring/procedures/serial_lipogram.py +113 -0
  326. denckring-0.1.0/src/denckring/procedures/sestina.py +31 -0
  327. denckring-0.1.0/src/denckring/procedures/shakespearean_sonnet.py +41 -0
  328. denckring-0.1.0/src/denckring/procedures/single_sentence.py +53 -0
  329. denckring-0.1.0/src/denckring/procedures/slenderizing.py +123 -0
  330. denckring-0.1.0/src/denckring/procedures/snowball.py +61 -0
  331. denckring-0.1.0/src/denckring/procedures/snowball_sentence.py +52 -0
  332. denckring-0.1.0/src/denckring/procedures/sonnet.py +66 -0
  333. denckring-0.1.0/src/denckring/procedures/spenserian_stanza.py +61 -0
  334. denckring-0.1.0/src/denckring/procedures/spoonerism.py +221 -0
  335. denckring-0.1.0/src/denckring/procedures/supervocalic.py +56 -0
  336. denckring-0.1.0/src/denckring/procedures/syllable_count.py +97 -0
  337. denckring-0.1.0/src/denckring/procedures/tanka.py +30 -0
  338. denckring-0.1.0/src/denckring/procedures/tautogram.py +72 -0
  339. denckring-0.1.0/src/denckring/procedures/tautonym.py +48 -0
  340. denckring-0.1.0/src/denckring/procedures/telestich.py +17 -0
  341. denckring-0.1.0/src/denckring/procedures/terza_rima.py +55 -0
  342. denckring-0.1.0/src/denckring/procedures/text_folding.py +126 -0
  343. denckring-0.1.0/src/denckring/procedures/tmesis.py +104 -0
  344. denckring-0.1.0/src/denckring/procedures/transposal.py +62 -0
  345. denckring-0.1.0/src/denckring/procedures/triolet.py +42 -0
  346. denckring-0.1.0/src/denckring/procedures/trochaic_tetrameter.py +42 -0
  347. denckring-0.1.0/src/denckring/procedures/univocalic.py +76 -0
  348. denckring-0.1.0/src/denckring/procedures/univocalic_translation.py +60 -0
  349. denckring-0.1.0/src/denckring/procedures/villanelle.py +42 -0
  350. denckring-0.1.0/src/denckring/procedures/wechselsatz.py +145 -0
  351. denckring-0.1.0/src/denckring/procedures/word_ladder.py +354 -0
  352. denckring-0.1.0/src/denckring/procedures/word_square.py +95 -0
  353. denckring-0.1.0/src/denckring/py.typed +0 -0
  354. denckring-0.1.0/tests/conftest.py +67 -0
  355. denckring-0.1.0/tests/strategies/__init__.py +16 -0
  356. denckring-0.1.0/tests/strategies/abecedarian.py +25 -0
  357. denckring-0.1.0/tests/strategies/acrostic.py +25 -0
  358. denckring-0.1.0/tests/strategies/alcaic_stanza.py +80 -0
  359. denckring-0.1.0/tests/strategies/alexandrine.py +20 -0
  360. denckring-0.1.0/tests/strategies/alliterative_verse.py +24 -0
  361. denckring-0.1.0/tests/strategies/alphabetical_sentence.py +15 -0
  362. denckring-0.1.0/tests/strategies/anagram.py +22 -0
  363. denckring-0.1.0/tests/strategies/anaphora.py +19 -0
  364. denckring-0.1.0/tests/strategies/antigram.py +20 -0
  365. denckring-0.1.0/tests/strategies/arca_musarithmica.py +48 -0
  366. denckring-0.1.0/tests/strategies/assonance_constraint.py +25 -0
  367. denckring-0.1.0/tests/strategies/ballade.py +53 -0
  368. denckring-0.1.0/tests/strategies/beau_present.py +17 -0
  369. denckring-0.1.0/tests/strategies/belle_absente.py +57 -0
  370. denckring-0.1.0/tests/strategies/bivocalic.py +19 -0
  371. denckring-0.1.0/tests/strategies/blank_verse.py +30 -0
  372. denckring-0.1.0/tests/strategies/boustrophedon.py +36 -0
  373. denckring-0.1.0/tests/strategies/buchstabwechsel.py +32 -0
  374. denckring-0.1.0/tests/strategies/calculator_word.py +57 -0
  375. denckring-0.1.0/tests/strategies/cent_mille_milliards.py +25 -0
  376. denckring-0.1.0/tests/strategies/charade.py +22 -0
  377. denckring-0.1.0/tests/strategies/chronogram.py +19 -0
  378. denckring-0.1.0/tests/strategies/cinquain.py +22 -0
  379. denckring-0.1.0/tests/strategies/clerihew.py +30 -0
  380. denckring-0.1.0/tests/strategies/column_reading.py +41 -0
  381. denckring-0.1.0/tests/strategies/consonantal_lipogram.py +17 -0
  382. denckring-0.1.0/tests/strategies/curtal_sonnet.py +34 -0
  383. denckring-0.1.0/tests/strategies/cut_up.py +24 -0
  384. denckring-0.1.0/tests/strategies/dactylic_hexameter.py +45 -0
  385. denckring-0.1.0/tests/strategies/definitional_expansion.py +29 -0
  386. denckring-0.1.0/tests/strategies/definitional_literature.py +31 -0
  387. denckring-0.1.0/tests/strategies/denckring.py +24 -0
  388. denckring-0.1.0/tests/strategies/diastic.py +32 -0
  389. denckring-0.1.0/tests/strategies/double_acrostic.py +30 -0
  390. denckring-0.1.0/tests/strategies/double_dactyl.py +83 -0
  391. denckring-0.1.0/tests/strategies/elegiac_couplet.py +64 -0
  392. denckring-0.1.0/tests/strategies/englyn.py +27 -0
  393. denckring-0.1.0/tests/strategies/eodermdrome.py +19 -0
  394. denckring-0.1.0/tests/strategies/epistrophe.py +22 -0
  395. denckring-0.1.0/tests/strategies/every_nth_word.py +19 -0
  396. denckring-0.1.0/tests/strategies/fold_in.py +59 -0
  397. denckring-0.1.0/tests/strategies/ghazal.py +30 -0
  398. denckring-0.1.0/tests/strategies/haibun.py +58 -0
  399. denckring-0.1.0/tests/strategies/haiku.py +22 -0
  400. denckring-0.1.0/tests/strategies/haikuization.py +31 -0
  401. denckring-0.1.0/tests/strategies/hemeling.py +41 -0
  402. denckring-0.1.0/tests/strategies/hendecasyllable.py +20 -0
  403. denckring-0.1.0/tests/strategies/heroic_couplet.py +30 -0
  404. denckring-0.1.0/tests/strategies/heterogram.py +19 -0
  405. denckring-0.1.0/tests/strategies/homoconsonantism.py +29 -0
  406. denckring-0.1.0/tests/strategies/homoteleuton.py +19 -0
  407. denckring-0.1.0/tests/strategies/homovocalism.py +30 -0
  408. denckring-0.1.0/tests/strategies/iambic_pentameter.py +27 -0
  409. denckring-0.1.0/tests/strategies/ideenwuerfeln.py +38 -0
  410. denckring-0.1.0/tests/strategies/kangaroo_word.py +19 -0
  411. denckring-0.1.0/tests/strategies/larding.py +40 -0
  412. denckring-0.1.0/tests/strategies/letter_bank.py +19 -0
  413. denckring-0.1.0/tests/strategies/limerick.py +32 -0
  414. denckring-0.1.0/tests/strategies/lipogram.py +20 -0
  415. denckring-0.1.0/tests/strategies/lipogrammatic_translation.py +25 -0
  416. denckring-0.1.0/tests/strategies/liponym.py +17 -0
  417. denckring-0.1.0/tests/strategies/llull_figure.py +25 -0
  418. denckring-0.1.0/tests/strategies/mathews_algorithm.py +51 -0
  419. denckring-0.1.0/tests/strategies/melting_text.py +19 -0
  420. denckring-0.1.0/tests/strategies/mesostic.py +31 -0
  421. denckring-0.1.0/tests/strategies/monoconsonantal.py +15 -0
  422. denckring-0.1.0/tests/strategies/monosyllabic_prose.py +16 -0
  423. denckring-0.1.0/tests/strategies/multiple_constraint.py +32 -0
  424. denckring-0.1.0/tests/strategies/n_plus_7.py +22 -0
  425. denckring-0.1.0/tests/strategies/ottava_rima.py +30 -0
  426. denckring-0.1.0/tests/strategies/palindrome.py +19 -0
  427. denckring-0.1.0/tests/strategies/pangram.py +17 -0
  428. denckring-0.1.0/tests/strategies/pangrammatic_lipogram.py +19 -0
  429. denckring-0.1.0/tests/strategies/pangrammatic_window.py +43 -0
  430. denckring-0.1.0/tests/strategies/pantoum.py +17 -0
  431. denckring-0.1.0/tests/strategies/paragram.py +44 -0
  432. denckring-0.1.0/tests/strategies/pasigraphy.py +50 -0
  433. denckring-0.1.0/tests/strategies/petrarchan_sonnet.py +42 -0
  434. denckring-0.1.0/tests/strategies/poesie_automat.py +31 -0
  435. denckring-0.1.0/tests/strategies/prisoners_constraint.py +18 -0
  436. denckring-0.1.0/tests/strategies/proteus_verse.py +42 -0
  437. denckring-0.1.0/tests/strategies/quenina.py +33 -0
  438. denckring-0.1.0/tests/strategies/recombination.py +25 -0
  439. denckring-0.1.0/tests/strategies/renga.py +58 -0
  440. denckring-0.1.0/tests/strategies/reverse_snowball.py +23 -0
  441. denckring-0.1.0/tests/strategies/rhyme_royal.py +33 -0
  442. denckring-0.1.0/tests/strategies/rhyme_scheme.py +30 -0
  443. denckring-0.1.0/tests/strategies/rondeau.py +42 -0
  444. denckring-0.1.0/tests/strategies/s_plus_7.py +22 -0
  445. denckring-0.1.0/tests/strategies/sapphic_stanza.py +48 -0
  446. denckring-0.1.0/tests/strategies/sator_square.py +19 -0
  447. denckring-0.1.0/tests/strategies/semordnilap.py +22 -0
  448. denckring-0.1.0/tests/strategies/senryu.py +22 -0
  449. denckring-0.1.0/tests/strategies/sentence_length_constraint.py +19 -0
  450. denckring-0.1.0/tests/strategies/serial_lipogram.py +52 -0
  451. denckring-0.1.0/tests/strategies/sestina.py +34 -0
  452. denckring-0.1.0/tests/strategies/shakespearean_sonnet.py +42 -0
  453. denckring-0.1.0/tests/strategies/single_sentence.py +15 -0
  454. denckring-0.1.0/tests/strategies/slenderizing.py +15 -0
  455. denckring-0.1.0/tests/strategies/snowball.py +23 -0
  456. denckring-0.1.0/tests/strategies/snowball_sentence.py +29 -0
  457. denckring-0.1.0/tests/strategies/sonnet.py +36 -0
  458. denckring-0.1.0/tests/strategies/spenserian_stanza.py +32 -0
  459. denckring-0.1.0/tests/strategies/spoonerism.py +25 -0
  460. denckring-0.1.0/tests/strategies/supervocalic.py +15 -0
  461. denckring-0.1.0/tests/strategies/syllable_count.py +31 -0
  462. denckring-0.1.0/tests/strategies/tanka.py +22 -0
  463. denckring-0.1.0/tests/strategies/tautogram.py +20 -0
  464. denckring-0.1.0/tests/strategies/tautonym.py +17 -0
  465. denckring-0.1.0/tests/strategies/telestich.py +25 -0
  466. denckring-0.1.0/tests/strategies/terza_rima.py +32 -0
  467. denckring-0.1.0/tests/strategies/text_folding.py +41 -0
  468. denckring-0.1.0/tests/strategies/tmesis.py +25 -0
  469. denckring-0.1.0/tests/strategies/transposal.py +19 -0
  470. denckring-0.1.0/tests/strategies/triolet.py +38 -0
  471. denckring-0.1.0/tests/strategies/trochaic_tetrameter.py +25 -0
  472. denckring-0.1.0/tests/strategies/univocalic.py +19 -0
  473. denckring-0.1.0/tests/strategies/univocalic_translation.py +24 -0
  474. denckring-0.1.0/tests/strategies/villanelle.py +47 -0
  475. denckring-0.1.0/tests/strategies/wechselsatz.py +25 -0
  476. denckring-0.1.0/tests/strategies/word_ladder.py +24 -0
  477. denckring-0.1.0/tests/strategies/word_square.py +30 -0
  478. denckring-0.1.0/tests/test_abecedarian.py +23 -0
  479. denckring-0.1.0/tests/test_acrostic.py +42 -0
  480. denckring-0.1.0/tests/test_alcaic_stanza.py +24 -0
  481. denckring-0.1.0/tests/test_alexandrine.py +25 -0
  482. denckring-0.1.0/tests/test_alphabetical_sentence.py +19 -0
  483. denckring-0.1.0/tests/test_anagram.py +176 -0
  484. denckring-0.1.0/tests/test_anaphora.py +19 -0
  485. denckring-0.1.0/tests/test_antigram.py +11 -0
  486. denckring-0.1.0/tests/test_apply_requires.py +66 -0
  487. denckring-0.1.0/tests/test_apply_spine.py +315 -0
  488. denckring-0.1.0/tests/test_arca_musarithmica.py +107 -0
  489. denckring-0.1.0/tests/test_beau_present.py +24 -0
  490. denckring-0.1.0/tests/test_belle_absente.py +61 -0
  491. denckring-0.1.0/tests/test_bivocalic.py +18 -0
  492. denckring-0.1.0/tests/test_blank_verse.py +24 -0
  493. denckring-0.1.0/tests/test_buchstabwechsel.py +126 -0
  494. denckring-0.1.0/tests/test_calculator_corpus.py +70 -0
  495. denckring-0.1.0/tests/test_calculator_display.py +58 -0
  496. denckring-0.1.0/tests/test_calculator_word.py +174 -0
  497. denckring-0.1.0/tests/test_capabilities.py +30 -0
  498. denckring-0.1.0/tests/test_catalogue.py +36 -0
  499. denckring-0.1.0/tests/test_catalogue_corrections.py +111 -0
  500. denckring-0.1.0/tests/test_catalogue_export.py +48 -0
  501. denckring-0.1.0/tests/test_catalogue_quality.py +95 -0
  502. denckring-0.1.0/tests/test_cent_mille_milliards.py +25 -0
  503. denckring-0.1.0/tests/test_charade.py +19 -0
  504. denckring-0.1.0/tests/test_chronogram.py +21 -0
  505. denckring-0.1.0/tests/test_cinquain.py +22 -0
  506. denckring-0.1.0/tests/test_cli.py +153 -0
  507. denckring-0.1.0/tests/test_cli_json.py +51 -0
  508. denckring-0.1.0/tests/test_cli_search.py +36 -0
  509. denckring-0.1.0/tests/test_composites.py +205 -0
  510. denckring-0.1.0/tests/test_consonantal_lipogram.py +15 -0
  511. denckring-0.1.0/tests/test_constraint_translations.py +87 -0
  512. denckring-0.1.0/tests/test_constructive_truth.py +43 -0
  513. denckring-0.1.0/tests/test_corpus.py +58 -0
  514. denckring-0.1.0/tests/test_cut_up.py +24 -0
  515. denckring-0.1.0/tests/test_dactylic_hexameter.py +55 -0
  516. denckring-0.1.0/tests/test_de_frequency_build.py +77 -0
  517. denckring-0.1.0/tests/test_de_frequency_corpus.py +68 -0
  518. denckring-0.1.0/tests/test_de_frequency_pack.py +58 -0
  519. denckring-0.1.0/tests/test_definitional.py +136 -0
  520. denckring-0.1.0/tests/test_denckring.py +135 -0
  521. denckring-0.1.0/tests/test_describe.py +136 -0
  522. denckring-0.1.0/tests/test_device.py +445 -0
  523. denckring-0.1.0/tests/test_device_address.py +238 -0
  524. denckring-0.1.0/tests/test_device_from_text.py +114 -0
  525. denckring-0.1.0/tests/test_disclosure.py +91 -0
  526. denckring-0.1.0/tests/test_double_acrostic.py +24 -0
  527. denckring-0.1.0/tests/test_double_dactyl.py +26 -0
  528. denckring-0.1.0/tests/test_elegiac_couplet.py +26 -0
  529. denckring-0.1.0/tests/test_elision_fr.py +140 -0
  530. denckring-0.1.0/tests/test_elision_seam.py +119 -0
  531. denckring-0.1.0/tests/test_eodermdrome.py +20 -0
  532. denckring-0.1.0/tests/test_epistrophe.py +15 -0
  533. denckring-0.1.0/tests/test_error_codes.py +167 -0
  534. denckring-0.1.0/tests/test_errors.py +103 -0
  535. denckring-0.1.0/tests/test_every_nth_word.py +19 -0
  536. denckring-0.1.0/tests/test_fold_param.py +23 -0
  537. denckring-0.1.0/tests/test_fold_symmetry.py +86 -0
  538. denckring-0.1.0/tests/test_folding_seam.py +192 -0
  539. denckring-0.1.0/tests/test_generators.py +154 -0
  540. denckring-0.1.0/tests/test_gloss_catalogue_corrections.py +37 -0
  541. denckring-0.1.0/tests/test_glosses.py +50 -0
  542. denckring-0.1.0/tests/test_golden.py +25 -0
  543. denckring-0.1.0/tests/test_haibun.py +47 -0
  544. denckring-0.1.0/tests/test_haiku.py +24 -0
  545. denckring-0.1.0/tests/test_harness.py +34 -0
  546. denckring-0.1.0/tests/test_hemeling.py +81 -0
  547. denckring-0.1.0/tests/test_hendecasyllable.py +10 -0
  548. denckring-0.1.0/tests/test_heroic_couplet.py +24 -0
  549. denckring-0.1.0/tests/test_heterogram.py +20 -0
  550. denckring-0.1.0/tests/test_homoteleuton.py +19 -0
  551. denckring-0.1.0/tests/test_iambic_pentameter.py +21 -0
  552. denckring-0.1.0/tests/test_ideenwuerfeln.py +139 -0
  553. denckring-0.1.0/tests/test_identical_rhyme_elision.py +55 -0
  554. denckring-0.1.0/tests/test_input_adequacy.py +162 -0
  555. denckring-0.1.0/tests/test_instruction_rules.py +95 -0
  556. denckring-0.1.0/tests/test_invariants.py +114 -0
  557. denckring-0.1.0/tests/test_kangaroo_word.py +38 -0
  558. denckring-0.1.0/tests/test_kangaroo_word_folding.py +37 -0
  559. denckring-0.1.0/tests/test_lang_de.py +52 -0
  560. denckring-0.1.0/tests/test_lang_de_data.py +195 -0
  561. denckring-0.1.0/tests/test_lang_de_wiktionary.py +227 -0
  562. denckring-0.1.0/tests/test_lang_discovery.py +62 -0
  563. denckring-0.1.0/tests/test_lang_en.py +45 -0
  564. denckring-0.1.0/tests/test_lang_en_data.py +99 -0
  565. denckring-0.1.0/tests/test_lang_fr.py +68 -0
  566. denckring-0.1.0/tests/test_lang_fr_data.py +350 -0
  567. denckring-0.1.0/tests/test_layers.py +81 -0
  568. denckring-0.1.0/tests/test_letter_bank.py +25 -0
  569. denckring-0.1.0/tests/test_letter_class.py +30 -0
  570. denckring-0.1.0/tests/test_limerick.py +26 -0
  571. denckring-0.1.0/tests/test_line_sound.py +47 -0
  572. denckring-0.1.0/tests/test_line_syllables.py +53 -0
  573. denckring-0.1.0/tests/test_lipogram.py +40 -0
  574. denckring-0.1.0/tests/test_liponym.py +19 -0
  575. denckring-0.1.0/tests/test_llull_figure.py +87 -0
  576. denckring-0.1.0/tests/test_mcp_tools.py +172 -0
  577. denckring-0.1.0/tests/test_melting_text.py +14 -0
  578. denckring-0.1.0/tests/test_monoconsonantal.py +18 -0
  579. denckring-0.1.0/tests/test_monosyllabic_prose.py +15 -0
  580. denckring-0.1.0/tests/test_n_plus_7.py +161 -0
  581. denckring-0.1.0/tests/test_non_degeneracy.py +137 -0
  582. denckring-0.1.0/tests/test_package.py +5 -0
  583. denckring-0.1.0/tests/test_packaging.py +187 -0
  584. denckring-0.1.0/tests/test_palindrome.py +22 -0
  585. denckring-0.1.0/tests/test_pangram.py +25 -0
  586. denckring-0.1.0/tests/test_pangrammatic_lipogram.py +23 -0
  587. denckring-0.1.0/tests/test_pangrammatic_window.py +76 -0
  588. denckring-0.1.0/tests/test_pantoum.py +15 -0
  589. denckring-0.1.0/tests/test_paragram.py +212 -0
  590. denckring-0.1.0/tests/test_pasigraphy.py +105 -0
  591. denckring-0.1.0/tests/test_petrarchan_sonnet.py +36 -0
  592. denckring-0.1.0/tests/test_poesie_automat.py +454 -0
  593. denckring-0.1.0/tests/test_prisoners_constraint.py +36 -0
  594. denckring-0.1.0/tests/test_production.py +244 -0
  595. denckring-0.1.0/tests/test_prosody.py +232 -0
  596. denckring-0.1.0/tests/test_prosody_robustness.py +182 -0
  597. denckring-0.1.0/tests/test_proteus_verse.py +138 -0
  598. denckring-0.1.0/tests/test_protocol.py +38 -0
  599. denckring-0.1.0/tests/test_published_docs.py +161 -0
  600. denckring-0.1.0/tests/test_quenina.py +67 -0
  601. denckring-0.1.0/tests/test_reading_boundary.py +56 -0
  602. denckring-0.1.0/tests/test_readme.py +132 -0
  603. denckring-0.1.0/tests/test_rearrangement.py +64 -0
  604. denckring-0.1.0/tests/test_rearrangement_remainder.py +91 -0
  605. denckring-0.1.0/tests/test_recombination.py +56 -0
  606. denckring-0.1.0/tests/test_registry.py +44 -0
  607. denckring-0.1.0/tests/test_renga.py +63 -0
  608. denckring-0.1.0/tests/test_repeating_forms.py +59 -0
  609. denckring-0.1.0/tests/test_requires_honesty.py +390 -0
  610. denckring-0.1.0/tests/test_reverse_snowball.py +11 -0
  611. denckring-0.1.0/tests/test_rhyme_royal.py +27 -0
  612. denckring-0.1.0/tests/test_rhyme_scheme.py +24 -0
  613. denckring-0.1.0/tests/test_round_trip.py +449 -0
  614. denckring-0.1.0/tests/test_s_plus_7.py +30 -0
  615. denckring-0.1.0/tests/test_sampa_fr.py +71 -0
  616. denckring-0.1.0/tests/test_sapphic_stanza.py +21 -0
  617. denckring-0.1.0/tests/test_sator_square.py +21 -0
  618. denckring-0.1.0/tests/test_scaffold.py +46 -0
  619. denckring-0.1.0/tests/test_selection.py +81 -0
  620. denckring-0.1.0/tests/test_selection_remainder.py +70 -0
  621. denckring-0.1.0/tests/test_semordnilap.py +21 -0
  622. denckring-0.1.0/tests/test_senryu.py +14 -0
  623. denckring-0.1.0/tests/test_sentence_length_constraint.py +20 -0
  624. denckring-0.1.0/tests/test_serial_lipogram.py +104 -0
  625. denckring-0.1.0/tests/test_sestina.py +47 -0
  626. denckring-0.1.0/tests/test_shakespearean_sonnet.py +36 -0
  627. denckring-0.1.0/tests/test_short_forms.py +46 -0
  628. denckring-0.1.0/tests/test_single_sentence.py +19 -0
  629. denckring-0.1.0/tests/test_slenderizing.py +125 -0
  630. denckring-0.1.0/tests/test_snowball.py +27 -0
  631. denckring-0.1.0/tests/test_snowball_sentence.py +14 -0
  632. denckring-0.1.0/tests/test_sonnet.py +47 -0
  633. denckring-0.1.0/tests/test_source_param.py +37 -0
  634. denckring-0.1.0/tests/test_stanzaic_forms.py +102 -0
  635. denckring-0.1.0/tests/test_strategies.py +39 -0
  636. denckring-0.1.0/tests/test_supervocalic.py +21 -0
  637. denckring-0.1.0/tests/test_syllable_count.py +14 -0
  638. denckring-0.1.0/tests/test_syllables.py +75 -0
  639. denckring-0.1.0/tests/test_syllables_de.py +71 -0
  640. denckring-0.1.0/tests/test_tanka.py +20 -0
  641. denckring-0.1.0/tests/test_tautogram.py +24 -0
  642. denckring-0.1.0/tests/test_tautonym.py +15 -0
  643. denckring-0.1.0/tests/test_telestich.py +9 -0
  644. denckring-0.1.0/tests/test_terza_rima.py +26 -0
  645. denckring-0.1.0/tests/test_text.py +66 -0
  646. denckring-0.1.0/tests/test_text_helpers.py +74 -0
  647. denckring-0.1.0/tests/test_transposal.py +16 -0
  648. denckring-0.1.0/tests/test_triolet.py +32 -0
  649. denckring-0.1.0/tests/test_trochaic_tetrameter.py +19 -0
  650. denckring-0.1.0/tests/test_univocalic.py +24 -0
  651. denckring-0.1.0/tests/test_villanelle.py +41 -0
  652. denckring-0.1.0/tests/test_vowel_inventory.py +70 -0
  653. denckring-0.1.0/tests/test_wechselsatz.py +18 -0
  654. denckring-0.1.0/tests/test_word_ladder.py +111 -0
  655. denckring-0.1.0/tests/test_word_square.py +28 -0
  656. denckring-0.1.0/tests/test_word_surgery.py +130 -0
  657. denckring-0.1.0/uv.lock +2105 -0
@@ -0,0 +1,37 @@
1
+ name: Bug report
2
+ description: A procedure gives the wrong answer, or something crashes
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ If a procedure disagrees with a literary authority about what a form requires,
9
+ that is a bug — please say which authority.
10
+ - type: input
11
+ id: procedure
12
+ attributes:
13
+ label: Procedure
14
+ placeholder: lipogram
15
+ validations:
16
+ required: true
17
+ - type: textarea
18
+ id: reproduce
19
+ attributes:
20
+ label: What you ran
21
+ description: The command or the `check(...)` call, with the text and parameters.
22
+ render: python
23
+ validations:
24
+ required: true
25
+ - type: textarea
26
+ id: expected
27
+ attributes:
28
+ label: What you expected, and what happened
29
+ validations:
30
+ required: true
31
+ - type: input
32
+ id: version
33
+ attributes:
34
+ label: Version
35
+ description: "`denckring --version`, and whether `denckring[en]` is installed."
36
+ validations:
37
+ required: true
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,49 @@
1
+ name: New procedure
2
+ description: Propose a writing procedure for the catalogue
3
+ labels: [catalogue]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ The catalogue's value is that its entries are sourced. An entry with no source
9
+ is not one we can add.
10
+ - type: input
11
+ id: name
12
+ attributes:
13
+ label: Name of the procedure
14
+ validations:
15
+ required: true
16
+ - type: textarea
17
+ id: definition
18
+ attributes:
19
+ label: Definition
20
+ description: One or two sentences. What must a text do to satisfy it?
21
+ validations:
22
+ required: true
23
+ - type: input
24
+ id: source
25
+ attributes:
26
+ label: Source
27
+ description: "Author, work and year, or the reference work that documents it."
28
+ validations:
29
+ required: true
30
+ - type: dropdown
31
+ id: attribution
32
+ attributes:
33
+ label: How is that source established?
34
+ options:
35
+ - primary — a named author, work and year
36
+ - reference — attested in a standard reference, origin not traced
37
+ - traditional — classical or folk, no single origin
38
+ validations:
39
+ required: true
40
+ - type: dropdown
41
+ id: checkability
42
+ attributes:
43
+ label: Can a program decide it?
44
+ options:
45
+ - self — from the text and its parameters alone
46
+ - source — only against the text it was made from
47
+ - none — no computable acceptance criterion exists
48
+ validations:
49
+ required: true
@@ -0,0 +1,39 @@
1
+ version: 2
2
+
3
+ # Each ecosystem opens ONE grouped pull request, not one per dependency.
4
+ #
5
+ # `ci.yml` is 9 jobs of which `test` is a 13-cell matrix, so every run is 21
6
+ # runner-jobs — 13 Linux, 4 Windows, 4 macOS — and `pull_request` fires the whole
7
+ # thing. Against a private repo's included minutes, where Windows counts double
8
+ # and macOS tenfold, that is roughly 280 billable minutes per run, about 71% of
9
+ # it macOS.
10
+ #
11
+ # Ungrouped, the default limit of five open PRs per ecosystem means up to ten
12
+ # runs a month from Dependabot alone, ~2,800 minutes, which exceeds the whole
13
+ # monthly allowance before any human pushes anything. That is not a projection:
14
+ # on 2026-08-30 the first push opened eight PRs at once and the nine resulting
15
+ # runs exhausted the quota, failing every job except `core-only`.
16
+ #
17
+ # Grouping costs bisectability — a red grouped PR does not say which member
18
+ # broke it — which is why the two ecosystems stay in separate groups rather than
19
+ # one. A lockfile bump and an Actions bump fail for entirely different reasons.
20
+ updates:
21
+ - package-ecosystem: github-actions
22
+ directory: "/"
23
+ schedule:
24
+ interval: monthly
25
+ commit-message:
26
+ prefix: "chore"
27
+ groups:
28
+ github-actions:
29
+ patterns: ["*"]
30
+
31
+ - package-ecosystem: uv
32
+ directory: "/"
33
+ schedule:
34
+ interval: monthly
35
+ commit-message:
36
+ prefix: "chore"
37
+ groups:
38
+ python-packages:
39
+ patterns: ["*"]
@@ -0,0 +1,18 @@
1
+ ## What this changes
2
+
3
+ <!-- One or two sentences. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `uv run pytest` passes
8
+ - [ ] `uv run ruff check` and `uv run ruff format --check` pass
9
+ - [ ] `uv run mypy --strict src tests packages/denckring-en-data/src` passes
10
+ - [ ] `uv run denckring eval --all` is green
11
+
12
+ If this adds a procedure:
13
+
14
+ - [ ] One module, named for the procedure id
15
+ - [ ] A catalogue row with a real source, an `attribution` and a `checkability`
16
+ - [ ] Golden fixtures: at least one genuine instance and one counterexample
17
+ - [ ] Hypothesis strategies under `tests/strategies/`
18
+ - [ ] The catalogue definition claims no more than the checker verifies
@@ -0,0 +1,293 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ env:
13
+ UV_FROZEN: "1"
14
+
15
+ jobs:
16
+ lint:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v5
21
+ with:
22
+ enable-cache: true
23
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr
24
+ - run: uv run ruff check
25
+ - run: uv run ruff format --check
26
+
27
+ typecheck:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: astral-sh/setup-uv@v5
32
+ with:
33
+ enable-cache: true
34
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr --extra mcp
35
+ - run: >-
36
+ uv run mypy --strict src tests packages/denckring-en-data/src
37
+ packages/denckring-de-data/src packages/denckring-de-wiktionary/src
38
+ packages/denckring-de-frequency/src
39
+
40
+ test:
41
+ strategy:
42
+ fail-fast: false
43
+ matrix:
44
+ os: [ubuntu-latest, macos-latest, windows-latest]
45
+ python: ["3.11", "3.12", "3.13", "3.14"]
46
+ include:
47
+ # The free-threaded build, on one platform. Nothing here holds a lock
48
+ # or touches a C extension, so the claim costs one job to keep honest
49
+ # — and an unverified claim is worse than none.
50
+ - os: ubuntu-latest
51
+ python: "3.14t"
52
+ runs-on: ${{ matrix.os }}
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: astral-sh/setup-uv@v5
56
+ with:
57
+ enable-cache: true
58
+ python-version: ${{ matrix.python }}
59
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr
60
+ - run: uv run pytest -q
61
+
62
+ coverage:
63
+ # A floor, not a report. The README's badge states the same 97 this job
64
+ # enforces, so the badge cannot go stale upward-or-downward without CI
65
+ # going red — which is the only way a number in prose stays true here.
66
+ #
67
+ # Measured 97.1% on 2026-09-05: 6,015 of 6,194 statements. The margin over
68
+ # the floor is about seven statements, and hypothesis moves the last one or
69
+ # two between runs, so a drop that turns this red is a real drop. Raising
70
+ # the floor means raising the badge in the same commit.
71
+ #
72
+ # `--extra mcp` because the matrix runs without it and `pytest.importorskip`
73
+ # then skips every MCP test: measuring the suite that skips them would
74
+ # report `mcp/server.py` as uncovered code nobody had chosen not to cover.
75
+ #
76
+ # Scoped to `denckring` alone. The workspace data packages are vendored
77
+ # tables with a few lines of accessor each, and folding them in would move
78
+ # the figure without saying anything about the library.
79
+ runs-on: ubuntu-latest
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+ - uses: astral-sh/setup-uv@v5
83
+ with:
84
+ enable-cache: true
85
+ - run: >-
86
+ uv sync --extra en --extra de --extra de-wiktionary
87
+ --extra de-frequency --extra fr --extra mcp
88
+ - run: uv run pytest -q --cov=denckring --cov-report=term-missing --cov-fail-under=97
89
+
90
+ eval:
91
+ # The acceptance criterion the project set itself for unattended runs. A
92
+ # passing test suite is a different claim from a green scoreboard, so this
93
+ # is a gate of its own.
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - uses: actions/checkout@v4
97
+ - uses: astral-sh/setup-uv@v5
98
+ with:
99
+ enable-cache: true
100
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr
101
+ - run: uv run denckring eval --all
102
+ - run: uv run denckring status
103
+
104
+ mcp:
105
+ # The matrix runs without [mcp], so `pytest.importorskip` skips every MCP
106
+ # test there — the extra's own code would otherwise be tested nowhere. One
107
+ # job installs it and runs them for real. A skipped test reports green.
108
+ runs-on: ubuntu-latest
109
+ steps:
110
+ - uses: actions/checkout@v4
111
+ - uses: astral-sh/setup-uv@v5
112
+ with:
113
+ enable-cache: true
114
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr --extra mcp
115
+ - run: uv run pytest tests/test_mcp_tools.py -q
116
+ - name: The server starts and lists its four tools
117
+ run: |
118
+ uv run python -c "
119
+ import asyncio
120
+ from denckring.mcp.server import server
121
+ names = {t.name for t in asyncio.run(server.list_tools())}
122
+ assert names == {'list_procedures', 'describe_procedure', 'check_text', 'apply_procedure'}, names
123
+ print('mcp:', sorted(names))
124
+ "
125
+
126
+ core-only:
127
+ # Nothing else exercises the install without the data package, and that is
128
+ # the path most likely to break silently: a procedure quietly requiring a
129
+ # capability core does not have would pass every other job here.
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v4
133
+ - uses: astral-sh/setup-uv@v5
134
+ with:
135
+ enable-cache: true
136
+ - name: Install the core distribution alone
137
+ run: |
138
+ uv venv /tmp/core-only
139
+ VIRTUAL_ENV=/tmp/core-only uv pip install .
140
+ - name: The data-free procedures work
141
+ run: |
142
+ /tmp/core-only/bin/python -c "
143
+ from denckring import check
144
+ assert check('lipogram', 'aaa', forbidden='z').satisfied
145
+ assert check('pangram', 'Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich', lang='de').satisfied
146
+ print('core-only: data-free procedures OK')
147
+ "
148
+ - name: The data-dependent ones raise, naming the extra
149
+ run: |
150
+ /tmp/core-only/bin/python -c "
151
+ from denckring import check
152
+ from denckring.core.errors import MissingCapability
153
+ try:
154
+ check('iambic_pentameter', 'the cat can see the moon above the tree')
155
+ except MissingCapability as exc:
156
+ print('core-only: raised as expected —', exc)
157
+ else:
158
+ raise SystemExit('iambic_pentameter should require a capability core lacks')
159
+ "
160
+
161
+ german-without-pronunciations:
162
+ # `denckring[de]` is two distributions and either may be installed alone, so
163
+ # the lexical-only install is a real shape a user can have. ADR 0030 puts the
164
+ # choice between the two packs in one factory; this is the job that proves the
165
+ # factory picks the lexical one and that a phonetic row then refuses rather
166
+ # than guessing. Nothing in the suite covers it, because the suite runs with
167
+ # both installed.
168
+ runs-on: ubuntu-latest
169
+ steps:
170
+ - uses: actions/checkout@v4
171
+ - uses: astral-sh/setup-uv@v5
172
+ with:
173
+ enable-cache: true
174
+ - name: Install the lexical German data and not the phonetic
175
+ run: |
176
+ uv venv /tmp/de-lexical
177
+ VIRTUAL_ENV=/tmp/de-lexical uv pip install ".[de]"
178
+ - name: The lexical rows work and the phonetic ones name what is missing
179
+ run: |
180
+ /tmp/de-lexical/bin/python -c "
181
+ from denckring import check
182
+ from denckring.core.errors import MissingCapability
183
+ from denckring.lang import get_pack
184
+ pack = get_pack('de')
185
+ assert type(pack).__name__ == 'GermanDataPack', type(pack).__name__
186
+ assert check('charade', 'Tal', lang='de').satisfied is not None
187
+ try:
188
+ check('iambic_pentameter', 'der Wind bewegt die Blätter in der Nacht', lang='de')
189
+ except MissingCapability as exc:
190
+ assert 'stress' in str(exc), exc
191
+ print('de-lexical: raised as expected —', exc)
192
+ else:
193
+ raise SystemExit('iambic_pentameter needs stress, which this install lacks')
194
+ "
195
+
196
+ french-without-data:
197
+ # French ships in core with no data at all, which is a real install shape and
198
+ # the one the suite never runs: it always has `denckring[fr]`. ADR 0034 leaves
199
+ # 18 rows blocked in French — down from ADR 0032's 41, and all of them on
200
+ # `stress` — so every row this job exercises is one `denckring[fr]` unblocks,
201
+ # and each must refuse by naming what is missing rather than guessing.
202
+ #
203
+ # The `phonemes` assertion below has no other home. ADR 0034 gave French
204
+ # `phonemes`, so no installed pack lacks that capability any more, and
205
+ # `tests/test_prosody_robustness.py` had to move its refusal test to `stress`.
206
+ # A core-only install is now the only place a phonemes refusal runs at all.
207
+ runs-on: ubuntu-latest
208
+ steps:
209
+ - uses: actions/checkout@v4
210
+ - uses: astral-sh/setup-uv@v5
211
+ with:
212
+ enable-cache: true
213
+ - name: Install the core distribution alone
214
+ run: |
215
+ uv venv /tmp/fr-core
216
+ VIRTUAL_ENV=/tmp/fr-core uv pip install .
217
+ - name: The data-free French rows work and the data-fed ones name what is missing
218
+ run: |
219
+ /tmp/fr-core/bin/python -c "
220
+ from denckring import check
221
+ from denckring.core.errors import MissingCapability
222
+ assert check('lipogram', 'aaa', forbidden='z', lang='fr').satisfied
223
+ try:
224
+ check('n_plus_7', 'la maison', lang='fr')
225
+ except MissingCapability as exc:
226
+ assert 'lexicon.nouns' in str(exc), exc
227
+ print('fr-core: lexical row raised as expected —', exc)
228
+ else:
229
+ raise SystemExit('n_plus_7 needs lexicon.nouns, which this install lacks')
230
+ try:
231
+ check('alexandrine', 'le temps n\'est plus', lang='fr')
232
+ except MissingCapability as exc:
233
+ assert 'syllables' in str(exc), exc
234
+ print('fr-core: syllabic row raised as expected —', exc)
235
+ else:
236
+ raise SystemExit('alexandrine needs syllables, which this install lacks')
237
+ try:
238
+ check('rhyme_scheme', 'le chat dort\nle chien court', scheme='AA', lang='fr')
239
+ except MissingCapability as exc:
240
+ assert 'phonemes' in str(exc), exc
241
+ print('fr-core: rhyme row raised as expected —', exc)
242
+ else:
243
+ raise SystemExit('rhyme_scheme needs phonemes, which this install lacks')
244
+ "
245
+
246
+ docs:
247
+ runs-on: ubuntu-latest
248
+ steps:
249
+ - uses: actions/checkout@v4
250
+ - uses: astral-sh/setup-uv@v5
251
+ with:
252
+ enable-cache: true
253
+ - run: uv sync --extra en --extra de --extra de-wiktionary --extra de-frequency --extra fr
254
+ - name: Regenerate the gallery
255
+ run: uv run python scripts/build_docs.py
256
+ - name: Build the site
257
+ run: uv run mkdocs build --strict
258
+
259
+ explorer:
260
+ # A development tool, but a tested one: its own suite, which no other job in
261
+ # this workflow collects and which has never run in CI before this job.
262
+ runs-on: ubuntu-latest
263
+ defaults:
264
+ run:
265
+ # uv run --project apps/explorer still executes with the repo root as
266
+ # cwd, so pytest's rootdir search finds this repo's own testpaths
267
+ # config and collects ../../tests instead. Running inside the app's
268
+ # directory is what actually resolves its test paths.
269
+ working-directory: apps/explorer
270
+ steps:
271
+ - uses: actions/checkout@v4
272
+ - uses: astral-sh/setup-uv@v5
273
+ with:
274
+ enable-cache: true
275
+ - run: uv sync
276
+ - run: uv run ruff check
277
+ - run: uv run ruff format --check
278
+ - run: uv run mypy --strict src tests
279
+ - run: uv run pytest -q
280
+
281
+ build:
282
+ runs-on: ubuntu-latest
283
+ steps:
284
+ - uses: actions/checkout@v4
285
+ - uses: astral-sh/setup-uv@v5
286
+ with:
287
+ enable-cache: true
288
+ - run: uv build --all-packages
289
+ - run: uvx twine check dist/*
290
+ - uses: actions/upload-artifact@v4
291
+ with:
292
+ name: distributions
293
+ path: dist/
@@ -0,0 +1,175 @@
1
+ name: Release
2
+
3
+ # Two entry points, and the second one is why this file changed before the first
4
+ # release rather than after it.
5
+ #
6
+ # A `v*` tag publishes to PyPI for real. `workflow_dispatch` publishes to TestPyPI
7
+ # instead, so the whole path — build, check, upload through Trusted Publishing —
8
+ # can be walked once against a registry where a mistake is recoverable. PyPI
9
+ # refuses a re-upload of a version that already exists, so without a rehearsal the
10
+ # first genuine tag is also the first time this workflow has ever run end to end.
11
+ on:
12
+ push:
13
+ tags: ["v*"]
14
+ workflow_dispatch:
15
+ inputs:
16
+ target:
17
+ description: Which index to publish to.
18
+ type: choice
19
+ default: testpypi
20
+ options: [testpypi, none]
21
+
22
+ jobs:
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: astral-sh/setup-uv@v5
28
+ with:
29
+ enable-cache: true
30
+ - run: uv build --all-packages
31
+ - run: uvx twine check dist/*
32
+ - uses: actions/upload-artifact@v4
33
+ with:
34
+ name: distributions
35
+ path: dist/
36
+
37
+ # Before `publish`, not after it. This used to run last, so a docs failure left
38
+ # the release on PyPI and undocumented — and unrecoverable, because PyPI will not
39
+ # take the version again. It also fails outright on a private repository, where
40
+ # `gh-deploy` needs a paid plan, which meant the old order guaranteed that the
41
+ # first release would publish and then go red.
42
+ docs:
43
+ runs-on: ubuntu-latest
44
+ permissions:
45
+ contents: write
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ with:
49
+ fetch-depth: 0
50
+ - uses: astral-sh/setup-uv@v5
51
+ with:
52
+ enable-cache: true
53
+ - run: uv sync --extra en
54
+ - run: uv run python scripts/build_docs.py
55
+ # A dispatch run rehearses the site build without deploying it: a rehearsal
56
+ # that pushed to gh-pages would not be a rehearsal.
57
+ - if: github.event_name == 'workflow_dispatch'
58
+ run: uv run mkdocs build --strict
59
+ - if: github.event_name == 'push'
60
+ run: uv run mkdocs gh-deploy --force --strict
61
+
62
+ rehearse:
63
+ # TestPyPI, from a manual run. One leg per distribution, for the reason the
64
+ # `publish` matrix below spells out.
65
+ if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
66
+ needs: [build, docs]
67
+ runs-on: ubuntu-latest
68
+ strategy:
69
+ # Every leg is independent and each says something different: stopping the
70
+ # others on the first failure would hide which of the six are misconfigured,
71
+ # which is the whole question a rehearsal answers.
72
+ fail-fast: false
73
+ matrix:
74
+ include:
75
+ - {dist: denckring, files: "denckring-", environment: testpypi}
76
+ - {dist: denckring-en-data, files: "denckring_en_data-", environment: testpypi-en-data}
77
+ - {dist: denckring-de-data, files: "denckring_de_data-", environment: testpypi-de-data}
78
+ - {dist: denckring-de-wiktionary, files: "denckring_de_wiktionary-", environment: testpypi-de-wiktionary}
79
+ - {dist: denckring-de-frequency, files: "denckring_de_frequency-", environment: testpypi-de-frequency}
80
+ - {dist: denckring-fr-data, files: "denckring_fr_data-", environment: testpypi-fr-data}
81
+ environment: ${{ matrix.environment }}
82
+ permissions:
83
+ id-token: write
84
+ steps:
85
+ - uses: actions/download-artifact@v4
86
+ with:
87
+ name: distributions
88
+ path: all-dist/
89
+ - name: Take only this distribution's artefacts
90
+ run: |
91
+ mkdir -p dist
92
+ cp all-dist/${{ matrix.files }}* dist/
93
+ # An empty or over-full `dist/` is the failure this split can produce and
94
+ # the one an upload would not report: publishing nothing succeeds. Exactly
95
+ # a wheel and an sdist, or stop.
96
+ count=$(ls dist | wc -l)
97
+ test "$count" -eq 2 || {
98
+ echo "expected 2 artefacts for ${{ matrix.dist }}, found $count"; ls -l dist; exit 1;
99
+ }
100
+ ls -l dist
101
+ - uses: pypa/gh-action-pypi-publish@release/v1
102
+ with:
103
+ repository-url: https://test.pypi.org/legacy/
104
+ # Off here and on for the real thing: TestPyPI is periodically pruned,
105
+ # so an attestation made against it outlives what it attests to.
106
+ attestations: false
107
+ # A rehearsal that has been rehearsed before should not fail on that.
108
+ skip-existing: true
109
+
110
+ publish:
111
+ # All six distributions publish from one workflow because they must stay
112
+ # version-locked: denckring-en-data subclasses the core English pack,
113
+ # denckring-de-data carries the sole `de` entry point,
114
+ # denckring-de-wiktionary is the pack that entry point resolves to when it is
115
+ # installed, denckring-de-frequency adds `lexicon.graded_words` to that pack
116
+ # under its own licence (ADR 0038), and denckring-fr-data subclasses the core
117
+ # French pack (ADR 0032). `uv build --all-packages` picks up every workspace
118
+ # member, so the sixth needed no change here.
119
+ #
120
+ # One job per distribution, each with its own environment, and that is forced
121
+ # rather than chosen. PyPI requires a *pending* publisher to be unique on
122
+ # (owner, repository, workflow, environment), so six pending publishers sharing
123
+ # one environment cannot be registered at all — the second is refused with "a
124
+ # pending trusted publisher matching this configuration has already been
125
+ # registered for a different project name" (pypi/warehouse#16920). Environment
126
+ # is the only part of that tuple this workflow can vary, so it varies.
127
+ #
128
+ # The alternatives were worse: create the projects with API tokens and enrol
129
+ # publishers afterwards, or publish one distribution per release event until
130
+ # each pending publisher has been converted. The first reintroduces the upload
131
+ # token this whole arrangement exists to avoid; the second breaks the version
132
+ # lock these six are released under.
133
+ if: github.event_name == 'push'
134
+ needs: [build, docs]
135
+ runs-on: ubuntu-latest
136
+ strategy:
137
+ # A partial release is the failure mode here, and it is worth seeing whole:
138
+ # if one distribution's publisher is misconfigured, the remaining five should
139
+ # still report rather than be cancelled. PyPI will not re-take a version that
140
+ # landed, so the recovery is a version bump either way — knowing which legs
141
+ # succeeded is what makes that recovery short.
142
+ fail-fast: false
143
+ matrix:
144
+ include:
145
+ - {dist: denckring, files: "denckring-", environment: pypi}
146
+ - {dist: denckring-en-data, files: "denckring_en_data-", environment: pypi-en-data}
147
+ - {dist: denckring-de-data, files: "denckring_de_data-", environment: pypi-de-data}
148
+ - {dist: denckring-de-wiktionary, files: "denckring_de_wiktionary-", environment: pypi-de-wiktionary}
149
+ - {dist: denckring-de-frequency, files: "denckring_de_frequency-", environment: pypi-de-frequency}
150
+ - {dist: denckring-fr-data, files: "denckring_fr_data-", environment: pypi-fr-data}
151
+ environment: ${{ matrix.environment }}
152
+ permissions:
153
+ # Trusted Publishing. No API token is stored in repository secrets.
154
+ id-token: write
155
+ attestations: write
156
+ steps:
157
+ - uses: actions/download-artifact@v4
158
+ with:
159
+ name: distributions
160
+ path: all-dist/
161
+ - name: Take only this distribution's artefacts
162
+ run: |
163
+ mkdir -p dist
164
+ cp all-dist/${{ matrix.files }}* dist/
165
+ # `denckring-` is unambiguous because every other distribution builds with
166
+ # underscores — denckring_en_data-0.1.0 and so on — so the core's prefix
167
+ # cannot swallow its siblings. The count below is what actually enforces it.
168
+ count=$(ls dist | wc -l)
169
+ test "$count" -eq 2 || {
170
+ echo "expected 2 artefacts for ${{ matrix.dist }}, found $count"; ls -l dist; exit 1;
171
+ }
172
+ ls -l dist
173
+ - uses: pypa/gh-action-pypi-publish@release/v1
174
+ with:
175
+ attestations: true
@@ -0,0 +1,39 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .hypothesis/
8
+ dist/
9
+ *.egg-info/
10
+ dist/
11
+
12
+ # Generated docs and site build
13
+ docs/gallery/
14
+ docs/index.md
15
+ docs/contributing.md
16
+ docs/changelog.md
17
+ site/
18
+
19
+ # The explorer keeps its own environment
20
+ apps/explorer/.venv/
21
+
22
+ # Local secrets and paths for the explorer
23
+ apps/explorer/.env
24
+
25
+ # Claude Code session state, including worktrees
26
+ .claude/
27
+
28
+ # Subagent-driven-development scratch workspace (ledgers, briefs, review packages).
29
+ .superpowers/
30
+
31
+ # Node modules for the explorer's browser reproductions (apps/explorer/tests/browser).
32
+ # The manifest and lockfile beside them ARE tracked, so `npm ci --prefix apps/explorer`
33
+ # reinstalls exactly what those scripts were verified against; the 19MB tree is not.
34
+ apps/explorer/node_modules/
35
+
36
+ # pytest-cov writes this on every --cov run; the report is CI's, not the repo's.
37
+ .coverage
38
+ .coverage.*
39
+ htmlcov/
@@ -0,0 +1,31 @@
1
+ # Mirrors the lint and typecheck jobs in .github/workflows/ci.yml, so a failure
2
+ # here is a failure there.
3
+ repos:
4
+ - repo: https://github.com/astral-sh/ruff-pre-commit
5
+ rev: v0.16.2
6
+ hooks:
7
+ - id: ruff-check
8
+ args: [--fix]
9
+ - id: ruff-format
10
+
11
+ - repo: https://github.com/pre-commit/pre-commit-hooks
12
+ rev: v5.0.0
13
+ hooks:
14
+ - id: check-yaml
15
+ exclude: ^mkdocs\.yml$
16
+ - id: check-toml
17
+ - id: end-of-file-fixer
18
+ - id: trailing-whitespace
19
+ - id: check-merge-conflict
20
+ - id: check-added-large-files
21
+ # The vendored pronouncing dictionary is 3.6 MB and belongs in the repo.
22
+ args: [--maxkb=4096]
23
+
24
+ - repo: local
25
+ hooks:
26
+ - id: mypy
27
+ name: mypy --strict
28
+ entry: uv run mypy --strict src tests packages/denckring-en-data/src
29
+ language: system
30
+ pass_filenames: false
31
+ types: [python]