about summary refs log tree commit diff
path: root/src/fead.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/fead.py')
-rwxr-xr-xsrc/fead.py36
1 files changed, 28 insertions, 8 deletions
diff --git a/src/fead.py b/src/fead.py
index ac1fcd7..ab23bd0 100755
--- a/src/fead.py
+++ b/src/fead.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# Generate advert from web feeds
+# Advert generator from web feeds
 # Copyright (C) 2022  Nguyễn Gia Phong
 #
 # This program is free software: you can redistribute it and/or modify
@@ -15,9 +15,10 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-__version__ = '0.0.1'
+__doc__ = 'Advert generator from web feeds'
+__version__ = '0.1.0'
 
-from argparse import ArgumentParser, FileType
+from argparse import ArgumentParser, FileType, HelpFormatter
 from asyncio import gather, open_connection, run
 from collections import namedtuple
 from datetime import datetime
@@ -41,6 +42,18 @@ Advert = namedtuple('Advert', ('source_title', 'source_link',
                                'title', 'link', 'time', 'summary'))
 
 
+class GNUHelpFormatter(HelpFormatter):
+    """Help formatter for ArgumentParser following GNU Coding Standards."""
+
+    def add_usage(self, usage, actions, groups, prefix='Usage: '):
+        """Substitute 'Usage:' for 'usage:'."""
+        super().add_usage(usage, actions, groups, prefix)
+
+    def start_section(self, heading):
+        """Substitute 'Options:' for 'options:'."""
+        super().start_section(heading.capitalize())
+
+
 def read_urls(path):
     """Read newline-separated URLs from given file path."""
     return Path(path).read_text().splitlines()
@@ -101,7 +114,7 @@ def parse_atom_entry(xml):
             rel = child.attrib.get('rel')
             if rel == 'alternate' or not rel: link = child.attrib['href']
         elif child.tag.endswith('Atom}published'):
-            iso = child.text.replace('Z', '+00:00') # normalized
+            iso = child.text.replace('Z', '+00:00')  # normalized
             time = datetime.fromisoformat(iso)
         elif child.tag.endswith('Atom}summary'):
             summary = child.text
@@ -172,7 +185,7 @@ async def fetch_all(urls):
     try:
         return await tasks
     except:
-        tasks.cancel() # structured concurrency
+        tasks.cancel()  # structured concurrency
         raise
 
 
@@ -187,10 +200,14 @@ def truncate(ad, summary_length):
                                        summary_length, placeholder='…'))
 
 
-if __name__ == '__main__':
-    parser = ArgumentParser(description='generate advert from web feeds')
+def main():
+    """Run command-line program."""
+    parser = ArgumentParser(prog='fead', usage='%(prog)s [OPTION]...',
+                            description='Generate adverts from web feeds.',
+                            epilog='Any use of -f before -F is ignored.',
+                            formatter_class=GNUHelpFormatter)
     parser.add_argument('-v', '--version', action='version',
-                        version=f'fead {__version__}')
+                        version=f'%(prog)s {__version__}')
     parser.add_argument('-F', '--feeds', metavar='PATH',
                         type=read_urls, default=[],
                         help='file containing newline-separated web feed URLs')
@@ -218,3 +235,6 @@ if __name__ == '__main__':
                                   for ad in select(args.per_feed, feed))):
         args.output.write(template.format(**truncate(ad, args.len)._asdict()))
     args.output.close()
+
+
+if __name__ == '__main__': main()