17
17
assert CHANGELOGS_DIR .is_dir ()
18
18
19
19
class Package :
20
- # SourceForge Wiki syntax:
21
- PATTERN = r"\[([a-zA-Z\ -\:\/\.\_0-9]* )\]\(([^\]\ ]* )\) \| ([^\|]*) \| ([^\|]*)"
22
- # Google Code Wiki syntax:
23
- PATTERN_OLD = r"\[([a-zA-Z\-\:\/\.\_0-9]*) ([^\]\ ]*)\] \| ([^\|]*) \| ([^\|]*)"
20
+ PATTERNS = [
21
+ r"\[([\w\ -\:\/\.\_]+ )\]\(([^)]+ )\) \| ([^\|]*) \| ([^\|]*)" , # SourceForge
22
+ r"\[([\w\-\:\/\.\_]+) ([^\]\ ]+)\] \| ([^\|]*) \| ([^\|]*)" # Google Code
23
+ ]
24
24
25
- def __init__ (self ):
26
- self .name = self .version = self .description = self .url = None
27
-
28
- def __str__ (self ):
29
- return f"{ self .name } { self .version } \r \n { self .description } \r \n Website: { self .url } "
25
+ def __init__ (self , text = None ):
26
+ self .name = self .url = self .version = self .description = None
27
+ if text :
28
+ self .from_text (text )
30
29
31
30
def from_text (self , text ):
32
- match = re .match (self .PATTERN_OLD , text ) or re .match (self .PATTERN , text )
33
- if not match :
34
- raise ValueError ("Text does not match expected pattern: " + text )
35
- self .name , self .url , self .version , self .description = match .groups ()
31
+ for pattern in self .PATTERNS :
32
+ match = re .match (pattern , text )
33
+ if match :
34
+ self .name , self .url , self .version , self .description = match .groups ()
35
+ return
36
+ raise ValueError (f"Unrecognized package line format: { text } " )
36
37
37
38
def to_wiki (self ):
38
39
return f" * [{ self .name } ]({ self .url } ) { self .version } ({ self .description } )\r \n "
39
40
40
41
def upgrade_wiki (self , other ):
41
- assert self .name .replace ("-" , "_" ).lower () == other .name .replace ("-" , "_" ).lower ()
42
42
return f" * [{ self .name } ]({ self .url } ) { other .version } → { self .version } ({ self .description } )\r \n "
43
43
44
44
class PackageIndex :
45
- WINPYTHON_PATTERN = r"\#\# WinPython\-*[0-9b-t]* ([0-9\.a-zA-Z]*)"
46
- TOOLS_LINE = "### Tools"
47
- PYTHON_PACKAGES_LINE = "### Python packages"
48
- WHEELHOUSE_PACKAGES_LINE = "### WheelHouse packages"
49
- HEADER_LINE1 = "Name | Version | Description"
50
- HEADER_LINE2 = "-----|---------|------------"
45
+ HEADERS = {"tools" : "### Tools" , "python" : "### Python packages" , "wheelhouse" : "### WheelHouse packages" }
46
+ BLANKS = ["Name | Version | Description" , "-----|---------|------------" , "" , "<details>" , "</details>" ]
51
47
52
48
def __init__ (self , version , basedir = None , flavor = "" , architecture = 64 ):
53
49
self .version = version
54
50
self .flavor = flavor
55
51
self .basedir = basedir
56
52
self .architecture = architecture
57
- self .other_packages = {}
58
- self .python_packages = {}
59
- self .wheelhouse_packages = {}
60
- self .from_file (basedir )
61
-
62
- def from_file (self , basedir ):
63
- fname = CHANGELOGS_DIR / f"WinPython{ self .flavor } -{ self .architecture } bit-{ self .version } .md"
64
- if not fname .exists ():
65
- raise FileNotFoundError (f"Changelog file not found: { fname } " )
66
- with open (fname , "r" , encoding = utils .guess_encoding (fname )[0 ]) as fdesc :
67
- self .from_text (fdesc .read ())
53
+ self .packages = {"tools" : {}, "python" : {}, "wheelhouse" : {}}
54
+ self ._load_index ()
68
55
69
- def from_text (self , text ):
70
- version = re .match (self .WINPYTHON_PATTERN + self .flavor , text ).groups ()[0 ]
71
- assert version == self .version
72
- tools_flag = python_flag = wheelhouse_flag = False
56
+ def _load_index (self ):
57
+ filename = CHANGELOGS_DIR / f"WinPython{ self .flavor } -{ self .architecture } bit-{ self .version } .md"
58
+ if not filename .exists ():
59
+ raise FileNotFoundError (f"Changelog not found: { filename } " )
60
+
61
+ with open (filename , "r" , encoding = utils .guess_encoding (filename )[0 ]) as f :
62
+ self ._parse_index (f .read ())
63
+
64
+ def _parse_index (self , text ):
65
+ current = None
73
66
for line in text .splitlines ():
74
- if line :
75
- if line == self .TOOLS_LINE :
76
- tools_flag , python_flag , wheelhouse_flag = True , False , False
77
- continue
78
- elif line == self .PYTHON_PACKAGES_LINE :
79
- tools_flag , python_flag , wheelhouse_flag = False , True , False
80
- continue
81
- elif line == self .WHEELHOUSE_PACKAGES_LINE :
82
- tools_flag , python_flag , wheelhouse_flag = False , False , True
83
- continue
84
- elif line in (self .HEADER_LINE1 , self .HEADER_LINE2 , "<details>" , "</details>" ):
85
- continue
86
- if tools_flag or python_flag or wheelhouse_flag :
87
- package = Package ()
88
- package .from_text (line )
89
- if tools_flag :
90
- self .other_packages [package .name ] = package
91
- elif python_flag :
92
- self .python_packages [package .name ] = package
93
- else :
94
- self .wheelhouse_packages [package .name ] = package
95
-
96
- def diff_package_dicts (old_packages , new_packages ):
67
+ if line in self .HEADERS .values ():
68
+ current = [k for k , v in self .HEADERS .items () if v == line ][0 ]
69
+ continue
70
+ if line .strip () in self .BLANKS :
71
+ continue
72
+ if current :
73
+ pkg = Package (line )
74
+ self .packages [current ][pkg .name ] = pkg
75
+
76
+ def compare_packages (old , new ):
97
77
"""Return difference between package old and package new"""
98
78
99
79
# wheel replace '-' per '_' in key
100
- old = {k .replace ("-" , "_" ).lower (): v for k , v in old_packages .items ()}
101
- new = {k .replace ("-" , "_" ).lower (): v for k , v in new_packages .items ()}
102
- text = ""
103
-
104
- if new_keys := sorted (set (new ) - set (old )):
105
- text += "New packages:\r \n \r \n " + "" .join (new [k ].to_wiki () for k in new_keys ) + "\r \n "
106
-
107
- if upgraded := [new [k ].upgrade_wiki (old [k ]) for k in sorted (set (old ) & set (new )) if old [k ].version != new [k ].version ]:
108
- text += "Upgraded packages:\r \n \r \n " + f"{ '' .join (upgraded )} " + "\r \n "
109
-
110
- if removed_keys := sorted (set (old ) - set (new )):
111
- text += "Removed packages:\r \n \r \n " + "" .join (old [k ].to_wiki () for k in removed_keys ) + "\r \n "
112
- return text
113
-
114
- def find_closer_version (version1 , basedir = None , flavor = "" , architecture = 64 ):
80
+ def normalize (d ): return {k .replace ("-" , "_" ).lower (): v for k , v in d .items ()}
81
+ old , new = normalize (old ), normalize (new )
82
+ output = ""
83
+
84
+ added = [new [k ].to_wiki () for k in new if k not in old ]
85
+ upgraded = [new [k ].upgrade_wiki (old [k ]) for k in new if k in old and new [k ].version != old [k ].version ]
86
+ removed = [old [k ].to_wiki () for k in old if k not in new ]
87
+
88
+ if added :
89
+ output += "New packages:\r \n \r \n " + "" .join (added ) + "\r \n "
90
+ if upgraded :
91
+ output += "Upgraded packages:\r \n \r \n " + "" .join (upgraded ) + "\r \n "
92
+ if removed :
93
+ output += "Removed packages:\r \n \r \n " + "" .join (removed ) + "\r \n "
94
+ return output
95
+
96
+ def find_previous_version (target_version , basedir = None , flavor = "" , architecture = 64 ):
115
97
"""Find version which is the closest to `version`"""
116
- builddir = Path (basedir ) / f"bu{ flavor } "
117
- pattern = re .compile (rf"WinPython{ flavor } -{ architecture } bit-([0-9\.]*)\.(txt|md)" )
118
- versions = [pattern .match (name ).groups ()[0 ] for name in os .listdir (builddir ) if pattern .match (name )]
119
-
120
- if version1 not in versions :
121
- raise ValueError (f"Unknown version { version1 } " )
122
-
123
- version_below = '0.0.0.0'
124
- for v in versions :
125
- if version .parse (version_below ) < version .parse (v ) and version .parse (v ) < version .parse (version1 ):
126
- version_below = v
98
+ build_dir = Path (basedir ) / f"bu{ flavor } "
99
+ pattern = re .compile (rf"WinPython{ flavor } -{ architecture } bit-([0-9\.]+)\.(txt|md)" )
100
+ versions = [pattern .match (f ).group (1 ) for f in os .listdir (build_dir ) if pattern .match (f )]
101
+ versions = [v for v in versions if version .parse (v ) < version .parse (target_version )]
102
+ return max (versions , key = version .parse , default = target_version )
127
103
128
- return version_below if version_below != '0.0.0.0' else version1
104
+ def compare_package_indexes (version2 , version1 = None , basedir = None , flavor = "" , flavor1 = None , architecture = 64 ):
105
+ version1 = version1 or find_previous_version (version2 , basedir , flavor , architecture )
106
+ flavor1 = flavor1 or flavor
129
107
130
- def compare_package_indexes (version2 , version1 = None , basedir = None , flavor = "" , flavor1 = None ,architecture = 64 ):
131
- """Compare two package index Wiki pages"""
132
- version1 = version1 if version1 else find_closer_version (version2 , basedir , flavor , architecture )
133
- flavor1 = flavor1 if flavor1 else flavor
134
108
pi1 = PackageIndex (version1 , basedir , flavor1 , architecture )
135
109
pi2 = PackageIndex (version2 , basedir , flavor , architecture )
136
110
@@ -140,37 +114,29 @@ def compare_package_indexes(version2, version1=None, basedir=None, flavor="", fl
140
114
"<details>\r \n \r \n "
141
115
)
142
116
143
- tools_text = diff_package_dicts (pi1 .other_packages , pi2 .other_packages )
144
- if tools_text :
145
- text += PackageIndex .TOOLS_LINE + "\r \n \r \n " + tools_text
117
+ for key in PackageIndex .HEADERS :
118
+ diff = compare_packages (pi1 .packages [key ], pi2 .packages [key ])
119
+ if diff :
120
+ text += f"{ PackageIndex .HEADERS [key ]} \r \n \r \n { diff } "
146
121
147
- py_text = diff_package_dicts (pi1 .python_packages , pi2 .python_packages )
148
- if py_text :
149
- text += PackageIndex .PYTHON_PACKAGES_LINE + "\r \n \r \n " + py_text
122
+ return text + "\r \n </details>\r \n * * *\r \n "
150
123
151
- py_text = diff_package_dicts (pi1 .wheelhouse_packages , pi2 .wheelhouse_packages )
152
- if py_text :
153
- text += PackageIndex .WHEELHOUSE_PACKAGES_LINE + "\r \n \r \n " + py_text
154
-
155
- text += "\r \n </details>\r \n * * *\r \n "
156
- return text
157
-
158
- def _copy_all_changelogs (version , basedir , flavor = "" , architecture = 64 ):
124
+ def copy_changelogs (version , basedir , flavor = "" , architecture = 64 ):
159
125
basever = "." .join (version .split ("." )[:2 ])
160
- pattern = re .compile (rf"WinPython{ flavor } -{ architecture } bit-{ basever } ([0-9\.]*)\.(txt|md)" )
161
- for name in os .listdir (CHANGELOGS_DIR ):
162
- if pattern .match (name ):
163
- shutil .copyfile (CHANGELOGS_DIR / name , Path (basedir ) / f"bu{ flavor } " / name )
126
+ pattern = re .compile (rf"WinPython{ flavor } -{ architecture } bit-{ basever } [0-9\.]*\.(txt|md)" )
127
+ dest = Path (basedir ) / f"bu{ flavor } "
128
+ for fname in os .listdir (CHANGELOGS_DIR ):
129
+ if pattern .match (fname ):
130
+ shutil .copyfile (CHANGELOGS_DIR / fname , dest / fname )
164
131
165
132
def write_changelog (version2 , version1 = None , basedir = None , flavor = "" , architecture = 64 ):
166
133
"""Write changelog between version1 and version2 of WinPython"""
167
- _copy_all_changelogs (version2 , basedir , flavor , architecture )
134
+ copy_changelogs (version2 , basedir , flavor , architecture )
168
135
print ("comparing_package_indexes" , version2 , basedir , flavor , architecture )
169
- changelog_text = compare_package_indexes (version2 , version1 , basedir , flavor , architecture = architecture )
136
+ changelog = compare_package_indexes (version2 , version1 , basedir , flavor , architecture = architecture )
170
137
output_file = Path (basedir ) / f"bu{ flavor } " / f"WinPython{ flavor } -{ architecture } bit-{ version2 } _History.md"
171
-
172
- with open (output_file , "w" , encoding = "utf-8" ) as fdesc :
173
- fdesc .write (changelog_text )
138
+ with open (output_file , "w" , encoding = "utf-8" ) as f :
139
+ f .write (changelog )
174
140
# Copy to winpython/changelogs
175
141
shutil .copyfile (output_file , CHANGELOGS_DIR / output_file .name )
176
142
0 commit comments