Source code for projectreport.analyzer.parsers.github

from os import getenv
from typing import Any, Dict, List, Optional, Sequence, Tuple, TypedDict

import github.GithubException
from cached_property import cached_property
from github import Github
from github.Repository import Repository

from projectreport.analyzer.parsers.base import Parser
from projectreport.analyzer.parsers.data_types import ParserDataType
from projectreport.analyzer.parsers.url import URLParser
from projectreport.license.model import License
from projectreport.logger import logger
from projectreport.tools.monkey_patch_github import (
    ResourceNotFoundException,
    monkey_patch_github_obj_for_throttling,
)
from projectreport.version import Version

access_token = getenv("GITHUB_TOKEN")

gh = Github(access_token)
monkey_patch_github_obj_for_throttling(gh)


[docs]class GithubData(TypedDict): name: str description: Optional[str] version: Optional[str] topics: Optional[List[str]]
[docs]class GithubParser(URLParser):
[docs] def __init__(self, path: str): self.repo_key = _github_url_to_owner_and_name(path) self.github_repo = get_repo(self.repo_key) monkey_patch_github_obj_for_throttling(self.github_repo) super().__init__(path)
[docs] @staticmethod def find_github_url(urls: Sequence[str]) -> Optional[str]: for url in urls: if "github.com" in url: return url return None
@cached_property def contents(self) -> str: raise NotImplementedError @cached_property def file_name(self) -> str: return f"repo:github:{self.github_repo.name}" @cached_property def parsed(self) -> GithubData: return GithubData( name=self.github_repo.name, description=self.github_repo.description, version=self._get_version_str_from_repo(), topics=self.github_repo.get_topics(), ) @cached_property def license(self) -> Optional[License]: # TODO: Implement getting license from Github return None def _get_version_str_from_repo(self) -> Optional[str]: try: release = self.github_repo.get_latest_release() except (github.UnknownObjectException, ResourceNotFoundException): return None else: if release is not None: return release.tag_name return None @cached_property def docstring(self) -> Optional[str]: return self.parsed.get("description") @cached_property def version(self) -> Optional[Version]: version_str = self.parsed.get("version") if version_str is None: return None return Version.from_str(version_str) @cached_property def topics(self) -> Optional[List[str]]: return self.parsed.get("topics")
[docs] @classmethod def matches_path(cls, path: str) -> bool: return "github.com" in path
[docs]def get_repo(name: str) -> Repository: logger.info(f"Requesting details for repository {name}") return gh.get_repo(name)
def _github_url_to_owner_and_name(url: str) -> str: if "github.com" not in url: raise ValueError(f"got a non-Github URL {url}") return "/".join(url.split("/")[-2:])