Visitar URL original
Compare any two indexes by stonebig · Pull Request #2105 · winpython/winpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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>
  • Loading branch information
stonebig and claude committed Sep 13, 2026
commit 960af94069aeece8c51e486599496ac1025254bf
43 changes: 31 additions & 12 deletions wppm/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,31 @@ def compare_markdown_section_pairs(md1, md2, header_pairs, label1="Input1", labe
text += f"\n## {label1} [{h1}] vs {label2} [{h2}]\n\n{diff}\n"
return text

def index_title(file1, file2):
"""## Changes from <first index> to <second index>.

Comparing two files says nothing about which two, and the direction
matters: every upgrade line is written "old -> new", so a reader who has
the pair the wrong way round sees every upgrade as a downgrade. The version
mode gets this from the versions it was handed; the file mode has only the
names, so it uses them, without directory or extension.
"""
name = lambda f: Path(f).stem
return f"## Changes from {name(file1)} to {name(file2)}\n"


def compare_files(file1, file2, mode="full", header1=None, header2=None, header_pairs=None):
with open(file1, encoding=utils.guess_encoding(file1)[0]) as f1, \
open(file2, encoding=utils.guess_encoding(file2)[0]) as f2:
md1, md2 = f1.read(), f2.read()
if mode == "full":
result = ""
result = index_title(file1, file2) + "\n"
for k in PackageIndex.HEADERS:
result += compare_markdown_sections(md1, md2, k, k, file1, file2) + "\n"
return result
elif mode == "section":
return compare_markdown_sections(md1, md2, header1, header2, file1, file2)
return (index_title(file1, file2) + "\n"
+ compare_markdown_sections(md1, md2, header1, header2, file1, file2))
elif mode == "pairs":
return compare_markdown_section_pairs(md1, md2, header_pairs, file1, file2)
else:
Expand Down Expand Up @@ -158,7 +172,6 @@ def compare_package_indexes(version2, version1=None, searchdir=None, flavor="",
flavor1 = flavor1 or flavor
md1 = load_version_markdown(version1, searchdir, flavor1, architecture)
md2 = load_version_markdown(version2, searchdir, flavor, architecture)
result = f"# WinPython {architecture}bit {version2}{flavor} vs {version1}{flavor1}\n"
result = (
f"## History of changes for WinPython-{architecture}bit {version2 + flavor}\r\n\r\n"
f"The following changes were made to WinPython-{architecture}bit distribution since version {version1 + flavor1}.\n\n\n"
Expand Down Expand Up @@ -193,19 +206,25 @@ def write_changelog(version2, version1=None, searchdir=None, flavor="", architec
shutil.copyfile(output_file, Path(basedir) / output_file.name)

def print_usage():
# "python diff.py" cannot work -- the relative imports need the package --
# so every line says how it is really run
print("Usage:")
print(" python diff.py file1.md file2.md")
print(" - Compare all sections of two markdown files.")
print(" python diff.py file1.md file2.md --section header1 header2")
print(" wppm -diff file1.md file2.md (or: python -m wppm.diff file1.md file2.md)")
print(" - Compare all sections of two package indexes, in any order or flavor.")
print(" python -m wppm.diff file1.md file2.md --section header1 header2")
print(" - Compare section 'header1' of file1 with section 'header2' of file2.")
print(" python diff.py file1.md file2.md --pairs header1a header2a [header1b header2b ...]")
print(" - Compare pairs of sections. Example: python diff.py f1.md f2.md --pairs python wheelhouse tools tools")
print(" python diff.py <version2> <version1> [searchdir] [flavor] [architecture]")
print(" - Compare WinPython markdown changelogs by version (historical mode).")
print(" python diff.py --write-changelog <version2> <version1> [searchdir] [flavor] [architecture] [basedir]")
print(" - Write changelog between version1 and version2 to file (and optionally copy to basedir).")
print(" python -m wppm.diff file1.md file2.md --pairs header1a header2a [header1b header2b ...]")
print(" - Compare pairs of sections. Example: --pairs python wheelhouse tools tools")
print(" python -m wppm.diff <version2> <version1> [searchdir] [flavor] [architecture]")
print(" - Compare WinPython package indexes by version, from a changelogs directory.")
print(" python -m wppm.diff --write-changelog <version2> <version1> [searchdir] [flavor] [architecture] [basedir]")
print(" - Write that comparison to a file (and optionally copy it to basedir).")

if __name__ == "__main__":
# every upgraded package prints an arrow, and a Windows console is cp1252,
# so the default encoding turned "compare two releases" into a traceback
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
args = sys.argv
if len(args) >= 3 and all(arg.lower().endswith('.md') for arg in args[1:3]):
file1, file2 = args[1], args[2]
Expand Down
13 changes: 13 additions & 0 deletions wppm/wppm.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ def main(test=False):
parser.add_argument("-ls", "--list", action="store_true", help="list installed packages matching [optional] expression: wppm -ls, wppm -ls pand")
parser.add_argument("-lsa", dest="all", action="store_true",help=f"list details of packages matching [optional] expression: wppm -lsa pandas -l1")
parser.add_argument("-md", dest="markdown", action="store_true",help=f"markdown summary of the installation")
parser.add_argument("-diff", dest="diff", nargs=2, metavar=("INDEX1", "INDEX2"), default=None,
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")
parser.add_argument("-p",dest="pipdown",action="store_true",help="show Package (!= missing) dependencies of the given package[option], [.]=all: wppm -p pandas[.]")
parser.add_argument("-r", dest="pipup", action="store_true", help=f"show Reverse (!= constraining) dependancies of the given package[option]: wppm -r pytest![test]")
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")
Expand All @@ -355,6 +357,17 @@ def main(test=False):
parser.add_argument("-u", "--uninstall", action="store_true", help="uninstall package (use pip for more features)")

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