feat: PyGithub and direct writing

This commit is contained in:
Henry Schreiner
2020-12-04 16:43:01 -05:00
parent c166426b50
commit b3bb527584
5 changed files with 83 additions and 41 deletions
+1 -1
View File
@@ -21,5 +21,5 @@ repos:
rev: v0.782 rev: v0.782
hooks: hooks:
- id: mypy - id: mypy
files: cibuildwheel/|test/ files: ^(cibuildwheel/|test/|bin/projects.py)
pass_filenames: false pass_filenames: false
+3 -2
View File
@@ -105,7 +105,7 @@ For more information, including how to build on GitHub Actions, Appveyor, Azure
Options Options
------- -------
<!-- START bin.project.py --> <!-- START bin/project.py -->
| Name | Stars&nbsp; | CI | OS | Notes | | Name | Stars&nbsp; | CI | OS | Notes |
|-------------------------|-------|----|----|:------| |-------------------------|-------|----|----|:------|
@@ -210,7 +210,8 @@ Options
<!-- xmlstarlet: 7, last pushed 10 days ago --> <!-- xmlstarlet: 7, last pushed 10 days ago -->
<!-- apriltags2-ethz: 1, last pushed 567 days ago --> <!-- apriltags2-ethz: 1, last pushed 567 days ago -->
<!-- END bin.project.py -->
<!-- END bin/project.py -->
> Add your repo here! Send a PR, adding your information to `bin/projects.yml`. > Add your repo here! Send a PR, adding your information to `bin/projects.yml`.
> >
+75 -36
View File
@@ -1,25 +1,42 @@
#!/usr/bin/env python3 #!/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 datetime import datetime
from io import StringIO
from typing import Dict, Any, List, Optional, TextIO
import click import click
import requests
import yaml import yaml
from github import Github
def get_info(gh: str) -> Dict[str, Any]: ICONS = (
url = f"https://api.github.com/repos/{gh}" "appveyor",
req = requests.get(url) "github",
return json.loads(req.content) "azure-pipelines",
"circleci",
"gitlab",
"travisci",
"windows",
"apple",
"linux",
)
class Project: class Project:
NAME: int = 0 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: try:
self.name: str = config["name"] self.name: str = config["name"]
self.gh: str = config["gh"] self.gh: str = config["gh"]
@@ -32,18 +49,15 @@ class Project:
self.ci: List[str] = config.get("ci", []) self.ci: List[str] = config.get("ci", [])
self.os: List[str] = config.get("os", []) self.os: List[str] = config.get("os", [])
if self.ONLINE: self.online = github is not None
info = get_info(self.stars_repo) if github is not None:
try: repo = github.get_repo(self.stars_repo)
self.num_stars = info["stargazers_count"] self.num_stars = repo.stargazers_count
self.pushed_at = datetime.strptime(info["pushed_at"], "%Y-%m-%dT%H:%M:%SZ") self.pushed_at = repo.pushed_at
if not self.notes: if not self.notes:
notes = info["description"] notes = repo.description
if notes: if notes:
self.notes = f":closed_book: {notes}" self.notes = f":closed_book: {notes}"
except KeyError:
print("Invalid response from Github:", info)
raise
else: else:
self.num_stars = 0 self.num_stars = 0
self.pushed_at = datetime.utcnow() self.pushed_at = datetime.utcnow()
@@ -52,13 +66,13 @@ class Project:
self.__class__.NAME = max(self.__class__.NAME, name_len) self.__class__.NAME = max(self.__class__.NAME, name_len)
def __lt__(self, other: "Project") -> bool: def __lt__(self, other: "Project") -> bool:
if self.ONLINE: if self.online:
return self.num_stars < other.num_stars return self.num_stars < other.num_stars
else: else:
return self.name < other.name return self.name < other.name
@classmethod @classmethod
def header(cls): def header(cls) -> str:
return ( return (
f"| {'Name':{cls.NAME}} | Stars&nbsp; | CI | OS | Notes |\n" f"| {'Name':{cls.NAME}} | Stars&nbsp; | CI | OS | Notes |\n"
f"|{'':-^{cls.NAME+2 }}|-------|----|----|:------|" f"|{'':-^{cls.NAME+2 }}|-------|----|----|:------|"
@@ -99,9 +113,15 @@ class Project:
return f"<!-- {self.name}: {self.num_stars}, last pushed {days} days ago -->" return f"<!-- {self.name}: {self.num_stars}, last pushed {days} days ago -->"
def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online: bool = True) -> None: def str_projects(
Project.ONLINE = online config: List[Dict[str, Any]], *, online: bool = True, auth: Optional[str] = None
projects = sorted((Project(item) for item in config), reverse=online) ) -> 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()) print(Project.header())
for project in projects: for project in projects:
@@ -112,22 +132,41 @@ def print_projects(config: List[Dict[str, Any]], *, debug: bool = False, online:
print(project.links()) print(project.links())
print() print()
for icon in {"appveyor", "github", "azure-pipelines", "circleci", "gitlab", "travisci", "windows", "apple", "linux"}: for icon in ICONS:
print(f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg") print(
f"[{icon} icon]: https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon}.svg"
)
if debug: print()
print() for project in projects:
for project in projects: print(project.info())
print(project.info())
return io.getvalue()
@click.command() @click.command()
@click.argument("input", type=click.File("r")) @click.argument("input", type=click.File("r"))
@click.option("--debug/--no-debug") @click.option("--online/--no-online", default=True, help="Get info from GitHub")
@click.option("--online/--no-online", default=True) @click.option("--auth", help="GitHub authentication token")
def projects(input: click.File, debug: bool, online: bool) -> None: @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) 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 = "<!-- START bin/project.py -->\n"
start = text.find(start_str)
end = text.find("<!-- END bin/project.py -->\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__": if __name__ == "__main__":
+3 -2
View File
@@ -29,8 +29,9 @@
ci: [github] ci: [github]
os: [windows, apple, linux] os: [windows, apple, linux]
- name: python-admesh # Seems to be defunct
gh: admesh/python-admesh # - name: python-admesh
# gh: admesh/python-admesh
- name: pybase64 - name: pybase64
gh: mayeut/pybase64 gh: mayeut/pybase64
+1
View File
@@ -8,4 +8,5 @@ requests
click click
mypy mypy
pyyaml pyyaml
pygithub
typing-extensions typing-extensions