diff --git a/diff.py b/diff.py
index 61b4ef80..02317cd0 100644
--- a/diff.py
+++ b/diff.py
@@ -17,120 +17,94 @@
assert CHANGELOGS_DIR.is_dir()
class Package:
- # SourceForge Wiki syntax:
- PATTERN = r"\[([a-zA-Z\-\:\/\.\_0-9]*)\]\(([^\]\ ]*)\) \| ([^\|]*) \| ([^\|]*)"
- # Google Code Wiki syntax:
- PATTERN_OLD = r"\[([a-zA-Z\-\:\/\.\_0-9]*) ([^\]\ ]*)\] \| ([^\|]*) \| ([^\|]*)"
+ PATTERNS = [
+ r"\[([\w\-\:\/\.\_]+)\]\(([^)]+)\) \| ([^\|]*) \| ([^\|]*)", # SourceForge
+ r"\[([\w\-\:\/\.\_]+) ([^\]\ ]+)\] \| ([^\|]*) \| ([^\|]*)" # Google Code
+ ]
- def __init__(self):
- self.name = self.version = self.description = self.url = None
-
- def __str__(self):
- return f"{self.name} {self.version}\r\n{self.description}\r\nWebsite: {self.url}"
+ def __init__(self, text=None):
+ self.name = self.url = self.version = self.description = None
+ if text:
+ self.from_text(text)
def from_text(self, text):
- match = re.match(self.PATTERN_OLD, text) or re.match(self.PATTERN, text)
- if not match:
- raise ValueError("Text does not match expected pattern: "+ text)
- self.name, self.url, self.version, self.description = match.groups()
+ for pattern in self.PATTERNS:
+ match = re.match(pattern, text)
+ if match:
+ self.name, self.url, self.version, self.description = match.groups()
+ return
+ raise ValueError(f"Unrecognized package line format: {text}")
def to_wiki(self):
return f" * [{self.name}]({self.url}) {self.version} ({self.description})\r\n"
def upgrade_wiki(self, other):
- assert self.name.replace("-", "_").lower() == other.name.replace("-", "_").lower()
return f" * [{self.name}]({self.url}) {other.version} → {self.version} ({self.description})\r\n"
class PackageIndex:
- WINPYTHON_PATTERN = r"\#\# WinPython\-*[0-9b-t]* ([0-9\.a-zA-Z]*)"
- TOOLS_LINE = "### Tools"
- PYTHON_PACKAGES_LINE = "### Python packages"
- WHEELHOUSE_PACKAGES_LINE = "### WheelHouse packages"
- HEADER_LINE1 = "Name | Version | Description"
- HEADER_LINE2 = "-----|---------|------------"
+ HEADERS = {"tools": "### Tools", "python": "### Python packages", "wheelhouse": "### WheelHouse packages"}
+ BLANKS = ["Name | Version | Description", "-----|---------|------------", "", "", " "]
def __init__(self, version, basedir=None, flavor="", architecture=64):
self.version = version
self.flavor = flavor
self.basedir = basedir
self.architecture = architecture
- self.other_packages = {}
- self.python_packages = {}
- self.wheelhouse_packages = {}
- self.from_file(basedir)
-
- def from_file(self, basedir):
- fname = CHANGELOGS_DIR / f"WinPython{self.flavor}-{self.architecture}bit-{self.version}.md"
- if not fname.exists():
- raise FileNotFoundError(f"Changelog file not found: {fname}")
- with open(fname, "r", encoding=utils.guess_encoding(fname)[0]) as fdesc:
- self.from_text(fdesc.read())
+ self.packages = {"tools": {}, "python": {}, "wheelhouse": {}}
+ self._load_index()
- def from_text(self, text):
- version = re.match(self.WINPYTHON_PATTERN + self.flavor, text).groups()[0]
- assert version == self.version
- tools_flag = python_flag = wheelhouse_flag = False
+ def _load_index(self):
+ filename = CHANGELOGS_DIR / f"WinPython{self.flavor}-{self.architecture}bit-{self.version}.md"
+ if not filename.exists():
+ raise FileNotFoundError(f"Changelog not found: {filename}")
+
+ with open(filename, "r", encoding=utils.guess_encoding(filename)[0]) as f:
+ self._parse_index(f.read())
+
+ def _parse_index(self, text):
+ current = None
for line in text.splitlines():
- if line:
- if line == self.TOOLS_LINE:
- tools_flag, python_flag, wheelhouse_flag = True, False, False
- continue
- elif line == self.PYTHON_PACKAGES_LINE:
- tools_flag, python_flag, wheelhouse_flag = False, True, False
- continue
- elif line == self.WHEELHOUSE_PACKAGES_LINE:
- tools_flag, python_flag, wheelhouse_flag = False, False, True
- continue
- elif line in (self.HEADER_LINE1, self.HEADER_LINE2, "", " "):
- continue
- if tools_flag or python_flag or wheelhouse_flag:
- package = Package()
- package.from_text(line)
- if tools_flag:
- self.other_packages[package.name] = package
- elif python_flag:
- self.python_packages[package.name] = package
- else:
- self.wheelhouse_packages[package.name] = package
-
-def diff_package_dicts(old_packages, new_packages):
+ if line in self.HEADERS.values():
+ current = [k for k, v in self.HEADERS.items() if v == line][0]
+ continue
+ if line.strip() in self.BLANKS:
+ continue
+ if current:
+ pkg = Package(line)
+ self.packages[current][pkg.name] = pkg
+
+def compare_packages(old, new):
"""Return difference between package old and package new"""
# wheel replace '-' per '_' in key
- old = {k.replace("-", "_").lower(): v for k, v in old_packages.items()}
- new = {k.replace("-", "_").lower(): v for k, v in new_packages.items()}
- text = ""
-
- if new_keys := sorted(set(new) - set(old)):
- text += "New packages:\r\n\r\n" + "".join(new[k].to_wiki() for k in new_keys) + "\r\n"
-
- if upgraded := [new[k].upgrade_wiki(old[k]) for k in sorted(set(old) & set(new)) if old[k].version != new[k].version]:
- text += "Upgraded packages:\r\n\r\n" + f"{''.join(upgraded)}" + "\r\n"
-
- if removed_keys := sorted(set(old) - set(new)):
- text += "Removed packages:\r\n\r\n" + "".join(old[k].to_wiki() for k in removed_keys) + "\r\n"
- return text
-
-def find_closer_version(version1, basedir=None, flavor="", architecture=64):
+ def normalize(d): return {k.replace("-", "_").lower(): v for k, v in d.items()}
+ old, new = normalize(old), normalize(new)
+ output = ""
+
+ added = [new[k].to_wiki() for k in new if k not in old]
+ upgraded = [new[k].upgrade_wiki(old[k]) for k in new if k in old and new[k].version != old[k].version]
+ removed = [old[k].to_wiki() for k in old if k not in new]
+
+ if added:
+ output += "New packages:\r\n\r\n" + "".join(added) + "\r\n"
+ if upgraded:
+ output += "Upgraded packages:\r\n\r\n" + "".join(upgraded) + "\r\n"
+ if removed:
+ output += "Removed packages:\r\n\r\n" + "".join(removed) + "\r\n"
+ return output
+
+def find_previous_version(target_version, basedir=None, flavor="", architecture=64):
"""Find version which is the closest to `version`"""
- builddir = Path(basedir) / f"bu{flavor}"
- pattern = re.compile(rf"WinPython{flavor}-{architecture}bit-([0-9\.]*)\.(txt|md)")
- versions = [pattern.match(name).groups()[0] for name in os.listdir(builddir) if pattern.match(name)]
-
- if version1 not in versions:
- raise ValueError(f"Unknown version {version1}")
-
- version_below = '0.0.0.0'
- for v in versions:
- if version.parse(version_below) < version.parse(v) and version.parse(v) < version.parse(version1):
- version_below = v
+ build_dir = Path(basedir) / f"bu{flavor}"
+ pattern = re.compile(rf"WinPython{flavor}-{architecture}bit-([0-9\.]+)\.(txt|md)")
+ versions = [pattern.match(f).group(1) for f in os.listdir(build_dir) if pattern.match(f)]
+ versions = [v for v in versions if version.parse(v) < version.parse(target_version)]
+ return max(versions, key=version.parse, default=target_version)
- return version_below if version_below != '0.0.0.0' else version1
+def compare_package_indexes(version2, version1=None, basedir=None, flavor="", flavor1=None, architecture=64):
+ version1 = version1 or find_previous_version(version2, basedir, flavor, architecture)
+ flavor1 = flavor1 or flavor
-def compare_package_indexes(version2, version1=None, basedir=None, flavor="", flavor1=None,architecture=64):
- """Compare two package index Wiki pages"""
- version1 = version1 if version1 else find_closer_version(version2, basedir, flavor, architecture)
- flavor1 = flavor1 if flavor1 else flavor
pi1 = PackageIndex(version1, basedir, flavor1, architecture)
pi2 = PackageIndex(version2, basedir, flavor, architecture)
@@ -140,37 +114,29 @@ def compare_package_indexes(version2, version1=None, basedir=None, flavor="", fl
"\r\n\r\n"
)
- tools_text = diff_package_dicts(pi1.other_packages, pi2.other_packages)
- if tools_text:
- text += PackageIndex.TOOLS_LINE + "\r\n\r\n" + tools_text
+ for key in PackageIndex.HEADERS:
+ diff = compare_packages(pi1.packages[key], pi2.packages[key])
+ if diff:
+ text += f"{PackageIndex.HEADERS[key]}\r\n\r\n{diff}"
- py_text = diff_package_dicts(pi1.python_packages, pi2.python_packages)
- if py_text:
- text += PackageIndex.PYTHON_PACKAGES_LINE + "\r\n\r\n" + py_text
+ return text + "\r\n \r\n* * *\r\n"
- py_text = diff_package_dicts(pi1.wheelhouse_packages, pi2.wheelhouse_packages)
- if py_text:
- text += PackageIndex.WHEELHOUSE_PACKAGES_LINE + "\r\n\r\n" + py_text
-
- text += "\r\n\r\n* * *\r\n"
- return text
-
-def _copy_all_changelogs(version, basedir, flavor="", architecture=64):
+def copy_changelogs(version, basedir, flavor="", architecture=64):
basever = ".".join(version.split(".")[:2])
- pattern = re.compile(rf"WinPython{flavor}-{architecture}bit-{basever}([0-9\.]*)\.(txt|md)")
- for name in os.listdir(CHANGELOGS_DIR):
- if pattern.match(name):
- shutil.copyfile(CHANGELOGS_DIR / name, Path(basedir) / f"bu{flavor}" / name)
+ pattern = re.compile(rf"WinPython{flavor}-{architecture}bit-{basever}[0-9\.]*\.(txt|md)")
+ dest = Path(basedir) / f"bu{flavor}"
+ for fname in os.listdir(CHANGELOGS_DIR):
+ if pattern.match(fname):
+ shutil.copyfile(CHANGELOGS_DIR / fname, dest / fname)
def write_changelog(version2, version1=None, basedir=None, flavor="", architecture=64):
"""Write changelog between version1 and version2 of WinPython"""
- _copy_all_changelogs(version2, basedir, flavor, architecture)
+ copy_changelogs(version2, basedir, flavor, architecture)
print("comparing_package_indexes", version2, basedir, flavor, architecture)
- changelog_text = compare_package_indexes(version2, version1, basedir, flavor, architecture=architecture)
+ changelog = compare_package_indexes(version2, version1, basedir, flavor, architecture=architecture)
output_file = Path(basedir) / f"bu{flavor}" / f"WinPython{flavor}-{architecture}bit-{version2}_History.md"
-
- with open(output_file, "w", encoding="utf-8") as fdesc:
- fdesc.write(changelog_text)
+ with open(output_file, "w", encoding="utf-8") as f:
+ f.write(changelog)
# Copy to winpython/changelogs
shutil.copyfile(output_file, CHANGELOGS_DIR / output_file.name)