8
8
# ===----------------------------------------------------------------------===##
9
9
10
10
from typing import List , Dict , Tuple , Optional
11
+ import copy
11
12
import csv
12
13
import itertools
13
14
import json
19
20
# Number of the 'Libc++ Standards Conformance' project on Github
20
21
LIBCXX_CONFORMANCE_PROJECT = '31'
21
22
23
+ def extract_between_markers (text : str , begin_marker : str , end_marker : str ) -> Optional [str ]:
24
+ """
25
+ Given a string containing special markers, extract everything located beetwen these markers.
26
+
27
+ If the beginning marker is not found, None is returned. If the beginning marker is found but
28
+ there is no end marker, it is an error (this is done to avoid silently accepting inputs that
29
+ are erroneous by mistake).
30
+ """
31
+ start = text .find (begin_marker )
32
+ if start == - 1 :
33
+ return None
34
+
35
+ start += len (begin_marker ) # skip the marker itself
36
+ end = text .find (end_marker , start )
37
+ if end == - 1 :
38
+ raise ArgumentError (f"Could not find end marker { end_marker } in: { text [start :]} " )
39
+
40
+ return text [start :end ]
41
+
22
42
class PaperStatus :
23
43
TODO = 1
24
44
IN_PROGRESS = 2
@@ -60,19 +80,16 @@ def from_csv_entry(entry: str):
60
80
- '|Partial|'
61
81
- '|Complete|'
62
82
- '|Nothing To Do|'
63
-
64
- Note that since we sometimes add additional notes after the status, we only check that the entry
65
- starts with the above patterns.
66
83
"""
67
84
if entry == '' :
68
85
return PaperStatus (PaperStatus .TODO , entry )
69
- elif entry . startswith ( '|In Progress|' ) :
86
+ elif entry == '|In Progress|' :
70
87
return PaperStatus (PaperStatus .IN_PROGRESS , entry )
71
- elif entry . startswith ( '|Partial|' ) :
88
+ elif entry == '|Partial|' :
72
89
return PaperStatus (PaperStatus .PARTIAL , entry )
73
- elif entry . startswith ( '|Complete|' ) :
90
+ elif entry == '|Complete|' :
74
91
return PaperStatus (PaperStatus .DONE , entry )
75
- elif entry . startswith ( '|Nothing To Do|' ) :
92
+ elif entry == '|Nothing To Do|' :
76
93
return PaperStatus (PaperStatus .NOTHING_TO_DO , entry )
77
94
else :
78
95
raise RuntimeError (f'Unexpected CSV entry for status: { entry } ' )
@@ -140,10 +157,10 @@ class PaperInfo:
140
157
First version of LLVM in which this paper/issue was resolved.
141
158
"""
142
159
143
- labels : Optional [List [ str ] ]
160
+ notes : Optional [str ]
144
161
"""
145
- List of labels to associate to the issue in the status-tracking table. Supported labels are
146
- 'format', 'ranges', 'spaceship', 'flat_containers', 'concurrency TS' and 'DR' .
162
+ Optional plain text string representing notes to associate to the paper.
163
+ This is used to populate the "Notes" column in the CSV status pages .
147
164
"""
148
165
149
166
original : Optional [object ]
@@ -156,14 +173,14 @@ def __init__(self, paper_number: str, paper_name: str,
156
173
status : PaperStatus ,
157
174
meeting : Optional [str ] = None ,
158
175
first_released_version : Optional [str ] = None ,
159
- labels : Optional [List [ str ] ] = None ,
176
+ notes : Optional [str ] = None ,
160
177
original : Optional [object ] = None ):
161
178
self .paper_number = paper_number
162
179
self .paper_name = paper_name
163
180
self .status = status
164
181
self .meeting = meeting
165
182
self .first_released_version = first_released_version
166
- self .labels = labels
183
+ self .notes = notes
167
184
self .original = original
168
185
169
186
def for_printing (self ) -> Tuple [str , str , str , str , str , str ]:
@@ -173,7 +190,7 @@ def for_printing(self) -> Tuple[str, str, str, str, str, str]:
173
190
self .meeting if self .meeting is not None else '' ,
174
191
self .status .to_csv_entry (),
175
192
self .first_released_version if self .first_released_version is not None else '' ,
176
- ' ' . join ( f'| { label } |' for label in self .labels ) if self .labels is not None else '' ,
193
+ self .notes if self .notes is not None else '' ,
177
194
)
178
195
179
196
def __repr__ (self ) -> str :
@@ -195,7 +212,7 @@ def from_csv_row(row: Tuple[str, str, str, str, str, str]):# -> PaperInfo:
195
212
status = PaperStatus .from_csv_entry (row [3 ]),
196
213
meeting = row [2 ] or None ,
197
214
first_released_version = row [4 ] or None ,
198
- labels = [ l . strip ( '|' ) for l in row [5 ]. split ( ' ' ) if l ] or None ,
215
+ notes = row [5 ] or None ,
199
216
original = row ,
200
217
)
201
218
@@ -210,20 +227,45 @@ def from_github_issue(issue: Dict):# -> PaperInfo:
210
227
raise RuntimeError (f"Issue doesn't have a title that we know how to parse: { issue } " )
211
228
paper = match .group (1 )
212
229
213
- # Handle labels
214
- valid_labels = ('format' , 'ranges' , 'spaceship' , 'flat_containers' , 'concurrency TS' , 'DR' )
215
- labels = [label for label in issue ['labels' ] if label in valid_labels ]
230
+ # Extract any notes from the Github issue and populate the RST notes with them
231
+ issue_description = issue ['content' ]['body' ]
232
+ notes = extract_between_markers (issue_description , 'BEGIN-RST-NOTES' , 'END-RST-NOTES' )
233
+ notes = notes .strip () if notes is not None else notes
216
234
217
235
return PaperInfo (
218
236
paper_number = paper ,
219
237
paper_name = issue ['title' ],
220
238
status = PaperStatus .from_github_issue (issue ),
221
239
meeting = issue .get ('meeting Voted' , None ),
222
240
first_released_version = None , # TODO
223
- labels = labels if labels else None ,
241
+ notes = notes ,
224
242
original = issue ,
225
243
)
226
244
245
+ def merge (paper : PaperInfo , gh : PaperInfo ) -> PaperInfo :
246
+ """
247
+ Merge a paper coming from a CSV row with a corresponding Github-tracked paper.
248
+
249
+ If the CSV row has a status that is "less advanced" than the Github issue, simply update the CSV
250
+ row with the newer status. Otherwise, report an error if they have a different status because
251
+ something must be wrong.
252
+
253
+ In case we don't update the CSV row's status, we still take any updated notes coming
254
+ from the Github issue.
255
+ """
256
+ if paper .status < gh .status :
257
+ return gh
258
+ elif paper .status != gh .status :
259
+ print (f"We found a CSV row and a Github issue with different statuses:\n row: { paper } \n Github issue: { gh } " )
260
+ return paper
261
+ else :
262
+ # Retain the notes from the Github issue, if any
263
+ if gh .notes is not None :
264
+ cp = copy .deepcopy (paper )
265
+ cp .notes = gh .notes
266
+ return cp
267
+ return paper
268
+
227
269
def load_csv (file : pathlib .Path ) -> List [Tuple ]:
228
270
rows = []
229
271
with open (file , newline = '' ) as f :
@@ -275,17 +317,7 @@ def sync_csv(rows: List[Tuple], from_github: List[PaperInfo]) -> List[Tuple]:
275
317
results .append (row )
276
318
continue
277
319
278
- gh = tracking [0 ]
279
-
280
- # If the CSV row has a status that is "less advanced" than the Github issue, simply update the CSV
281
- # row with the newer status. Otherwise, report an error if they have a different status because
282
- # something must be wrong.
283
- if paper .status < gh .status :
284
- results .append (gh .for_printing ())
285
- continue
286
- elif paper .status != gh .status :
287
- print (f"We found a CSV row and a Github issue with different statuses:\n row: { row } \n Github issue: { gh } " )
288
- results .append (row )
320
+ results .append (merge (paper , tracking [0 ]).for_printing ())
289
321
290
322
return results
291
323
0 commit comments