Pārlūkot izejas kodu

Modernize README API parser to Python 3

Upgrades the API generator script from Python 2 compatibility to modern
Python 3 practices to improve maintainability and reliability.

Changes include:
- Add type hints to all functions for better code documentation
- Replace deprecated datetime.utcnow() with timezone-aware datetime.now()
- Use pathlib.Path for cleaner file operations instead of open()/codecs
- Add automatic directory creation eliminating need for manual mkdir
- Replace bare except with specific exception handling for better debugging
- Use f-strings instead of % formatting for readability
- Add proper docstrings to document function behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Thi Bot 5 dienas atpakaļ
vecāks
revīzija
6a50a97975
1 mainītis faili ar 50 papildinājumiem un 36 dzēšanām
  1. 50 36
      .github/api.py

+ 50 - 36
.github/api.py

@@ -1,27 +1,26 @@
-# -*- coding: utf-8 -*-
-#
-# This script parses the README.md and generates a machine-readable
-# data structure from it, for publication as an API.
-#
-# https://springload.github.io/awesome-wagtail/api/v1/readme.json
-#
-# It is automatically ran as part of Travis builds, also validating the README
-# formatting, and the API endpoint is deployed on successful builds on master.
-#
-# See also:
-# - https://djangopackages.org/api/v3/grids/wagtail-cms/
-# - https://github.com/awesomerank/rank
-
-from __future__ import absolute_import, unicode_literals
+"""
+This script parses the README.md and generates a machine-readable
+data structure from it, for publication as an API.
+
+https://springload.github.io/awesome-wagtail/api/v1/readme.json
+
+It is automatically ran as part of Travis builds, also validating the README
+formatting, and the API endpoint is deployed on successful builds on master.
+
+See also:
+- https://djangopackages.org/api/v3/grids/wagtail-cms/
+- https://github.com/awesomerank/rank
+"""
 
 
 import json
 import json
-import codecs
-import datetime
+from datetime import datetime, timezone
+from pathlib import Path
 
 
 API_PATH = '/api/v1/readme.json'
 API_PATH = '/api/v1/readme.json'
 
 
 
 
-def parse_line(line, category):
+def parse_line(line: str, category: str) -> dict[str, str]:
+    """Parse a single line from the README into a structured dictionary."""
     print(line)
     print(line)
     name = line.split('](')[0][3:]
     name = line.split('](')[0][3:]
     url = line.split('](')[1].split(')')[0]
     url = line.split('](')[1].split(')')[0]
@@ -35,11 +34,13 @@ def parse_line(line, category):
     }
     }
 
 
 
 
-def parse_section(section, category=''):
-    return [parse_line(l, category) for l in section.split('\n')]
+def parse_section(section: str, category: str = '') -> list[dict[str, str]]:
+    """Parse a section of lines into a list of structured items."""
+    return [parse_line(line, category) for line in section.split('\n')]
 
 
 
 
-def parse_subsections(section):
+def parse_subsections(section: str) -> list[dict[str, str]]:
+    """Parse a section containing multiple subsections."""
     subsections = section.split('### ')[1:]
     subsections = section.split('### ')[1:]
 
 
     items = []
     items = []
@@ -53,33 +54,46 @@ def parse_subsections(section):
     return items
     return items
 
 
 
 
-def cut_section(start):
-    return readme.split('## %s\n\n' % start)[1].split('\n\n## ')[0]
+def cut_section(readme: str, start: str) -> str:
+    """Extract a specific section from the README."""
+    return readme.split(f'## {start}\n\n')[1].split('\n\n## ')[0]
 
 
 
 
-def parse_readme(readme):
+def parse_readme(readme: str) -> dict:
+    """Parse the entire README into a structured dictionary."""
     return {
     return {
-        'apps': parse_subsections(cut_section('Apps')),
-        'tools': parse_subsections(cut_section('Tools')),
-        'resources': parse_subsections(cut_section('Resources')),
-        'sites': parse_section(cut_section('Open-source sites')),
+        'apps': parse_subsections(cut_section(readme, 'Apps')),
+        'tools': parse_subsections(cut_section(readme, 'Tools')),
+        'resources': parse_subsections(cut_section(readme, 'Resources')),
+        'sites': parse_section(cut_section(readme, 'Open-source sites')),
         'metadata': {
         'metadata': {
-            'updated': '%sZ' % datetime.datetime.utcnow().isoformat(),
+            'updated': datetime.now(timezone.utc).isoformat(),
         },
         },
     }
     }
 
 
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
-    readme = open('README.md', 'r').read()
+    readme_path = Path('README.md')
 
 
     try:
     try:
+        readme = readme_path.read_text(encoding='utf-8')
         parsed_readme = parse_readme(readme)
         parsed_readme = parse_readme(readme)
-        json_path = './dist%s' % API_PATH
 
 
-        with codecs.open(json_path, mode='w+', encoding='utf8') as f:
-            readme_payload = json.dumps(parsed_readme, indent=True, ensure_ascii=False)
-            print(readme_payload)
-            f.write(readme_payload)
-    except:
+        json_path = Path(f'./dist{API_PATH}')
+        json_path.parent.mkdir(parents=True, exist_ok=True)
+
+        readme_payload = json.dumps(parsed_readme, indent=2, ensure_ascii=False)
+        print(readme_payload)
+
+        json_path.write_text(readme_payload, encoding='utf-8')
+
+    except FileNotFoundError as e:
+        print(f'Error: Could not find file - {e}')
+        raise
+    except (KeyError, IndexError) as e:
+        print(f'Error: README formatting issue - {e}')
         print('Is the README well formatted?')
         print('Is the README well formatted?')
         raise
         raise
+    except Exception as e:
+        print(f'Unexpected error: {e}')
+        raise