我们从Python开源项目中,提取了以下47个代码示例,用于说明如何使用github3.login()。
def disconnect_github(access_token, repo_hooks): """Uninstall webhooks.""" # Note at this point the remote account and all associated data have # already been deleted. The celery task is passed the access_token to make # some last cleanup and afterwards delete itself remotely. import github3 from .api import GitHubAPI try: gh = github3.login(token=access_token) for repo_id, repo_hook in repo_hooks: ghrepo = gh.repository_with_id(repo_id) if ghrepo: hook = ghrepo.hook(repo_hook) if hook and hook.delete(): info_msg = u'Deleted hook {hook} from {repo}'.format( hook=hook.id, repo=ghrepo.full_name) current_app.logger.info(info_msg) # If we finished our clean-up successfully, we can revoke the token GitHubAPI.revoke_token(access_token) except Exception as exc: # Retry in case GitHub may be down... disconnect_github.retry(exc=exc)
def init_account(self): """Setup a new GitHub account.""" ghuser = self.api.me() # Setup local access tokens to be used by the webhooks hook_token = ProviderToken.create_personal( 'github-webhook', self.user_id, scopes=['webhooks:event'], is_internal=True, ) # Initial structure of extra data self.account.extra_data = dict( id=ghuser.id, login=ghuser.login, name=ghuser.name, tokens=dict( webhook=hook_token.id, ), repos=dict(), last_sync=iso_utcnow(), ) db.session.add(self.account) # Sync data from GitHub, but don't check repository hooks yet. self.sync(hooks=False)
def fetch_metadata(self, package): self.manage_ratelimit() repo = self._get_repo(package) if repo is None: return package package.repo_watchers = repo.watchers package.repo_forks = repo.forks package.repo_description = repo.description contributors = [] github_account_type = AccountType.objects.get(name="GITHUB") for contributor in repo.iter_contributors(): account, created = Account.objects.get_or_create(account_type=github_account_type, name=contributor.login) contributors.append(account) self.manage_ratelimit() package.contributors.set(contributors) package.save() return package
def get_stats(self, username='', password='', organization='llnl', force=True): """ Retrieves the traffic for the users of the given organization. Requires organization admin credentials token to access the data. """ date = str(datetime.date.today()) stargazers_file_path = ('../github_stats_output/stargazers.csv') if force or not os.path.isfile(file_path): my_github.login(username, password) calls_beginning = self.logged_in_gh.ratelimit_remaining + 1 print 'Rate Limit: ' + str(calls_beginning) my_github.get_org(organization) my_github.get_repos() my_github.write_to_file(file_path=stargazers_file_path) #my_github.write_to_file(file_path=stargazers_file_path) calls_remaining = self.logged_in_gh.ratelimit_remaining calls_used = calls_beginning - calls_remaining print ('Rate Limit Remaining: ' + str(calls_remaining) + '\nUsed ' + str(calls_used) + ' API calls.')
def get_year_commits(self, username='', password='', organization='llnl', force=True): """ Does setup such as login, printing API info, and waiting for GitHub to build the commit statistics. Then gets the last year of commits and prints them to file. """ date = str(datetime.date.today()) file_path = ('year_commits.csv') if force or not os.path.isfile(file_path): my_github.login(username, password) calls_beginning = self.logged_in_gh.ratelimit_remaining + 1 print 'Rate Limit: ' + str(calls_beginning) my_github.get_org(organization) my_github.repos(building_stats=True) print "Letting GitHub build statistics." time.sleep(30) print "Trying again." my_github.repos(building_stats=False) my_github.calc_total_commits(starting_commits=35163) my_github.write_to_file() calls_remaining = self.logged_in_gh.ratelimit_remaining calls_used = calls_beginning - calls_remaining print ('Rate Limit Remaining: ' + str(calls_remaining) + '\nUsed ' + str(calls_used) + ' API calls.')
def get_stats(self, username='', password='', organization='llnl', force=True): """ Retrieves the emails for the users of the given organization. """ date = str(datetime.date.today()) file_path = ('../github_stats_output/users_emails.csv') if force or not os.path.isfile(file_path): my_github.login(username, password) calls_beginning = self.logged_in_gh.ratelimit_remaining + 1 print 'Rate Limit: ' + str(calls_beginning) my_github.get_org(organization) count_members = my_github.get_mems_of_org() my_github.write_to_file(file_path) calls_remaining = self.logged_in_gh.ratelimit_remaining calls_used = calls_beginning - calls_remaining print ('Rate Limit Remaining: ' + str(calls_remaining) + '\nUsed ' + str(calls_used) + ' API calls.')
def get_mems_of_org(self): """ Retrieves the emails of the members of the organization. Note this Only gets public emails. Private emails would need authentication for each user. """ print 'Getting members\' emails.' for member in self.org_retrieved.iter_members(): login = member.to_json()['login'] user_email = self.logged_in_gh.user(login).to_json()['email'] if user_email is not None: self.emails[login] = user_email else:#user has no public email self.emails[login] = 'none' #used for sorting regardless of case self.logins_lower[login.lower()] = login
def authenticate(): """ Authenticate the user and store the 'token' for further use Return the authentication 'token' """ print LOGIN_INIT_MESSAGE username = raw_input('{0}: '.format(LOGIN_USER_MESSAGE)) password = None while password is None: password = getpass('Password for {0}: '.format(username)) gh = login(username, password=password) try: gh.user() instance = authorize(username, password, APP_SCOPE, APP_DESC, APP_URL) except Exception, e: raise e with open(credentials_file, 'w') as f: f.write(instance.token) return instance.token
def __init__(self, cfg_path): try: cfg = yaml.load(open(cfg_path)) except (IOError, yaml.parser.ParserError) as exc: raise ValueError(exc) self.gh = github3.login( username=cfg.get('username'), password=cfg.get('password'), token=cfg.get('token'), ) if not self.gh: raise ValueError('wrong credentials') try: owner = cfg['owner'] repo = cfg['repo'] except KeyError as exc: raise ValueError('missing {}'.format(exc)) self.repo = self.gh.repository(owner, repo) if not self.repo: raise ValueError('wrong repository')
def check_authorization(self, access_token): """OAuth applications can use this method to check token validity without hitting normal rate limits because of failed login attempts. If the token is valid, it will return True, otherwise it will return False. :returns: bool """ p = self._session.params auth = (p.get('client_id'), p.get('client_secret')) if access_token and auth: url = self._build_url('applications', str(auth[0]), 'tokens', str(access_token)) resp = self._get(url, auth=auth, params={ 'client_id': None, 'client_secret': None }) return self._boolean(resp, 200, 404) return False
def create_gist(self, description, files, public=True): """Create a new gist. If no login was provided, it will be anonymous. :param str description: (required), description of gist :param dict files: (required), file names with associated dictionaries for content, e.g. ``{'spam.txt': {'content': 'File contents ...'}}`` :param bool public: (optional), make the gist public if True :returns: :class:`Gist <github3.gists.Gist>` """ new_gist = {'description': description, 'public': public, 'files': files} url = self._build_url('gists') json = self._json(self._post(url, data=new_gist), 201) return Gist(json, self) if json else None
def iter_orgs(self, login=None, number=-1, etag=None): """Iterate over public organizations for login if provided; otherwise iterate over public and private organizations for the authenticated user. :param str login: (optional), user whose orgs you wish to list :param int number: (optional), number of organizations to return. Default: -1 returns all available organizations :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Organization <github3.orgs.Organization>`\ s """ if login: url = self._build_url('users', login, 'orgs') else: url = self._build_url('user', 'orgs') return self._iter(int(number), url, Organization, etag=etag)
def iter_subscriptions(self, login=None, number=-1, etag=None): """Iterate over repositories subscribed to by ``login`` or the authenticated user. :param str login: (optional), name of user whose subscriptions you want to see :param int number: (optional), number of repositories to return. Default: -1 returns all repositories :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ if login: return self.user(login).iter_subscriptions() url = self._build_url('user', 'subscriptions') return self._iter(int(number), url, Repository, etag=etag)
def login(self, username=None, password=None, token=None, two_factor_callback=None): """Logs the user into GitHub for protected API calls. :param str username: login name :param str password: password for the login :param str token: OAuth token :param func two_factor_callback: (optional), function you implement to provide the Two Factor Authentication code to GitHub when necessary """ if username and password: self._session.basic_auth(username, password) elif token: self._session.token_auth(token) # The Session method handles None for free. self._session.two_factor_auth_callback(two_factor_callback)
def connect_to_github(): """ Authenticates and Connects to the Github repo """ # Authentication from os.path import isfile if isfile("github-logins.json"): with open("github-logins.json", "r") as loginfile: logins = json.load(loginfile) gh = login(username=logins["username"], password=logins["password"]) else: from getpass import getpass password = getpass() gh = login(username="yourusername", password=password) # Connect to the repo repo = gh.repository("ghostofgoes", "botnet-example") branch = repo.branch("master") return gh, repo, branch
def get_gh_api(): if not os.environ.get("OSXSTRAP_GITHUB_USERNAME"): username = click.prompt('Please enter your Github username') common.set_dotenv_key("OSXSTRAP_GITHUB_USERNAME", username) else: username = os.environ.get("OSXSTRAP_GITHUB_USERNAME") if not os.environ.get("OSXSTRAP_GITHUB_API_TOKEN"): token = click.prompt('Please create a Github access token by going to https://github.com/settings/tokens/new?scopes=gist&description=osxstrap+gist+cli and enter it here') common.set_dotenv_key("OSXSTRAP_GITHUB_API_TOKEN", token) else: token = os.environ.get("OSXSTRAP_GITHUB_API_TOKEN") gh = login(token=token) return gh files = { config_filename : { 'content': 'What... is the air-speed velocity of an unladen swallow?' } } gist = gh.create_gist('Answer this to cross the bridge', files, public=False) # gist == <Gist [gist-id]> print(gist.html_url) system_username = common.run('whoami', capture=True)
def _github_get_repository(self, conf: dict): """Create repository object according to configuration. """ try: import github3 except ImportError: raise common.InputError(self, "github3 module not found") github = None if conf.get("github_user") and conf.get("github_token"): try: github = github3.login(username=conf.get("github_user"), token=conf.get("github_token")) except Exception as err: raise common.InputError(self, "Github auth error: " + str(err)) if not github: github = github3.GitHub() repository = github.repository(conf["owner"], conf["repository"]) return repository
def check_authorization(self, access_token): """Check an authorization created by a registered application. OAuth applications can use this method to check token validity without hitting normal rate limits because of failed login attempts. If the token is valid, it will return True, otherwise it will return False. :returns: bool """ p = self.session.params auth = (p.get('client_id'), p.get('client_secret')) if access_token and auth: url = self._build_url('applications', str(auth[0]), 'tokens', str(access_token)) resp = self._get(url, auth=auth, params={ 'client_id': None, 'client_secret': None }) return self._boolean(resp, 200, 404) return False
def create_gist(self, description, files, public=True): """Create a new gist. If no login was provided, it will be anonymous. :param str description: (required), description of gist :param dict files: (required), file names with associated dictionaries for content, e.g. ``{'spam.txt': {'content': 'File contents ...'}}`` :param bool public: (optional), make the gist public if True :returns: :class:`Gist <github3.gists.Gist>` """ new_gist = {'description': description, 'public': public, 'files': files} url = self._build_url('gists') json = self._json(self._post(url, data=new_gist), 201) return self._instance_or_null(Gist, json)
def followed_by(self, username, number=-1, etag=None): """Iterate over users being followed by ``username``. .. versionadded:: 1.0.0 This replaces iter_following('sigmavirus24'). :param str username: (required), login of the user to check :param int number: (optional), number of people to return. Default: -1 returns all people you follow :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`User <github3.users.User>`\ s """ url = self._build_url('users', username, 'following') return self._iter(int(number), url, users.User, etag=etag)
def login(self, username=None, password=None, token=None, two_factor_callback=None): """Logs the user into GitHub for protected API calls. :param str username: login name :param str password: password for the login :param str token: OAuth token :param func two_factor_callback: (optional), function you implement to provide the Two Factor Authentication code to GitHub when necessary """ if username and password: self.session.basic_auth(username, password) elif token: self.session.token_auth(token) # The Session method handles None for free. self.session.two_factor_auth_callback(two_factor_callback)
def starred_by(self, username, sort=None, direction=None, number=-1, etag=None): """Iterate over repositories starred by ``username``. .. versionadded:: 1.0 This was split from ``iter_starred`` and requires the login parameter. :param str username: name of user whose stars you want to see :param str sort: (optional), either 'created' (when the star was created) or 'updated' (when the repository was last pushed to) :param str direction: (optional), either 'asc' or 'desc'. Default: 'desc' :param int number: (optional), number of repositories to return. Default: -1 returns all repositories :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of :class:`Repository <github3.repos.Repository>` """ params = {'sort': sort, 'direction': direction} self._remove_none(params) url = self._build_url('users', str(username), 'starred') return self._iter(int(number), url, Repository, params, etag)
def high_cli(repo_name, login, with_blog, as_list, role): """Extract mails from stargazers, collaborators and people involved with issues of given repository. """ passw = getpass.getpass() github = gh_login(login, passw) repo = github.repository(login, repo_name) role = [ROLES[k] for k in role] users = fetch_logins(role, repo) mails, blogs = contacts(github, users) if 'issue' in role: mails |= extract_mail(repo.issues(state='all')) # Print results sep = ', ' if as_list else '\n' print(sep.join(mails)) if with_blog: print(sep.join(blogs))
def status(sha): repo = login(token=token).repository('','') repo.create_status(sha,'pending') try: yield repo.create_status(sha,'success') except: repo.create_status(sha,'error')
def github_api(self): gh = login(settings.GITHUB_USERNAME, settings.GITHUB_PASSWORD) repo = gh.repository(self.owner, self.name) return repo
def create_status(build): if not build.plan.context: # skip setting Github status if the context field is empty return github = login(settings.GITHUB_USERNAME, settings.GITHUB_PASSWORD) repo = github.repository(build.repo.owner, build.repo.name) if build.get_status() == 'queued': state = 'pending' description = 'The build is queued' if build.get_status() == 'waiting': state = 'pending' description = 'The build is waiting for another build to complete' if build.get_status() == 'running': state = 'pending' description = 'The build is running' if build.get_status() == 'success': state = 'success' description = 'The build was successful' elif build.get_status() == 'error': state = 'error' description = 'An error occurred during build' elif build.get_status() == 'fail': state = 'failure' description = 'Tests failed' response = repo.create_status( sha=build.commit, state=state, target_url=build.get_external_url(), description=description, context=build.plan.context, ) return response
def get_client(): """Returns an authenticated github3 client.""" gh = github3.login( github_user(), os.environ['GITHUB_ACCESS_TOKEN']) gh.session.headers.update({ # Enable the preview API for merges and invitations 'Accept': 'application/vnd.github.polaris-preview+json' }) return gh
def get_repository(gh, data): """Gets the repository from hook event data.""" return gh.repository( data['repository']['owner']['login'], data['repository']['name'])
def get_pull_request(gh, data): """Gets the pull request from hook event data.""" return gh.pull_request( data['repository']['owner']['login'], data['repository']['name'], data['issue']['number'])
def is_pr_approved(pr): """True if the PR has been completely approved.""" review_requests = get_pr_requested_reviewers(pr) if not len(review_requests): return True approved_users = [ review['user']['login'] for review in get_pr_reviews(pr) if review['status'] == 'APPROVED'] requested_users = [user['login'] for user in review_requests] return set(approved_users) == set(requested_users)
def is_sha_green(repo, sha): url = 'https://api.github.com/repos/{}/{}/commits/{}/status'.format( repo.owner.login, repo.name, sha) result = repo.session.get(url).json() return result['state'] == 'success'
def api(self): """Return an authenticated GitHub API.""" return github3.login(token=self.access_token)
def author(self): """Extract the author's GitHub username from a release.""" return self.release.get('author', {}).get('login')
def extra_metadata(self): """Get extra metadata for file in repository.""" return get_extra_metadata( self.gh.api, self.repository['owner']['login'], self.repository['name'], self.release['tag_name'], )
def handle(self, *args, **options): github = github_login(token=settings.GITHUB_TOKEN) for index, package in enumerate(Project.objects.iterator()): logging.info("{} ...".format(package.name)) print("{} ...".format(package.name)) # Simple attempt to deal with Github rate limiting while True: if github.ratelimit_remaining < 50: sleep(120) break try: try: package.fetch_metadata(fetch_pypi=False) package.fetch_commits() except Exception as e: raise PackageUpdaterException(e, package.name) except PackageUpdaterException: pass # We've already caught the error so let's move on now sleep(5) message = "TODO - load logfile here" # TODO send_mail( subject="Package Updating complete", message=message, from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[x[1] for x in settings.ADMINS] )
def __init__(self): if settings.GITHUB_TOKEN: self.github = login(token=settings.GITHUB_TOKEN) else: self.github = GitHub()
def login(self, username='', password=''): """ Performs a login and sets the Github object via given credentials. If credentials are empty or incorrect then prompts user for credentials. Stores the authentication token in a CREDENTIALS_FILE used for future logins. Handles Two Factor Authentication. """ try: self.token = '' id = '' if not os.path.isfile('CREDENTIALS_FILE'): if(username == '' or password == ''): username = raw_input('Username: ') password = getpass.getpass('Password: ') note = 'GitHub Organization Stats App' note_url = 'http://software.llnl.gov/' scopes = ['user', 'repo'] auth = github3.authorize(username, password, scopes, note, note_url, two_factor_callback=self.prompt_2fa) self.token = auth.token id = auth.id with open('CREDENTIALS_FILE', 'w+') as fd: fd.write(self.token + '\n') fd.write(str(id)) fd.close() else: with open('CREDENTIALS_FILE', 'r') as fd: self.token = fd.readline().strip() id = fd.readline().strip() fd.close() print "Logging in." self.logged_in_gh = github3.login(token=self.token, two_factor_callback=self.prompt_2fa) self.logged_in_gh.user().to_json() except (ValueError, AttributeError, github3.models.GitHubError) as e: print 'Bad credentials. Try again.' self.login()
def get_stats(self, username='', password='', organization='llnl', force=True): """ Retrieves the traffic for the users of the given organization. Requires organization admin credentials token to access the data. """ date = str(datetime.date.today()) referrers_file_path = ('../github_stats_output/referrers.csv') views_file_path = ('../github_stats_output/views.csv') clones_file_path = ('../github_stats_output/clones.csv') if force or not os.path.isfile(file_path): my_github.login(username, password) calls_beginning = self.logged_in_gh.ratelimit_remaining + 1 print 'Rate Limit: ' + str(calls_beginning) my_github.get_org(organization) my_github.get_traffic() views_row_count = my_github.check_data_redundancy(file_path=views_file_path, dict_to_check=self.views) clones_row_count = my_github.check_data_redundancy(file_path=clones_file_path, dict_to_check=self.clones) my_github.write_to_file(referrers_file_path=referrers_file_path, views_file_path=views_file_path, clones_file_path=clones_file_path, views_row_count=views_row_count, clones_row_count=clones_row_count) my_github.write_json(dict_to_write=self.referrers_json, path_ending_type='traffic_popular_referrers') my_github.write_json(dict_to_write=self.views_json, path_ending_type='traffic_views') my_github.write_json(dict_to_write=self.clones_json, path_ending_type='traffic_clones') my_github.write_json(dict_to_write=self.releases_json, path_ending_type='releases') calls_remaining = self.logged_in_gh.ratelimit_remaining calls_used = calls_beginning - calls_remaining print ('Rate Limit Remaining: ' + str(calls_remaining) + '\nUsed ' + str(calls_used) + ' API calls.')
def login(self, username='', password=''): """ Performs a login and sets the Github object via given credentials. If credentials are empty or incorrect then prompts user for credentials. Stores the authentication token in a CREDENTIALS_FILE used for future logins. Handles Two Factor Authentication. """ try: self.token = '' id = '' if not os.path.isfile('CREDENTIALS_FILE_ADMIN'): if(username == '' or password == ''): username = raw_input('Username: ') password = getpass.getpass('Password: ') note = 'GitHub Organization Stats App' note_url = 'http://software.llnl.gov/' scopes = ['user', 'repo'] auth = github3.authorize(username, password, scopes, note, note_url, two_factor_callback=self.prompt_2fa) self.token = auth.token id = auth.id with open('CREDENTIALS_FILE_ADMIN', 'w+') as fd: fd.write(self.token + '\n') fd.write(str(id)) fd.close() else: with open('CREDENTIALS_FILE_ADMIN', 'r') as fd: self.token = fd.readline().strip() id = fd.readline().strip() fd.close() print "Logging in." self.logged_in_gh = github3.login(token=self.token, two_factor_callback=self.prompt_2fa) self.logged_in_gh.user().to_json() except (ValueError, AttributeError, github3.models.GitHubError) as e: print 'Bad credentials. Try again.' self.login()
def login(self, username='', password=''): """ Performs a login and sets the Github object via given credentials. If credentials are empty or incorrect then prompts user for credentials. Stores the authentication token in a CREDENTIALS_FILE used for future logins. Handles Two Factor Authentication. """ try: token = '' id = '' if not os.path.isfile('CREDENTIALS_FILE'): if(username == '' or password == ''): username = raw_input('Username: ') password = getpass.getpass('Password: ') note = 'GitHub Organization Stats App' note_url = 'http://software.llnl.gov/' scopes = ['user', 'repo'] auth = github3.authorize(username, password, scopes, note, note_url, two_factor_callback=self.prompt_2fa) token = auth.token id = auth.id with open('CREDENTIALS_FILE', 'w+') as fd: fd.write(token + '\n') fd.write(str(id)) fd.close() else: with open('CREDENTIALS_FILE', 'r') as fd: token = fd.readline().strip() id = fd.readline().strip() fd.close() print "Logging in." self.logged_in_gh = github3.login(token=token, two_factor_callback=self.prompt_2fa) self.logged_in_gh.user().to_json() except (ValueError, AttributeError, github3.models.GitHubError) as e: print 'Bad credentials. Try again.' self.login()
def generate_network(user=None, reset=False): """ Assemble the network connections for a given user """ token = collect_token() try: gh = login(token=token) root_user = gh.user(user) except Exception, e: # Failed to login using the token, github3.models.GitHubError raise e graph_nodes = [] graph_edges = [] username = user if user is not None else root_user.login if not is_cached(username_to_file(username)) or reset: graph_nodes.append(username) # @TODO: take care of the 'rate limit exceeding' if imposed try: for person in gh.iter_following(username): graph_nodes.append(str(person)) graph_edges.append((root_user.login, str(person))) for i in range(1, root_user.following): user = gh.user(graph_nodes[i]) user_following_edges = [(user.login, str(person)) for person in gh.iter_following( user) if str(person) in graph_nodes] graph_edges += user_following_edges except Exception, e: raise e generate_gml(username, graph_nodes, graph_edges, True) else: reuse_gml(username) return username
def main(): module = AnsibleModule( argument_spec=dict( repo=dict(required=True), user=dict(required=True), token=dict(required=True, no_log=True), action=dict(required=True, choices=['latest_release']), ), supports_check_mode=True ) if not HAS_GITHUB_API: module.fail_json(msg='Missing requried github3 module (check docs or install with: pip install github3)') repo = module.params['repo'] user = module.params['user'] login_token = module.params['token'] action = module.params['action'] # login to github try: gh = github3.login(token=str(login_token)) # test if we're actually logged in gh.me() except github3.AuthenticationFailed: e = get_exception() module.fail_json(msg='Failed to connect to Github: %s' % e) repository = gh.repository(str(user), str(repo)) if not repository: module.fail_json(msg="Repository %s/%s doesn't exist" % (user, repo)) if action == 'latest_release': release = repository.latest_release() if release: module.exit_json(tag=release.tag_name) else: module.exit_json(tag=None)
def __init__(self, username, password, completed_labels=GITHUB_COMPLETED_LABELS): """Initialize attributes.""" log.debug("GitHubPytestPlugin initialized") # initialize issue cache self._issue_cache = {} # Process parameters self.username = username self.password = password self.completed_labels = GITHUB_COMPLETED_LABELS # Initialize github api connection self.api = github3.login(self.username, self.password)
def get_github_client(token=None): ghc = github3.login(token=token or settings.GITHUB_TOKEN) assert ghc.__class__ == github3.github.GitHub ghc.__class__ = GitHub return ghc
def _repr(self): return '<RepositoryInvitation [{} by {}]>'.format( self.repository.full_name, self.inviter.login, )
def __init__(self): self._pandoc = find_executable('pandoc') if self._pandoc is None: sys.stderr.write("ERROR: pandoc not found on PATH.\n") raise SystemExit(1) self._gh_token = os.environ.get('GITHUB_TOKEN', None) if self._gh_token is None: sys.stderr.write("ERROR: GITHUB_TOKEN env var must be set\n") raise SystemExit(1) self._gh = login(token=self._gh_token) self._repo = self._gh.repository('jantman', 'biweeklybudget')