From 16a65bab070f6be294d486020da19de0ce96b412 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 18 Oct 2023 08:25:38 -0400 Subject: Move all binaries to a "bin" directory. --- proposals/check_index | 22 ------ proposals/reindex.py | 212 -------------------------------------------------- 2 files changed, 234 deletions(-) delete mode 100755 proposals/check_index delete mode 100755 proposals/reindex.py (limited to 'proposals') diff --git a/proposals/check_index b/proposals/check_index deleted file mode 100755 index 61db50d..0000000 --- a/proposals/check_index +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# -# Give an error if somebody forgot to run ./reindex.py. -# -# Only works on a clean git checkout. - -set -e -u -o pipefail -x - -TOPLEVEL=$(dirname "$0")/.. - -if ! git diff --quiet ; then - echo "Git repository is not clean. Cannot procede." - exit 2 -fi - -cd "$TOPLEVEL/proposals" -./reindex.py - -if ! git diff --quiet ; then - echo "Proposal index is not up-to-date. Run ./reindex.py to regenerate it." >&2 - exit 1 -fi diff --git a/proposals/reindex.py b/proposals/reindex.py deleted file mode 100755 index 8bc9729..0000000 --- a/proposals/reindex.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python3 - -import sys -if sys.version_info[0] < 3: - print("No support for Python 2.") - sys.exit(1) - -import codecs, re, os -class Error(Exception): pass - -STATUSES = """DRAFT NEEDS-REVISION NEEDS-RESEARCH OPEN ACCEPTED META FINISHED - CLOSED SUPERSEDED DEAD REJECTED OBSOLETE RESERVE INFORMATIONAL""".split() -REQUIRED_FIELDS = [ "Filename", "Status", "Title"] -CONDITIONAL_FIELDS = { "OPEN" : [ "Target", "Ticket" ], - "ACCEPTED" : [ "Target", "Ticket" ], - "CLOSED" : [ "Implemented-In", "Ticket" ], - "FINISHED" : [ "Implemented-In", "Ticket" ] } -FNAME_RE = re.compile(r'^(\d\d\d)-.*[^\~]$') -DIR = "." -OUTFILE_TXT = "000-index.txt" -TMPFILE_TXT = OUTFILE_TXT+".tmp" - -TEMPFILES = [TMPFILE_TXT] - -def unlink_if_present(fname): - try: - os.unlink(fname) - except OSError: - pass - -def indexed(seq): - n = 0 - for i in seq: - yield n, i - n += 1 - -def readProposal(fn): - fields = { } - f = codecs.open(fn, 'r', encoding='utf-8') - lastField = None - try: - for lineno, line in indexed(f): - line = line.rstrip() - if not line: - return fields - if line[0].isspace(): - fields[lastField] += " %s"%(line.strip()) - elif line == "```": - pass - else: - parts = line.split(":", 1) - if len(parts) != 2: - raise Error("%s:%s: Neither field, continuation, nor ```."% - (fn,lineno)) - else: - fields[parts[0]] = parts[1].strip() - lastField = parts[0] - - return fields - finally: - f.close() - -def getProposalNumber(fn): - """Get the proposal's assigned number from its filename `fn`.""" - parts = fn.split('-', 1) - - assert len(parts) == 2, \ - "Filename must have a proposal number and title separated by a '-'" - - return int(parts[0]) - -def checkProposal(fn, fields): - status = fields.get("Status") - need_fields = REQUIRED_FIELDS + CONDITIONAL_FIELDS.get(status, []) - - number = getProposalNumber(fn) - # Since prop#288 was the newest when we began requiring the 'Ticket:' - # field, we don't require the field for it or any older proposal. - # (Although you're encouraged to add it to your proposal, and add it for - # older proposals where you know the correct ticket, as it greatly helps - # newcomers find more information on the implementation.) - if number <= 288: - if "Ticket" in need_fields: - need_fields.remove("Ticket") - - for f in need_fields: - if f not in fields: - raise Error("%s has no %s field"%(fn, f)) - if fn != fields['Filename']: - raise Error("Mismatched Filename field in %s"%fn) - if fields['Title'][-1] == '.': - fields['Title'] = fields['Title'][:-1] - - status = fields['Status'] = status.upper() - if status not in STATUSES: - raise Error("I've never heard of status %s in %s"%(status,fn)) - if status in [ "SUPERSEDED", "DEAD" ]: - for f in [ 'Implemented-In', 'Target' ]: - if f in fields: del fields[f] - - fields['FilenameTruncated'] = os.path.splitext(fields['Filename'])[0] - -def readProposals(): - res = [] - for fn in os.listdir(DIR): - m = FNAME_RE.match(fn) - if not m: continue - if fn.endswith(".tmp"): - continue - if not (fn.endswith(".txt") or fn.endswith(".md")): - raise Error("%s doesn't end with .txt or .md"%fn) - num = m.group(1) - fields = readProposal(fn) - checkProposal(fn, fields) - fields['num'] = num - res.append(fields) - return res - -def writeTextIndexFile(proposals): - proposals.sort(key=lambda f:f['num']) - seenStatuses = set() - for p in proposals: - seenStatuses.add(p['Status']) - - out = open(TMPFILE_TXT, 'w') - inf = open(OUTFILE_TXT, 'r') - for line in inf: - out.write(line) - if line.startswith("====="): break - inf.close() - - out.write("Proposals by number:\n\n") - for prop in proposals: - out.write("%(num)s %(Title)s [%(Status)s]\n"%prop) - out.write("\n\nProposals by status:\n\n") - for s in STATUSES: - if s not in seenStatuses: continue - out.write(" %s:\n"%s) - for prop in proposals: - if s == prop['Status']: - out.write(" %(num)s %(Title)s"%prop) - if "Target" in prop: - out.write(" [for %(Target)s]"%prop) - if "Implemented-In" in prop: - out.write(" [in %(Implemented-In)s]"%prop) - out.write("\n") - - out.write("```\n") - out.close() - os.rename(TMPFILE_TXT, OUTFILE_TXT) - -def formatMarkdownEntry(prop, withStatus=False): - if withStatus: - fmt = "* [`{Filename}`](/proposals/{Filename}): {Title} [{Status}]\n" - else: - fmt = "* [`{Filename}`](/proposals/{Filename}): {Title}\n" - return fmt.format(**prop) - -def formatSummaryEntry(prop): - return " - [`{FilenameTruncated}`](./{Filename}): {Title} ({Status})\n".format(**prop) - -def writeMarkdownFile(prefix, format_inputs): - template = prefix+"_template.md" - output = prefix+".md" - t = open(template).read() - content = t.format(**format_inputs) - with open(output, 'w') as f: - f.write(content) - -def writeMarkdownIndexFiles(proposals): - markdown_files = [ "BY_INDEX", "BY_STATUS", "SUMMARY" ] - format_inputs = {} - - format_inputs['warning'] = "" - - entries = [] - for prop in proposals: - entries.append(formatMarkdownEntry(prop, withStatus=True)) - format_inputs["BY_INDEX"] = "".join(entries) - - entries = [] - for prop in proposals: - entries.append(formatSummaryEntry(prop)) - format_inputs["SUMMARY_TABLE"] = "".join(entries) - - for s in STATUSES: - entries = [] - for prop in proposals: - if s == prop['Status']: - entries.append(formatMarkdownEntry(prop)) - if entries: - format_inputs[s] = "".join(entries) - else: - format_inputs[s] = "(There are no proposals in this category)\n" - - entries = [] - for prop in proposals: - if prop['Status'] in ('DEAD', 'REJECTED', 'OBSOLETE'): - entries.append(formatMarkdownEntry(prop, withStatus=True)) - format_inputs['DEAD_REJECTED_OBSOLETE'] = "".join(entries) - - for prefix in markdown_files: - writeMarkdownFile(prefix, format_inputs) - -if __name__ == '__main__': - proposals = readProposals() - try: - writeTextIndexFile(proposals) - writeMarkdownIndexFiles(proposals) - finally: - for tempfile in TEMPFILES: - unlink_if_present(tempfile) -- cgit v1.2.3-54-g00ecf