webdiff 1.2.0__tar.gz → 1.3.0b1__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 (287) hide show
  1. {webdiff-1.2.0 → webdiff-1.3.0b1}/PKG-INFO +10 -5
  2. {webdiff-1.2.0 → webdiff-1.3.0b1}/README.md +6 -2
  3. {webdiff-1.2.0 → webdiff-1.3.0b1}/pyproject.toml +5 -4
  4. webdiff-1.3.0b1/webdiff/app.py +341 -0
  5. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/diff.py +14 -2
  6. webdiff-1.3.0b1/webdiff/static/js/file_diff.js +61 -0
  7. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/templates/file_diff.html +0 -8
  8. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/util.py +19 -0
  9. webdiff-1.2.0/webdiff/app.py +0 -347
  10. webdiff-1.2.0/webdiff/static/js/file_diff.js +0 -47
  11. webdiff-1.2.0/webdiff/static/js/jquery-2.1.1.min.js +0 -4
  12. {webdiff-1.2.0 → webdiff-1.3.0b1}/LICENSE +0 -0
  13. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/__init__.py +0 -0
  14. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/argparser.py +0 -0
  15. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/dirdiff.py +0 -0
  16. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/github_fetcher.py +0 -0
  17. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/githubdiff.py +0 -0
  18. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/gitwebdiff.py +0 -0
  19. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/gitwebshow.py +0 -0
  20. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/localfilediff.py +0 -0
  21. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/options.py +0 -0
  22. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/__init__.py +0 -0
  23. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/codediff.js/codediff.css +0 -0
  24. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/codediff.js/googlecode.css +0 -0
  25. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/Inconsolata-Bold.ttf +0 -0
  26. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/Inconsolata-Regular.ttf +0 -0
  27. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/OFL.txt +0 -0
  28. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/googlecode.css +0 -0
  29. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/inconsolata.css +0 -0
  30. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/style.css +0 -0
  31. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/a11y-dark.css +0 -0
  32. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/a11y-light.css +0 -0
  33. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/agate.css +0 -0
  34. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/an-old-hope.css +0 -0
  35. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/androidstudio.css +0 -0
  36. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/arduino-light.css +0 -0
  37. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/arta.css +0 -0
  38. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/ascetic.css +0 -0
  39. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-dark-reasonable.css +0 -0
  40. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-dark.css +0 -0
  41. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-light.css +0 -0
  42. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/3024.css +0 -0
  43. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/apathy.css +0 -0
  44. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/apprentice.css +0 -0
  45. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ashes.css +0 -0
  46. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-cave-light.css +0 -0
  47. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-cave.css +0 -0
  48. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-dune-light.css +0 -0
  49. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-dune.css +0 -0
  50. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-estuary-light.css +0 -0
  51. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-estuary.css +0 -0
  52. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-forest-light.css +0 -0
  53. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-forest.css +0 -0
  54. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-heath-light.css +0 -0
  55. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-heath.css +0 -0
  56. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-lakeside-light.css +0 -0
  57. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-lakeside.css +0 -0
  58. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-plateau-light.css +0 -0
  59. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-plateau.css +0 -0
  60. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-savanna-light.css +0 -0
  61. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-savanna.css +0 -0
  62. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-seaside-light.css +0 -0
  63. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-seaside.css +0 -0
  64. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-sulphurpool-light.css +0 -0
  65. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-sulphurpool.css +0 -0
  66. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atlas.css +0 -0
  67. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/bespin.css +0 -0
  68. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-bathory.css +0 -0
  69. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-burzum.css +0 -0
  70. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-dark-funeral.css +0 -0
  71. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-gorgoroth.css +0 -0
  72. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-immortal.css +0 -0
  73. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-khold.css +0 -0
  74. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-marduk.css +0 -0
  75. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-mayhem.css +0 -0
  76. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-nile.css +0 -0
  77. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-venom.css +0 -0
  78. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal.css +0 -0
  79. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brewer.css +0 -0
  80. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/bright.css +0 -0
  81. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brogrammer.css +0 -0
  82. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brush-trees-dark.css +0 -0
  83. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brush-trees.css +0 -0
  84. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/chalk.css +0 -0
  85. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/circus.css +0 -0
  86. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/classic-dark.css +0 -0
  87. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/classic-light.css +0 -0
  88. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/codeschool.css +0 -0
  89. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/colors.css +0 -0
  90. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/cupcake.css +0 -0
  91. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/cupertino.css +0 -0
  92. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/danqing.css +0 -0
  93. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darcula.css +0 -0
  94. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dark-violet.css +0 -0
  95. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darkmoss.css +0 -0
  96. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darktooth.css +0 -0
  97. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/decaf.css +0 -0
  98. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/default-dark.css +0 -0
  99. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/default-light.css +0 -0
  100. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dirtysea.css +0 -0
  101. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dracula.css +0 -0
  102. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/edge-dark.css +0 -0
  103. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/edge-light.css +0 -0
  104. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eighties.css +0 -0
  105. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/embers.css +0 -0
  106. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-dark.css +0 -0
  107. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-gray-dark.css +0 -0
  108. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-gray-light.css +0 -0
  109. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-light.css +0 -0
  110. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/espresso.css +0 -0
  111. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eva-dim.css +0 -0
  112. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eva.css +0 -0
  113. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/flat.css +0 -0
  114. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/framer.css +0 -0
  115. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/fruit-soda.css +0 -0
  116. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gigavolt.css +0 -0
  117. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/github.css +0 -0
  118. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/google-dark.css +0 -0
  119. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/google-light.css +0 -0
  120. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/grayscale-dark.css +0 -0
  121. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/grayscale-light.css +0 -0
  122. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/green-screen.css +0 -0
  123. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-hard.css +0 -0
  124. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-medium.css +0 -0
  125. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-pale.css +0 -0
  126. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-soft.css +0 -0
  127. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-hard.css +0 -0
  128. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-medium.css +0 -0
  129. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-soft.css +0 -0
  130. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/hardcore.css +0 -0
  131. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/harmonic16-dark.css +0 -0
  132. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/harmonic16-light.css +0 -0
  133. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/heetch-dark.css +0 -0
  134. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/heetch-light.css +0 -0
  135. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/helios.css +0 -0
  136. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/hopscotch.css +0 -0
  137. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/horizon-dark.css +0 -0
  138. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/horizon-light.css +0 -0
  139. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/humanoid-dark.css +0 -0
  140. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/humanoid-light.css +0 -0
  141. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ia-dark.css +0 -0
  142. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ia-light.css +0 -0
  143. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/icy-dark.css +0 -0
  144. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ir-black.css +0 -0
  145. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/isotope.css +0 -0
  146. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/kimber.css +0 -0
  147. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/london-tube.css +0 -0
  148. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/macintosh.css +0 -0
  149. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/marrakesh.css +0 -0
  150. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/materia.css +0 -0
  151. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-darker.css +0 -0
  152. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-lighter.css +0 -0
  153. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-palenight.css +0 -0
  154. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-vivid.css +0 -0
  155. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material.css +0 -0
  156. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mellow-purple.css +0 -0
  157. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mexico-light.css +0 -0
  158. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mocha.css +0 -0
  159. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/monokai.css +0 -0
  160. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nebula.css +0 -0
  161. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nord.css +0 -0
  162. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nova.css +0 -0
  163. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ocean.css +0 -0
  164. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/oceanicnext.css +0 -0
  165. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/one-light.css +0 -0
  166. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/onedark.css +0 -0
  167. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/outrun-dark.css +0 -0
  168. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/papercolor-dark.css +0 -0
  169. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/papercolor-light.css +0 -0
  170. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/paraiso.css +0 -0
  171. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pasque.css +0 -0
  172. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/phd.css +0 -0
  173. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pico.css +0 -0
  174. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pop.css +0 -0
  175. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/porple.css +0 -0
  176. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/qualia.css +0 -0
  177. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/railscasts.css +0 -0
  178. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/rebecca.css +0 -0
  179. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine-dawn.css +0 -0
  180. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine-moon.css +0 -0
  181. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine.css +0 -0
  182. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/sagelight.css +0 -0
  183. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/sandcastle.css +0 -0
  184. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/seti-ui.css +0 -0
  185. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/shapeshifter.css +0 -0
  186. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/silk-dark.css +0 -0
  187. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/silk-light.css +0 -0
  188. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/snazzy.css +0 -0
  189. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solar-flare-light.css +0 -0
  190. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solar-flare.css +0 -0
  191. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solarized-dark.css +0 -0
  192. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solarized-light.css +0 -0
  193. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/spacemacs.css +0 -0
  194. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summercamp.css +0 -0
  195. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summerfruit-dark.css +0 -0
  196. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summerfruit-light.css +0 -0
  197. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/synth-midnight-terminal-dark.css +0 -0
  198. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/synth-midnight-terminal-light.css +0 -0
  199. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tango.css +0 -0
  200. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tender.css +0 -0
  201. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tomorrow-night.css +0 -0
  202. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tomorrow.css +0 -0
  203. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/twilight.css +0 -0
  204. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/unikitty-dark.css +0 -0
  205. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/unikitty-light.css +0 -0
  206. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/vulcan.css +0 -0
  207. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-10-light.css +0 -0
  208. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-10.css +0 -0
  209. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-95-light.css +0 -0
  210. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-95.css +0 -0
  211. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-high-contrast-light.css +0 -0
  212. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-high-contrast.css +0 -0
  213. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-nt-light.css +0 -0
  214. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-nt.css +0 -0
  215. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/woodland.css +0 -0
  216. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/xcode-dusk.css +0 -0
  217. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/zenburn.css +0 -0
  218. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/brown-paper.css +0 -0
  219. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/brown-papersq.png +0 -0
  220. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/codepen-embed.css +0 -0
  221. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/color-brewer.css +0 -0
  222. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/dark.css +0 -0
  223. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/default.css +0 -0
  224. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/devibeans.css +0 -0
  225. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/docco.css +0 -0
  226. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/far.css +0 -0
  227. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/felipec.css +0 -0
  228. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/foundation.css +0 -0
  229. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github-dark-dimmed.css +0 -0
  230. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github-dark.css +0 -0
  231. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github.css +0 -0
  232. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gml.css +0 -0
  233. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/googlecode.css +0 -0
  234. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gradient-dark.css +0 -0
  235. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gradient-light.css +0 -0
  236. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/grayscale.css +0 -0
  237. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/hybrid.css +0 -0
  238. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/idea.css +0 -0
  239. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/intellij-light.css +0 -0
  240. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/ir-black.css +0 -0
  241. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/isbl-editor-dark.css +0 -0
  242. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/isbl-editor-light.css +0 -0
  243. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/kimbie-dark.css +0 -0
  244. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/kimbie-light.css +0 -0
  245. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/lightfair.css +0 -0
  246. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/lioshi.css +0 -0
  247. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/magula.css +0 -0
  248. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/mono-blue.css +0 -0
  249. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/monokai-sublime.css +0 -0
  250. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/monokai.css +0 -0
  251. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/night-owl.css +0 -0
  252. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nnfx-dark.css +0 -0
  253. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nnfx-light.css +0 -0
  254. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nord.css +0 -0
  255. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/obsidian.css +0 -0
  256. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/panda-syntax-dark.css +0 -0
  257. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/panda-syntax-light.css +0 -0
  258. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/paraiso-dark.css +0 -0
  259. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/paraiso-light.css +0 -0
  260. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/pojoaque.css +0 -0
  261. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/pojoaque.jpg +0 -0
  262. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/purebasic.css +0 -0
  263. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/qtcreator-dark.css +0 -0
  264. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/qtcreator-light.css +0 -0
  265. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/rainbow.css +0 -0
  266. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/routeros.css +0 -0
  267. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/school-book.css +0 -0
  268. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/shades-of-purple.css +0 -0
  269. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/srcery.css +0 -0
  270. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/stackoverflow-dark.css +0 -0
  271. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/stackoverflow-light.css +0 -0
  272. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/sunburst.css +0 -0
  273. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tokyo-night-dark.css +0 -0
  274. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tokyo-night-light.css +0 -0
  275. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tomorrow-night-blue.css +0 -0
  276. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tomorrow-night-bright.css +0 -0
  277. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/vs.css +0 -0
  278. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/vs2015.css +0 -0
  279. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/xcode.css +0 -0
  280. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/xt256.css +0 -0
  281. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/favicon.ico +0 -0
  282. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/sprites.png +0 -0
  283. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/trans_bg.gif +0 -0
  284. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/js/highlight.min.js +0 -0
  285. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/js/highlight.pack.min.js +0 -0
  286. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/templates/__init__.py +0 -0
  287. {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/unified_diff.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: webdiff
3
- Version: 1.2.0
3
+ Version: 1.3.0b1
4
4
  Summary: Two-column web-based git difftool
5
5
  Home-page: https://github.com/danvk/webdiff/
6
6
  License: Apache-2.0
@@ -18,10 +18,11 @@ Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Software Development :: Version Control
21
- Requires-Dist: PyGithub (==2.3.0)
21
+ Requires-Dist: PyGithub (>=2.3.0,<3.0.0)
22
+ Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
22
23
  Requires-Dist: binaryornot
23
24
  Requires-Dist: pillow
24
- Requires-Dist: unidiff (==0.7.4)
25
+ Requires-Dist: unidiff (>=0.7.4,<0.8.0)
25
26
  Project-URL: Repository, https://github.com/danvk/webdiff/
26
27
  Description-Content-Type: text/markdown
27
28
 
@@ -55,6 +56,10 @@ Features include:
55
56
 
56
57
  pip install webdiff
57
58
 
59
+ or using [pipx]:
60
+
61
+ pipx install webdiff
62
+
58
63
  or, if you prefer [Homebrew]:
59
64
 
60
65
  brew install danvk/webdiff/webdiff
@@ -135,8 +140,7 @@ Options are:
135
140
  poetry install
136
141
  cd ts
137
142
  yarn
138
- # see https://github.com/webpack/webpack/issues/14532
139
- NODE_OPTIONS=--openssl-legacy-provider webpack
143
+ yarn build
140
144
 
141
145
  Then from the root directory:
142
146
 
@@ -216,4 +220,5 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
216
220
  [git config]: https://git-scm.com/docs/git-config
217
221
  [themes]: http://example.com
218
222
  [poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
223
+ [pipx]: https://pipx.pypa.io/stable/
219
224
 
@@ -28,6 +28,10 @@ Features include:
28
28
 
29
29
  pip install webdiff
30
30
 
31
+ or using [pipx]:
32
+
33
+ pipx install webdiff
34
+
31
35
  or, if you prefer [Homebrew]:
32
36
 
33
37
  brew install danvk/webdiff/webdiff
@@ -108,8 +112,7 @@ Options are:
108
112
  poetry install
109
113
  cd ts
110
114
  yarn
111
- # see https://github.com/webpack/webpack/issues/14532
112
- NODE_OPTIONS=--openssl-legacy-provider webpack
115
+ yarn build
113
116
 
114
117
  Then from the root directory:
115
118
 
@@ -189,3 +192,4 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
189
192
  [git config]: https://git-scm.com/docs/git-config
190
193
  [themes]: http://example.com
191
194
  [poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
195
+ [pipx]: https://pipx.pypa.io/stable/
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "webdiff"
7
- version = "1.2.0"
7
+ version = "1.3.0b1"
8
8
  description = "Two-column web-based git difftool"
9
9
  license = "Apache-2.0"
10
10
  readme = "README.md"
@@ -28,12 +28,13 @@ include = ["webdiff/static/js/file_diff.js"]
28
28
  python = "^3.10"
29
29
  binaryornot = "*"
30
30
  pillow = "*"
31
- PyGithub = "2.3.0"
32
- unidiff = "==0.7.4"
31
+ PyGithub = "^2.3.0"
32
+ unidiff = "^0.7.4"
33
+ aiohttp = "^3.9.5"
33
34
 
34
35
  [tool.poetry.group.dev.dependencies]
35
36
  pytest = "^7.1.3"
36
- ruff = "^0.4.6"
37
+ ruff = "0.7"
37
38
 
38
39
  [tool.poetry.scripts]
39
40
  webdiff = "webdiff.app:run"
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env python
2
+ """Web-based file differ.
3
+
4
+ For usage, see README.md.
5
+ """
6
+
7
+ import dataclasses
8
+ import importlib.metadata
9
+ import json
10
+ import logging
11
+ import mimetypes
12
+ import os
13
+ import platform
14
+ import signal
15
+ import socket
16
+ import sys
17
+ import threading
18
+ import time
19
+ import webbrowser
20
+
21
+ import aiohttp
22
+ import aiohttp.web_request
23
+ from aiohttp import web
24
+ from binaryornot.check import is_binary
25
+
26
+ from webdiff import argparser, diff, options, util
27
+
28
+ VERSION = importlib.metadata.version('webdiff')
29
+
30
+
31
+ def determine_path():
32
+ """Borrowed from wxglade.py"""
33
+ try:
34
+ root = __file__
35
+ if os.path.islink(root):
36
+ root = os.path.realpath(root)
37
+ return os.path.dirname(os.path.abspath(root))
38
+ except Exception as e:
39
+ print(f"I'm sorry, but something is wrong. Error: {e}")
40
+ print('There is no __file__ variable. Please contact the author.')
41
+ sys.exit()
42
+
43
+
44
+ GIT_CONFIG = {}
45
+ DIFF = None
46
+ PORT = None
47
+ HOSTNAME = 'localhost'
48
+ DEBUG = os.environ.get('DEBUG')
49
+ WEBDIFF_DIR = determine_path()
50
+
51
+ if DEBUG:
52
+ handler = logging.StreamHandler()
53
+ handler.setLevel(logging.DEBUG)
54
+ formatter = logging.Formatter(
55
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
56
+ )
57
+ handler.setFormatter(formatter)
58
+
59
+ for logname in ['']:
60
+ log = logging.getLogger(logname)
61
+ log.setLevel(logging.DEBUG)
62
+ log.addHandler(handler)
63
+ logging.getLogger('github').setLevel(logging.ERROR)
64
+ logging.getLogger('binaryornot').setLevel(logging.ERROR)
65
+
66
+
67
+ async def handle_index(request: aiohttp.web_request.Request):
68
+ idx = int(request.match_info.get('idx', '0'))
69
+ pairs = diff.get_thin_list(DIFF)
70
+ with open(os.path.join(WEBDIFF_DIR, 'templates/file_diff.html'), 'r') as file:
71
+ html = file.read()
72
+ html = html.replace(
73
+ '{{data}}',
74
+ json.dumps(
75
+ {
76
+ 'idx': idx,
77
+ 'has_magick': util.is_imagemagick_available(),
78
+ 'pairs': pairs,
79
+ 'git_config': GIT_CONFIG,
80
+ },
81
+ indent=2,
82
+ ),
83
+ )
84
+ return web.Response(body=html, content_type='text/html', charset='utf-8')
85
+
86
+
87
+ async def handle_thick(request: aiohttp.web_request.Request):
88
+ idx = int(request.match_info.get('idx'))
89
+ return web.json_response(diff.get_thick_dict(DIFF[idx]))
90
+
91
+
92
+ async def handle_get_contents(request: aiohttp.web_request.Request):
93
+ side = request.match_info['side']
94
+ form_data = await request.post()
95
+ path = form_data.get('path', '')
96
+ if not path:
97
+ return web.json_response({'error': 'incomplete'}, status=400)
98
+ should_normalize = form_data.get('normalize_json')
99
+
100
+ idx = diff.find_diff_index(DIFF, side, path)
101
+ if idx is None:
102
+ return web.json_response({'error': 'not found'}, status=400)
103
+
104
+ d = DIFF[idx]
105
+ abs_path = d.a_path if side == 'a' else d.b_path
106
+
107
+ try:
108
+ if is_binary(abs_path):
109
+ size = os.path.getsize(abs_path)
110
+ return web.Response(text=f'Binary file ({size} bytes)')
111
+ else:
112
+ if should_normalize:
113
+ abs_path = util.normalize_json(abs_path)
114
+ return web.FileResponse(abs_path, headers={'Content-Type': 'text/plain'})
115
+ except Exception as e:
116
+ return web.json_response({'error': str(e)}, status=500)
117
+
118
+
119
+ async def handle_diff_ops(request: aiohttp.web_request.Request):
120
+ idx = int(request.match_info.get('idx'))
121
+ payload = await request.json()
122
+ options = payload.get('options') or []
123
+ should_normalize = payload.get('normalize_json')
124
+ logging.debug([*payload.keys()])
125
+ logging.debug({**payload})
126
+ extra_args = GIT_CONFIG['webdiff']['extraFileDiffArgs']
127
+ if extra_args:
128
+ options += extra_args.split(' ')
129
+ diff_ops = [
130
+ dataclasses.asdict(op)
131
+ for op in diff.get_diff_ops(DIFF[idx], options, normalize_json=should_normalize)
132
+ ]
133
+ return web.json_response(diff_ops, status=200)
134
+
135
+
136
+ async def handle_theme(request: aiohttp.web_request.Request):
137
+ theme = GIT_CONFIG['webdiff']['theme']
138
+ theme_dir = os.path.dirname(theme)
139
+ theme_file = os.path.basename(theme)
140
+ theme_path = os.path.join(
141
+ WEBDIFF_DIR, 'static/css/themes', theme_dir, theme_file + '.css'
142
+ )
143
+ return web.FileResponse(theme_path)
144
+
145
+
146
+ async def handle_favicon(request):
147
+ return web.FileResponse(os.path.join(WEBDIFF_DIR, 'static/img/favicon.ico'))
148
+
149
+
150
+ async def handle_get_image(request: aiohttp.web_request.Request):
151
+ side = request.match_info.get('side')
152
+ path = request.match_info.get('path')
153
+ mime_type, _ = mimetypes.guess_type(path)
154
+ if not mime_type or not mime_type.startswith('image/'):
155
+ return web.json_response({'error': 'wrong type'}, status=400)
156
+
157
+ idx = diff.find_diff_index(DIFF, side, path)
158
+ if idx is None:
159
+ return web.json_response({'error': 'not found'}, status=400)
160
+
161
+ d = DIFF[idx]
162
+ abs_path = d.a_path if side == 'a' else d.b_path
163
+ return web.FileResponse(abs_path, headers={'Content-Type': mime_type})
164
+
165
+
166
+ async def handle_pdiff(request: aiohttp.web_request.Request):
167
+ idx = int(request.match_info.get('idx'))
168
+ d = DIFF[idx]
169
+ try:
170
+ _, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
171
+ dilated_image_path = util.generate_dilated_pdiff_image(pdiff_image)
172
+ return web.FileResponse(dilated_image_path)
173
+ except util.ImageMagickNotAvailableError:
174
+ return web.Response(status=501, text='ImageMagick is not available')
175
+ except util.ImageMagickError as e:
176
+ return web.Response(status=501, text=f'ImageMagick error {e}')
177
+
178
+
179
+ async def handle_pdiff_bbox(request: aiohttp.web_request.Request):
180
+ idx = int(request.match_info.get('idx'))
181
+ d = DIFF[idx]
182
+ try:
183
+ _, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
184
+ bbox = util.get_pdiff_bbox(pdiff_image)
185
+ return web.json_response(bbox, status=200)
186
+ except util.ImageMagickNotAvailableError:
187
+ return web.json_response('ImageMagick is not available', status=501)
188
+ except util.ImageMagickError as e:
189
+ return web.json_response(f'ImageMagick error {e}', status=501)
190
+
191
+
192
+ async def websocket_handler(request: aiohttp.web_request.Request):
193
+ ws = web.WebSocketResponse()
194
+ await ws.prepare(request)
195
+
196
+ async for msg in ws:
197
+ if msg.type == aiohttp.WSMsgType.TEXT:
198
+ await ws.send_str(msg.data + '/answer')
199
+ elif msg.type == aiohttp.WSMsgType.ERROR:
200
+ print('ws connection closed with exception %s' % ws.exception())
201
+
202
+ maybe_shutdown()
203
+ return ws
204
+
205
+
206
+ @web.middleware
207
+ async def request_time_middleware(request, handler):
208
+ note_request_time()
209
+ response = await handler(request)
210
+ return response
211
+
212
+
213
+ app = web.Application(middlewares=[request_time_middleware])
214
+ app.add_routes(
215
+ [
216
+ web.get('/', handle_index),
217
+ web.get(r'/{idx:\d+}', handle_index),
218
+ web.get('/favicon.ico', handle_favicon),
219
+ web.get('/theme.css', handle_theme),
220
+ web.static('/static', os.path.join(WEBDIFF_DIR, 'static')),
221
+ web.get(r'/thick/{idx:\d+}', handle_thick),
222
+ web.post(r'/{side:a|b}/get_contents', handle_get_contents),
223
+ web.post(r'/diff/{idx:\d+}', handle_diff_ops),
224
+ # Image diffs
225
+ web.get(r'/{side:a|b}/image/{path:.*}', handle_get_image),
226
+ web.get(r'/pdiff/{idx:\d+}', handle_pdiff),
227
+ web.get(r'/pdiffbbox/{idx:\d+}', handle_pdiff_bbox),
228
+ # Websocket for detecting when the tab is closed
229
+ web.get('/ws', websocket_handler),
230
+ ]
231
+ )
232
+
233
+
234
+ def note_request_time():
235
+ global LAST_REQUEST_MS
236
+ LAST_REQUEST_MS = time.time() * 1000
237
+
238
+
239
+ def open_browser():
240
+ global PORT
241
+ global HOSTNAME
242
+ global GIT_CONFIG
243
+ if not os.environ.get('WEBDIFF_NO_OPEN') and GIT_CONFIG['webdiff']['openBrowser']:
244
+ webbrowser.open_new_tab('http://%s:%s' % (HOSTNAME, PORT))
245
+
246
+
247
+ def usage_and_die():
248
+ sys.stderr.write(argparser.USAGE)
249
+ sys.exit(1)
250
+
251
+
252
+ def random_port():
253
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
254
+ sock.bind(('localhost', 0))
255
+ port = sock.getsockname()[1]
256
+ sock.close()
257
+ return port
258
+
259
+
260
+ def pick_a_port(args, webdiff_config):
261
+ if 'port' in args:
262
+ return args['port']
263
+
264
+ env_port = os.environ.get('WEBDIFF_PORT')
265
+ if env_port:
266
+ return int(env_port)
267
+
268
+ # gitconfig
269
+ if webdiff_config['port'] != -1:
270
+ return webdiff_config['port']
271
+
272
+ return random_port()
273
+
274
+
275
+ def run_http():
276
+ sys.stderr.write(
277
+ """Serving diffs on http://%s:%s
278
+ Close the browser tab or hit Ctrl-C when you're done.
279
+ """
280
+ % (HOSTNAME, PORT)
281
+ )
282
+ threading.Timer(0.1, open_browser).start()
283
+
284
+ web.run_app(app, host=HOSTNAME, port=PORT)
285
+ logging.debug('http server shut down')
286
+
287
+
288
+ def maybe_shutdown():
289
+ """Wait a second for new requests, then shut down the server."""
290
+ last_ms = LAST_REQUEST_MS
291
+
292
+ def shutdown():
293
+ if LAST_REQUEST_MS <= last_ms: # subsequent requests abort shutdown
294
+ sys.stderr.write('Shutting down...\n')
295
+ signal.raise_signal(signal.SIGINT)
296
+ else:
297
+ logging.debug('Received subsequent request; shutdown aborted.')
298
+
299
+ logging.debug(
300
+ 'Received request to shut down; waiting 500ms for subsequent requests...'
301
+ )
302
+ threading.Timer(0.5, shutdown).start()
303
+
304
+
305
+ def run():
306
+ global DIFF, PORT, HOSTNAME, GIT_CONFIG
307
+ try:
308
+ parsed_args = argparser.parse(sys.argv[1:], VERSION)
309
+ except argparser.UsageError as e:
310
+ sys.stderr.write('Error: %s\n\n' % e)
311
+ usage_and_die()
312
+
313
+ GIT_CONFIG = options.get_config()
314
+ WEBDIFF_CONFIG = GIT_CONFIG['webdiff']
315
+ DIFF = argparser.diff_for_args(parsed_args, WEBDIFF_CONFIG)
316
+
317
+ if DEBUG:
318
+ sys.stderr.write('Invoked as: %s\n' % sys.argv)
319
+ sys.stderr.write('Args: %s\n' % parsed_args)
320
+ sys.stderr.write('Diff: %s\n' % DIFF)
321
+ sys.stderr.write('GitConfig: %s\n' % GIT_CONFIG)
322
+
323
+ PORT = pick_a_port(parsed_args, WEBDIFF_CONFIG)
324
+ HOSTNAME = (
325
+ parsed_args.get('host')
326
+ or os.environ.get('WEBDIFF_HOST')
327
+ or WEBDIFF_CONFIG['host']
328
+ )
329
+ if HOSTNAME == '<hostname>':
330
+ _hostname = platform.node()
331
+ # platform.node will return empty string if it can't find the hostname
332
+ if not _hostname:
333
+ sys.stderr.write('Warning: hostname could not be determined\n')
334
+ else:
335
+ HOSTNAME = _hostname
336
+
337
+ run_http()
338
+
339
+
340
+ if __name__ == '__main__':
341
+ run()
@@ -29,7 +29,13 @@ def get_thin_dict(diff):
29
29
  - change type (add, delete, move, change)
30
30
  - (in the future) diffstats
31
31
  """
32
- return {'a': diff.a, 'b': diff.b, 'type': diff.type, 'num_add': diff.num_add, 'num_delete': diff.num_delete}
32
+ return {
33
+ 'a': diff.a,
34
+ 'b': diff.b,
35
+ 'type': diff.type,
36
+ 'num_add': diff.num_add,
37
+ 'num_delete': diff.num_delete,
38
+ }
33
39
 
34
40
 
35
41
  def fast_num_lines(path: str) -> int:
@@ -39,7 +45,9 @@ def fast_num_lines(path: str) -> int:
39
45
  return int(subprocess.check_output(['grep', '-c', '', path]))
40
46
 
41
47
 
42
- def get_diff_ops(diff: LocalFileDiff, git_diff_args=None) -> List[Code]:
48
+ def get_diff_ops(
49
+ diff: LocalFileDiff, git_diff_args=None, normalize_json=False
50
+ ) -> List[Code]:
43
51
  """Run git diff on the file pair and convert the results to a sequence of codes.
44
52
 
45
53
  git_diff_args is passed directly to git diff. It can be something like ['-w'] or
@@ -48,6 +56,10 @@ def get_diff_ops(diff: LocalFileDiff, git_diff_args=None) -> List[Code]:
48
56
  # git diff --no-index doesn't follow symlinks. So we help it a bit.
49
57
  a_path = os.path.realpath(diff.a_path) if diff.a_path else ''
50
58
  b_path = os.path.realpath(diff.b_path) if diff.b_path else ''
59
+ if normalize_json:
60
+ a_path = a_path and util.normalize_json(a_path)
61
+ b_path = b_path and util.normalize_json(b_path)
62
+
51
63
  if a_path and b_path:
52
64
  num_lines = fast_num_lines(b_path)
53
65
  args = (