2
2
# This file is placed in the public domain or under the
3
3
# CC0-1.0-Universal license, whichever is more permissive.
4
4
5
+ import argparse
5
6
import datetime
6
7
import email .utils
8
+ from html import escape
7
9
from pathlib import Path
8
10
import re
9
11
12
14
from docutils import utils
13
15
from docutils .parsers import rst
14
16
from docutils .parsers .rst import roles
15
- from feedgen import entry
16
- from feedgen import feed
17
17
18
18
# get the directory with the PEP sources
19
19
PEP_ROOT = Path (__file__ ).parent
20
20
21
21
22
- # Monkeypatch feedgen.util.formatRFC2822
23
22
def _format_rfc_2822 (dt : datetime .datetime ) -> str :
23
+ dt = dt .replace (tzinfo = datetime .timezone .utc )
24
24
return email .utils .format_datetime (dt , usegmt = True )
25
25
26
26
27
- entry .formatRFC2822 = feed .formatRFC2822 = _format_rfc_2822
28
27
line_cache : dict [Path , dict [str , str ]] = {}
29
28
30
29
# Monkeypatch PEP and RFC reference roles to match Sphinx behaviour
@@ -137,6 +136,15 @@ def pep_abstract(full_path: Path) -> str:
137
136
138
137
139
138
def main ():
139
+ parser = argparse .ArgumentParser (description = "Generate RSS feed" )
140
+ parser .add_argument (
141
+ "-o" ,
142
+ "--output-dir" ,
143
+ default = "build" , # synchronise with render.yaml -> deploy step
144
+ help = "Output directory, relative to root. Default 'build'." ,
145
+ )
146
+ args = parser .parse_args ()
147
+
140
148
# get list of peps with creation time (from "Created:" string in pep source)
141
149
peps_with_dt = sorted ((pep_creation (path ), path ) for path in PEP_ROOT .glob ("pep-????.???" ))
142
150
@@ -152,21 +160,20 @@ def main():
152
160
author = first_line_starting_with (full_path , "Author:" )
153
161
if "@" in author or " at " in author :
154
162
parsed_authors = email .utils .getaddresses ([author ])
155
- # ideal would be to pass as a list of dicts with names and emails to
156
- # item.author, but FeedGen's RSS <author/> output doesn't pass W3C
157
- # validation (as of 12/06/2021)
158
163
joined_authors = ", " .join (f"{ name } ({ email_address } )" for name , email_address in parsed_authors )
159
164
else :
160
165
joined_authors = author
161
166
url = f"https://peps.python.org/pep-{ pep_num :0>4} /"
162
167
163
- item = entry .FeedEntry ()
164
- item .title (f"PEP { pep_num } : { title } " )
165
- item .link (href = url )
166
- item .description (pep_abstract (full_path ))
167
- item .guid (url , permalink = True )
168
- item .published (dt .replace (tzinfo = datetime .timezone .utc )) # ensure datetime has a timezone
169
- item .author (email = joined_authors )
168
+ item = f"""\
169
+ <item>
170
+ <title>PEP { pep_num } : { escape (title , quote = False )} </title>
171
+ <link>{ escape (url , quote = False )} </link>
172
+ <description>{ escape (pep_abstract (full_path ), quote = False )} </description>
173
+ <author>{ escape (joined_authors , quote = False )} </author>
174
+ <guid isPermaLink="true">{ url } </guid>
175
+ <pubDate>{ _format_rfc_2822 (dt )} </pubDate>
176
+ </item>"""
170
177
items .append (item )
171
178
172
179
# The rss envelope
@@ -175,28 +182,28 @@ def main():
175
182
language features, and some meta-information like release
176
183
procedure and schedules.
177
184
"""
178
-
179
- # Setup feed generator
180
- fg = feed . FeedGenerator ()
181
- fg . language ( "en" )
182
- fg . generator ( "" )
183
- fg . docs ( "https://cyber.harvard.edu/rss/rss.html" )
184
-
185
- # Add metadata
186
- fg . title ( "Newest Python PEPs" )
187
- fg . link ( href = "https://peps.python.org" )
188
- fg . link ( href = " https://peps.python.org/peps. rss" , rel = "self" )
189
- fg . description ( " " . join ( desc . split ()))
190
- fg . lastBuildDate ( datetime . datetime . utcnow (). replace ( tzinfo = datetime . timezone . utc ))
191
-
192
- # Add PEP information (ordered by newest first)
193
- for item in items :
194
- fg . add_entry ( item )
185
+ last_build_date = _format_rfc_2822 ( datetime . datetime . utcnow ())
186
+ items = " \n " . join ( reversed ( items ))
187
+ output = f""" \
188
+ <?xml version='1.0' encoding='UTF-8'?>
189
+ <rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
190
+ <channel>
191
+ <title>Newest Python PEPs</title>
192
+ <link>https://peps.python.org/peps.rss</link>
193
+ <description> { " " . join ( desc . split ()) } </description>
194
+ <atom: link href="https://peps.python.org/peps.rss" rel="self"/>
195
+ <docs> https://cyber.harvard.edu/ rss/rss.html</docs>
196
+ <language>en</language>
197
+ < lastBuildDate> { last_build_date } </lastBuildDate>
198
+ { items }
199
+ </channel>
200
+ </rss>
201
+ """
195
202
196
203
# output directory for target HTML files
197
- out_dir = PEP_ROOT / "build"
198
- out_dir .mkdir (exist_ok = True )
199
- out_dir .joinpath ("peps.rss" ).write_bytes ( fg . rss_str ( pretty = True ) )
204
+ out_dir = PEP_ROOT / args . output_dir
205
+ out_dir .mkdir (exist_ok = True , parents = True )
206
+ out_dir .joinpath ("peps.rss" ).write_text ( output )
200
207
201
208
202
209
if __name__ == "__main__" :
0 commit comments