From b3bb527584eff1757c9e4fa090aa44ad8114a0f2 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 4 Dec 2020 16:21:09 -0500 Subject: [PATCH] feat: PyGithub and direct writing --- .pre-commit-config.yaml | 2 +- README.md | 5 +- bin/projects.py | 111 +++++++++++++++++++++++++++------------- bin/projects.yml | 5 +- requirements-dev.txt | 1 + 5 files changed, 83 insertions(+), 41 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a01336bf..1af3a9cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,5 +21,5 @@ repos: rev: v0.782 hooks: - id: mypy - files: cibuildwheel/|test/ + files: ^(cibuildwheel/|test/|bin/projects.py) pass_filenames: false diff --git a/README.md b/README.md index 51b643c6..50d0e5ab 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ For more information, including how to build on GitHub Actions, Appveyor, Azure Options ------- - + | Name | Stars  | CI | OS | Notes | |-------------------------|-------|----|----|:------| @@ -210,7 +210,8 @@ Options - + + > Add your repo here! Send a PR, adding your information to `bin/projects.yml`. > diff --git a/bin/projects.py b/bin/projects.py index e0deb0eb..7b2fb7dc 100755 --- a/bin/projects.py +++ b/bin/projects.py @@ -1,25 +1,42 @@ #!/usr/bin/env python3 -import json -from typing import Dict, Any, List +""" +Convert a yaml project list into a nice table. + +Suggested usage: + + ./bin/projects.py bin/projects.yml --online --auth $GITHUB_API_TOKEN --readme README.md + git diff +""" + +import builtins +import functools from datetime import datetime +from io import StringIO +from typing import Dict, Any, List, Optional, TextIO import click -import requests import yaml +from github import Github -def get_info(gh: str) -> Dict[str, Any]: - url = f"https://api.github.com/repos/{gh}" - req = requests.get(url) - return json.loads(req.content) +ICONS = ( + "appveyor", + "github", + "azure-pipelines", + "circleci", + "gitlab", + "travisci", + "windows", + "apple", + "linux", +) class Project: NAME: int = 0 - ONLINE: bool = True - def __init__(self, config: Dict[str, Any]): + def __init__(self, config: Dict[str, Any], github: Optional[Github] = None): try: self.name: str = config["name"] self.gh: str = config["gh"] @@ -32,18 +49,15 @@ class Project: self.ci: List[str] = config.get("ci", []) self.os: List[str] = config.get("os", []) - if self.ONLINE: - info = get_info(self.stars_repo) - try: - self.num_stars = info["stargazers_count"] - self.pushed_at = datetime.strptime(info["pushed_at"], "%Y-%m-%dT%H:%M:%SZ") - if not self.notes: - notes = info["description"] - if notes: - self.notes = f":closed_book: {notes}" - except KeyError: - print("Invalid response from Github:", info) - raise + self.online = github is not None + if github is not None: + repo = github.get_repo(self.stars_repo) + self.num_stars = repo.stargazers_count + self.pushed_at = repo.pushed_at + if not self.notes: + notes = repo.description + if notes: + self.notes = f":closed_book: {notes}" else: self.num_stars = 0 self.pushed_at = datetime.utcnow() @@ -52,13 +66,13 @@ class Project: self.__class__.NAME = max(self.__class__.NAME, name_len) def __lt__(self, other: "Project") -> bool: - if self.ONLINE: + if self.online: return self.num_stars < other.num_stars else: return self.name < other.name @classmethod - def header(cls): + def header(cls) -> str: return ( f"| {'Name':{cls.NAME}} | Stars  | CI | OS | Notes |\n" f"|{'':-^{cls.NAME+2 }}|-------|----|----|:------|" @@ -99,9 +113,15 @@ class Project: return f"" -def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online: bool = True) -> None: - Project.ONLINE = online - projects = sorted((Project(item) for item in config), reverse=online) +def str_projects( + config: List[Dict[str, Any]], *, online: bool = True, auth: Optional[str] = None +) -> str: + io = StringIO() + print = functools.partial(builtins.print, file=io) + + github = Github(auth) if online else None + + projects = sorted((Project(item, github) for item in config), reverse=online) print(Project.header()) for project in projects: @@ -112,22 +132,41 @@ def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online: print(project.links()) print() - for icon in {"appveyor", "github", "azure-pipelines", "circleci", "gitlab", "travisci", "windows", "apple", "linux"}: - print(f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg") + for icon in ICONS: + print( + f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg" + ) - if debug: - print() - for project in projects: - print(project.info()) + print() + for project in projects: + print(project.info()) + + return io.getvalue() @click.command() @click.argument("input", type=click.File("r")) -@click.option("--debug/--no-debug") -@click.option("--online/--no-online", default=True) -def projects(input: click.File, debug: bool, online: bool) -> None: +@click.option("--online/--no-online", default=True, help="Get info from GitHub") +@click.option("--auth", help="GitHub authentication token") +@click.option("--readme", type=click.File("r+"), help="Modify a readme file if given") +def projects( + input: TextIO, online: bool, auth: Optional[str], readme: Optional[TextIO] +) -> None: config = yaml.safe_load(input) - print_projects(config, debug=debug, online=online) + output = str_projects(config, online=online, auth=auth) + + if readme is None: + print(output) + else: + text = readme.read() + start_str = "\n" + start = text.find(start_str) + end = text.find("\n") + new_text = f"{text[:start + len(start_str)]}\n{output}\n{text[end:]}" + + readme.seek(0) + readme.write(new_text) + readme.truncate() if __name__ == "__main__": diff --git a/bin/projects.yml b/bin/projects.yml index ed0cb416..42a954d3 100644 --- a/bin/projects.yml +++ b/bin/projects.yml @@ -29,8 +29,9 @@ ci: [github] os: [windows, apple, linux] -- name: python-admesh - gh: admesh/python-admesh +# Seems to be defunct +# - name: python-admesh +# gh: admesh/python-admesh - name: pybase64 gh: mayeut/pybase64 diff --git a/requirements-dev.txt b/requirements-dev.txt index a4417881..a7b0f124 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,4 +8,5 @@ requests click mypy pyyaml +pygithub typing-extensions