# -*- coding: utf-8 -*- # # Copyright © 2013 Pierre Raybaut # Licensed under the terms of the MIT License # (see winpython/__init__.py for details) """ WinPython diff script Created on Tue Jan 29 11:56:54 2013 """ import os from pathlib import Path import re import shutil # Local imports from winpython import utils # pep503 defines normalized package names: www.python.org/dev/peps/pep-0503 def normalize(name): return re.sub(r"[-_.]+", "-", name).lower() CHANGELOGS_DIR = str(Path(__file__).parent / "changelogs") assert Path(CHANGELOGS_DIR).is_dir() class Package(object): # SourceForge Wiki syntax: PATTERN = r"\[([a-zA-Z\-\:\/\.\_0-9]*)\]\(([^\]\ ]*)\) \| ([^\|]*) \| ([^\|]*)" # Google Code Wiki syntax: PATTERN_OLD = r"\[([a-zA-Z\-\:\/\.\_0-9]*) ([^\]\ ]*)\] \| ([^\|]*) \| ([^\|]*)" def __init__(self): self.name = None self.version = None self.description = None self.url = None def __str__(self): text = f"{self.name} {self.version}" text += f"\r\n{self.description}\r\nWebsite: {self.url}" return text def from_text(self, text): try: self.url, self.name, self.version, self.description = re.match( self.PATTERN_OLD, text ).groups() except AttributeError: self.name, self.url, self.version, self.description = re.match( self.PATTERN, text ).groups() def to_wiki(self): return f" * [{self.name}]({self.url}) {self.version} ({self.description})\r\n" def upgrade_wiki(self, other): # wheel replace '-' per '_' in package name 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(object): WINPYTHON_PATTERN = r"\#\# WinPython\-*[0-9b-t]* ([0-9\.a-zA-Z]*)" TOOLS_LINE = "### Tools" PYTHON_PACKAGES_LINE = "### Python packages" HEADER_LINE1 = "Name | Version | Description" HEADER_LINE2 = "-----|---------|------------" def __init__( self, version, basedir=None, flavor="", architecture=64, ): self.version = version self.other_packages = {} self.python_packages = {} self.flavor = flavor self.basedir = basedir self.architecture = architecture self.from_file(basedir) def from_file(self, basedir): fname = str( Path(CHANGELOGS_DIR) / f"WinPython{self.flavor}-{self.architecture}bit-{self.version}.md" ) try: with open(fname, "r", encoding = 'utf-8') as fdesc: # python3 doesn't like 'rb' text = fdesc.read() except: with open(fname, "r") as fdesc: # python3 doesn't like 'rb' text = fdesc.read() self.from_text(text) def from_text(self, text): version = re.match(self.WINPYTHON_PATTERN + self.flavor, text).groups()[0] assert version == self.version tools_flag = False python_flag = False for line in text.splitlines(): if line: if line == self.TOOLS_LINE: tools_flag = True continue elif line == self.PYTHON_PACKAGES_LINE: tools_flag = False python_flag = True continue elif line in ( self.HEADER_LINE1, self.HEADER_LINE2, "
", "
", ): continue if tools_flag or python_flag: package = Package() package.from_text(line) if tools_flag: self.other_packages[package.name] = package else: self.python_packages[package.name] = package def diff_package_dicts(dict1_in, dict2_in): """Return difference between package dict1 and package dict2""" text = "" # wheel replace '-' per '_' in key dict1 = {} dict2 = {} for key in dict1_in: dict1[key.replace("-", "_").lower()] = dict1_in[key] for key in dict2_in: dict2[key.replace("-", "_").lower()] = dict2_in[key] set1, set2 = set(dict1.keys()), set(dict2.keys()) # New packages new = sorted(set2 - set1) if new: text += "New packages:\r\n\r\n" for name in new: package = dict2[name] text += package.to_wiki() text += "\r\n" # Upgraded packages upgraded_list = [] for name in sorted(set1 & set2): package1 = dict1[name] package2 = dict2[name] if package1.version != package2.version: upgraded_list.append(package2.upgrade_wiki(package1)) if upgraded_list: text += "Upgraded packages:\r\n\r\n" + f"{''.join(upgraded_list)}" + "\r\n" # Removed packages removed = sorted(set1 - set2) if removed: text += "Removed packages:\r\n\r\n" for name in removed: package = dict1[name] text += package.to_wiki() text += "\r\n" return text def find_closer_version(version1, basedir=None, flavor="", architecture=64): """Find version which is the closest to `version`""" builddir = str(Path(basedir) / f"bu{flavor}") func = lambda name: re.match( r"WinPython%s-%sbit-([0-9\.]*)\.(txt|md)" % (flavor, architecture), name, ) versions = [func(name).groups()[0] for name in os.listdir(builddir) if func(name)] # versions:['3.10.0.1', '3.10.10.0', '3.10.2.0'.... '3.10.8.1', '3.10.9.0'] try: index = versions.index(version1) except ValueError: raise ValueError(f"Unknown version {version1}") from packaging import version version_below = '0.0.0.0' for v in versions: if version.parse(v) > version.parse(version_below) and version.parse(v)", "", ] ) pi1 = PackageIndex( version1, basedir=basedir, flavor=flavor1, architecture=architecture, ) pi2 = PackageIndex( version2, basedir=basedir, flavor=flavor, architecture=architecture, ) tools_text = diff_package_dicts(pi1.other_packages, pi2.other_packages) if tools_text: text += PackageIndex.TOOLS_LINE + "\r\n\r\n" + tools_text 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 text += "\r\n\r\n* * *\r\n" return text def _copy_all_changelogs(version, basedir, flavor="", architecture=64): basever = ".".join(version.split(".")[:2]) for name in os.listdir(CHANGELOGS_DIR): if re.match( r"WinPython%s-%sbit-%s([0-9\.]*)\.(txt|md)" % (flavor, architecture, basever), name, ): shutil.copyfile( str(Path(CHANGELOGS_DIR) / name), str(Path(basedir) / f"bu{flavor}" / name), ) def write_changelog( version2, version1=None, basedir=None, flavor="", release_level="", architecture=64, ): """Write changelog between version1 and version2 of WinPython""" _copy_all_changelogs( version2, basedir, flavor=flavor, architecture=architecture, ) print( "comparing_package_indexes", version2, basedir, flavor, architecture, ) text = compare_package_indexes( version2, version1, basedir=basedir, flavor=flavor, architecture=architecture, ) fname = str( Path(basedir) / f"bu{flavor}" / f"WinPython{flavor}-{architecture}bit-{version2}_History.md" ) with open(fname, "w", encoding="utf-8-sig") as fdesc: # python 3 need fdesc.write(text) # Copy to winpython/changelogs shutil.copyfile(fname, str(Path(CHANGELOGS_DIR) / Path(fname).name)) def test_parse_package_index_wiki(version, basedir=None, flavor="", architecture=64): """Parse the package index Wiki page""" pi = PackageIndex( version, basedir=basedir, flavor=flavor, architecture=architecture, ) utils.print_box(f"WinPython {pi.version}:") utils.print_box("Tools:") for package in pi.other_packages.values(): print(package) print("") utils.print_box("Python packages:") for package in pi.python_packages.values(): print(package) print("") def test_compare(basedir, version2, version1, architecture=64): print( compare_package_indexes( basedir, version2, version1, architecture=architecture, ) ) if __name__ == "__main__": print( compare_package_indexes( version2="3.7.4.0", version1="3.7.2.0", basedir=r"C:\WinP\bd37", flavor="Zero", flavor1="Zero", architecture=32, ) ) write_changelog( version2="3.7.4.0", version1="3.7.2.0", basedir=r"C:\WinP\bd37", flavor="Ps2", architecture=64, ) # test_parse_package_index_wiki('2.7.3.3') # print(compare_package_indexes('2.7.3.3', '2.7.3.1')) # write_changelog('2.7.4.1', '2.7.4.0') # write_changelog('3.3.0.0beta2', '3.3.0.0beta1')