Source code for labelord.github

"""GitHub module

This module contains classes and methods for working and communicating with
GitHub API (including error handling).
"""

import hashlib
import hmac
import requests

[docs]class GitHubError(Exception): """GitHub error class. This class contains methods describing and used for working with errors returned by GitHub. Initializing an instance will parse status code and message from GitHub response. Example: .. testcode:: import requests import labelord res = requests.Response() res.status_code = 400 res.json = lambda: {} gh_error = labelord.github.GitHubError(res) .. doctest:: >>> gh_error.status_code 400 >>> gh_error.message 'No message provided' Args: response (requests.Response): GitHub response. """ def __init__(self, response): self.status_code = response.status_code self.message = response.json().get('message', 'No message provided') def __str__(self): return 'GitHub: ERROR {}'.format(self.code_message) @property def code_message(self, sep=' - '): """Formats an error. This method returns formatted GitHub error. Example: .. testcode:: import requests import labelord res = requests.Response() res.status_code = 400 res.json = lambda: {} gh_error = labelord.github.GitHubError(res) .. doctest:: >>> gh_error.code_message '400 - No message provided' Args: sep (Optional[str]): Separator, default: ' - '. Returns: str: GitHub error in the format of code **sep** message. """ return sep.join([str(self.status_code), self.message])
[docs]class GitHub: """GitHub API class. This class contains methods for communicating with GitHub API. Initializing an instance will set its token and session. Example: .. testcode:: import requests import labelord github = labelord.github.GitHub('123456') .. doctest:: >>> github.token '123456' .. testcode:: req = requests.Request() github.session.auth(req) .. doctest:: >>> req.headers['Authorization'] 'token 123456' >>> req.headers['User-Agent'] 'Python/Labelord' Args: token (str): GitHub API token. session (requests.Session): Session object. """ GH_API_ENDPOINT = 'https://api.github.com' """str: URL of GitHub API endpoint.""" def __init__(self, token, session=None): self.token = token self.set_session(session)
[docs] def set_session(self, session): """Sets session This method sets new session including headers for use within class. Args: session (requests.Session): Session to use. """ self.session = session or requests.Session() self.session.auth = self._session_auth()
def _session_auth(self): def github_auth(req): req.headers = { 'Authorization': 'token ' + self.token, 'User-Agent': 'Python/Labelord' } return req return github_auth def _get_raising(self, url, expected_code=200): response = self.session.get(url) if response.status_code != expected_code: raise GitHubError(response) return response def _get_all_data(self, resource): """Get all data spread across multiple pages""" response = self._get_raising('{}{}?per_page=100&page=1'.format( self.GH_API_ENDPOINT, resource )) yield from response.json() while 'next' in response.links: response = self._get_raising(response.links['next']['url']) yield from response.json()
[docs] def list_repositories(self): """Gets a list of names of accessible repositories (including owner) This method gets a list of names of GitHub repositiories accessible with given GitHub API token. Returns: list: List of repository names (full, eg. User/Repo). """ data = self._get_all_data('/user/repos') return [repo['full_name'] for repo in data]
[docs] def list_labels(self, repository): """Gets dict of labels with colors for given repository slug This method gets labels with colors for given GitHub repository. Args: repository (str): GitHub repository slug (eg. User/Repo). Returns: dict: Color for each label in repository. """ data = self._get_all_data('/repos/{}/labels'.format(repository)) return {l['name']: str(l['color']) for l in data}
[docs] def create_label(self, repository, name, color, **kwargs): """Creates new label in given repository This method creates new label with specified name and color in given repository. Args: repository (str): GitHub repository slug (eg. User/Repo). name (str): New label's name. color (str): New label's color. kwargs (dict): Other arguments placeholder, not used. Raises: GitHubError: If status code returned by GitHub is not 201. """ data = {'name': name, 'color': color} response = self.session.post( '{}/repos/{}/labels'.format(self.GH_API_ENDPOINT, repository), json=data ) if response.status_code != 201: raise GitHubError(response)
[docs] def update_label(self, repository, name, color, old_name=None, **kwargs): """Updates existing label in given repository This method updates given repository's existing label's name and color. Args: repository (str): GitHub repository slug (eg. User/Repo). name (str): Updated label's new name. color (str): Updated label's new color. old_name (Optional[str]): Updated label's old name, default: None. kwargs (dict): Other arguments placeholder, not used. Raises: GitHubError: If status code returned by GitHub is not 200. """ data = {'name': name, 'color': color} response = self.session.patch( '{}/repos/{}/labels/{}'.format( self.GH_API_ENDPOINT, repository, old_name or name ), json=data ) if response.status_code != 200: raise GitHubError(response)
[docs] def delete_label(self, repository, name, **kwargs): """Deletes existing label in given repository This method deletes existing label from given repository. Args: repository (str): GitHub repository slug (eg. User/Repo). name (str): Deleted label's name. kwargs (dict): Other arguments placeholder, not used. Raises: GitHubError: If status code returned by GitHub is not 204. """ response = self.session.delete( '{}/repos/{}/labels/{}'.format( self.GH_API_ENDPOINT, repository, name ) ) if response.status_code != 204: raise GitHubError(response)
[docs] @staticmethod def webhook_verify_signature(data, signature, secret, encoding='utf-8'): """Verifies webhook signature This method verifies webhook signature. Example: .. testcode:: import hashlib import hmac import labelord github = labelord.github.GitHub('123456') data = 'test data'.encode('utf-8') secret = 'secret' signature = 'sha1=a81d790e8312e98f84aacaf95ca9dd04e5305fbf' .. doctest:: >>> github.webhook_verify_signature(data, signature, secret) True .. testcode:: data = 'other data'.encode('utf-8') .. doctest:: >>> github.webhook_verify_signature(data, signature, secret) False Args: data (str): Incoming request data. signature (str): GitHub signature. secret (str): Webhook secret. encoding (Optional[str]): Character encoding used for secret, default: utf-8. Returns: bool: Webhook signature verification result. """ h = hmac.new(secret.encode(encoding), data, hashlib.sha1) return hmac.compare_digest('sha1=' + h.hexdigest(), signature)