webdiff 1.0.0__tar.gz → 1.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 (297) hide show
  1. {webdiff-1.0.0/webdiff.egg-info → webdiff-1.1.0}/PKG-INFO +38 -35
  2. {webdiff-1.0.0 → webdiff-1.1.0}/README.md +24 -25
  3. webdiff-1.1.0/pyproject.toml +47 -0
  4. webdiff-1.1.0/webdiff/app.py +347 -0
  5. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/argparser.py +16 -9
  6. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/diff.py +9 -7
  7. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/dirdiff.py +6 -5
  8. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/github_fetcher.py +21 -8
  9. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/githubdiff.py +5 -5
  10. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/gitwebdiff.py +6 -8
  11. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/localfilediff.py +1 -1
  12. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/options.py +25 -21
  13. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/codediff.js/codediff.css +35 -10
  14. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/codediff.js/codediff.js +86 -38
  15. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/js/file_diff.js +1 -1
  16. webdiff-1.0.0/webdiff/templates/base.html → webdiff-1.1.0/webdiff/templates/file_diff.html +18 -3
  17. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/unified_diff.py +20 -8
  18. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/util.py +7 -7
  19. webdiff-1.0.0/MANIFEST.in +0 -3
  20. webdiff-1.0.0/PKG-INFO +0 -209
  21. webdiff-1.0.0/setup.cfg +0 -4
  22. webdiff-1.0.0/setup.py +0 -46
  23. webdiff-1.0.0/webdiff/app.py +0 -368
  24. webdiff-1.0.0/webdiff/templates/file_diff.html +0 -27
  25. webdiff-1.0.0/webdiff.egg-info/SOURCES.txt +0 -293
  26. webdiff-1.0.0/webdiff.egg-info/dependency_links.txt +0 -1
  27. webdiff-1.0.0/webdiff.egg-info/entry_points.txt +0 -3
  28. webdiff-1.0.0/webdiff.egg-info/requires.txt +0 -7
  29. webdiff-1.0.0/webdiff.egg-info/top_level.txt +0 -1
  30. {webdiff-1.0.0 → webdiff-1.1.0}/LICENSE +0 -0
  31. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/__init__.py +0 -0
  32. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/__init__.py +0 -0
  33. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/codediff.js/difflib.js +0 -0
  34. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/codediff.js/googlecode.css +0 -0
  35. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/Inconsolata/Inconsolata-Bold.ttf +0 -0
  36. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/Inconsolata/Inconsolata-Regular.ttf +0 -0
  37. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/Inconsolata/OFL.txt +0 -0
  38. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/googlecode.css +0 -0
  39. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/inconsolata.css +0 -0
  40. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/style.css +0 -0
  41. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/a11y-dark.css +0 -0
  42. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/a11y-light.css +0 -0
  43. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/agate.css +0 -0
  44. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/an-old-hope.css +0 -0
  45. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/androidstudio.css +0 -0
  46. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/arduino-light.css +0 -0
  47. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/arta.css +0 -0
  48. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/ascetic.css +0 -0
  49. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/atom-one-dark-reasonable.css +0 -0
  50. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/atom-one-dark.css +0 -0
  51. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/atom-one-light.css +0 -0
  52. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/3024.css +0 -0
  53. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/apathy.css +0 -0
  54. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/apprentice.css +0 -0
  55. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ashes.css +0 -0
  56. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-cave-light.css +0 -0
  57. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-cave.css +0 -0
  58. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-dune-light.css +0 -0
  59. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-dune.css +0 -0
  60. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-estuary-light.css +0 -0
  61. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-estuary.css +0 -0
  62. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-forest-light.css +0 -0
  63. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-forest.css +0 -0
  64. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-heath-light.css +0 -0
  65. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-heath.css +0 -0
  66. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-lakeside-light.css +0 -0
  67. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-lakeside.css +0 -0
  68. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-plateau-light.css +0 -0
  69. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-plateau.css +0 -0
  70. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-savanna-light.css +0 -0
  71. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-savanna.css +0 -0
  72. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-seaside-light.css +0 -0
  73. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-seaside.css +0 -0
  74. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-sulphurpool-light.css +0 -0
  75. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atelier-sulphurpool.css +0 -0
  76. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/atlas.css +0 -0
  77. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/bespin.css +0 -0
  78. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-bathory.css +0 -0
  79. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-burzum.css +0 -0
  80. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-dark-funeral.css +0 -0
  81. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-gorgoroth.css +0 -0
  82. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-immortal.css +0 -0
  83. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-khold.css +0 -0
  84. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-marduk.css +0 -0
  85. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-mayhem.css +0 -0
  86. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-nile.css +0 -0
  87. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal-venom.css +0 -0
  88. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/black-metal.css +0 -0
  89. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/brewer.css +0 -0
  90. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/bright.css +0 -0
  91. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/brogrammer.css +0 -0
  92. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/brush-trees-dark.css +0 -0
  93. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/brush-trees.css +0 -0
  94. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/chalk.css +0 -0
  95. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/circus.css +0 -0
  96. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/classic-dark.css +0 -0
  97. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/classic-light.css +0 -0
  98. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/codeschool.css +0 -0
  99. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/colors.css +0 -0
  100. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/cupcake.css +0 -0
  101. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/cupertino.css +0 -0
  102. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/danqing.css +0 -0
  103. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/darcula.css +0 -0
  104. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/dark-violet.css +0 -0
  105. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/darkmoss.css +0 -0
  106. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/darktooth.css +0 -0
  107. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/decaf.css +0 -0
  108. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/default-dark.css +0 -0
  109. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/default-light.css +0 -0
  110. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/dirtysea.css +0 -0
  111. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/dracula.css +0 -0
  112. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/edge-dark.css +0 -0
  113. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/edge-light.css +0 -0
  114. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/eighties.css +0 -0
  115. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/embers.css +0 -0
  116. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/equilibrium-dark.css +0 -0
  117. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/equilibrium-gray-dark.css +0 -0
  118. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/equilibrium-gray-light.css +0 -0
  119. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/equilibrium-light.css +0 -0
  120. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/espresso.css +0 -0
  121. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/eva-dim.css +0 -0
  122. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/eva.css +0 -0
  123. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/flat.css +0 -0
  124. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/framer.css +0 -0
  125. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/fruit-soda.css +0 -0
  126. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gigavolt.css +0 -0
  127. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/github.css +0 -0
  128. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/google-dark.css +0 -0
  129. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/google-light.css +0 -0
  130. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/grayscale-dark.css +0 -0
  131. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/grayscale-light.css +0 -0
  132. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/green-screen.css +0 -0
  133. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-dark-hard.css +0 -0
  134. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-dark-medium.css +0 -0
  135. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-dark-pale.css +0 -0
  136. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-dark-soft.css +0 -0
  137. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-light-hard.css +0 -0
  138. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-light-medium.css +0 -0
  139. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/gruvbox-light-soft.css +0 -0
  140. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/hardcore.css +0 -0
  141. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/harmonic16-dark.css +0 -0
  142. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/harmonic16-light.css +0 -0
  143. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/heetch-dark.css +0 -0
  144. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/heetch-light.css +0 -0
  145. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/helios.css +0 -0
  146. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/hopscotch.css +0 -0
  147. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/horizon-dark.css +0 -0
  148. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/horizon-light.css +0 -0
  149. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/humanoid-dark.css +0 -0
  150. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/humanoid-light.css +0 -0
  151. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ia-dark.css +0 -0
  152. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ia-light.css +0 -0
  153. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/icy-dark.css +0 -0
  154. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ir-black.css +0 -0
  155. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/isotope.css +0 -0
  156. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/kimber.css +0 -0
  157. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/london-tube.css +0 -0
  158. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/macintosh.css +0 -0
  159. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/marrakesh.css +0 -0
  160. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/materia.css +0 -0
  161. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/material-darker.css +0 -0
  162. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/material-lighter.css +0 -0
  163. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/material-palenight.css +0 -0
  164. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/material-vivid.css +0 -0
  165. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/material.css +0 -0
  166. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/mellow-purple.css +0 -0
  167. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/mexico-light.css +0 -0
  168. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/mocha.css +0 -0
  169. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/monokai.css +0 -0
  170. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/nebula.css +0 -0
  171. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/nord.css +0 -0
  172. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/nova.css +0 -0
  173. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ocean.css +0 -0
  174. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/oceanicnext.css +0 -0
  175. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/one-light.css +0 -0
  176. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/onedark.css +0 -0
  177. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/outrun-dark.css +0 -0
  178. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/papercolor-dark.css +0 -0
  179. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/papercolor-light.css +0 -0
  180. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/paraiso.css +0 -0
  181. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/pasque.css +0 -0
  182. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/phd.css +0 -0
  183. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/pico.css +0 -0
  184. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/pop.css +0 -0
  185. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/porple.css +0 -0
  186. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/qualia.css +0 -0
  187. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/railscasts.css +0 -0
  188. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/rebecca.css +0 -0
  189. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ros-pine-dawn.css +0 -0
  190. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ros-pine-moon.css +0 -0
  191. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/ros-pine.css +0 -0
  192. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/sagelight.css +0 -0
  193. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/sandcastle.css +0 -0
  194. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/seti-ui.css +0 -0
  195. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/shapeshifter.css +0 -0
  196. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/silk-dark.css +0 -0
  197. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/silk-light.css +0 -0
  198. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/snazzy.css +0 -0
  199. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/solar-flare-light.css +0 -0
  200. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/solar-flare.css +0 -0
  201. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/solarized-dark.css +0 -0
  202. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/solarized-light.css +0 -0
  203. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/spacemacs.css +0 -0
  204. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/summercamp.css +0 -0
  205. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/summerfruit-dark.css +0 -0
  206. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/summerfruit-light.css +0 -0
  207. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/synth-midnight-terminal-dark.css +0 -0
  208. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/synth-midnight-terminal-light.css +0 -0
  209. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/tango.css +0 -0
  210. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/tender.css +0 -0
  211. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/tomorrow-night.css +0 -0
  212. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/tomorrow.css +0 -0
  213. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/twilight.css +0 -0
  214. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/unikitty-dark.css +0 -0
  215. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/unikitty-light.css +0 -0
  216. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/vulcan.css +0 -0
  217. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-10-light.css +0 -0
  218. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-10.css +0 -0
  219. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-95-light.css +0 -0
  220. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-95.css +0 -0
  221. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-high-contrast-light.css +0 -0
  222. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-high-contrast.css +0 -0
  223. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-nt-light.css +0 -0
  224. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/windows-nt.css +0 -0
  225. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/woodland.css +0 -0
  226. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/xcode-dusk.css +0 -0
  227. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/base16/zenburn.css +0 -0
  228. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/brown-paper.css +0 -0
  229. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/brown-papersq.png +0 -0
  230. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/codepen-embed.css +0 -0
  231. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/color-brewer.css +0 -0
  232. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/dark.css +0 -0
  233. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/default.css +0 -0
  234. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/devibeans.css +0 -0
  235. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/docco.css +0 -0
  236. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/far.css +0 -0
  237. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/felipec.css +0 -0
  238. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/foundation.css +0 -0
  239. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/github-dark-dimmed.css +0 -0
  240. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/github-dark.css +0 -0
  241. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/github.css +0 -0
  242. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/gml.css +0 -0
  243. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/googlecode.css +0 -0
  244. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/gradient-dark.css +0 -0
  245. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/gradient-light.css +0 -0
  246. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/grayscale.css +0 -0
  247. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/hybrid.css +0 -0
  248. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/idea.css +0 -0
  249. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/intellij-light.css +0 -0
  250. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/ir-black.css +0 -0
  251. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/isbl-editor-dark.css +0 -0
  252. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/isbl-editor-light.css +0 -0
  253. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/kimbie-dark.css +0 -0
  254. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/kimbie-light.css +0 -0
  255. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/lightfair.css +0 -0
  256. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/lioshi.css +0 -0
  257. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/magula.css +0 -0
  258. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/mono-blue.css +0 -0
  259. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/monokai-sublime.css +0 -0
  260. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/monokai.css +0 -0
  261. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/night-owl.css +0 -0
  262. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/nnfx-dark.css +0 -0
  263. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/nnfx-light.css +0 -0
  264. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/nord.css +0 -0
  265. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/obsidian.css +0 -0
  266. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/panda-syntax-dark.css +0 -0
  267. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/panda-syntax-light.css +0 -0
  268. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/paraiso-dark.css +0 -0
  269. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/paraiso-light.css +0 -0
  270. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/pojoaque.css +0 -0
  271. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/pojoaque.jpg +0 -0
  272. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/purebasic.css +0 -0
  273. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/qtcreator-dark.css +0 -0
  274. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/qtcreator-light.css +0 -0
  275. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/rainbow.css +0 -0
  276. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/routeros.css +0 -0
  277. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/school-book.css +0 -0
  278. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/shades-of-purple.css +0 -0
  279. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/srcery.css +0 -0
  280. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/stackoverflow-dark.css +0 -0
  281. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/stackoverflow-light.css +0 -0
  282. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/sunburst.css +0 -0
  283. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/tokyo-night-dark.css +0 -0
  284. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/tokyo-night-light.css +0 -0
  285. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/tomorrow-night-blue.css +0 -0
  286. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/tomorrow-night-bright.css +0 -0
  287. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/vs.css +0 -0
  288. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/vs2015.css +0 -0
  289. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/xcode.css +0 -0
  290. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/css/themes/xt256.css +0 -0
  291. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/img/favicon.ico +0 -0
  292. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/img/sprites.png +0 -0
  293. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/img/trans_bg.gif +0 -0
  294. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/js/highlight.min.js +0 -0
  295. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/js/highlight.pack.min.js +0 -0
  296. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/static/js/jquery-2.1.1.min.js +0 -0
  297. {webdiff-1.0.0 → webdiff-1.1.0}/webdiff/templates/__init__.py +0 -0
@@ -1,26 +1,29 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: webdiff
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Two-column web-based git difftool
5
5
  Home-page: https://github.com/danvk/webdiff/
6
+ License: Apache-2.0
7
+ Keywords: git,difftool,web
6
8
  Author: Dan Vanderkam
7
9
  Author-email: danvdk@gmail.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 4 - Beta
8
12
  Classifier: Environment :: Console
9
13
  Classifier: Environment :: Web Environment
10
- Classifier: Framework :: Flask
11
- Classifier: Development Status :: 4 - Beta
12
14
  Classifier: Intended Audience :: Developers
13
15
  Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
14
20
  Classifier: Topic :: Software Development :: Version Control
15
- Description-Content-Type: text/markdown
16
- License-File: LICENSE
21
+ Requires-Dist: PyGithub (==2.3.0)
17
22
  Requires-Dist: binaryornot
18
- Requires-Dist: flask==2.2.2
19
23
  Requires-Dist: pillow
20
- Requires-Dist: requests
21
- Requires-Dist: PyGithub==1.55
22
- Requires-Dist: unidiff==0.7.4
23
- Requires-Dist: Werkzeug==2.2.2
24
+ Requires-Dist: unidiff (==0.7.4)
25
+ Project-URL: Repository, https://github.com/danvk/webdiff/
26
+ Description-Content-Type: text/markdown
24
27
 
25
28
  # git webdiff
26
29
 
@@ -41,11 +44,12 @@ Features include:
41
44
  * Step back and forth through multiple files in a single diff
42
45
  * Rich support for image diffs
43
46
 
47
+ <!-- These are absolute URLs so that they display on pypi.org -->
44
48
  <!-- This is `git webdiff 05157bba^..05157bba`, in this repo -->
45
- ![Screenshot of webdiff in action](/images/webdiff.png)
49
+ ![Screenshot of webdiff in action](https://raw.githubusercontent.com/danvk/webdiff/master/images/webdiff.png)
46
50
 
47
51
  <!-- This is `git webdiff c80f969^..c80f969` in the dygraphs-dpxdt repo -->
48
- ![Screenshot of image diffs](/images/webdiff-images.png)
52
+ ![Screenshot of image diffs](https://raw.githubusercontent.com/danvk/webdiff/master/images/webdiff-images.png)
49
53
 
50
54
  ## Installation
51
55
 
@@ -78,7 +82,7 @@ You can pass all the same arguments that you would to `git diff`, e.g.
78
82
  You can also use `webdiff` to view GitHub pull requests:
79
83
 
80
84
  webdiff https://github.com/owner/repo/pull/123
81
- webdiff '#123' # if you're in a git repo with a github remote
85
+ webdiff '#150' # if you're in a git repo with a github remote
82
86
 
83
87
  This will download the files relevant to the Pull Request and run `webdiff`.
84
88
 
@@ -122,9 +126,7 @@ Options are:
122
126
 
123
127
  ## Development
124
128
 
125
- python3 -m venv venv
126
- source venv/bin/activate
127
- pip install -r requirements.txt
129
+ poetry install
128
130
  cd ts
129
131
  yarn
130
132
  # see https://github.com/webpack/webpack/issues/14532
@@ -132,7 +134,7 @@ Options are:
132
134
 
133
135
  Then from the root directory:
134
136
 
135
- PYTHONPATH=. ./webdiff/app.py testdata/dygraphsjs/{left,right}
137
+ poetry run webdiff/app.py testdata/dygraphsjs/{left,right}
136
138
 
137
139
  or to launch in debug mode:
138
140
 
@@ -142,43 +144,42 @@ or to launch in debug mode:
142
144
 
143
145
  To run the Python tests:
144
146
 
145
- pytest
147
+ poetry run pytest
146
148
 
147
149
  To format the code, run:
148
150
 
149
- ./scripts/black.sh
151
+ poetry run ruff format
150
152
  cd ts
151
153
  yarn prettier
152
154
 
153
155
  To debug `git webdiff`, run:
154
156
 
155
- WEBDIFF_CONFIG=$(pwd)/testing.cfg ./webdiff/gitwebdiff.py
157
+ ./test-gitwebdiff.sh
158
+
159
+ ## Publishing
156
160
 
157
161
  To iterate on the PyPI package, run:
158
162
 
159
- # from outside the webdiff virtualenv:
160
163
  pip3 uninstall webdiff
161
-
162
- # from inside the webdiff virtualenv, adjust for current version
163
- python setup.py sdist
164
- mkdir /tmp/webdiff-test
165
- cp dist/webdiff-?.?.?.tar.gz /tmp/webdiff-test
166
-
167
- deactivate
168
- cd /tmp/webdiff-test
169
- pip3 install webdiff-?.?.?.tar.gz
164
+ poetry build
165
+ pip3 install dist/webdiff-?.?.?.tar.gz
170
166
 
171
167
  To publish to pypitest:
172
168
 
173
- pip install --upgrade wheel setuptools twine
174
- python setup.py sdist bdist_wheel
175
- twine upload -r testpypi dist/*
169
+ poetry build
170
+ poetry publish -r testpypi
176
171
 
177
172
  And to the real pypi:
178
173
 
179
- twine upload dist/*
174
+ poetry publish
175
+
176
+ See [pypirc][] and [poetry][] docs for details on setting up tokens for pypi.
180
177
 
181
- See [pypirc][] docs for details on setting up `~/.pypirc`.
178
+ Publication checklist. Do these from _outside_ the webdiff directory:
179
+
180
+ - Run `webdiff webdiff/testdata/.../{left,right}`
181
+ - Run `git webdiff 52aa15f^..52aa15f` in the codediff.js repo
182
+ - Run `webdiff https://github.com/danvk/webdiff/pull/160`
182
183
 
183
184
  ## Implementation notes
184
185
 
@@ -207,3 +208,5 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
207
208
  [ImageMagick]: https://imagemagick.org/index.php
208
209
  [git config]: https://git-scm.com/docs/git-config
209
210
  [themes]: http://example.com
211
+ [poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
212
+
@@ -17,11 +17,12 @@ Features include:
17
17
  * Step back and forth through multiple files in a single diff
18
18
  * Rich support for image diffs
19
19
 
20
+ <!-- These are absolute URLs so that they display on pypi.org -->
20
21
  <!-- This is `git webdiff 05157bba^..05157bba`, in this repo -->
21
- ![Screenshot of webdiff in action](/images/webdiff.png)
22
+ ![Screenshot of webdiff in action](https://raw.githubusercontent.com/danvk/webdiff/master/images/webdiff.png)
22
23
 
23
24
  <!-- This is `git webdiff c80f969^..c80f969` in the dygraphs-dpxdt repo -->
24
- ![Screenshot of image diffs](/images/webdiff-images.png)
25
+ ![Screenshot of image diffs](https://raw.githubusercontent.com/danvk/webdiff/master/images/webdiff-images.png)
25
26
 
26
27
  ## Installation
27
28
 
@@ -54,7 +55,7 @@ You can pass all the same arguments that you would to `git diff`, e.g.
54
55
  You can also use `webdiff` to view GitHub pull requests:
55
56
 
56
57
  webdiff https://github.com/owner/repo/pull/123
57
- webdiff '#123' # if you're in a git repo with a github remote
58
+ webdiff '#150' # if you're in a git repo with a github remote
58
59
 
59
60
  This will download the files relevant to the Pull Request and run `webdiff`.
60
61
 
@@ -98,9 +99,7 @@ Options are:
98
99
 
99
100
  ## Development
100
101
 
101
- python3 -m venv venv
102
- source venv/bin/activate
103
- pip install -r requirements.txt
102
+ poetry install
104
103
  cd ts
105
104
  yarn
106
105
  # see https://github.com/webpack/webpack/issues/14532
@@ -108,7 +107,7 @@ Options are:
108
107
 
109
108
  Then from the root directory:
110
109
 
111
- PYTHONPATH=. ./webdiff/app.py testdata/dygraphsjs/{left,right}
110
+ poetry run webdiff/app.py testdata/dygraphsjs/{left,right}
112
111
 
113
112
  or to launch in debug mode:
114
113
 
@@ -118,43 +117,42 @@ or to launch in debug mode:
118
117
 
119
118
  To run the Python tests:
120
119
 
121
- pytest
120
+ poetry run pytest
122
121
 
123
122
  To format the code, run:
124
123
 
125
- ./scripts/black.sh
124
+ poetry run ruff format
126
125
  cd ts
127
126
  yarn prettier
128
127
 
129
128
  To debug `git webdiff`, run:
130
129
 
131
- WEBDIFF_CONFIG=$(pwd)/testing.cfg ./webdiff/gitwebdiff.py
130
+ ./test-gitwebdiff.sh
131
+
132
+ ## Publishing
132
133
 
133
134
  To iterate on the PyPI package, run:
134
135
 
135
- # from outside the webdiff virtualenv:
136
136
  pip3 uninstall webdiff
137
-
138
- # from inside the webdiff virtualenv, adjust for current version
139
- python setup.py sdist
140
- mkdir /tmp/webdiff-test
141
- cp dist/webdiff-?.?.?.tar.gz /tmp/webdiff-test
142
-
143
- deactivate
144
- cd /tmp/webdiff-test
145
- pip3 install webdiff-?.?.?.tar.gz
137
+ poetry build
138
+ pip3 install dist/webdiff-?.?.?.tar.gz
146
139
 
147
140
  To publish to pypitest:
148
141
 
149
- pip install --upgrade wheel setuptools twine
150
- python setup.py sdist bdist_wheel
151
- twine upload -r testpypi dist/*
142
+ poetry build
143
+ poetry publish -r testpypi
152
144
 
153
145
  And to the real pypi:
154
146
 
155
- twine upload dist/*
147
+ poetry publish
148
+
149
+ See [pypirc][] and [poetry][] docs for details on setting up tokens for pypi.
150
+
151
+ Publication checklist. Do these from _outside_ the webdiff directory:
156
152
 
157
- See [pypirc][] docs for details on setting up `~/.pypirc`.
153
+ - Run `webdiff webdiff/testdata/.../{left,right}`
154
+ - Run `git webdiff 52aa15f^..52aa15f` in the codediff.js repo
155
+ - Run `webdiff https://github.com/danvk/webdiff/pull/160`
158
156
 
159
157
  ## Implementation notes
160
158
 
@@ -183,3 +181,4 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
183
181
  [ImageMagick]: https://imagemagick.org/index.php
184
182
  [git config]: https://git-scm.com/docs/git-config
185
183
  [themes]: http://example.com
184
+ [poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["poetry-core"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "webdiff"
7
+ version = "1.1.0"
8
+ description = "Two-column web-based git difftool"
9
+ license = "Apache-2.0"
10
+ readme = "README.md"
11
+ authors = [ "Dan Vanderkam <danvdk@gmail.com>" ]
12
+ homepage = "https://github.com/danvk/webdiff/"
13
+ repository = "https://github.com/danvk/webdiff/"
14
+ keywords = [ "git", "difftool", "web" ]
15
+ exclude = ["tests*"]
16
+ classifiers = [
17
+ "Environment :: Console",
18
+ "Environment :: Web Environment",
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "License :: OSI Approved :: Apache Software License",
22
+ "Topic :: Software Development :: Version Control"
23
+ ]
24
+ # This is generated, so it's in .gitignore. But we need to package it.
25
+ include = ["webdiff/static/js/file_diff.js"]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = "^3.10"
29
+ binaryornot = "*"
30
+ pillow = "*"
31
+ PyGithub = "2.3.0"
32
+ unidiff = "==0.7.4"
33
+
34
+ [tool.poetry.group.dev.dependencies]
35
+ pytest = "^7.1.3"
36
+ ruff = "^0.4.6"
37
+
38
+ [tool.poetry.scripts]
39
+ webdiff = "webdiff.app:run"
40
+ git-webdiff = "webdiff.gitwebdiff:run"
41
+
42
+ [tool.ruff]
43
+ line-length = 88
44
+
45
+ [tool.ruff.format]
46
+ quote-style = "single"
47
+ docstring-code-format = true
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env python
2
+ """Web-based file differ.
3
+
4
+ For usage, see README.md.
5
+ """
6
+
7
+ import dataclasses
8
+ import json
9
+ import logging
10
+ import importlib.metadata
11
+ import mimetypes
12
+ import os
13
+ import re
14
+ import platform
15
+ import socket
16
+ import sys
17
+ import threading
18
+ import time
19
+ import webbrowser
20
+ from binaryornot.check import is_binary
21
+ from http.server import BaseHTTPRequestHandler, HTTPServer
22
+ from urllib.parse import urlparse, parse_qs
23
+
24
+ from webdiff import diff, util, argparser, options
25
+
26
+ VERSION = importlib.metadata.version('webdiff')
27
+
28
+
29
+ def determine_path():
30
+ """Borrowed from wxglade.py"""
31
+ try:
32
+ root = __file__
33
+ if os.path.islink(root):
34
+ root = os.path.realpath(root)
35
+ return os.path.dirname(os.path.abspath(root))
36
+ except Exception as e:
37
+ print(f"I'm sorry, but something is wrong. Error: {e}")
38
+ print('There is no __file__ variable. Please contact the author.')
39
+ sys.exit()
40
+
41
+
42
+ GIT_CONFIG = {}
43
+ DIFF = None
44
+ PORT = None
45
+ HOSTNAME = 'localhost'
46
+ DEBUG = os.environ.get('DEBUG')
47
+ WEBDIFF_DIR = determine_path()
48
+
49
+ if DEBUG:
50
+ handler = logging.StreamHandler()
51
+ handler.setLevel(logging.DEBUG)
52
+ formatter = logging.Formatter(
53
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
54
+ )
55
+ handler.setFormatter(formatter)
56
+
57
+ for logname in ['']:
58
+ log = logging.getLogger(logname)
59
+ log.setLevel(logging.DEBUG)
60
+ log.addHandler(handler)
61
+ logging.getLogger('github').setLevel(logging.ERROR)
62
+
63
+ # GPT's alternative:
64
+ # logging.basicConfig(level=logging.DEBUG if 'DEBUG' in os.environ else logging.INFO)
65
+ # logger = logging.getLogger(__name__)
66
+
67
+
68
+ class CustomHTTPRequestHandler(BaseHTTPRequestHandler):
69
+ def do_GET(self):
70
+ note_request_time()
71
+ path = urlparse(self.path).path.removesuffix('/') or '/'
72
+
73
+ if path == '/':
74
+ self.handle_index(0)
75
+ elif m := re.match(r'/(?P<idx>\d+)$', path):
76
+ self.handle_index(int(m['idx']))
77
+ elif path == '/favicon.ico':
78
+ self.handle_favicon()
79
+ elif path == '/theme.css':
80
+ self.handle_theme()
81
+ elif path.startswith('/static/'):
82
+ self.handle_static(path[1:])
83
+ elif m := re.match(r'/thick/(?P<idx>\d+)', path):
84
+ self.handle_thick(int(m['idx']))
85
+ elif m := re.match(r'/(?P<side>a|b)/image/(?P<path>.*)', path):
86
+ self.handle_image(m['side'], m['path'])
87
+ elif m := re.match(r'/pdiff/(?P<idx>\d+)', path):
88
+ self.handle_pdiff(int(m['idx']))
89
+ elif m := re.match(r'/pdiffbbox/(?P<idx>\d+)', path):
90
+ self.handle_pdiff_bbox(int(m['idx']))
91
+ else:
92
+ self.send_error(404, 'File not found')
93
+
94
+ def do_POST(self):
95
+ note_request_time()
96
+ path = urlparse(self.path).path.removesuffix('/') or '/'
97
+
98
+ content_length = int(self.headers['Content-Length'])
99
+ post_data = self.rfile.read(content_length)
100
+
101
+ if m := re.match(r'/(?P<side>a|b)/get_contents', path):
102
+ form_data = parse_qs(post_data.decode('utf-8'))
103
+ self.handle_get_contents(m['side'], form_data)
104
+ elif m := re.match(r'/diff/(?P<idx>\d+)', path):
105
+ payload = json.loads(post_data.decode('utf-8'))
106
+ self.handle_diff_ops(int(m['idx']), payload)
107
+ elif path == '/kill':
108
+ self.handle_kill()
109
+ else:
110
+ self.send_error(404, 'File not found')
111
+
112
+ def handle_index(self, idx: int):
113
+ pairs = diff.get_thin_list(DIFF)
114
+ self.send_response(200)
115
+ self.send_header('Content-Type', 'text/html; charset=utf-8')
116
+ self.end_headers()
117
+ with open(os.path.join(WEBDIFF_DIR, 'templates/file_diff.html'), 'r') as file:
118
+ html = file.read()
119
+ html = html.replace(
120
+ '{{data}}',
121
+ json.dumps(
122
+ {
123
+ 'idx': idx,
124
+ 'has_magick': util.is_imagemagick_available(),
125
+ 'pairs': pairs,
126
+ 'git_config': GIT_CONFIG,
127
+ },
128
+ indent=2,
129
+ ),
130
+ )
131
+ self.wfile.write(html.encode('utf-8'))
132
+
133
+ def handle_thick(self, idx: int):
134
+ self.send_response_with_json(200, diff.get_thick_dict(DIFF[idx]))
135
+
136
+ def handle_favicon(self):
137
+ self.serve_static_file('static/img/favicon.ico', 'image/vnd.microsoft.icon')
138
+
139
+ def handle_theme(self):
140
+ theme = GIT_CONFIG['webdiff']['theme']
141
+ theme_dir = os.path.dirname(theme)
142
+ theme_file = os.path.basename(theme)
143
+ theme_path = os.path.join('static/css/themes', theme_dir, theme_file + '.css')
144
+ self.serve_static_file(theme_path, 'text/css')
145
+
146
+ def handle_static(self, path: str):
147
+ mime_type, _ = mimetypes.guess_type(path)
148
+ self.serve_static_file(path, mime_type)
149
+
150
+ def handle_image(self, side, path):
151
+ mime_type, _ = mimetypes.guess_type(path)
152
+ if not mime_type or not mime_type.startswith('image/'):
153
+ return self.send_response_with_json(400, {'error': 'wrong type'})
154
+
155
+ idx = diff.find_diff_index(DIFF, side, path)
156
+ if idx is None:
157
+ return self.send_response_with_json(400, {'error': 'not found'})
158
+
159
+ d = DIFF[idx]
160
+ abs_path = d.a_path if side == 'a' else d.b_path
161
+ self.serve_file(abs_path, mime_type)
162
+
163
+ def handle_pdiff(self, idx):
164
+ d = DIFF[idx]
165
+ try:
166
+ _, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
167
+ dilated_image_path = util.generate_dilated_pdiff_image(pdiff_image)
168
+ self.serve_static_file(dilated_image_path, 'image/png')
169
+ except util.ImageMagickNotAvailableError:
170
+ self.send_error(501, 'ImageMagick is not available')
171
+ except util.ImageMagickError as e:
172
+ self.send_error(501, f'ImageMagick error {e}')
173
+
174
+ def handle_pdiff_bbox(self, idx):
175
+ d = DIFF[idx]
176
+ try:
177
+ _, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
178
+ bbox = util.get_pdiff_bbox(pdiff_image)
179
+ self.send_response_with_json(200, bbox)
180
+ except util.ImageMagickNotAvailableError:
181
+ self.send_error(501, 'ImageMagick is not available')
182
+ except util.ImageMagickError as e:
183
+ self.send_error(501, f'ImageMagick error {e}')
184
+
185
+ def handle_get_contents(self, side: str, form_data: dict[str, list[str]]):
186
+ path = form_data.get('path', [''])[0]
187
+ if not path:
188
+ return self.send_response_with_json(400, {'error': 'incomplete'})
189
+
190
+ idx = diff.find_diff_index(DIFF, side, path)
191
+ if idx is None:
192
+ return self.send_response_with_json(400, {'error': 'not found'})
193
+
194
+ d = DIFF[idx]
195
+ abs_path = d.a_path if side == 'a' else d.b_path
196
+
197
+ try:
198
+ if is_binary(abs_path):
199
+ size = os.path.getsize(abs_path)
200
+ contents = f'Binary file ({size} bytes)'
201
+ else:
202
+ with open(abs_path, 'r') as file:
203
+ contents = file.read()
204
+ self.send_response(200)
205
+ self.send_header('Content-Type', 'text/plain')
206
+ self.end_headers()
207
+ self.wfile.write(contents.encode('utf-8'))
208
+ except Exception as e:
209
+ self.send_response_with_json(500, {'error': str(e)})
210
+
211
+ def handle_diff_ops(self, idx: int, payload):
212
+ options = payload.get('options') or []
213
+ extra_args = GIT_CONFIG['webdiff']['extraFileDiffArgs']
214
+ if extra_args:
215
+ options += extra_args.split(' ')
216
+ diff_ops = [
217
+ dataclasses.asdict(op) for op in diff.get_diff_ops(DIFF[idx], options)
218
+ ]
219
+ self.send_response_with_json(200, diff_ops)
220
+
221
+ def handle_kill(self):
222
+ last_ms = LAST_REQUEST_MS
223
+
224
+ def shutdown():
225
+ if LAST_REQUEST_MS <= last_ms: # subsequent requests abort shutdown
226
+ # See https://stackoverflow.com/a/19040484/388951
227
+ # and https://stackoverflow.com/q/4330111/388951
228
+ sys.stderr.write('Shutting down...\n')
229
+ threading.Thread(target=self.server.shutdown, daemon=True).start()
230
+ else:
231
+ logging.debug('Received subsequent request; canceling shutdown')
232
+
233
+ logging.debug('Received request to shut down; waiting 500ms for subsequent requests...')
234
+ threading.Timer(0.5, shutdown).start()
235
+ self.send_response(200)
236
+ self.send_header('Content-Type', 'text/plain')
237
+ self.end_headers()
238
+ self.wfile.write(b'Shutting down...')
239
+
240
+ def send_response_with_json(self, code, payload):
241
+ self.send_response(code)
242
+ self.send_header('Content-Type', 'application/json')
243
+ self.end_headers()
244
+ self.wfile.write(json.dumps(payload).encode('utf-8'))
245
+
246
+ def serve_static_file(self, file_path, mime_type):
247
+ self.serve_file(os.path.join(WEBDIFF_DIR, file_path), mime_type)
248
+
249
+ def serve_file(self, file_path, mime_type):
250
+ try:
251
+ with open(file_path, 'rb') as file:
252
+ contents = file.read()
253
+ self.send_response(200)
254
+ self.send_header('Content-Type', mime_type)
255
+ self.end_headers()
256
+ self.wfile.write(contents)
257
+ except Exception as e:
258
+ self.send_response_with_json(500, {'error': str(e)})
259
+
260
+ def log_request(self, *args):
261
+ if DEBUG:
262
+ super().log_request(*args)
263
+
264
+
265
+ def note_request_time():
266
+ global LAST_REQUEST_MS
267
+ LAST_REQUEST_MS = time.time() * 1000
268
+
269
+
270
+ def open_browser():
271
+ global PORT
272
+ global HOSTNAME
273
+ global GIT_CONFIG
274
+ if not os.environ.get('WEBDIFF_NO_OPEN') and GIT_CONFIG['webdiff']['openBrowser']:
275
+ webbrowser.open_new_tab('http://%s:%s' % (HOSTNAME, PORT))
276
+
277
+
278
+ def usage_and_die():
279
+ sys.stderr.write(argparser.USAGE)
280
+ sys.exit(1)
281
+
282
+
283
+ def pick_a_port(args, webdiff_config):
284
+ if 'port' in args:
285
+ return args['port']
286
+
287
+ env_port = os.environ.get('WEBDIFF_PORT')
288
+ if env_port:
289
+ return int(env_port)
290
+
291
+ # gitconfig
292
+ if webdiff_config['port'] != -1:
293
+ return webdiff_config['port']
294
+
295
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
296
+ sock.bind(('localhost', 0))
297
+ port = sock.getsockname()[1]
298
+ sock.close()
299
+ return port
300
+
301
+
302
+ def run():
303
+ global DIFF, PORT, HOSTNAME, GIT_CONFIG
304
+ try:
305
+ parsed_args = argparser.parse(sys.argv[1:], VERSION)
306
+ except argparser.UsageError as e:
307
+ sys.stderr.write('Error: %s\n\n' % e)
308
+ usage_and_die()
309
+
310
+ GIT_CONFIG = options.get_config()
311
+ WEBDIFF_CONFIG = GIT_CONFIG['webdiff']
312
+ DIFF = argparser.diff_for_args(parsed_args, WEBDIFF_CONFIG)
313
+
314
+ if DEBUG:
315
+ sys.stderr.write('Invoked as: %s\n' % sys.argv)
316
+ sys.stderr.write('Args: %s\n' % parsed_args)
317
+ sys.stderr.write('Diff: %s\n' % DIFF)
318
+ sys.stderr.write('GitConfig: %s\n' % GIT_CONFIG)
319
+
320
+ PORT = pick_a_port(parsed_args, WEBDIFF_CONFIG)
321
+ HOSTNAME = (
322
+ parsed_args.get('host')
323
+ or os.environ.get('WEBDIFF_HOST')
324
+ or WEBDIFF_CONFIG['host']
325
+ )
326
+ if HOSTNAME == '<hostname>':
327
+ _hostname = platform.node()
328
+ # platform.node will return empty string if it can't find the hostname
329
+ if not _hostname:
330
+ sys.stderr.write('Warning: hostname could not be determined\n')
331
+ else:
332
+ HOSTNAME = _hostname
333
+
334
+ sys.stderr.write(
335
+ """Serving diffs on http://%s:%s
336
+ Close the browser tab or hit Ctrl-C when you're done.
337
+ """
338
+ % (HOSTNAME, PORT)
339
+ )
340
+ threading.Timer(0.1, open_browser).start()
341
+
342
+ server = HTTPServer((HOSTNAME, PORT), CustomHTTPRequestHandler)
343
+ server.serve_forever()
344
+
345
+
346
+ if __name__ == '__main__':
347
+ run()