Skip to content

Commit 960af94

Browse files
stonebigclaude
andcommitted
Compare any two package indexes, not just consecutive ones
wppm.diff could already compare two package indexes, and nothing could reach it. It was not on the wppm command line, so nothing in -h mentioned it; its own usage text said "python diff.py", which the relative imports make impossible; and printing a comparison died on Windows with a UnicodeEncodeError, because an upgrade is written "1.2.3 -> 1.2.4" with a U+2192 and the console is cp1252 -- so any two releases far enough apart to have one upgraded package could not be compared at all. wppm -diff <a>.md <b>.md now does it, on any two indexes: two releases, a year apart, or two flavors of the same release. It runs before the target is resolved, so it needs no distribution -- two downloaded files and a pip-installed wppm are enough. The file mode also had no title, and the direction is not guessable from the output: every upgrade line reads "old -> new", so a reader holding the pair the wrong way round sees every upgrade as a downgrade. It now opens with "## Changes from <first> to <second>". The version mode keeps the header it has always had; the dead line above it, assigned and then overwritten before it could be printed, is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent f640865 commit 960af94

2 files changed

Lines changed: 44 additions & 12 deletions

File tree

‎wppm/diff.py‎

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,31 @@ def compare_markdown_section_pairs(md1, md2, header_pairs, label1="Input1", labe
9494
text += f"\n## {label1} [{h1}] vs {label2} [{h2}]\n\n{diff}\n"
9595
return text
9696

97+
def index_title(file1, file2):
98+
"""## Changes from <first index> to <second index>.
99+
100+
Comparing two files says nothing about which two, and the direction
101+
matters: every upgrade line is written "old -> new", so a reader who has
102+
the pair the wrong way round sees every upgrade as a downgrade. The version
103+
mode gets this from the versions it was handed; the file mode has only the
104+
names, so it uses them, without directory or extension.
105+
"""
106+
name = lambda f: Path(f).stem
107+
return f"## Changes from {name(file1)} to {name(file2)}\n"
108+
109+
97110
def compare_files(file1, file2, mode="full", header1=None, header2=None, header_pairs=None):
98111
with open(file1, encoding=utils.guess_encoding(file1)[0]) as f1, \
99112
open(file2, encoding=utils.guess_encoding(file2)[0]) as f2:
100113
md1, md2 = f1.read(), f2.read()
101114
if mode == "full":
102-
result = ""
115+
result = index_title(file1, file2) + "\n"
103116
for k in PackageIndex.HEADERS:
104117
result += compare_markdown_sections(md1, md2, k, k, file1, file2) + "\n"
105118
return result
106119
elif mode == "section":
107-
return compare_markdown_sections(md1, md2, header1, header2, file1, file2)
120+
return (index_title(file1, file2) + "\n"
121+
+ compare_markdown_sections(md1, md2, header1, header2, file1, file2))
108122
elif mode == "pairs":
109123
return compare_markdown_section_pairs(md1, md2, header_pairs, file1, file2)
110124
else:
@@ -158,7 +172,6 @@ def compare_package_indexes(version2, version1=None, searchdir=None, flavor="",
158172
flavor1 = flavor1 or flavor
159173
md1 = load_version_markdown(version1, searchdir, flavor1, architecture)
160174
md2 = load_version_markdown(version2, searchdir, flavor, architecture)
161-
result = f"# WinPython {architecture}bit {version2}{flavor} vs {version1}{flavor1}\n"
162175
result = (
163176
f"## History of changes for WinPython-{architecture}bit {version2 + flavor}\r\n\r\n"
164177
f"The following changes were made to WinPython-{architecture}bit distribution since version {version1 + flavor1}.\n\n\n"
@@ -193,19 +206,25 @@ def write_changelog(version2, version1=None, searchdir=None, flavor="", architec
193206
shutil.copyfile(output_file, Path(basedir) / output_file.name)
194207

195208
def print_usage():
209+
# "python diff.py" cannot work -- the relative imports need the package --
210+
# so every line says how it is really run
196211
print("Usage:")
197-
print(" python diff.py file1.md file2.md")
198-
print(" - Compare all sections of two markdown files.")
199-
print(" python diff.py file1.md file2.md --section header1 header2")
212+
print(" wppm -diff file1.md file2.md (or: python -m wppm.diff file1.md file2.md)")
213+
print(" - Compare all sections of two package indexes, in any order or flavor.")
214+
print(" python -m wppm.diff file1.md file2.md --section header1 header2")
200215
print(" - Compare section 'header1' of file1 with section 'header2' of file2.")
201-
print(" python diff.py file1.md file2.md --pairs header1a header2a [header1b header2b ...]")
202-
print(" - Compare pairs of sections. Example: python diff.py f1.md f2.md --pairs python wheelhouse tools tools")
203-
print(" python diff.py <version2> <version1> [searchdir] [flavor] [architecture]")
204-
print(" - Compare WinPython markdown changelogs by version (historical mode).")
205-
print(" python diff.py --write-changelog <version2> <version1> [searchdir] [flavor] [architecture] [basedir]")
206-
print(" - Write changelog between version1 and version2 to file (and optionally copy to basedir).")
216+
print(" python -m wppm.diff file1.md file2.md --pairs header1a header2a [header1b header2b ...]")
217+
print(" - Compare pairs of sections. Example: --pairs python wheelhouse tools tools")
218+
print(" python -m wppm.diff <version2> <version1> [searchdir] [flavor] [architecture]")
219+
print(" - Compare WinPython package indexes by version, from a changelogs directory.")
220+
print(" python -m wppm.diff --write-changelog <version2> <version1> [searchdir] [flavor] [architecture] [basedir]")
221+
print(" - Write that comparison to a file (and optionally copy it to basedir).")
207222

208223
if __name__ == "__main__":
224+
# every upgraded package prints an arrow, and a Windows console is cp1252,
225+
# so the default encoding turned "compare two releases" into a traceback
226+
if hasattr(sys.stdout, "reconfigure"):
227+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
209228
args = sys.argv
210229
if len(args) >= 3 and all(arg.lower().endswith('.md') for arg in args[1:3]):
211230
file1, file2 = args[1], args[2]

‎wppm/wppm.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ def main(test=False):
345345
parser.add_argument("-ls", "--list", action="store_true", help="list installed packages matching [optional] expression: wppm -ls, wppm -ls pand")
346346
parser.add_argument("-lsa", dest="all", action="store_true",help=f"list details of packages matching [optional] expression: wppm -lsa pandas -l1")
347347
parser.add_argument("-md", dest="markdown", action="store_true",help=f"markdown summary of the installation")
348+
parser.add_argument("-diff", dest="diff", nargs=2, metavar=("INDEX1", "INDEX2"), default=None,
349+
help="compare two package indexes, whichever two you choose: wppm -diff WinPythonslim-64bit-3.13.5.0.md WinPythonslim-64bit-3.14.7.0.md\nany two releases or flavors, in either direction -- they need not be consecutive")
348350
parser.add_argument("-p",dest="pipdown",action="store_true",help="show Package (!= missing) dependencies of the given package[option], [.]=all: wppm -p pandas[.]")
349351
parser.add_argument("-r", dest="pipup", action="store_true", help=f"show Reverse (!= constraining) dependancies of the given package[option]: wppm -r pytest![test]")
350352
parser.add_argument("-tl", "--top-level", action="store_true", help="keep only the entries no other entry pulls in, sorted: wppm -tl, wppm requirements.txt -tl -v\nwith -p or -r, start the tree from them instead of every package: wppm -tl -p")
@@ -355,6 +357,17 @@ def main(test=False):
355357
parser.add_argument("-u", "--uninstall", action="store_true", help="uninstall package (use pip for more features)")
356358

357359
args = parser.parse_args()
360+
if args.diff:
361+
# no distribution involved: this reads two files. The indexes are in
362+
# the changelogs/ directory of the winpython repository, one per
363+
# release and flavor, and any two of them can be compared.
364+
# utf-8 here and not just errors="replace": an upgrade is written
365+
# "1.2.3 → 1.2.4", and replacing that arrow with "?" is most of the
366+
# output on a page whose whole subject is upgrades.
367+
if sys.stdout and hasattr(sys.stdout, "reconfigure"):
368+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
369+
print(diff.compare_files(*args.diff))
370+
sys.exit()
358371
targetpython = None
359372
if args.target and args.target != sys.prefix:
360373
targetpython = args.target if args.target.lower().endswith('.exe') else str(Path(args.target) / 'python.exe')

0 commit comments

Comments
 (0)