urwid 2.6.5__tar.gz → 2.6.7__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.

Potentially problematic release.


This version of urwid might be problematic. Click here for more details.

Files changed (315) hide show
  1. {urwid-2.6.5 → urwid-2.6.7}/PKG-INFO +1 -1
  2. {urwid-2.6.5 → urwid-2.6.7}/pyproject.toml +4 -0
  3. {urwid-2.6.5 → urwid-2.6.7}/tests/test_columns.py +82 -3
  4. urwid-2.6.7/tests/test_main_loop.py +117 -0
  5. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/main_loop.py +20 -25
  6. {urwid-2.6.5 → urwid-2.6.7}/urwid/version.py +2 -2
  7. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/columns.py +70 -26
  8. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/frame.py +17 -0
  9. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/grid_flow.py +39 -4
  10. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/overlay.py +62 -2
  11. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/padding.py +10 -0
  12. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/pile.py +45 -14
  13. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/widget.py +1 -1
  14. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/PKG-INFO +1 -1
  15. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/SOURCES.txt +1 -0
  16. {urwid-2.6.5 → urwid-2.6.7}/.coveralls.yml +0 -0
  17. {urwid-2.6.5 → urwid-2.6.7}/.devcontainer/dev.dockerfile +0 -0
  18. {urwid-2.6.5 → urwid-2.6.7}/.devcontainer/devcontainer.json +0 -0
  19. {urwid-2.6.5 → urwid-2.6.7}/.dockerignore +0 -0
  20. {urwid-2.6.5 → urwid-2.6.7}/.github/CODE_OF_CONDUCT.md +0 -0
  21. {urwid-2.6.5 → urwid-2.6.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  22. {urwid-2.6.5 → urwid-2.6.7}/.github/ISSUE_TEMPLATE/docs.md +0 -0
  23. {urwid-2.6.5 → urwid-2.6.7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  24. {urwid-2.6.5 → urwid-2.6.7}/.github/ISSUE_TEMPLATE/question.md +0 -0
  25. {urwid-2.6.5 → urwid-2.6.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {urwid-2.6.5 → urwid-2.6.7}/.github/dependabot.yml +0 -0
  27. {urwid-2.6.5 → urwid-2.6.7}/.github/labeler.yml +0 -0
  28. {urwid-2.6.5 → urwid-2.6.7}/.github/release.yml +0 -0
  29. {urwid-2.6.5 → urwid-2.6.7}/.github/workflows/documentation.yml +0 -0
  30. {urwid-2.6.5 → urwid-2.6.7}/.github/workflows/isolated_static_check.yml +0 -0
  31. {urwid-2.6.5 → urwid-2.6.7}/.github/workflows/labels.yml +0 -0
  32. {urwid-2.6.5 → urwid-2.6.7}/.github/workflows/pythonpackage.yml +0 -0
  33. {urwid-2.6.5 → urwid-2.6.7}/.github/workflows/yamllint.yml +0 -0
  34. {urwid-2.6.5 → urwid-2.6.7}/.gitignore +0 -0
  35. {urwid-2.6.5 → urwid-2.6.7}/.pre-commit-config.yaml +0 -0
  36. {urwid-2.6.5 → urwid-2.6.7}/.yamllint.yml +0 -0
  37. {urwid-2.6.5 → urwid-2.6.7}/COPYING +0 -0
  38. {urwid-2.6.5 → urwid-2.6.7}/MANIFEST.in +0 -0
  39. {urwid-2.6.5 → urwid-2.6.7}/README.rst +0 -0
  40. {urwid-2.6.5 → urwid-2.6.7}/black-requirements.txt +0 -0
  41. {urwid-2.6.5 → urwid-2.6.7}/classifiers.txt +0 -0
  42. {urwid-2.6.5 → urwid-2.6.7}/docs/Makefile +0 -0
  43. {urwid-2.6.5 → urwid-2.6.7}/docs/changelog.rst +0 -0
  44. {urwid-2.6.5 → urwid-2.6.7}/docs/conf.py +0 -0
  45. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/bigtext.py +0 -0
  46. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/bigtext.py.xdotool +0 -0
  47. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/bigtext1.png +0 -0
  48. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/bigtext2.png +0 -0
  49. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/bigtext3.png +0 -0
  50. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/browse.py +0 -0
  51. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/browse.py.xdotool +0 -0
  52. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/browse1.png +0 -0
  53. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/browse2.png +0 -0
  54. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/edit.py +0 -0
  55. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/edit.py.xdotool +0 -0
  56. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/edit1.png +0 -0
  57. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/edit2.png +0 -0
  58. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/edit_text.txt +0 -0
  59. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/graph.py +0 -0
  60. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/graph.py.xdotool +0 -0
  61. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/graph1.png +0 -0
  62. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/graph2.png +0 -0
  63. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/index.rst +0 -0
  64. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/palette_test.py +0 -0
  65. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/palette_test.py.xdotool +0 -0
  66. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/palette_test1.png +0 -0
  67. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/palette_test2.png +0 -0
  68. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/pop_up.py +0 -0
  69. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/pop_up.py.xdotool +0 -0
  70. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/pop_up1.png +0 -0
  71. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/pop_up2.png +0 -0
  72. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/real_browse.py +0 -0
  73. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/real_edit.py +0 -0
  74. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/subproc.py +0 -0
  75. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/subproc.py.xdotool +0 -0
  76. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/subproc1.png +0 -0
  77. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/subproc2.png +0 -0
  78. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/subproc2.py +0 -0
  79. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/tour.py +0 -0
  80. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/tour.py.xdotool +0 -0
  81. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/tour1.png +0 -0
  82. {urwid-2.6.5 → urwid-2.6.7}/docs/examples/tour2.png +0 -0
  83. {urwid-2.6.5 → urwid-2.6.7}/docs/index.rst +0 -0
  84. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/bright_combinations.py +0 -0
  85. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/bright_combinations.py.xdotool +0 -0
  86. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/bright_combinations1.png +0 -0
  87. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/canvascache.rst +0 -0
  88. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/displayattributes.rst +0 -0
  89. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/displaymodules.rst +0 -0
  90. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/encodings.rst +0 -0
  91. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/display_modules.png +0 -0
  92. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/introduction.png +0 -0
  93. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/urwid_widgets.svgz +0 -0
  94. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/urwid_widgets_1.png +0 -0
  95. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/urwid_widgets_1.xcf +0 -0
  96. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/urwid_widgets_2.png +0 -0
  97. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/urwid_widgets_2.xcf +0 -0
  98. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/images/widget_layout.png +0 -0
  99. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/index.rst +0 -0
  100. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/mainloop.rst +0 -0
  101. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/overview.rst +0 -0
  102. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/safe_combinations.py +0 -0
  103. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/safe_combinations.py.xdotool +0 -0
  104. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/safe_combinations1.png +0 -0
  105. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/textlayout.rst +0 -0
  106. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/userinput.rst +0 -0
  107. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wanat.py +0 -0
  108. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wanat_multi.py +0 -0
  109. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wcur1.py +0 -0
  110. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wcur2.py +0 -0
  111. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/widgets.rst +0 -0
  112. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wmod.py +0 -0
  113. {urwid-2.6.5 → urwid-2.6.7}/docs/manual/wsel.py +0 -0
  114. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/attrspec.rst +0 -0
  115. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/canvas.rst +0 -0
  116. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/command_map.rst +0 -0
  117. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/constants.rst +0 -0
  118. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/deprecated.rst +0 -0
  119. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/display_modules.rst +0 -0
  120. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/exceptions.rst +0 -0
  121. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/global_settings.rst +0 -0
  122. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/index.rst +0 -0
  123. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/list_walkers.rst +0 -0
  124. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/main_loop.rst +0 -0
  125. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/meta.rst +0 -0
  126. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/signals.rst +0 -0
  127. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/text_layout.rst +0 -0
  128. {urwid-2.6.5 → urwid-2.6.7}/docs/reference/widget.rst +0 -0
  129. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/compile_pngs.sh +0 -0
  130. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/screenshots.sh +0 -0
  131. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/static/.placeholder +0 -0
  132. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/templates/indexcontent.html +0 -0
  133. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/templates/indexsidebar.html +0 -0
  134. {urwid-2.6.5 → urwid-2.6.7}/docs/tools/templates/localtoc.html +0 -0
  135. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure.py +0 -0
  136. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure.py.xdotool +0 -0
  137. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure1.png +0 -0
  138. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure2.png +0 -0
  139. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure3.png +0 -0
  140. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/adventure4.png +0 -0
  141. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu.py +0 -0
  142. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu.py.xdotool +0 -0
  143. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu1.png +0 -0
  144. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu2.png +0 -0
  145. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu3.png +0 -0
  146. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/cmenu4.png +0 -0
  147. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/highcolors.py +0 -0
  148. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/highcolors.py.xdotool +0 -0
  149. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/highcolors1.png +0 -0
  150. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu.py +0 -0
  151. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu.py.xdotool +0 -0
  152. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu1.png +0 -0
  153. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu2.png +0 -0
  154. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu3.png +0 -0
  155. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/hmenu4.png +0 -0
  156. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/index.rst +0 -0
  157. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input.py +0 -0
  158. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input.py.xdotool +0 -0
  159. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input1.png +0 -0
  160. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input2.png +0 -0
  161. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input3.png +0 -0
  162. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input4.png +0 -0
  163. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/input5.png +0 -0
  164. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/menu25.png +0 -0
  165. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/minimal.py +0 -0
  166. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/minimal.py.xdotool +0 -0
  167. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/minimal1.png +0 -0
  168. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple.py +0 -0
  169. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple.py.xdotool +0 -0
  170. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple1.png +0 -0
  171. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple2.png +0 -0
  172. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple3.png +0 -0
  173. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/multiple4.png +0 -0
  174. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/qa.py +0 -0
  175. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/qa.py.xdotool +0 -0
  176. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/qa1.png +0 -0
  177. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/qa2.png +0 -0
  178. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/qa3.png +0 -0
  179. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig.py +0 -0
  180. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig.py.xdotool +0 -0
  181. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig1.png +0 -0
  182. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig2.png +0 -0
  183. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig3.png +0 -0
  184. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/sig4.png +0 -0
  185. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/smenu.py +0 -0
  186. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/smenu.py.xdotool +0 -0
  187. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/smenu1.png +0 -0
  188. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/smenu2.png +0 -0
  189. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/smenu3.png +0 -0
  190. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr.py +0 -0
  191. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr.py.xdotool +0 -0
  192. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr1.png +0 -0
  193. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr2.png +0 -0
  194. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr3.png +0 -0
  195. {urwid-2.6.5 → urwid-2.6.7}/docs/tutorial/urwid_attr4.png +0 -0
  196. {urwid-2.6.5 → urwid-2.6.7}/docs/urwid-logo.png +0 -0
  197. {urwid-2.6.5 → urwid-2.6.7}/examples/asyncio_socket_server.py +0 -0
  198. {urwid-2.6.5 → urwid-2.6.7}/examples/bigtext.py +0 -0
  199. {urwid-2.6.5 → urwid-2.6.7}/examples/browse.py +0 -0
  200. {urwid-2.6.5 → urwid-2.6.7}/examples/calc.py +0 -0
  201. {urwid-2.6.5 → urwid-2.6.7}/examples/dialog.py +0 -0
  202. {urwid-2.6.5 → urwid-2.6.7}/examples/edit.py +0 -0
  203. {urwid-2.6.5 → urwid-2.6.7}/examples/fib.py +0 -0
  204. {urwid-2.6.5 → urwid-2.6.7}/examples/graph.py +0 -0
  205. {urwid-2.6.5 → urwid-2.6.7}/examples/input_test.py +0 -0
  206. {urwid-2.6.5 → urwid-2.6.7}/examples/lcd_cf635.py +0 -0
  207. {urwid-2.6.5 → urwid-2.6.7}/examples/palette_test.py +0 -0
  208. {urwid-2.6.5 → urwid-2.6.7}/examples/pop_up.py +0 -0
  209. {urwid-2.6.5 → urwid-2.6.7}/examples/subproc.py +0 -0
  210. {urwid-2.6.5 → urwid-2.6.7}/examples/subproc2.py +0 -0
  211. {urwid-2.6.5 → urwid-2.6.7}/examples/terminal.py +0 -0
  212. {urwid-2.6.5 → urwid-2.6.7}/examples/tour.py +0 -0
  213. {urwid-2.6.5 → urwid-2.6.7}/examples/treesample.py +0 -0
  214. {urwid-2.6.5 → urwid-2.6.7}/examples/twisted_serve_ssh.py +0 -0
  215. {urwid-2.6.5 → urwid-2.6.7}/examples/twisted_serve_ssh.tac +0 -0
  216. {urwid-2.6.5 → urwid-2.6.7}/isort-requirements.txt +0 -0
  217. {urwid-2.6.5 → urwid-2.6.7}/pylint-requirements.txt +0 -0
  218. {urwid-2.6.5 → urwid-2.6.7}/requirements.txt +0 -0
  219. {urwid-2.6.5 → urwid-2.6.7}/ruff-requirements.txt +0 -0
  220. {urwid-2.6.5 → urwid-2.6.7}/setup.cfg +0 -0
  221. {urwid-2.6.5 → urwid-2.6.7}/setup.py +0 -0
  222. {urwid-2.6.5 → urwid-2.6.7}/test_requirements.txt +0 -0
  223. {urwid-2.6.5 → urwid-2.6.7}/tests/__init__.py +0 -0
  224. {urwid-2.6.5 → urwid-2.6.7}/tests/test_canvas.py +0 -0
  225. {urwid-2.6.5 → urwid-2.6.7}/tests/test_container.py +0 -0
  226. {urwid-2.6.5 → urwid-2.6.7}/tests/test_doctests.py +0 -0
  227. {urwid-2.6.5 → urwid-2.6.7}/tests/test_escapes.py +0 -0
  228. {urwid-2.6.5 → urwid-2.6.7}/tests/test_event_loops.py +0 -0
  229. {urwid-2.6.5 → urwid-2.6.7}/tests/test_filler.py +0 -0
  230. {urwid-2.6.5 → urwid-2.6.7}/tests/test_floatedit.py +0 -0
  231. {urwid-2.6.5 → urwid-2.6.7}/tests/test_font.py +0 -0
  232. {urwid-2.6.5 → urwid-2.6.7}/tests/test_frame.py +0 -0
  233. {urwid-2.6.5 → urwid-2.6.7}/tests/test_graphics.py +0 -0
  234. {urwid-2.6.5 → urwid-2.6.7}/tests/test_grid_flow.py +0 -0
  235. {urwid-2.6.5 → urwid-2.6.7}/tests/test_line_box.py +0 -0
  236. {urwid-2.6.5 → urwid-2.6.7}/tests/test_listbox.py +0 -0
  237. {urwid-2.6.5 → urwid-2.6.7}/tests/test_moved_imports.py +0 -0
  238. {urwid-2.6.5 → urwid-2.6.7}/tests/test_overlay.py +0 -0
  239. {urwid-2.6.5 → urwid-2.6.7}/tests/test_padding.py +0 -0
  240. {urwid-2.6.5 → urwid-2.6.7}/tests/test_pile.py +0 -0
  241. {urwid-2.6.5 → urwid-2.6.7}/tests/test_raw_display.py +0 -0
  242. {urwid-2.6.5 → urwid-2.6.7}/tests/test_scrollable.py +0 -0
  243. {urwid-2.6.5 → urwid-2.6.7}/tests/test_signals.py +0 -0
  244. {urwid-2.6.5 → urwid-2.6.7}/tests/test_str_util.py +0 -0
  245. {urwid-2.6.5 → urwid-2.6.7}/tests/test_text_layout.py +0 -0
  246. {urwid-2.6.5 → urwid-2.6.7}/tests/test_tree.py +0 -0
  247. {urwid-2.6.5 → urwid-2.6.7}/tests/test_util.py +0 -0
  248. {urwid-2.6.5 → urwid-2.6.7}/tests/test_vterm.py +0 -0
  249. {urwid-2.6.5 → urwid-2.6.7}/tests/test_widget.py +0 -0
  250. {urwid-2.6.5 → urwid-2.6.7}/tests/util.py +0 -0
  251. {urwid-2.6.5 → urwid-2.6.7}/tox.ini +0 -0
  252. {urwid-2.6.5 → urwid-2.6.7}/urwid/__init__.py +0 -0
  253. {urwid-2.6.5 → urwid-2.6.7}/urwid/canvas.py +0 -0
  254. {urwid-2.6.5 → urwid-2.6.7}/urwid/command_map.py +0 -0
  255. {urwid-2.6.5 → urwid-2.6.7}/urwid/container.py +0 -0
  256. {urwid-2.6.5 → urwid-2.6.7}/urwid/decoration.py +0 -0
  257. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/__init__.py +0 -0
  258. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_posix_raw_display.py +0 -0
  259. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_raw_display_base.py +0 -0
  260. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_web.css +0 -0
  261. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_web.js +0 -0
  262. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_win32.py +0 -0
  263. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/_win32_raw_display.py +0 -0
  264. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/common.py +0 -0
  265. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/curses.py +0 -0
  266. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/escape.py +0 -0
  267. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/html_fragment.py +0 -0
  268. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/lcd.py +0 -0
  269. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/raw.py +0 -0
  270. {urwid-2.6.5 → urwid-2.6.7}/urwid/display/web.py +0 -0
  271. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/__init__.py +0 -0
  272. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/abstract_loop.py +0 -0
  273. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/asyncio_loop.py +0 -0
  274. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/glib_loop.py +0 -0
  275. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/select_loop.py +0 -0
  276. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/tornado_loop.py +0 -0
  277. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/trio_loop.py +0 -0
  278. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/twisted_loop.py +0 -0
  279. {urwid-2.6.5 → urwid-2.6.7}/urwid/event_loop/zmq_loop.py +0 -0
  280. {urwid-2.6.5 → urwid-2.6.7}/urwid/font.py +0 -0
  281. {urwid-2.6.5 → urwid-2.6.7}/urwid/graphics.py +0 -0
  282. {urwid-2.6.5 → urwid-2.6.7}/urwid/numedit.py +0 -0
  283. {urwid-2.6.5 → urwid-2.6.7}/urwid/signals.py +0 -0
  284. {urwid-2.6.5 → urwid-2.6.7}/urwid/split_repr.py +0 -0
  285. {urwid-2.6.5 → urwid-2.6.7}/urwid/str_util.py +0 -0
  286. {urwid-2.6.5 → urwid-2.6.7}/urwid/text_layout.py +0 -0
  287. {urwid-2.6.5 → urwid-2.6.7}/urwid/util.py +0 -0
  288. {urwid-2.6.5 → urwid-2.6.7}/urwid/vterm.py +0 -0
  289. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/__init__.py +0 -0
  290. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/attr_map.py +0 -0
  291. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/attr_wrap.py +0 -0
  292. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/bar_graph.py +0 -0
  293. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/big_text.py +0 -0
  294. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/box_adapter.py +0 -0
  295. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/constants.py +0 -0
  296. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/container.py +0 -0
  297. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/divider.py +0 -0
  298. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/edit.py +0 -0
  299. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/filler.py +0 -0
  300. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/line_box.py +0 -0
  301. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/listbox.py +0 -0
  302. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/monitored_list.py +0 -0
  303. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/popup.py +0 -0
  304. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/progress_bar.py +0 -0
  305. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/scrollable.py +0 -0
  306. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/solid_fill.py +0 -0
  307. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/text.py +0 -0
  308. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/treetools.py +0 -0
  309. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/widget_decoration.py +0 -0
  310. {urwid-2.6.5 → urwid-2.6.7}/urwid/widget/wimp.py +0 -0
  311. {urwid-2.6.5 → urwid-2.6.7}/urwid/wimp.py +0 -0
  312. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/dependency_links.txt +0 -0
  313. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/not-zip-safe +0 -0
  314. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/requires.txt +0 -0
  315. {urwid-2.6.5 → urwid-2.6.7}/urwid.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: urwid
3
- Version: 2.6.5
3
+ Version: 2.6.7
4
4
  Summary: A full-featured console (xterm et al.) user interface library
5
5
  Home-page: https://urwid.org/
6
6
  Author-email: Ian Ward <ian@excess.org>
@@ -90,6 +90,10 @@ exclude_lines = [
90
90
 
91
91
  # Don't complain about missing debug-only code:
92
92
  "def __repr__",
93
+ "def __rich_repr__",
94
+ # own repr construct magic
95
+ "def _repr_words",
96
+ "def _repr_attrs",
93
97
 
94
98
  # Don't complain if tests don't hit defensive assertion code:
95
99
  "raise NotImplementedError",
@@ -80,13 +80,40 @@ class ColumnsTest(unittest.TestCase):
80
80
 
81
81
  with self.subTest("FIXED only"):
82
82
  widget = urwid.Columns(((urwid.PACK, fixed_only),))
83
- self.assertEqual(frozenset((urwid.FIXED,)), widget.sizing())
83
+ self.assertEqual(frozenset((urwid.FLOW, urwid.FIXED)), widget.sizing())
84
84
  cols, rows = 3, 3
85
85
  self.assertEqual((cols, rows), widget.pack(()))
86
86
  canvas = widget.render(())
87
87
  self.assertEqual(cols, canvas.cols())
88
88
  self.assertEqual(rows, canvas.rows())
89
89
 
90
+ with self.subTest("FIXED only use FLOW methods - drop & replace by solid"):
91
+ widget = urwid.Columns(((urwid.PACK, fixed_only),))
92
+ self.assertEqual(frozenset((urwid.FLOW, urwid.FIXED)), widget.sizing())
93
+ cols, rows = 2, 1
94
+ self.assertEqual((cols, rows), widget.pack((cols,)))
95
+ canvas = widget.render((cols,))
96
+ self.assertEqual(cols, canvas.cols())
97
+ self.assertEqual(rows, canvas.rows())
98
+ self.assertEqual(" ", str(canvas))
99
+
100
+ with self.subTest("FIXED only use FLOW methods - add empty space"):
101
+ widget = urwid.Columns(((urwid.PACK, fixed_only),))
102
+ self.assertEqual(frozenset((urwid.FLOW, urwid.FIXED)), widget.sizing())
103
+ cols, rows = 5, 3
104
+ self.assertEqual((cols, rows), widget.pack((cols,)))
105
+ canvas = widget.render((cols,))
106
+ self.assertEqual(cols, canvas.cols())
107
+ self.assertEqual(rows, canvas.rows())
108
+ self.assertEqual(
109
+ (
110
+ "┌─┐ ",
111
+ "│ │ ",
112
+ "└─┘ ",
113
+ ),
114
+ canvas.decoded_text,
115
+ )
116
+
90
117
  with self.subTest("GIVEN BOX + FLOW/FIXED, but other widgets do not support box"):
91
118
  widget = urwid.Columns((flow_fixed, (3, box_only)), box_columns=(1,))
92
119
  self.assertEqual(frozenset((urwid.FLOW, urwid.FIXED)), widget.sizing())
@@ -153,8 +180,7 @@ class ColumnsTest(unittest.TestCase):
153
180
  )
154
181
 
155
182
  cols, rows = 30, 1
156
- with self.assertRaises(AttributeError):
157
- widget.pack((cols,))
183
+ self.assertEqual((cols, rows), widget.pack((cols,)))
158
184
 
159
185
  canvas = widget.render((cols,))
160
186
  self.assertEqual(rows, canvas.rows())
@@ -241,6 +267,59 @@ class ColumnsTest(unittest.TestCase):
241
267
  str(ctx.warnings[0].message),
242
268
  )
243
269
 
270
+ def test_pack_render_skip_widget(self):
271
+ widget = urwid.Columns(((0, urwid.Text("Ignore\nme")), (urwid.Text("Count"))))
272
+ cols, rows = 5, 1
273
+ self.assertEqual((cols, rows), widget.pack(()))
274
+ self.assertEqual((cols, rows), widget.pack((cols,)))
275
+ self.assertEqual("Count", str(widget.render(())))
276
+ self.assertEqual("Count", str(widget.render((cols,))))
277
+
278
+ def test_pack_flow_with_fixed_item(self):
279
+ widget = urwid.Columns(
280
+ (
281
+ (urwid.PACK, urwid.BigText("3", urwid.Thin3x3Font())),
282
+ (urwid.Text("Multi\nline\ntext\n???")),
283
+ ),
284
+ dividechars=1,
285
+ )
286
+ with self.subTest("Skip"):
287
+ self.assertEqual(3, widget.rows((3,)))
288
+ self.assertEqual((3, 3), widget.pack((3,)))
289
+ self.assertEqual(
290
+ (
291
+ "┌─┐",
292
+ " ─┤",
293
+ "└─┘",
294
+ ),
295
+ widget.render((3,)).decoded_text,
296
+ )
297
+ with self.subTest("Fit all"):
298
+ self.assertEqual(4, widget.rows((9,)))
299
+ self.assertEqual((9, 4), widget.pack((9,)))
300
+ self.assertEqual(
301
+ (
302
+ "┌─┐ Multi",
303
+ " ─┤ line ",
304
+ "└─┘ text ",
305
+ " ??? ",
306
+ ),
307
+ widget.render((9,)).decoded_text,
308
+ )
309
+ with self.subTest("Use SolidCanvas instead"):
310
+ self.assertEqual(1, widget.rows((2,)))
311
+ self.assertEqual((2, 1), widget.pack((2,)))
312
+ self.assertEqual((" ",), widget.render((2,)).decoded_text)
313
+
314
+ def test_pack_render_empty_widget(self):
315
+ widget = urwid.Columns(())
316
+ self.assertEqual(frozenset((urwid.FLOW, urwid.BOX)), widget.sizing())
317
+ cols, rows = 10, 1
318
+ self.assertEqual((cols, rows), widget.pack((cols,)))
319
+ canvas = widget.render((cols,))
320
+ self.assertEqual(rows, canvas.rows())
321
+ self.assertEqual((" ",), canvas.decoded_text)
322
+
244
323
  def test_not_a_widget(self):
245
324
  class NotAWidget:
246
325
  __slots__ = ("name", "symbol")
@@ -0,0 +1,117 @@
1
+ from __future__ import annotations
2
+
3
+ import concurrent.futures
4
+ import os
5
+ import socket
6
+ import sys
7
+ import threading
8
+ import typing
9
+ import unittest.mock
10
+
11
+ import urwid
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from types import TracebackType
15
+
16
+ IS_WINDOWS = sys.platform == "win32"
17
+
18
+
19
+ class ClosingTemporaryFilesPair(typing.ContextManager[typing.Tuple[typing.TextIO, typing.TextIO]]):
20
+ """File pair context manager that closes temporary files on exit.
21
+
22
+ Since `sys.stdout` is TextIO, tests have to use compatible api for the proper behavior imitation.
23
+ """
24
+
25
+ __slots__ = ("rd_s", "wr_s", "rd_f", "wr_f")
26
+
27
+ def __init__(self) -> None:
28
+ self.rd_s: socket.socket | None = None
29
+ self.wr_s: socket.socket | None = None
30
+ self.rd_f: typing.TextIO | None = None
31
+ self.wr_f: typing.TextIO | None = None
32
+
33
+ def __enter__(self) -> tuple[typing.TextIO, typing.TextIO]:
34
+ self.rd_s, self.wr_s = socket.socketpair()
35
+ self.rd_f = os.fdopen(self.rd_s.fileno(), "r", encoding="utf-8", closefd=False)
36
+ self.wr_f = os.fdopen(self.wr_s.fileno(), "w", encoding="utf-8", closefd=False)
37
+ return self.rd_f, self.wr_f
38
+
39
+ def __exit__(
40
+ self,
41
+ exc_type: type[BaseException] | None,
42
+ exc_val: BaseException | None,
43
+ exc_tb: TracebackType | None,
44
+ ) -> None:
45
+ """Close everything explicit without waiting for garbage collected."""
46
+ if self.rd_f is not None and not self.rd_f.closed:
47
+ self.rd_f.close()
48
+ if self.rd_s is not None:
49
+ self.rd_s.close()
50
+ if self.wr_f is not None and not self.wr_f.closed:
51
+ self.wr_f.close()
52
+ if self.wr_s is not None:
53
+ self.wr_s.close()
54
+
55
+
56
+ def stop_screen_cb(*_args, **_kwargs) -> typing.NoReturn:
57
+ raise urwid.ExitMainLoop
58
+
59
+
60
+ class TestMainLoop(unittest.TestCase):
61
+ @unittest.skipIf(IS_WINDOWS, "selectors for pipe are not supported on Windows")
62
+ def test_watch_pipe(self):
63
+ """Test watching pipe is stopped on explicit False only."""
64
+ evt = threading.Event() # We need thread synchronization
65
+ outcome: list[bytes] = []
66
+
67
+ def pipe_cb(data: bytes) -> typing.Any:
68
+ outcome.append(data)
69
+
70
+ if not evt.is_set():
71
+ evt.set()
72
+
73
+ if data == b"false":
74
+ return False
75
+ if data == b"true":
76
+ return True
77
+ if data == b"null":
78
+ return None
79
+ return object()
80
+
81
+ def pipe_writer(fd: int) -> None:
82
+ os.write(fd, b"something")
83
+ if evt.wait(0.1):
84
+ evt.clear()
85
+ os.write(fd, b"true")
86
+ if evt.wait(0.1):
87
+ evt.clear()
88
+ os.write(fd, b"null")
89
+ if evt.wait(0.1):
90
+ evt.clear()
91
+ os.write(fd, b"false")
92
+
93
+ with ClosingTemporaryFilesPair() as (
94
+ rd_r,
95
+ wr_r,
96
+ ), ClosingTemporaryFilesPair() as (
97
+ rd_w,
98
+ wr_w,
99
+ ), concurrent.futures.ThreadPoolExecutor(
100
+ max_workers=1,
101
+ ) as executor, unittest.mock.patch(
102
+ "subprocess.Popen", # we want to be sure that nothing outside is called
103
+ autospec=True,
104
+ ):
105
+ evl = urwid.MainLoop(
106
+ urwid.SolidFill(),
107
+ screen=urwid.display.raw.Screen(input=rd_r, output=wr_w), # We need screen which support mocked IO
108
+ handle_mouse=False, # Less external calls - better
109
+ )
110
+ evl.set_alarm_in(1, stop_screen_cb)
111
+ pipe_fd = evl.watch_pipe(pipe_cb)
112
+ executor.submit(pipe_writer, pipe_fd)
113
+
114
+ evl.run()
115
+ self.assertEqual([b"something", b"true", b"null", b"false"], outcome)
116
+ not_removed = evl.remove_watch_pipe(pipe_fd)
117
+ self.assertFalse(not_removed)
@@ -190,7 +190,7 @@ class MainLoop:
190
190
  else:
191
191
  self._topmost_widget = self._widget
192
192
 
193
- def _set_pop_ups(self, pop_ups) -> None:
193
+ def _set_pop_ups(self, pop_ups: bool) -> None:
194
194
  warnings.warn(
195
195
  f"method `{self.__class__.__name__}._set_pop_ups` is deprecated, "
196
196
  f"please use `{self.__class__.__name__}.pop_ups` property",
@@ -214,7 +214,7 @@ class MainLoop:
214
214
  """
215
215
  self.logger.debug(f"Setting alarm in {sec!r} seconds with callback {callback!r}")
216
216
 
217
- def cb():
217
+ def cb() -> None:
218
218
  callback(self, user_data)
219
219
 
220
220
  return self.event_loop.alarm(sec, cb)
@@ -236,7 +236,7 @@ class MainLoop:
236
236
  sec = tm - time.time()
237
237
  self.logger.debug(f"Setting alarm in {sec!r} seconds with callback {callback!r}")
238
238
 
239
- def cb():
239
+ def cb() -> None:
240
240
  callback(self, user_data)
241
241
 
242
242
  return self.event_loop.alarm(sec, cb)
@@ -250,33 +250,28 @@ class MainLoop:
250
250
 
251
251
  if not IS_WINDOWS:
252
252
 
253
- def watch_pipe(self, callback: Callable[[bytes], bool]) -> int:
253
+ def watch_pipe(self, callback: Callable[[bytes], bool | None]) -> int:
254
254
  """
255
255
  Create a pipe for use by a subprocess or thread to trigger a callback
256
256
  in the process/thread running the main loop.
257
257
 
258
- :param callback: function taking one parameter to call from within
259
- the process/thread running the main loop
258
+ :param callback: function taking one parameter to call from within the process/thread running the main loop
260
259
  :type callback: callable
261
260
 
262
- This method returns a file descriptor attached to the write end of a
263
- pipe. The read end of the pipe is added to the list of files
264
- :attr:`event_loop` is watching. When data is written to the pipe the
265
- callback function will be called and passed a single value containing
266
- data read from the pipe.
261
+ This method returns a file descriptor attached to the write end of a pipe.
262
+ The read end of the pipe is added to the list of files :attr:`event_loop` is watching.
263
+ When data is written to the pipe the callback function will be called
264
+ and passed a single value containing data read from the pipe.
267
265
 
268
- This method may be used any time you want to update widgets from
269
- another thread or subprocess.
266
+ This method may be used any time you want to update widgets from another thread or subprocess.
270
267
 
271
- Data may be written to the returned file descriptor with
272
- ``os.write(fd, data)``. Ensure that data is less than 512 bytes (or 4K
273
- on Linux) so that the callback will be triggered just once with the
274
- complete value of data passed in.
268
+ Data may be written to the returned file descriptor with ``os.write(fd, data)``.
269
+ Ensure that data is less than 512 bytes (or 4K on Linux)
270
+ so that the callback will be triggered just once with the complete value of data passed in.
275
271
 
276
- If the callback returns ``False`` then the watch will be removed from
277
- :attr:`event_loop` and the read end of the pipe will be closed. You
278
- are responsible for closing the write end of the pipe with
279
- ``os.close(fd)``.
272
+ If the callback returns ``False`` then the watch will be removed from :attr:`event_loop`
273
+ and the read end of the pipe will be closed.
274
+ You are responsible for closing the write end of the pipe with ``os.close(fd)``.
280
275
  """
281
276
  import fcntl
282
277
 
@@ -286,7 +281,7 @@ class MainLoop:
286
281
 
287
282
  def cb() -> None:
288
283
  data = os.read(pipe_rd, PIPE_BUFFER_READ_SIZE)
289
- if not callback(data):
284
+ if callback(data) is False:
290
285
  self.event_loop.remove_watch_file(watch_handle)
291
286
  os.close(pipe_rd)
292
287
 
@@ -296,9 +291,9 @@ class MainLoop:
296
291
 
297
292
  def remove_watch_pipe(self, write_fd: int) -> bool:
298
293
  """
299
- Close the read end of the pipe and remove the watch created by
300
- :meth:`watch_pipe`. You are responsible for closing the write end of
301
- the pipe.
294
+ Close the read end of the pipe and remove the watch created by :meth:`watch_pipe`.
295
+
296
+ ..note:: You are responsible for closing the write end of the pipe.
302
297
 
303
298
  Returns ``True`` if the watch pipe exists, ``False`` otherwise
304
299
  """
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.6.5'
16
- __version_tuple__ = version_tuple = (2, 6, 5)
15
+ __version__ = version = '2.6.7'
16
+ __version_tuple__ = version_tuple = (2, 6, 7)
@@ -7,6 +7,7 @@ from itertools import chain, repeat
7
7
  import urwid
8
8
  from urwid.canvas import Canvas, CanvasJoin, CompositeCanvas, SolidCanvas
9
9
  from urwid.command_map import Command
10
+ from urwid.split_repr import remove_defaults
10
11
  from urwid.util import is_mouse_press
11
12
 
12
13
  from .constants import Align, Sizing, WHSettings
@@ -15,7 +16,7 @@ from .monitored_list import MonitoredFocusList, MonitoredList
15
16
  from .widget import Widget, WidgetError, WidgetWarning
16
17
 
17
18
  if typing.TYPE_CHECKING:
18
- from collections.abc import Iterable, Sequence
19
+ from collections.abc import Iterable, Iterator, Sequence
19
20
 
20
21
  from typing_extensions import Literal
21
22
 
@@ -66,43 +67,43 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
66
67
 
67
68
  # BOX-only widget
68
69
  >>> Columns((SolidFill("#"),))
69
- <Columns box widget with 1 item>
70
+ <Columns box widget (1 item)>
70
71
 
71
72
  # BOX-only widget with "get height from max"
72
73
  >>> Columns((SolidFill("#"),), box_columns=(0,))
73
- <Columns box widget with 1 item>
74
+ <Columns box widget (1 item)>
74
75
 
75
76
  # FLOW-only
76
77
  >>> Columns((Edit(),))
77
- <Columns selectable flow widget with 1 item>
78
+ <Columns selectable flow widget (1 item)>
78
79
 
79
80
  # FLOW allowed by "box_columns"
80
81
  >>> Columns((Edit(), SolidFill("#")), box_columns=(1,))
81
- <Columns selectable flow widget with 2 items>
82
+ <Columns selectable flow widget (2 items) focus_column=0>
82
83
 
83
84
  # FLOW/FIXED
84
85
  >>> Columns((Text("T"),))
85
- <Columns fixed/flow widget with 1 item>
86
+ <Columns fixed/flow widget (1 item)>
86
87
 
87
88
  # GIVEN BOX only -> BOX only
88
89
  >>> Columns(((5, SolidFill("#")),), box_columns=(0,))
89
- <Columns box widget with 1 item>
90
+ <Columns box widget (1 item)>
90
91
 
91
92
  # No FLOW - BOX only
92
93
  >>> Columns(((5, SolidFill("#")), SolidFill("*")), box_columns=(0, 1))
93
- <Columns box widget with 2 items>
94
+ <Columns box widget (2 items) focus_column=0>
94
95
 
95
- # FIXED only -> FIXED
96
+ # FIXED only -> FIXED (and FLOW via drop/expand)
96
97
  >>> Columns(((WHSettings.PACK, BigText("1", font)),))
97
- <Columns fixed widget with 1 item>
98
+ <Columns fixed/flow widget (1 item)>
98
99
 
99
100
  # Invalid sizing combination -> use fallback settings (and produce warning)
100
101
  >>> Columns(((WHSettings.PACK, SolidFill("#")),))
101
- <Columns box/flow widget with 1 item>
102
+ <Columns box/flow widget (1 item)>
102
103
 
103
104
  # Special case: empty columns widget sizing is impossible to calculate
104
105
  >>> Columns(())
105
- <Columns box/flow widget without contents>
106
+ <Columns box/flow widget ()>
106
107
  """
107
108
  if not self.contents:
108
109
  return frozenset((urwid.BOX, urwid.FLOW))
@@ -183,6 +184,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
183
184
  supported.add(Sizing.FLOW)
184
185
 
185
186
  if has_fixed and not block_fixed:
187
+ supported.add(Sizing.FLOW)
186
188
  supported.add(Sizing.FIXED)
187
189
 
188
190
  if not supported:
@@ -289,10 +291,50 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
289
291
 
290
292
  def _repr_words(self) -> list[str]:
291
293
  if len(self.contents) > 1:
292
- return [*super()._repr_words(), f"with {len(self.contents)} items"]
293
- if self.contents:
294
- return [*super()._repr_words(), "with 1 item"]
295
- return [*super()._repr_words(), "without contents"]
294
+ contents_string = f"({len(self.contents)} items)"
295
+ elif self.contents:
296
+ contents_string = "(1 item)"
297
+ else:
298
+ contents_string = "()"
299
+ return [*super()._repr_words(), contents_string]
300
+
301
+ def _repr_attrs(self) -> dict[str, typing.Any]:
302
+ attrs = {
303
+ **super()._repr_attrs(),
304
+ "dividechars": self.dividechars,
305
+ "focus_column": self.focus_position if len(self._contents) > 1 else None,
306
+ "min_width": self.min_width,
307
+ }
308
+ return remove_defaults(attrs, Columns.__init__)
309
+
310
+ def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
311
+ widget_list: list[
312
+ Widget
313
+ | tuple[Literal[WHSettings.PACK] | int, Widget]
314
+ | tuple[Literal[WHSettings.WEIGHT], int | float, Widget]
315
+ ] = []
316
+ box_columns: list[int] = []
317
+ for idx, (w_instance, (sizing, amount, is_box)) in enumerate(self._contents):
318
+ if sizing == WHSettings.GIVEN:
319
+ widget_list.append((amount, w_instance))
320
+ elif sizing == WHSettings.PACK:
321
+ widget_list.append((WHSettings.PACK, w_instance))
322
+ elif amount == 1:
323
+ widget_list.append(w_instance)
324
+ else:
325
+ widget_list.append((WHSettings.WEIGHT, amount, w_instance))
326
+
327
+ if is_box:
328
+ box_columns.append(idx)
329
+
330
+ yield "widget_list", widget_list
331
+ yield "dividechars", self.dividechars
332
+ yield "focus_column", self.focus_position if self._contents else None
333
+ yield "min_width", self.min_width
334
+ yield "box_columns", box_columns
335
+
336
+ def __len__(self) -> int:
337
+ return len(self._contents)
296
338
 
297
339
  def _contents_modified(self) -> None:
298
340
  """
@@ -904,11 +946,17 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
904
946
  box.append(i)
905
947
 
906
948
  elif Sizing.FLOW in w_sizing:
907
- heights[i] = widget.rows((width,), focus and i == self.focus_position)
949
+ if width > 0:
950
+ heights[i] = widget.rows((width,), focus and i == self.focus_position)
951
+ else:
952
+ heights[i] = 0
908
953
  w_h_args[i] = (width,)
909
954
 
910
955
  elif size_kind == WHSettings.PACK:
911
- heights[i] = widget.pack((), focus and i == self.focus_position)[1]
956
+ if width > 0:
957
+ heights[i] = widget.pack((), focus and i == self.focus_position)[1]
958
+ else:
959
+ heights[i] = 0
912
960
  w_h_args[i] = ()
913
961
 
914
962
  else:
@@ -1135,14 +1183,10 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
1135
1183
 
1136
1184
  see :meth:`Widget.rows` for details
1137
1185
  """
1138
- widths = self.column_widths(size, focus)
1139
-
1140
- rows = 1
1141
- for i, (mc, (w, (_t, _n, b))) in enumerate(zip(widths, self.contents)):
1142
- if b or mc <= 0:
1143
- continue
1144
- rows = max(rows, w.rows((mc,), focus=focus and self.focus_position == i))
1145
- return rows
1186
+ _, heights, _ = self.get_column_sizes(size, focus)
1187
+ if heights:
1188
+ return max(1, *heights)
1189
+ return 1
1146
1190
 
1147
1191
  def keypress(
1148
1192
  self,
@@ -4,6 +4,7 @@ import typing
4
4
  import warnings
5
5
 
6
6
  from urwid.canvas import CanvasCombine, CompositeCanvas
7
+ from urwid.split_repr import remove_defaults
7
8
  from urwid.util import is_mouse_press
8
9
 
9
10
  from .constants import Sizing, VAlign
@@ -89,6 +90,22 @@ class Frame(Widget, WidgetContainerMixin, typing.Generic[BodyWidget, HeaderWidge
89
90
  _check_widget_subclass(body)
90
91
  _check_widget_subclass(footer)
91
92
 
93
+ def _repr_attrs(self) -> dict[str, typing.Any]:
94
+ attrs = {
95
+ **super()._repr_attrs(),
96
+ "body": self._body,
97
+ "header": self._header,
98
+ "footer": self._footer,
99
+ "focus_part": self.focus_part,
100
+ }
101
+ return remove_defaults(attrs, Frame.__init__)
102
+
103
+ def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
104
+ yield "body", self._body
105
+ yield "header", self._header
106
+ yield "footer", self._footer
107
+ yield "focus_part", self.focus_part
108
+
92
109
  @property
93
110
  def header(self) -> HeaderWidget:
94
111
  return self._header
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import typing
4
4
  import warnings
5
5
 
6
+ from urwid.split_repr import remove_defaults
7
+
6
8
  from .columns import Columns
7
9
  from .constants import Align, Sizing, WHSettings
8
10
  from .container import WidgetContainerListContentsMixin, WidgetContainerMixin
@@ -13,7 +15,7 @@ from .pile import Pile
13
15
  from .widget import Widget, WidgetError, WidgetWarning, WidgetWrap
14
16
 
15
17
  if typing.TYPE_CHECKING:
16
- from collections.abc import Iterable, Sequence
18
+ from collections.abc import Iterable, Iterator, Sequence
17
19
 
18
20
  from typing_extensions import Literal
19
21
 
@@ -78,12 +80,45 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
78
80
  self._cache_maxcol = self._get_maxcol(())
79
81
  super().__init__(self.generate_display_widget((self._cache_maxcol,)))
80
82
 
83
+ def _repr_words(self) -> list[str]:
84
+ if len(self.contents) > 1:
85
+ contents_string = f"({len(self.contents)} items)"
86
+ elif self.contents:
87
+ contents_string = "(1 item)"
88
+ else:
89
+ contents_string = "()"
90
+ return [*super()._repr_words(), contents_string]
91
+
92
+ def _repr_attrs(self) -> dict[str, typing.Any]:
93
+ attrs = {
94
+ **super()._repr_attrs(),
95
+ "cell_width": self.cell_width,
96
+ "h_sep": self.h_sep,
97
+ "v_sep": self.v_sep,
98
+ "align": self.align,
99
+ "focus": self.focus_position if len(self._contents) > 1 else None,
100
+ }
101
+ return remove_defaults(attrs, GridFlow.__init__)
102
+
103
+ def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
104
+ yield "cells", [widget for widget, _ in self.contents]
105
+ yield "cell_width", self.cell_width
106
+ yield "h_sep", self.h_sep
107
+ yield "v_sep", self.v_sep
108
+ yield "align", self.align
109
+ yield "focus", self.focus_position
110
+
111
+ def __len__(self) -> int:
112
+ return len(self._contents)
113
+
81
114
  def _invalidate(self) -> None:
82
115
  self._cache_maxcol = None
83
116
  super()._invalidate()
84
117
 
85
118
  def _contents_modified(
86
- self, slc, new_items: Iterable[tuple[Widget, tuple[Literal["given", WHSettings.GIVEN], int]]]
119
+ self,
120
+ _slc: tuple[int, int, int],
121
+ new_items: Iterable[tuple[Widget, tuple[Literal["given", WHSettings.GIVEN], int]]],
87
122
  ) -> None:
88
123
  for item in new_items:
89
124
  try:
@@ -258,7 +293,7 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
258
293
  return None
259
294
  return self.contents[self.focus_position][0]
260
295
 
261
- def _get_focus(self) -> Widget:
296
+ def _get_focus(self) -> Widget | None:
262
297
  warnings.warn(
263
298
  f"method `{self.__class__.__name__}._get_focus` is deprecated, "
264
299
  f"please use `{self.__class__.__name__}.focus` property",
@@ -446,7 +481,7 @@ class GridFlow(WidgetWrap[Pile], WidgetContainerMixin, WidgetContainerListConten
446
481
 
447
482
  # Use width == maxcol in case of maxcol < width amount
448
483
  # Columns will use empty widget in case of GIVEN width > maxcol
449
- c.contents.append((w, c.options(WHSettings.GIVEN, width_amount if width_amount <= maxcol else maxcol)))
484
+ c.contents.append((w, c.options(WHSettings.GIVEN, min(width_amount, maxcol))))
450
485
  if (i == self.focus_position) or (not column_focused and w.selectable()):
451
486
  c.focus_position = len(c.contents) - 1
452
487
  column_focused = True