From 519d3185412b82b5d1eac3cfa2daf674ffca732a Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Thu, 20 Oct 2016 12:50:24 +0200 Subject: [PATCH 1/3] Install github3.py via pip --- gitsome/config.py | 4 +- gitsome/github.py | 4 +- gitsome/lib/github3/__about__.py | 16 - gitsome/lib/github3/__init__.py | 50 - gitsome/lib/github3/api.py | 648 ------- gitsome/lib/github3/auths.py | 121 -- gitsome/lib/github3/decorators.py | 100 -- gitsome/lib/github3/events.py | 186 -- gitsome/lib/github3/exceptions.py | 127 -- gitsome/lib/github3/gists/__init__.py | 18 - gitsome/lib/github3/gists/comment.py | 42 - gitsome/lib/github3/gists/file.py | 46 - gitsome/lib/github3/gists/gist.py | 256 --- gitsome/lib/github3/gists/history.py | 67 - gitsome/lib/github3/git.py | 248 --- gitsome/lib/github3/github.py | 1770 ------------------ gitsome/lib/github3/issues/__init__.py | 37 - gitsome/lib/github3/issues/comment.py | 55 - gitsome/lib/github3/issues/event.py | 82 - gitsome/lib/github3/issues/issue.py | 339 ---- gitsome/lib/github3/issues/label.py | 58 - gitsome/lib/github3/issues/milestone.py | 99 - gitsome/lib/github3/licenses.py | 35 - gitsome/lib/github3/models.py | 419 ----- gitsome/lib/github3/notifications.py | 149 -- gitsome/lib/github3/null.py | 55 - gitsome/lib/github3/orgs.py | 617 ------- gitsome/lib/github3/pulls.py | 447 ----- gitsome/lib/github3/repos/__init__.py | 12 - gitsome/lib/github3/repos/branch.py | 92 - gitsome/lib/github3/repos/comment.py | 69 - gitsome/lib/github3/repos/commit.py | 112 -- gitsome/lib/github3/repos/comparison.py | 84 - gitsome/lib/github3/repos/contents.py | 170 -- gitsome/lib/github3/repos/deployment.py | 125 -- gitsome/lib/github3/repos/hook.py | 110 -- gitsome/lib/github3/repos/issue_import.py | 35 - gitsome/lib/github3/repos/pages.py | 55 - gitsome/lib/github3/repos/release.py | 281 --- gitsome/lib/github3/repos/repo.py | 1998 --------------------- gitsome/lib/github3/repos/stats.py | 42 - gitsome/lib/github3/repos/status.py | 41 - gitsome/lib/github3/repos/tag.py | 34 - gitsome/lib/github3/search/__init__.py | 8 - gitsome/lib/github3/search/code.py | 29 - gitsome/lib/github3/search/issue.py | 19 - gitsome/lib/github3/search/repository.py | 19 - gitsome/lib/github3/search/user.py | 19 - gitsome/lib/github3/session.py | 153 -- gitsome/lib/github3/structs.py | 154 -- gitsome/lib/github3/users.py | 498 ----- gitsome/lib/github3/utils.py | 103 -- setup.py | 9 +- tests/mock_github_api.py | 4 +- 54 files changed, 8 insertions(+), 10362 deletions(-) delete mode 100644 gitsome/lib/github3/__about__.py delete mode 100644 gitsome/lib/github3/__init__.py delete mode 100644 gitsome/lib/github3/api.py delete mode 100644 gitsome/lib/github3/auths.py delete mode 100644 gitsome/lib/github3/decorators.py delete mode 100644 gitsome/lib/github3/events.py delete mode 100644 gitsome/lib/github3/exceptions.py delete mode 100644 gitsome/lib/github3/gists/__init__.py delete mode 100644 gitsome/lib/github3/gists/comment.py delete mode 100644 gitsome/lib/github3/gists/file.py delete mode 100644 gitsome/lib/github3/gists/gist.py delete mode 100644 gitsome/lib/github3/gists/history.py delete mode 100644 gitsome/lib/github3/git.py delete mode 100644 gitsome/lib/github3/github.py delete mode 100644 gitsome/lib/github3/issues/__init__.py delete mode 100644 gitsome/lib/github3/issues/comment.py delete mode 100644 gitsome/lib/github3/issues/event.py delete mode 100644 gitsome/lib/github3/issues/issue.py delete mode 100644 gitsome/lib/github3/issues/label.py delete mode 100644 gitsome/lib/github3/issues/milestone.py delete mode 100644 gitsome/lib/github3/licenses.py delete mode 100644 gitsome/lib/github3/models.py delete mode 100644 gitsome/lib/github3/notifications.py delete mode 100644 gitsome/lib/github3/null.py delete mode 100644 gitsome/lib/github3/orgs.py delete mode 100644 gitsome/lib/github3/pulls.py delete mode 100644 gitsome/lib/github3/repos/__init__.py delete mode 100644 gitsome/lib/github3/repos/branch.py delete mode 100644 gitsome/lib/github3/repos/comment.py delete mode 100644 gitsome/lib/github3/repos/commit.py delete mode 100644 gitsome/lib/github3/repos/comparison.py delete mode 100644 gitsome/lib/github3/repos/contents.py delete mode 100644 gitsome/lib/github3/repos/deployment.py delete mode 100644 gitsome/lib/github3/repos/hook.py delete mode 100644 gitsome/lib/github3/repos/issue_import.py delete mode 100644 gitsome/lib/github3/repos/pages.py delete mode 100644 gitsome/lib/github3/repos/release.py delete mode 100644 gitsome/lib/github3/repos/repo.py delete mode 100644 gitsome/lib/github3/repos/stats.py delete mode 100644 gitsome/lib/github3/repos/status.py delete mode 100644 gitsome/lib/github3/repos/tag.py delete mode 100644 gitsome/lib/github3/search/__init__.py delete mode 100644 gitsome/lib/github3/search/code.py delete mode 100644 gitsome/lib/github3/search/issue.py delete mode 100644 gitsome/lib/github3/search/repository.py delete mode 100644 gitsome/lib/github3/search/user.py delete mode 100644 gitsome/lib/github3/session.py delete mode 100644 gitsome/lib/github3/structs.py delete mode 100644 gitsome/lib/github3/users.py delete mode 100644 gitsome/lib/github3/utils.py diff --git a/gitsome/config.py b/gitsome/config.py index 95db91d..6c560a2 100644 --- a/gitsome/config.py +++ b/gitsome/config.py @@ -23,8 +23,8 @@ from requests.packages.urllib3.exceptions import InsecureRequestWarning from .compat import configparser -from .lib.github3 import authorize, enterprise_login, login -from .lib.github3.exceptions import AuthenticationFailed, UnprocessableEntity +from github3 import authorize, enterprise_login, login +from github3.exceptions import AuthenticationFailed, UnprocessableEntity class Config(object): diff --git a/gitsome/github.py b/gitsome/github.py index b16f69a..85f36a4 100644 --- a/gitsome/github.py +++ b/gitsome/github.py @@ -22,8 +22,8 @@ import urllib import webbrowser -from .lib.github3 import null -from .lib.github3.exceptions import AuthenticationFailed, UnprocessableEntity +from github3 import null +from github3.exceptions import AuthenticationFailed, UnprocessableEntity from .lib.img2txt import img2txt import click import feedparser diff --git a/gitsome/lib/github3/__about__.py b/gitsome/lib/github3/__about__.py deleted file mode 100644 index feb456a..0000000 --- a/gitsome/lib/github3/__about__.py +++ /dev/null @@ -1,16 +0,0 @@ -"""The module that holds much of the metadata about github3.py.""" -__package_name__ = 'github3.py' -__title__ = 'github3' -__author__ = 'Ian Cordasco' -__author_email__ = 'graffatcolmingov@gmail.com' -__license__ = 'Modified BSD' -__copyright__ = 'Copyright 2012-2016 Ian Cordasco' -__version__ = '1.0.0a4' -__version_info__ = tuple(int(i) for i in __version__.split('.') if i.isdigit()) -__url__ = 'http://github3.readthedocs.org' - -__all__ = ( - '__package_name__', '__title__', '__author__', '__author_email__', - '__license__', '__copyright__', '__version__', '__version_info__', - '__url__', -) diff --git a/gitsome/lib/github3/__init__.py b/gitsome/lib/github3/__init__.py deleted file mode 100644 index 8ac968c..0000000 --- a/gitsome/lib/github3/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3 -======= - -See http://github3.readthedocs.org/ for documentation. - -:copyright: (c) 2012-2016 by Ian Cordasco -:license: Modified BSD, see LICENSE for more details - -""" - -from .__about__ import ( - __package_name__, __title__, __author__, __author_email__, - __license__, __copyright__, __version__, __version_info__, - __url__, -) -from .api import ( - authorize, login, enterprise_login, emojis, gist, gitignore_template, - create_gist, issue, markdown, octocat, organization, pull_request, - followers_of, followed_by, public_gists, gists_by, issues_on, - gitignore_templates, all_repositories, all_users, all_events, - organizations_with, repositories_by, starred_by, subscriptions_for, - rate_limit, repository, search_code, search_repositories, search_users, - search_issues, user, zen -) -from .github import GitHub, GitHubEnterprise, GitHubStatus -from .exceptions import ( - BadRequest, AuthenticationFailed, ForbiddenError, GitHubError, - MethodNotAllowed, NotFoundError, ServerError, NotAcceptable, - UnprocessableEntity -) - -__all__ = ( - 'AuthenticationFailed', 'BadRequest', 'ForbiddenError', 'GitHub', - 'GitHubEnterprise', 'GitHubError', 'GitHubStatus', - 'MethodNotAllowed', 'NotAcceptable', 'NotFoundError', 'ServerError', - 'UnprocessableEntity', 'authorize', 'login', 'enterprise_login', 'emojis', - 'gist', 'gitignore_template', 'create_gist', 'issue', 'markdown', - 'octocat', 'organization', 'pull_request', 'followers_of', 'followed_by', - 'public_gists', 'gists_by', 'issues_on', 'gitignore_templates', - 'all_repositories', 'all_users', 'all_events', 'organizations_with', - 'repositories_by', 'starred_by', 'subscriptions_for', 'rate_limit', - 'repository', 'search_code', 'search_repositories', 'search_users', - 'search_issues', 'user', 'zen', - # Metadata attributes - '__package_name__', '__title__', '__author__', '__author_email__', - '__license__', '__copyright__', '__version__', '__version_info__', - '__url__', -) diff --git a/gitsome/lib/github3/api.py b/gitsome/lib/github3/api.py deleted file mode 100644 index 68549cc..0000000 --- a/gitsome/lib/github3/api.py +++ /dev/null @@ -1,648 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.api -=========== - -:copyright: (c) 2012-2014 by Ian Cordasco -:license: Modified BSD, see LICENSE for more details - -""" - -from .github import GitHub, GitHubEnterprise - -gh = GitHub() - - -def authorize(username, password, scopes, note='', note_url='', client_id='', - client_secret='', two_factor_callback=None): - """Obtain an authorization token for the GitHub API. - - :param str username: (required) - :param str password: (required) - :param list scopes: (required), areas you want this token to apply to, - i.e., 'gist', 'user' - :param str note: (optional), note about the authorization - :param str note_url: (optional), url for the application - :param str client_id: (optional), 20 character OAuth client key for which - to create a token - :param str client_secret: (optional), 40 character OAuth client secret for - which to create the token - :param func two_factor_callback: (optional), function to call when a - Two-Factor Authentication code needs to be provided by the user. - :returns: :class:`Authorization ` - - """ - gh = GitHub() - gh.login(two_factor_callback=two_factor_callback) - return gh.authorize(username, password, scopes, note, note_url, client_id, - client_secret) - - -def login(username=None, password=None, token=None, two_factor_callback=None): - """Construct and return an authenticated GitHub session. - - .. note:: - - To allow you to specify either a username and password combination or - a token, none of the parameters are required. If you provide none of - them, you will receive a - :class:`NullObject ` - - :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 - :returns: :class:`GitHub ` - - """ - g = None - - if (username and password) or token: - g = GitHub() - g.login(username, password, token, two_factor_callback) - - return g - - -def enterprise_login(username=None, password=None, token=None, url=None, - two_factor_callback=None, verify=True): - """Construct and return an authenticated GitHubEnterprise session. - - .. note:: - - To allow you to specify either a username and password combination or - a token, none of the parameters are required. If you provide none of - them, you will receive a - :class:`NullObject ` - - :param str username: login name - :param str password: password for the login - :param str token: OAuth token - :param str url: URL of a GitHub Enterprise instance - :param func two_factor_callback: (optional), function you implement to - provide the Two Factor Authentication code to GitHub when necessary - :returns: :class:`GitHubEnterprise ` - - """ - if not url: - raise ValueError('GitHub Enterprise requires you provide the URL of' - ' the instance') - - g = None - - if (username and password) or token: - g = GitHubEnterprise(url, verify=verify) - g.login(username, password, token, two_factor_callback) - - return g - - -def emojis(): - return gh.emojis() -emojis.__doc__ = gh.emojis.__doc__ - - -def gist(id_num): - """Retrieve the gist identified by ``id_num``. - - :param int id_num: (required), unique id of the gist - :returns: :class:`Gist ` - - """ - return gh.gist(id_num) - - -def gitignore_template(language): - """Return the template for language. - - :returns: str - - """ - return gh.gitignore_template(language) - - -def gitignore_templates(): - """Return the list of available templates. - - :returns: list of template names - - """ - return gh.gitignore_templates() - - -def all_repositories(number=-1, etag=None): - """Iterate over every repository in the order they were created. - - :param int number: (optional), number of repositories to return. - Default: -1, returns all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Repository ` - - """ - return gh.all_repositories(number, etag) - - -def all_users(number=-1, etag=None): - """Iterate over every user in the order they signed up for GitHub. - - :param int number: (optional), number of users to return. Default: -1, - returns all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`User ` - - """ - return gh.all_users(number, etag) - - -def all_events(number=-1, etag=None): - """Iterate over public events. - - :param int number: (optional), number of events to return. Default: -1 - returns all available events - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Event ` - - """ - return gh.all_events(number, etag) - - -def followers_of(username, number=-1, etag=None): - """List the followers of ``username``. - - :param str username: (required), login of the person to list the followers - of - :param int number: (optional), number of followers to return, Default: -1, - return all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`User ` - - """ - return gh.followers_of(username, number, etag) if username else [] - - -def followed_by(username, number=-1, etag=None): - """List the people ``username`` follows. - - :param str username: (required), login of the user - :param int number: (optional), number of users being followed by username - to return. Default: -1, return all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`User ` - - """ - return gh.followed_by(username, number, etag) if username else [] - - -def public_gists(number=-1, etag=None): - """Iterate over all public gists. - - .. versionadded:: 1.0 - - This was split from ``github3.iter_gists`` before 1.0. - - :param int number: (optional), number of gists to return. Default: -1, - return all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Gist ` - - """ - return gh.public_gists(number, etag) - - -def gists_by(username, number=-1, etag=None): - """Iterate over gists created by the provided username. - - :param str username: (required), if provided, get the gists for this user - instead of the authenticated user. - :param int number: (optional), number of gists to return. Default: -1, - return all of them - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Gist ` - - """ - if username: - return gh.gists_by(username, number, etag) - return iter([]) - - -def issues_on(owner, repository, milestone=None, state=None, assignee=None, - mentioned=None, labels=None, sort=None, direction=None, - since=None, number=-1, etag=None): - """Iterate over issues on owner/repository. - - .. versionchanged:: 0.9.0 - - - The ``state`` parameter now accepts 'all' in addition to 'open' - and 'closed'. - - :param str owner: login of the owner of the repository - :param str repository: name of the repository - :param int milestone: None, '*', or ID of milestone - :param str state: accepted values: ('all', 'open', 'closed') - api-default: 'open' - :param str assignee: '*' or login of the user - :param str mentioned: login of the user - :param str labels: comma-separated list of label names, e.g., - 'bug,ui,@high' - :param str sort: accepted values: ('created', 'updated', 'comments') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - api-default: desc - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :param int number: (optional), number of issues to return. - Default: -1 returns all issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Issue `\ s - - """ - if owner and repository: - return gh.issues_on(owner, repository, milestone, state, assignee, - mentioned, labels, sort, direction, since, number, - etag) - return iter([]) - - -def organizations_with(username, number=-1, etag=None): - """List the organizations with ``username`` as a member. - - :param str username: (required), login of the user - :param int number: (optional), number of orgs to return. Default: -1, - return all of the issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of - :class:`Organization ` - - """ - return gh.organizations_with(username, number, etag) - - -def repositories_by(username, type=None, sort=None, direction=None, number=-1, - etag=None): - """List public repositories for the specified ``username``. - - .. versionadded:: 0.6 - - .. note:: This replaces github3.iter_repos - - :param str username: (required) - :param str type: (optional), accepted values: - ('all', 'owner', 'member') - API default: 'all' - :param str sort: (optional), accepted values: - ('created', 'updated', 'pushed', 'full_name') - API default: 'created' - :param str direction: (optional), accepted values: - ('asc', 'desc'), API default: 'asc' when using 'full_name', - 'desc' otherwise - :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 ` - objects - - """ - if login: - return gh.repositories_by(username, type, sort, direction, number, - etag) - return iter([]) - - -def starred_by(username, number=-1, etag=None): - """Iterate over repositories starred by ``username``. - - :param str username: (optional), name of user whose stars 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 ` - - """ - return gh.starred_by(username, number, etag) - - -def subscriptions_for(username, number=-1, etag=None): - """Iterate over repositories subscribed to by ``username``. - - :param str username: 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 ` - - """ - return gh.subscriptions_for(username, number, etag) - - -def create_gist(description, files): - """Create an anonymous public gist. - - :param str description: (required), short description of the gist - :param dict files: (required), file names with associated - dictionaries for content, e.g. - {'spam.txt': {'content': 'File contents ...'}} - :returns: :class:`Gist ` - - """ - return gh.create_gist(description, files) # (No coverage) - - -def issue(owner, repository, number): - """Anonymously gets issue :number on :owner/:repository. - - :param str owner: (required), repository owner - :param str repository: (required), repository name - :param int number: (required), issue number - :returns: :class:`Issue ` - - """ - return gh.issue(owner, repository, number) - - -def markdown(text, mode='', context='', raw=False): - """Render an arbitrary markdown document. - - :param str text: (required), the text of the document to render - :param str mode: (optional), 'markdown' or 'gfm' - :param str context: (optional), only important when using mode 'gfm', - this is the repository to use as the context for the rendering - :param bool raw: (optional), renders a document like a README.md, no gfm, - no context - :returns: str -- HTML formatted text - - """ - return gh.markdown(text, mode, context, raw) - - -def octocat(say=None): - """Return an easter egg from the API. - - :params str say: (optional), pass in what you'd like Octocat to say - :returns: ascii art of Octocat - - """ - return gh.octocat(say) - - -def organization(name): - return gh.organization(name) -organization.__doc__ = gh.organization.__doc__ - - -def pull_request(owner, repository, number): - """Anonymously retrieve pull request :number on :owner/:repository. - - :param str owner: (required), repository owner - :param str repository: (required), repository name - :param int number: (required), pull request number - :returns: :class:`PullRequest ` - - """ - return gh.pull_request(owner, repository, number) - - -def rate_limit(): - return gh.rate_limit() -rate_limit.__doc__ = gh.rate_limit.__doc__ - - -def repository(owner, repository): - return gh.repository(owner, repository) -repository.__doc__ = gh.repository.__doc__ - - -def search_code(query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find code via the code search API. - - .. warning:: - - You will only be able to make 5 calls with this or other search - functions. To raise the rate-limit on this set of endpoints, create an - authenticated :class:`GitHub ` Session with - ``login``. - - The query can contain any combination of the following supported - qualifiers: - - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the file contents, the file path, or - both. - - ``language`` Searches code based on the language it’s written in. - - ``fork`` Specifies that code from forked repositories should be - searched. Repository forks will not be searchable unless the fork - has more stars than the parent repository. - - ``size`` Finds files that match a certain size (in bytes). - - ``path`` Specifies the path that the resulting file must be at. - - ``extension`` Matches files with a certain extension. - - ``user`` or ``repo`` Limits searches to a specific user or - repository. - - For more information about these qualifiers, see: http://git.io/-DvAuA - - :param str query: (required), a valid query as described above, e.g., - ``addClass in:file language:js repo:jquery/jquery`` - :param str sort: (optional), how the results should be sorted; - option(s): ``indexed``; default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/4ct1eQ for more information - :param int number: (optional), number of repositories to return. - Default: -1, returns all available repositories - :param str etag: (optional), previous ETag header value - :return: generator of :class:`CodeSearchResult - ` - """ - return gh.search_code(query, sort, order, per_page, text_match, number, - etag) - - -def search_issues(query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find issues by state and keyword - - .. warning:: - - You will only be able to make 5 calls with this or other search - functions. To raise the rate-limit on this set of endpoints, create an - authenticated :class:`GitHub ` Session with - ``login``. - - The query can contain any combination of the following supported - qualifers: - - - ``type`` With this qualifier you can restrict the search to issues or - pull request only. - - ``in`` Qualifies which fields are searched. With this qualifier you can - restrict the search to just the title, body, comments, or any - combination of these. - - ``author`` Finds issues created by a certain user. - - ``assignee`` Finds issues that are assigned to a certain user. - - ``mentions`` Finds issues that mention a certain user. - - ``commenter`` Finds issues that a certain user commented on. - - ``involves`` Finds issues that were either created by a certain user, - assigned to that user, mention that user, or were commented on by that - user. - - ``state`` Filter issues based on whether they’re open or closed. - - ``labels`` Filters issues based on their labels. - - ``language`` Searches for issues within repositories that match a - certain language. - - ``created`` or ``updated`` Filters issues based on times of creation, or - when they were last updated. - - ``comments`` Filters issues based on the quantity of comments. - - ``user`` or ``repo`` Limits searches to a specific user or repository. - - For more information about these qualifiers, see: http://git.io/d1oELA - - :param str query: (required), a valid query as described above, e.g., - ``windows label:bug`` - :param str sort: (optional), how the results should be sorted; - options: ``created``, ``comments``, ``updated``; default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/QLQuSQ for more information - :param int number: (optional), number of issues to return. - Default: -1, returns all available issues - :param str etag: (optional), previous ETag header value - :return: generator of :class:`IssueSearchResult - ` - """ - return gh.search_issues(query, sort, order, per_page, text_match, - number, etag) - - -def search_repositories(query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find repositories via various criteria. - - .. warning:: - - You will only be able to make 5 calls with this or other search - functions. To raise the rate-limit on this set of endpoints, create an - authenticated :class:`GitHub ` Session with - ``login``. - - The query can contain any combination of the following supported - qualifers: - - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the repository name, description, - readme, or any combination of these. - - ``size`` Finds repositories that match a certain size (in - kilobytes). - - ``forks`` Filters repositories based on the number of forks, and/or - whether forked repositories should be included in the results at - all. - - ``created`` or ``pushed`` Filters repositories based on times of - creation, or when they were last updated. Format: ``YYYY-MM-DD``. - Examples: ``created:<2011``, ``pushed:<2013-02``, - ``pushed:>=2013-03-06`` - - ``user`` or ``repo`` Limits searches to a specific user or - repository. - - ``language`` Searches repositories based on the language they're - written in. - - ``stars`` Searches repositories based on the number of stars. - - For more information about these qualifiers, see: http://git.io/4Z8AkA - - :param str query: (required), a valid query as described above, e.g., - ``tetris language:assembly`` - :param str sort: (optional), how the results should be sorted; - options: ``stars``, ``forks``, ``updated``; default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/4ct1eQ for more information - :param int number: (optional), number of repositories to return. - Default: -1, returns all available repositories - :param str etag: (optional), previous ETag header value - :return: generator of :class:`Repository ` - """ - return gh.search_repositories(query, sort, order, per_page, text_match, - number, etag) - - -def search_users(query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find users via the Search API. - - .. warning:: - - You will only be able to make 5 calls with this or other search - functions. To raise the rate-limit on this set of endpoints, create an - authenticated :class:`GitHub ` Session with - ``login``. - - The query can contain any combination of the following supported - qualifers: - - - - ``type`` With this qualifier you can restrict the search to just - personal accounts or just organization accounts. - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the username, public email, full - name, or any combination of these. - - ``repos`` Filters users based on the number of repositories they - have. - - ``location`` Filter users by the location indicated in their - profile. - - ``language`` Search for users that have repositories that match a - certain language. - - ``created`` Filter users based on when they joined. - - ``followers`` Filter users based on the number of followers they - have. - - For more information about these qualifiers see: http://git.io/wjVYJw - - :param str query: (required), a valid query as described above, e.g., - ``tom repos:>42 followers:>1000`` - :param str sort: (optional), how the results should be sorted; - options: ``followers``, ``repositories``, or ``joined``; default: - best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/_V1zRwa for more information - :param int number: (optional), number of search results to return; - Default: -1 returns all available - :param str etag: (optional), ETag header value of the last request. - :return: generator of :class:`UserSearchResult - ` - """ - return gh.search_users(query, sort, order, per_page, text_match, number, - etag) - - -def user(username): - return gh.user(username) -user.__doc__ = gh.user.__doc__ - - -def zen(): - """Return a quote from the Zen of GitHub. Yet another API Easter Egg. - - :returns: str - - """ - return gh.zen() diff --git a/gitsome/lib/github3/auths.py b/gitsome/lib/github3/auths.py deleted file mode 100644 index 5292aae..0000000 --- a/gitsome/lib/github3/auths.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.auths -============= - -This module contains the Authorization object. - -""" -from __future__ import unicode_literals - -from .decorators import requires_basic_auth -from .models import GitHubCore - - -class Authorization(GitHubCore): - - """The :class:`Authorization ` object. - - Two authorization instances can be checked like so:: - - a1 == a2 - a1 != a2 - - And is equivalent to:: - - a1.id == a2.id - a1.id != a2.id - - See also: http://developer.github.com/v3/oauth/#oauth-authorizations-api - - """ - - def _update_attributes(self, auth): - self._api = auth.get('url') - #: Details about the application (name, url) - self.app = auth.get('app', {}) - #: Returns the Authorization token - self.token = auth.get('token', '') - #: App name - self.name = self.app.get('name', '') - #: URL about the note - self.note_url = auth.get('note_url') or '' - #: Note about the authorization - self.note = auth.get('note') or '' - #: List of scopes this applies to - self.scopes = auth.get('scopes', []) - #: Unique id of the authorization - self.id = auth.get('id', 0) - #: datetime object representing when the authorization was created. - self.created_at = self._strptime(auth.get('created_at')) - #: datetime object representing when the authorization was updated. - self.updated_at = self._strptime(auth.get('updated_at')) - - def _repr(self): - return ''.format(self.name) - - def _update(self, scopes_data, note, note_url): - """Helper for add_scopes, replace_scopes, remove_scopes.""" - if note is not None: - scopes_data['note'] = note - if note_url is not None: - scopes_data['note_url'] = note_url - json = self._json(self._post(self._api, data=scopes_data), 200) - - if json: - self._update_attributes(json) - return True - - return False - - @requires_basic_auth - def add_scopes(self, scopes, note=None, note_url=None): - """Adds the scopes to this authorization. - - .. versionadded:: 1.0 - - :param list scopes: Adds these scopes to the ones present on this - authorization - :param str note: (optional), Note about the authorization - :param str note_url: (optional), URL to link to when the user views - the authorization - :returns: True if successful, False otherwise - :rtype: bool - """ - return self._update({'add_scopes': scopes}, note, note_url) - - @requires_basic_auth - def delete(self): - """Delete this authorization.""" - return self._boolean(self._delete(self._api), 204, 404) - - @requires_basic_auth - def remove_scopes(self, scopes, note=None, note_url=None): - """Remove the scopes from this authorization. - - .. versionadded:: 1.0 - - :param list scopes: Remove these scopes from the ones present on this - authorization - :param str note: (optional), Note about the authorization - :param str note_url: (optional), URL to link to when the user views - the authorization - :returns: True if successful, False otherwise - :rtype: bool - """ - return self._update({'rm_scopes': scopes}, note, note_url) - - @requires_basic_auth - def replace_scopes(self, scopes, note=None, note_url=None): - """Replace the scopes on this authorization. - - .. versionadded:: 1.0 - - :param list scopes: Use these scopes instead of the previous list - :param str note: (optional), Note about the authorization - :param str note_url: (optional), URL to link to when the user views - the authorization - :returns: True if successful, False otherwise - :rtype: bool - """ - return self._update({'scopes': scopes}, note, note_url) diff --git a/gitsome/lib/github3/decorators.py b/gitsome/lib/github3/decorators.py deleted file mode 100644 index 474098f..0000000 --- a/gitsome/lib/github3/decorators.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.decorators -================== - -This module provides decorators to the rest of the library - -""" - -from functools import wraps -from requests.models import Response -import os - -try: # (No coverage) - # python2 - from StringIO import StringIO # (No coverage) -except ImportError: # (No coverage) - # python3 - from io import BytesIO as StringIO - - -class RequestsStringIO(StringIO): - def read(self, n=-1, *args, **kwargs): - # StringIO is an old-style class, so can't use super - return StringIO.read(self, n) - - -def requires_auth(func): - """Decorator to note which object methods require authorization.""" - @wraps(func) - def auth_wrapper(self, *args, **kwargs): - if hasattr(self, 'session') and self.session.has_auth(): - return func(self, *args, **kwargs) - else: - from .exceptions import error_for - # Mock a 401 response - r = generate_fake_error_response( - '{"message": "Requires authentication"}' - ) - raise error_for(r) - return auth_wrapper - - -def requires_basic_auth(func): - """Specific (basic) authentication decorator. - - This is used to note which object methods require username/password - authorization and won't work with token based authorization. - - """ - @wraps(func) - def auth_wrapper(self, *args, **kwargs): - if hasattr(self, 'session') and self.session.auth: - return func(self, *args, **kwargs) - else: - from .exceptions import error_for - # Mock a 401 response - r = generate_fake_error_response( - '{"message": "Requires username/password authentication"}' - ) - raise error_for(r) - return auth_wrapper - - -def requires_app_credentials(func): - """Require client_id and client_secret to be associated. - - This is used to note and enforce which methods require a client_id and - client_secret to be used. - - """ - @wraps(func) - def auth_wrapper(self, *args, **kwargs): - client_id, client_secret = self.session.retrieve_client_credentials() - if client_id and client_secret: - return func(self, *args, **kwargs) - else: - from .exceptions import error_for - # Mock a 401 response - r = generate_fake_error_response( - '{"message": "Requires username/password authentication"}' - ) - raise error_for(r) - - return auth_wrapper - - -def generate_fake_error_response(msg, status_code=401, encoding='utf-8'): - r = Response() - r.status_code = status_code - r.encoding = encoding - r.raw = RequestsStringIO(msg.encode()) - r._content_consumed = True - r._content = r.raw.read() - return r - -# Use mock decorators when generating documentation, so all functino signatures -# are displayed correctly -if os.getenv('GENERATING_DOCUMENTATION', None) == 'github3': - requires_auth = requires_basic_auth = lambda x: x # noqa # (No coverage) diff --git a/gitsome/lib/github3/events.py b/gitsome/lib/github3/events.py deleted file mode 100644 index 0ef3986..0000000 --- a/gitsome/lib/github3/events.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.events -============== - -This module contains the class(es) related to Events - -""" -from __future__ import unicode_literals - -from .models import GitHubCore - - -class Event(GitHubCore): - - """The :class:`Event ` object. It structures and handles the data - returned by via the `Events `_ - section of the GitHub API. - - Two events can be compared like so:: - - e1 == e2 - e1 != e2 - - And that is equivalent to:: - - e1.id == e2.id - e1.id != e2.id - - """ - - def _update_attributes(self, event): - from .users import User - from .orgs import Organization - #: :class:`User ` object representing the actor. - self.actor = User(event.get('actor')) if event.get('actor') else None - #: datetime object representing when the event was created. - self.created_at = self._strptime(event.get('created_at')) - #: Unique id of the event - self.id = event.get('id') - #: List all possible types of Events - self.org = None - if event.get('org'): - self.org = Organization(event.get('org')) - #: Event type http://developer.github.com/v3/activity/events/types/ - self.type = event.get('type') - handler = _payload_handlers.get(self.type, identity) - #: Dictionary with the payload. Payload structure is defined by type_. - # _type: http://developer.github.com/v3/events/types - self.payload = handler(event.get('payload'), self) - #: Return ``tuple(owner, repository_name)`` - self.repo = event.get('repo') - if self.repo is not None: - self.repo = tuple(self.repo['name'].split('/')) - #: Indicates whether the Event is public or not. - self.public = event.get('public') - - def _repr(self): - return ''.format(self.type[:-5]) - - @staticmethod - def list_types(): - """List available payload types.""" - return sorted(_payload_handlers.keys()) - - -def _commitcomment(payload, session): - from .repos.comment import RepoComment - if payload.get('comment'): - payload['comment'] = RepoComment(payload['comment'], session) - return payload - - -def _follow(payload, session): - from .users import User - if payload.get('target'): - payload['target'] = User(payload['target'], session) - return payload - - -def _forkev(payload, session): - from .repos import Repository - if payload.get('forkee'): - payload['forkee'] = Repository(payload['forkee'], session) - return payload - - -def _gist(payload, session): - from .gists import Gist - if payload.get('gist'): - payload['gist'] = Gist(payload['gist'], session) - return payload - - -def _issuecomm(payload, session): - from .issues import Issue - from .issues.comment import IssueComment - if payload.get('issue'): - payload['issue'] = Issue(payload['issue'], session) - if payload.get('comment'): - payload['comment'] = IssueComment(payload['comment'], session) - return payload - - -def _issueevent(payload, session): - from .issues import Issue - if payload.get('issue'): - payload['issue'] = Issue(payload['issue'], session) - return payload - - -def _member(payload, session): - from .users import User - if payload.get('member'): - payload['member'] = User(payload['member'], session) - return payload - - -def _pullreqev(payload, session): - from .pulls import PullRequest - if payload.get('pull_request'): - payload['pull_request'] = PullRequest(payload['pull_request'], - session) - return payload - - -def _pullreqcomm(payload, session): - from .pulls import PullRequest, ReviewComment - # Transform the Pull Request attribute - pull = payload.get('pull_request') - if pull: - payload['pull_request'] = PullRequest(pull, session) - - # Transform the Comment attribute - comment = payload.get('comment') - if comment: - payload['comment'] = ReviewComment(comment, session) - return payload - - -def _release(payload, session): - from .repos.release import Release - release = payload.get('release') - if release: - payload['release'] = Release(release, session) - return payload - - -def _team(payload, session): - from .orgs import Team - from .repos import Repository - from .users import User - if payload.get('team'): - payload['team'] = Team(payload['team'], session) - if payload.get('repo'): - payload['repo'] = Repository(payload['repo'], session) - if payload.get('sender'): - payload['sender'] = User(payload['sender'], session) - return payload - - -def identity(x, session): - return x - - -_payload_handlers = { - 'CommitCommentEvent': _commitcomment, - 'CreateEvent': identity, - 'DeleteEvent': identity, - 'FollowEvent': _follow, - 'ForkEvent': _forkev, - 'ForkApplyEvent': identity, - 'GistEvent': _gist, - 'GollumEvent': identity, - 'IssueCommentEvent': _issuecomm, - 'IssuesEvent': _issueevent, - 'MemberEvent': _member, - 'PublicEvent': identity, - 'PullRequestEvent': _pullreqev, - 'PullRequestReviewCommentEvent': _pullreqcomm, - 'PushEvent': identity, - 'ReleaseEvent': _release, - 'StatusEvent': identity, - 'TeamAddEvent': _team, - 'WatchEvent': identity, -} diff --git a/gitsome/lib/github3/exceptions.py b/gitsome/lib/github3/exceptions.py deleted file mode 100644 index f665f85..0000000 --- a/gitsome/lib/github3/exceptions.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -"""All exceptions for the github3 library.""" - - -class GitHubError(Exception): - """The base exception class.""" - - def __init__(self, resp): - super(GitHubError, self).__init__(resp) - #: Response code that triggered the error - self.response = resp - self.code = resp.status_code - self.errors = [] - try: - error = resp.json() - #: Message associated with the error - self.msg = error.get('message') - #: List of errors provided by GitHub - if error.get('errors'): - self.errors = error.get('errors') - except: # Amazon S3 error - self.msg = resp.content or '[No message]' - - def __repr__(self): - return '<{0} [{1}]>'.format(self.__class__.__name__, - self.msg or self.code) - - def __str__(self): - return '{0} {1}'.format(self.code, self.msg) - - @property - def message(self): - """The actual message returned by the API.""" - return self.msg - - -class UnprocessableResponseBody(GitHubError): - """Exception class for response objects that cannot be handled.""" - def __init__(self, message, body): - Exception.__init__(self, message) - self.body = body - self.msg = message - - def __repr__(self): - return '<{0} [{1}]>'.format('UnprocessableResponseBody', self.body) - - def __str__(self): - return self.message - - -class BadRequest(GitHubError): - """Exception class for 400 responses.""" - pass - - -class AuthenticationFailed(GitHubError): - """Exception class for 401 responses. - - Possible reasons: - - - Need one time password (for two-factor authentication) - - You are not authorized to access the resource - """ - pass - - -class ForbiddenError(GitHubError): - """Exception class for 403 responses. - - Possible reasons: - - - Too many requests (you've exceeded the ratelimit) - - Too many login failures - """ - pass - - -class NotFoundError(GitHubError): - """Exception class for 404 responses.""" - pass - - -class MethodNotAllowed(GitHubError): - """Exception class for 405 responses.""" - pass - - -class NotAcceptable(GitHubError): - """Exception class for 406 responses.""" - pass - - -class UnprocessableEntity(GitHubError): - """Exception class for 422 responses.""" - pass - - -class ClientError(GitHubError): - """Catch-all for 400 responses that aren't specific errors.""" - pass - - -class ServerError(GitHubError): - """Exception class for 5xx responses.""" - pass - - -error_classes = { - 400: BadRequest, - 401: AuthenticationFailed, - 403: ForbiddenError, - 404: NotFoundError, - 405: MethodNotAllowed, - 406: NotAcceptable, - 422: UnprocessableEntity, -} - - -def error_for(response): - """Return the appropriate initialized exception class for a response.""" - klass = error_classes.get(response.status_code) - if klass is None: - if 400 <= response.status_code < 500: - klass = ClientError - if 500 <= response.status_code < 600: - klass = ServerError - return klass(response) diff --git a/gitsome/lib/github3/gists/__init__.py b/gitsome/lib/github3/gists/__init__.py deleted file mode 100644 index 377ca2e..0000000 --- a/gitsome/lib/github3/gists/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -github3.gists -============= - -Module which contains all the gist related material. - -Sub-modules: - github3.gists.gist - github3.gists.file - github3.gists.comment - github3.gists.history - -See also: http://developer.github.com/v3/gists/ -""" - -from .gist import Gist - -__all__ = [Gist] diff --git a/gitsome/lib/github3/gists/comment.py b/gitsome/lib/github3/gists/comment.py deleted file mode 100644 index 1de1047..0000000 --- a/gitsome/lib/github3/gists/comment.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.gists.comment ---------------------- - -Module containing the logic for a GistComment - -""" -from __future__ import unicode_literals - -from ..models import BaseComment -from ..users import User - - -class GistComment(BaseComment): - - """This object represents a comment on a gist. - - Two comment instances can be checked like so:: - - c1 == c2 - c1 != c2 - - And is equivalent to:: - - c1.id == c2.id - c1.id != c2.id - - See also: http://developer.github.com/v3/gists/comments/ - - """ - - def _update_attributes(self, comment): - self._api = comment.get('url') - #: :class:`User ` who made the comment - #: Unless it is not associated with an account - self.user = None - if comment.get('user'): - self.user = User(comment.get('user'), self) # (No coverage) - - def _repr(self): - return ''.format(self.user.login) diff --git a/gitsome/lib/github3/gists/file.py b/gitsome/lib/github3/gists/file.py deleted file mode 100644 index 2a388ba..0000000 --- a/gitsome/lib/github3/gists/file.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.gists.file ------------------- - -Module containing the logic for the GistFile object. -""" -from __future__ import unicode_literals - -from ..models import GitHubCore - - -class GistFile(GitHubCore): - - """This represents the file object returned by interacting with gists. - - It stores the raw url of the file, the file name, language, size and - content. - - """ - - def _update_attributes(self, attributes): - #: The raw URL for the file at GitHub. - self.raw_url = attributes.get('raw_url') - #: The name of the file. - self.filename = attributes.get('filename') - #: The name of the file. - self.name = attributes.get('filename') - #: The language associated with the file. - self.language = attributes.get('language') - #: The size of the file. - self.size = attributes.get('size') - #: The content of the file. - self.original_content = attributes.get('content') - - def _repr(self): - return ''.format(self.name) - - def content(self): - """Retrieve contents of file from key 'raw_url' if there is no - 'content' key in Gist object. - """ - resp = self._get(self.raw_url) - if self._boolean(resp, 200, 404): - return resp.content - return None diff --git a/gitsome/lib/github3/gists/gist.py b/gitsome/lib/github3/gists/gist.py deleted file mode 100644 index 7ea3949..0000000 --- a/gitsome/lib/github3/gists/gist.py +++ /dev/null @@ -1,256 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.gists.gist -================== - -This module contains the Gist class alone for simplicity. - -""" -from __future__ import unicode_literals - -from json import dumps -from ..models import GitHubCore -from ..decorators import requires_auth -from .comment import GistComment -from .file import GistFile -from .history import GistHistory -from ..users import User - - -class Gist(GitHubCore): - - """This object holds all the information returned by Github about a gist. - - With it you can comment on or fork the gist (assuming you are - authenticated), edit or delete the gist (assuming you own it). You can - also "star" or "unstar" the gist (again assuming you have authenticated). - - Two gist instances can be checked like so:: - - g1 == g2 - g1 != g2 - - And is equivalent to:: - - g1.id == g2.id - g1.id != g2.id - - See also: http://developer.github.com/v3/gists/ - - """ - - def _update_attributes(self, data): - #: Number of comments on this gist - self.comments_count = data.get('comments', 0) - - #: Unique id for this gist. - self.id = '{0}'.format(data.get('id', '')) - - #: Description of the gist - self.description = data.get('description', '') - - # e.g. https://api.github.com/gists/1 - self._api = data.get('url', '') - - #: URL of this gist at Github, e.g., https://gist.github.com/1 - self.html_url = data.get('html_url') - #: Boolean describing if the gist is public or private - self.public = data.get('public') - - self._forks = data.get('forks', []) - - #: Git URL to pull this gist, e.g., git://gist.github.com/1.git - self.git_pull_url = data.get('git_pull_url', '') - - #: Git URL to push to gist, e.g., git@gist.github.com/1.git - self.git_push_url = data.get('git_push_url', '') - - #: datetime object representing when the gist was created. - self.created_at = self._strptime(data.get('created_at')) - - #: datetime object representing the last time this gist was updated. - self.updated_at = self._strptime(data.get('updated_at')) - - owner = data.get('owner') - #: :class:`User ` object representing the owner of - #: the gist. - self.owner = User(owner, self) if owner else None - - self._files = [GistFile(data['files'][f]) for f in data['files']] - - #: History of this gist, list of - #: :class:`GistHistory ` - self.history = [GistHistory(h, self) for h in data.get('history', [])] - - # New urls - - #: Comments URL (not a template) - self.comments_url = data.get('comments_url', '') - - #: Commits URL (not a template) - self.commits_url = data.get('commits_url', '') - - #: Forks URL (not a template) - self.forks_url = data.get('forks_url', '') - - #: Whether the content of this Gist has been truncated or not - self.truncated = data.get('truncated') - - def __str__(self): - return self.id - - def _repr(self): - return ''.format(self.id) - - @requires_auth - def create_comment(self, body): - """Create a comment on this gist. - - :param str body: (required), body of the comment - :returns: :class:`GistComment ` - - """ - json = None - if body: - url = self._build_url('comments', base_url=self._api) - json = self._json(self._post(url, data={'body': body}), 201) - return self._instance_or_null(GistComment, json) - - @requires_auth - def delete(self): - """Delete this gist. - - :returns: bool -- whether the deletion was successful - - """ - return self._boolean(self._delete(self._api), 204, 404) - - @requires_auth - def edit(self, description='', files={}): - """Edit this gist. - - :param str description: (optional), description of the gist - :param dict files: (optional), files that make up this gist; the - key(s) should be the file name(s) and the values should be another - (optional) dictionary with (optional) keys: 'content' and - 'filename' where the former is the content of the file and the - latter is the new name of the file. - :returns: bool -- whether the edit was successful - - """ - data = {} - json = None - if description: - data['description'] = description - if files: - data['files'] = files - if data: - json = self._json(self._patch(self._api, data=dumps(data)), 200) - if json: - self._update_attributes(json) - return True - return False - - @requires_auth - def fork(self): - """Fork this gist. - - :returns: :class:`Gist ` if successful, ``None`` otherwise - - """ - url = self._build_url('forks', base_url=self._api) - json = self._json(self._post(url), 201) - return self._instance_or_null(Gist, json) - - @requires_auth - def is_starred(self): - """Check to see if this gist is starred by the authenticated user. - - :returns: bool -- True if it is starred, False otherwise - - """ - url = self._build_url('star', base_url=self._api) - return self._boolean(self._get(url), 204, 404) - - def comments(self, number=-1, etag=None): - """Iterate over comments on this gist. - - :param int number: (optional), number of comments to iterate over. - Default: -1 will iterate over all comments on the gist - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of - :class:`GistComment ` - - """ - url = self._build_url('comments', base_url=self._api) - return self._iter(int(number), url, GistComment, etag=etag) - - def commits(self, number=-1, etag=None): - """Iterate over the commits on this gist. - - These commits will be requested from the API and should be the same as - what is in ``Gist.history``. - - .. versionadded:: 0.6 - - .. versionchanged:: 0.9 - - Added param ``etag``. - - :param int number: (optional), number of commits to iterate over. - Default: -1 will iterate over all commits associated with this - gist. - :param str etag: (optional), ETag from a previous request to this - endpoint. - :returns: generator of - :class:`GistHistory ` - - """ - url = self._build_url('commits', base_url=self._api) - return self._iter(int(number), url, GistHistory) - - def files(self): - """Iterator over the files stored in this gist. - - :returns: generator of :class`GistFile ` - - """ - return iter(self._files) - - def forks(self, number=-1, etag=None): - """Iterator of forks of this gist. - - .. versionchanged:: 0.9 - - Added params ``number`` and ``etag``. - - :param int number: (optional), number of forks to iterate over. - Default: -1 will iterate over all forks of this gist. - :param str etag: (optional), ETag from a previous request to this - endpoint. - :returns: generator of :class:`Gist ` - - """ - url = self._build_url('forks', base_url=self._api) - return self._iter(int(number), url, Gist, etag=etag) - - @requires_auth - def star(self): - """Star this gist. - - :returns: bool -- True if successful, False otherwise - - """ - url = self._build_url('star', base_url=self._api) - return self._boolean(self._put(url), 204, 404) - - @requires_auth - def unstar(self): - """Un-star this gist. - - :returns: bool -- True if successful, False otherwise - - """ - url = self._build_url('star', base_url=self._api) - return self._boolean(self._delete(url), 204, 404) diff --git a/gitsome/lib/github3/gists/history.py b/gitsome/lib/github3/gists/history.py deleted file mode 100644 index 4c8b208..0000000 --- a/gitsome/lib/github3/gists/history.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.gists.history ---------------------- - -Module containing the logic for the GistHistory object. - -""" -from __future__ import unicode_literals - -from ..models import GitHubCore -from ..users import User - - -class GistHistory(GitHubCore): - - """Thisobject represents one version (or revision) of a gist. - - Two history instances can be checked like so:: - - h1 == h2 - h1 != h2 - - And is equivalent to:: - - h1.version == h2.version - h1.version != h2.version - - """ - - def _update_attributes(self, history): - self._api = history.get('url', '') - - #: SHA of the commit associated with this version - self.version = history.get('version', '') - - #: user who made these changes - self.user = User(history.get('user') or {}, self) - - #: dict containing the change status; see also: deletions, additions, - #: total - self.change_status = history.get('change_status', {}) - - #: number of additions made - self.additions = self.change_status.get('additions', 0) - - #: number of deletions made - self.deletions = self.change_status.get('deletions', 0) - - #: total number of changes made - self.total = self.change_status.get('total', 0) - - #: datetime representation of when the commit was made - self.committed_at = self._strptime(history.get('committed_at')) - - def _repr(self): - return ''.format(self.version) - - def get_gist(self): - """Retrieve the gist at this version. - - :returns: :class:`Gist ` - - """ - from .gist import Gist - json = self._json(self._get(self._api), 200) - return self._instance_or_null(Gist, json) diff --git a/gitsome/lib/github3/git.py b/gitsome/lib/github3/git.py deleted file mode 100644 index da8677e..0000000 --- a/gitsome/lib/github3/git.py +++ /dev/null @@ -1,248 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.git -=========== - -This module contains all the classes relating to Git Data. - -See also: http://developer.github.com/v3/git/ -""" -from __future__ import unicode_literals - -from json import dumps -from base64 import b64decode -from .models import GitHubCore, BaseCommit -from .users import User -from .decorators import requires_auth - - -class Blob(GitHubCore): - - """The :class:`Blob ` object. - - See also: http://developer.github.com/v3/git/blobs/ - - """ - - def _update_attributes(self, blob): - self._api = blob.get('url', '') - - #: Raw content of the blob. - self.content = blob.get('content').encode() - - #: Encoding of the raw content. - self.encoding = blob.get('encoding') - - #: Decoded content of the blob. - self.decoded = self.content - if self.encoding == 'base64': - self.decoded = b64decode(self.content) - - #: Size of the blob in bytes - self.size = blob.get('size') - #: SHA1 of the blob - self.sha = blob.get('sha') - - def _repr(self): - return ''.format(self.sha) - - -class GitData(GitHubCore): - - """The :class:`GitData ` object. This isn't directly returned to - the user (developer) ever. This is used to prevent duplication of some - common items among other Git Data objects. - - """ - - def _update_attributes(self, data): - #: SHA of the object - self.sha = data.get('sha') - self._api = data.get('url', '') - - -class Commit(BaseCommit): - - """The :class:`Commit ` object. This represents a commit made in a - repository. - - See also: http://developer.github.com/v3/git/commits/ - - """ - - def _update_attributes(self, commit): - super(Commit, self)._update_attributes(commit) - #: dict containing at least the name, email and date the commit was - #: created - self.author = commit.get('author', {}) or {} - # If GH returns nil/None then make sure author is a dict - self._author_name = self.author.get('name', '') - - #: dict containing similar information to the author attribute - self.committer = commit.get('committer', {}) or {} - # blank the data if GH returns no data - - self._commit_name = self.committer.get('name', '') - - #: :class:`Tree ` the commit belongs to. - self.tree = None - if commit.get('tree'): - self.tree = Tree(commit.get('tree'), self) - - def _repr(self): - return ''.format(self._author_name, self.sha) - - def author_as_User(self): - """Attempt to return the author attribute as a - :class:`User `. No guarantees are made about the - validity of this object, i.e., having a login or created_at object. - - """ - return User(self.author, self) - - def committer_as_User(self): - """Attempt to return the committer attribute as a - :class:`User ` object. No guarantees are made - about the validity of this object. - - """ - return User(self.committer, self) - - -class Reference(GitHubCore): - - """The :class:`Reference ` object. This represents a reference - created on a repository. - - See also: http://developer.github.com/v3/git/refs/ - - """ - - def _update_attributes(self, ref): - self._api = ref.get('url', '') - #: The reference path, e.g., refs/heads/sc/featureA - self.ref = ref.get('ref') - #: :class:`GitObject ` the reference points to - self.object = GitObject(ref.get('object', {})) - - def _repr(self): - return ''.format(self.ref) - - @requires_auth - def delete(self): - """Delete this reference. - - :returns: bool - - """ - return self._boolean(self._delete(self._api), 204, 404) - - @requires_auth - def update(self, sha, force=False): - """Update this reference. - - :param str sha: (required), sha of the reference - :param bool force: (optional), force the update or not - :returns: bool - - """ - data = {'sha': sha, 'force': force} - json = self._json(self._patch(self._api, data=dumps(data)), 200) - if json: - self._update_attributes(json) - return True - return False - - -class GitObject(GitData): - - """The :class:`GitObject ` object.""" - - def _update_attributes(self, obj): - super(GitObject, self)._update_attributes(obj) - #: The type of object. - self.type = obj.get('type') - - def _repr(self): - return ''.format(self.sha) - - -class Tag(GitData): - - """The :class:`Tag ` object. - - See also: http://developer.github.com/v3/git/tags/ - - """ - - def _update_attributes(self, tag): - super(Tag, self)._update_attributes(tag) - #: String of the tag - self.tag = tag.get('tag') - #: Commit message for the tag - self.message = tag.get('message') - #: dict containing the name and email of the person - self.tagger = tag.get('tagger') - #: :class:`GitObject ` for the tag - self.object = GitObject(tag.get('object', {})) - - def _repr(self): - return ''.format(self.tag) - - -class Tree(GitData): - - """The :class:`Tree ` object. - - See also: http://developer.github.com/v3/git/trees/ - - """ - - def _update_attributes(self, tree): - super(Tree, self)._update_attributes(tree) - #: list of :class:`Hash ` objects - self.tree = [Hash(t) for t in tree.get('tree', [])] - - def _repr(self): - return ''.format(self.sha) - - def __eq__(self, other): - return self.as_dict() == other.as_dict() - - def __ne__(self, other): - return self.as_dict() != other.as_dict() - - def recurse(self): - """Recurse into the tree. - - :returns: :class:`Tree ` - """ - json = self._json(self._get(self._api, params={'recursive': '1'}), - 200) - return self._instance_or_null(Tree, json) - - -class Hash(GitHubCore): - - """The :class:`Hash ` object. - - See also: http://developer.github.com/v3/git/trees/#create-a-tree - - """ - - def _update_attributes(self, info): - #: Path to file - self.path = info.get('path') - #: File mode - self.mode = info.get('mode') - #: Type of hash, e.g., blob - self.type = info.get('type') - #: Size of hash - self.size = info.get('size') - #: SHA of the hash - self.sha = info.get('sha') - #: URL of this object in the GitHub API - self.url = info.get('url') - - def _repr(self): - return ''.format(self.sha) diff --git a/gitsome/lib/github3/github.py b/gitsome/lib/github3/github.py deleted file mode 100644 index 262a7c5..0000000 --- a/gitsome/lib/github3/github.py +++ /dev/null @@ -1,1770 +0,0 @@ -# -*- coding: utf-8 -*- -""" -github3.github -============== - -This module contains the main GitHub session object. - -""" -from __future__ import unicode_literals - -import json - -from .auths import Authorization -from .decorators import (requires_auth, requires_basic_auth, - requires_app_credentials) -from .events import Event -from .gists import Gist -from .issues import Issue, issue_params -from .models import GitHubCore -from .orgs import Membership, Organization, Team -from .pulls import PullRequest -from .repos.repo import Repository, repo_issue_params -from .search import (CodeSearchResult, IssueSearchResult, - RepositorySearchResult, UserSearchResult) -from .structs import SearchIterator -from . import users -from .notifications import Thread -from .licenses import License -from uritemplate import URITemplate - - -class GitHub(GitHubCore): - - """Stores all the session information. - - There are two ways to log into the GitHub API - - :: - - from github3 import login - g = login(user, password) - g = login(token=token) - g = login(user, token=token) - - or - - :: - - from github3 import GitHub - g = GitHub(user, password) - g = GitHub(token=token) - g = GitHub(user, token=token) - - This is simple backward compatibility since originally there was no way to - call the GitHub object with authentication parameters. - """ - - def __init__(self, username='', password='', token=''): - super(GitHub, self).__init__({}) - if token: - self.login(username, token=token) - elif username and password: - self.login(username, password) - - def _repr(self): - if self.session.auth: - return ''.format(self.session.auth) - return ''.format(id(self)) - - @requires_auth - def add_email_addresses(self, addresses=[]): - """Add the email addresses in ``addresses`` to the authenticated - user's account. - - :param list addresses: (optional), email addresses to be added - :returns: list of :class:`~github3.users.Email` - """ - json = [] - if addresses: - url = self._build_url('user', 'emails') - json = self._json(self._post(url, data=addresses), 201) - return [users.Email(email) for email in json] if json else [] - - def all_events(self, number=-1, etag=None): - """Iterate over public events. - - :param int number: (optional), number of events to return. Default: -1 - returns all available events - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Event `\ s - """ - url = self._build_url('events') - return self._iter(int(number), url, Event, etag=etag) - - def all_organizations(self, number=-1, since=None, etag=None, - per_page=None): - """Iterate over every organization in the order they were created. - - :param int number: (optional), number of organizations to return. - Default: -1, returns all of them - :param int since: (optional), last organization id seen (allows - restarting this iteration) - :param str etag: (optional), ETag from a previous request to the same - endpoint - :param int per_page: (optional), number of organizations to list per - request - :returns: generator of :class:`Organization - ` - """ - url = self._build_url('organizations') - return self._iter(int(number), url, Organization, - params={'since': since, 'per_page': per_page}, - etag=etag) - - def all_repositories(self, number=-1, since=None, etag=None, - per_page=None): - """Iterate over every repository in the order they were created. - - :param int number: (optional), number of repositories to return. - Default: -1, returns all of them - :param int since: (optional), last repository id seen (allows - restarting this iteration) - :param str etag: (optional), ETag from a previous request to the same - endpoint - :param int per_page: (optional), number of repositories to list per - request - :returns: generator of :class:`Repository ` - """ - url = self._build_url('repositories') - return self._iter(int(number), url, Repository, - params={'since': since, 'per_page': per_page}, - etag=etag) - - def all_users(self, number=-1, etag=None, per_page=None, since=None): - """Iterate over every user in the order they signed up for GitHub. - - .. versionchanged:: 1.0.0 - - Inserted the ``since`` parameter after the ``number`` parameter. - - :param int number: (optional), number of users to return. Default: -1, - returns all of them - :param int since: (optional), ID of the last user that you've seen. - :param str etag: (optional), ETag from a previous request to the same - endpoint - :param int per_page: (optional), number of users to list per request - :returns: generator of :class:`User ` - """ - url = self._build_url('users') - return self._iter(int(number), url, users.User, etag=etag, - params={'per_page': per_page, 'since': since}) - - @requires_basic_auth - def authorization(self, id_num): - """Get information about authorization ``id``. - - :param int id_num: (required), unique id of the authorization - :returns: :class:`Authorization ` - """ - json = None - if int(id_num) > 0: - url = self._build_url('authorizations', str(id_num)) - json = self._json(self._get(url), 200) - return self._instance_or_null(Authorization, json) - - @requires_basic_auth - def authorizations(self, number=-1, etag=None): - """Iterate over authorizations for the authenticated user. This will - return a 404 if you are using a token for authentication. - - :param int number: (optional), number of authorizations to return. - Default: -1 returns all available authorizations - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Authorization `\ s - """ - url = self._build_url('authorizations') - return self._iter(int(number), url, Authorization, etag=etag) - - def authorize(self, username, password, scopes=None, note='', note_url='', - client_id='', client_secret=''): - """Obtain an authorization token. - - The retrieved token will allow future consumers to use the API without - a username and password. - - :param str username: (required) - :param str password: (required) - :param list scopes: (optional), areas you want this token to apply to, - i.e., 'gist', 'user' - :param str note: (optional), note about the authorization - :param str note_url: (optional), url for the application - :param str client_id: (optional), 20 character OAuth client key for - which to create a token - :param str client_secret: (optional), 40 character OAuth client secret - for which to create the token - :returns: :class:`Authorization ` - """ - json = None - - if username and password: - url = self._build_url('authorizations') - data = {'note': note, 'note_url': note_url, - 'client_id': client_id, 'client_secret': client_secret} - if scopes: - data['scopes'] = scopes - - with self.session.temporary_basic_auth(username, password): - json = self._json(self._post(url, data=data), 201) - - return self._instance_or_null(Authorization, json) - - 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 ` - """ - 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) - - @requires_auth - def create_issue(self, owner, repository, title, body=None, assignee=None, - milestone=None, labels=[]): - """Create an issue on the project 'repository' owned by 'owner' - with title 'title'. - - ``body``, ``assignee``, ``milestone``, ``labels`` are all optional. - - .. warning:: - - This method retrieves the repository first and then uses it to - create an issue. If you're making several issues, you should use - :py:meth:`repository ` and then - use :py:meth:`create_issue - ` - - :param str owner: (required), login of the owner - :param str repository: (required), repository name - :param str title: (required), Title of issue to be created - :param str body: (optional), The text of the issue, markdown - formatted - :param str assignee: (optional), Login of person to assign - the issue to - :param int milestone: (optional), id number of the milestone to - attribute this issue to (e.g. ``m`` is a :class:`Milestone - ` object, ``m.number`` is what you pass - here.) - :param list labels: (optional), List of label names. - :returns: :class:`Issue ` if successful - """ - repo = None - if owner and repository and title: - repo = self.repository(owner, repository) - - # repo can be None or a NullObject. - # If repo is None, than one of owner, repository, or title were - # False-y. If repo is a NullObject then owner/repository 404's. - - if repo is not None: - # If repo is a NullObject then that's most likely because the - # repository was not found (404). In that case, calling the - # create_issue method will still return - # which will ideally help the user understand what went wrong. - return repo.create_issue(title, body, assignee, milestone, labels) - - return self._instance_or_null(Issue, None) - - @requires_auth - def create_key(self, title, key, read_only=False): - """Create a new key for the authenticated user. - - :param str title: (required), key title - :param str key: (required), actual key contents, accepts path - as a string or file-like object - :param bool read_only: (optional), restrict key access to read-only, - default to False - :returns: :class:`Key ` - """ - json = None - - if title and key: - data = {'title': title, 'key': key, 'read_only': read_only} - url = self._build_url('user', 'keys') - req = self._post(url, data=data) - json = self._json(req, 201) - return self._instance_or_null(users.Key, json) - - @requires_auth - def create_repository(self, name, description='', homepage='', - private=False, has_issues=True, has_wiki=True, - auto_init=False, gitignore_template=''): - """Create a repository for the authenticated user. - - :param str name: (required), name of the repository - :param str description: (optional) - :param str homepage: (optional) - :param str private: (optional), If ``True``, create a - private repository. API default: ``False`` - :param bool has_issues: (optional), If ``True``, enable - issues for this repository. API default: ``True`` - :param bool has_wiki: (optional), If ``True``, enable the - wiki for this repository. API default: ``True`` - :param bool auto_init: (optional), auto initialize the repository - :param str gitignore_template: (optional), name of the git template to - use; ignored if auto_init = False. - :returns: :class:`Repository ` - - .. warning: ``name`` should be no longer than 100 characters - """ - url = self._build_url('user', 'repos') - data = {'name': name, 'description': description, - 'homepage': homepage, 'private': private, - 'has_issues': has_issues, 'has_wiki': has_wiki, - 'auto_init': auto_init, - 'gitignore_template': gitignore_template} - json = self._json(self._post(url, data=data), 201) - return self._instance_or_null(Repository, json) - - @requires_auth - def delete_email_addresses(self, addresses=[]): - """Delete the email addresses in ``addresses`` from the - authenticated user's account. - - :param list addresses: (optional), email addresses to be removed - :returns: bool - """ - url = self._build_url('user', 'emails') - return self._boolean(self._delete(url, data=json.dumps(addresses)), - 204, 404) - - @requires_auth - def emails(self, number=-1, etag=None): - """Iterate over email addresses for the authenticated user. - - :param int number: (optional), number of email addresses to return. - Default: -1 returns all available email addresses - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of dicts - """ - url = self._build_url('user', 'emails') - return self._iter(int(number), url, users.Email, etag=etag) - - def emojis(self): - """Retrieves a dictionary of all of the emojis that GitHub supports. - - :returns: dictionary where the key is what would be in between the - colons and the value is the URL to the image, e.g., :: - - { - '+1': 'https://github.global.ssl.fastly.net/images/...', - # ... - } - """ - url = self._build_url('emojis') - return self._json(self._get(url), 200, include_cache_info=False) - - @requires_basic_auth - def feeds(self): - """List GitHub's timeline resources in Atom format. - - :returns: dictionary parsed to include URITemplates - """ - def replace_href(feed_dict): - if not feed_dict: - return feed_dict - ret_dict = {} - # Let's pluck out what we're most interested in, the href value - href = feed_dict.pop('href', None) - # Then we update the return dictionary with the rest of the values - ret_dict.update(feed_dict) - if href is not None: - # So long as there is something to template, let's template it - ret_dict['href'] = URITemplate(href) - return ret_dict - - url = self._build_url('feeds') - json = self._json(self._get(url), 200, include_cache_info=False) - if json is None: # If something went wrong, get out early - return None - - # We have a response body to parse - feeds = {} - - # Let's pop out the old links so we don't have to skip them below - old_links = json.pop('_links', {}) - _links = {} - # If _links is in the response JSON, iterate over that and recreate it - # so that any templates contained inside can be turned into - # URITemplates - for key, value in old_links.items(): - if isinstance(value, list): - # If it's an array/list of links, let's replace that with a - # new list of links - _links[key] = [replace_href(d) for d in value] - else: - # Otherwise, just use the new value - _links[key] = replace_href(value) - - # Start building up our return dictionary - feeds['_links'] = _links - - for key, value in json.items(): - # This should roughly be the same logic as above. - if isinstance(value, list): - feeds[key] = [URITemplate(v) for v in value] - else: - feeds[key] = URITemplate(value) - - return feeds - - @requires_auth - def follow(self, username): - """Make the authenticated user follow the provided username. - - :param str username: (required), user to follow - :returns: bool - """ - resp = False - if username: - url = self._build_url('user', 'following', username) - resp = self._boolean(self._put(url), 204, 404) - return resp - - 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 `\ s - """ - url = self._build_url('users', username, 'following') - return self._iter(int(number), url, users.User, etag=etag) - - @requires_auth - def followers(self, number=-1, etag=None): - """Iterate over followers of the authenticated user. - - .. versionadded:: 1.0.0 - - This replaces iter_followers(). - - :param int number: (optional), number of followers to return. Default: - -1 returns all followers - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`User `\ s - """ - url = self._build_url('user', 'followers') - return self._iter(int(number), url, users.User, etag=etag) - - def followers_of(self, username, number=-1, etag=None): - """Iterate over followers of ``username``. - - .. versionadded:: 1.0.0 - - This replaces iter_followers('sigmavirus24'). - - :param str username: (required), login of the user to check - :param int number: (optional), number of followers to return. Default: - -1 returns all followers - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`User `\ s - """ - url = self._build_url('users', username, 'followers') - return self._iter(int(number), url, users.User, etag=etag) - - @requires_auth - def following(self, number=-1, etag=None): - """Iterate over users the authenticated user is following. - - .. versionadded:: 1.0.0 - - This replaces iter_following(). - - :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 `\ s - """ - url = self._build_url('user', 'following') - return self._iter(int(number), url, users.User, etag=etag) - - def gist(self, id_num): - """Retrieve the gist using the specified id number. - - :param int id_num: (required), unique id of the gist - :returns: :class:`Gist ` - """ - url = self._build_url('gists', str(id_num)) - json = self._json(self._get(url), 200) - return self._instance_or_null(Gist, json) - - @requires_auth - def gists(self, number=-1, etag=None): - """Retrieve the authenticated user's gists. - - .. versionadded:: 1.0 - - :param int number: (optional), number of gists to return. Default: -1, - returns all available gists - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Gist `\ s - """ - url = self._build_url('gists') - return self._iter(int(number), url, Gist, etag=etag) - - def gists_by(self, username, number=-1, etag=None): - """Iterate over the gists owned by a user. - - .. versionadded:: 1.0 - - :param str username: login of the user who owns the gists - :param int number: (optional), number of gists to return. Default: -1 - returns all available gists - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Gist `\ s - """ - url = self._build_url('users', username, 'gists') - return self._iter(int(number), url, Gist, etag=etag) - - def gitignore_template(self, language): - """Return the template for language. - - :returns: str - """ - url = self._build_url('gitignore', 'templates', language) - json = self._json(self._get(url), 200) - if not json: - return '' - return json.get('source', '') - - def gitignore_templates(self): - """Return the list of available templates. - - :returns: list of template names - """ - url = self._build_url('gitignore', 'templates') - return self._json(self._get(url), 200) or [] - - @requires_auth - def is_following(self, username): - """Check if the authenticated user is following login. - - :param str username: (required), login of the user to check if the - authenticated user is checking - :returns: bool - """ - json = False - if username: - url = self._build_url('user', 'following', username) - json = self._boolean(self._get(url), 204, 404) - return json - - @requires_auth - def is_starred(self, username, repo): - """Check if the authenticated user starred username/repo. - - :param str username: (required), owner of repository - :param str repo: (required), name of repository - :returns: bool - """ - json = False - if username and repo: - url = self._build_url('user', 'starred', username, repo) - json = self._boolean(self._get(url), 204, 404) - return json - - def issue(self, username, repository, number): - """Fetch issue from owner/repository. - - :param str username: (required), owner of the repository - :param str repository: (required), name of the repository - :param int number: (required), issue number - :return: :class:`Issue ` - """ - json = None - if username and repository and int(number) > 0: - url = self._build_url('repos', username, repository, 'issues', - str(number)) - json = self._json(self._get(url), 200) - return self._instance_or_null(Issue, json) - - @requires_auth - def issues(self, filter='', state='', labels='', sort='', direction='', - since=None, number=-1, etag=None): - """List all of the authenticated user's (and organization's) issues. - - .. versionchanged:: 0.9.0 - - - The ``state`` parameter now accepts 'all' in addition to 'open' - and 'closed'. - - :param str filter: accepted values: - ('assigned', 'created', 'mentioned', 'subscribed') - api-default: 'assigned' - :param str state: accepted values: ('all', 'open', 'closed') - api-default: 'open' - :param str labels: comma-separated list of label names, e.g., - 'bug,ui,@high' - :param str sort: accepted values: ('created', 'updated', 'comments') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - api-default: desc - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :param int number: (optional), number of issues to return. - Default: -1 returns all issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Issue ` - """ - url = self._build_url('issues') - # issue_params will handle the since parameter - params = issue_params(filter, state, labels, sort, direction, since) - return self._iter(int(number), url, Issue, params, etag) - - def issues_on(self, username, repository, milestone=None, state=None, - assignee=None, mentioned=None, labels=None, sort=None, - direction=None, since=None, number=-1, etag=None): - """List issues on owner/repository. Only owner and repository are - required. - - .. versionchanged:: 0.9.0 - - - The ``state`` parameter now accepts 'all' in addition to 'open' - and 'closed'. - - :param str username: login of the owner of the repository - :param str repository: name of the repository - :param int milestone: None, '*', or ID of milestone - :param str state: accepted values: ('all', 'open', 'closed') - api-default: 'open' - :param str assignee: '*' or login of the user - :param str mentioned: login of the user - :param str labels: comma-separated list of label names, e.g., - 'bug,ui,@high' - :param str sort: accepted values: ('created', 'updated', 'comments') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - api-default: desc - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :param int number: (optional), number of issues to return. - Default: -1 returns all issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Issue `\ s - """ - if username and repository: - url = self._build_url('repos', username, repository, 'issues') - - params = repo_issue_params(milestone, state, assignee, mentioned, - labels, sort, direction, since) - return self._iter(int(number), url, Issue, params=params, - etag=etag) - return iter([]) - - @requires_auth - def key(self, id_num): - """Gets the authenticated user's key specified by id_num. - - :param int id_num: (required), unique id of the key - :returns: :class:`Key ` - """ - json = None - if int(id_num) > 0: - url = self._build_url('user', 'keys', str(id_num)) - json = self._json(self._get(url), 200) - return self._instance_or_null(users.Key, json) - - @requires_auth - def keys(self, number=-1, etag=None): - """Iterate over public keys for the authenticated user. - - :param int number: (optional), number of keys to return. Default: -1 - returns all your keys - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Key `\ s - """ - url = self._build_url('user', 'keys') - return self._iter(int(number), url, users.Key, etag=etag) - - def license(self, name): - """Retrieve the license specified by the name. - - :param string name: (required), name of license - :returns: :class:`License ` - """ - - url = self._build_url('licenses', name) - json = self._json(self._get(url, headers=License.CUSTOM_HEADERS), 200) - return self._instance_or_null(License, json) - - def licenses(self, number=-1, etag=None): - """Iterate over open source licenses. - - :returns: generator of :class:`License ` - """ - url = self._build_url('licenses') - return self._iter(int(number), url, License, etag=etag, - headers=License.CUSTOM_HEADERS) - - 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 markdown(self, text, mode='', context='', raw=False): - """Render an arbitrary markdown document. - - :param str text: (required), the text of the document to render - :param str mode: (optional), 'markdown' or 'gfm' - :param str context: (optional), only important when using mode 'gfm', - this is the repository to use as the context for the rendering - :param bool raw: (optional), renders a document like a README.md, no - gfm, no context - :returns: str (or unicode on Python 2) -- HTML formatted text - """ - data = None - json = False - headers = {} - if raw: - url = self._build_url('markdown', 'raw') - data = text - headers['content-type'] = 'text/plain' - else: - url = self._build_url('markdown') - data = {} - - if text: - data['text'] = text - - if mode in ('markdown', 'gfm'): - data['mode'] = mode - - if context: - data['context'] = context - json = True - - html = '' - if data: - req = self._post(url, data=data, json=json, headers=headers) - if req.ok: - html = req.text - return html - - @requires_auth - def me(self): - """Retrieves the info for the authenticated user. - - .. versionadded:: 1.0 - - This was separated from the ``user`` method. - - :returns: The representation of the authenticated user. - :rtype: :class:`User ` - """ - url = self._build_url('user') - json = self._json(self._get(url), 200) - return self._instance_or_null(users.User, json) - - @requires_auth - def membership_in(self, organization): - """Retrieve the user's membership in the specified organization.""" - url = self._build_url('user', 'memberships', 'orgs', - str(organization)) - json = self._json(self._get(url), 200) - return self._instance_or_null(Membership, json) - - def meta(self): - """Returns a dictionary with arrays of addresses in CIDR format - specifying theaddresses that the incoming service hooks will originate - from. - - .. versionadded:: 0.5 - """ - url = self._build_url('meta') - return self._json(self._get(url), 200) or {} - - @requires_auth - def notifications(self, all=False, participating=False, number=-1, - etag=None): - """Iterate over the user's notification. - - :param bool all: (optional), iterate over all notifications - :param bool participating: (optional), only iterate over notifications - in which the user is participating - :param int number: (optional), how many notifications to return - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of - :class:`Thread ` - """ - params = None - if all is True: - params = {'all': 'true'} - elif participating is True: - params = {'participating': 'true'} - - url = self._build_url('notifications') - return self._iter(int(number), url, Thread, params, etag=etag) - - def octocat(self, say=None): - """Returns an easter egg of the API. - - :params str say: (optional), pass in what you'd like Octocat to say - :returns: ascii art of Octocat - :rtype: str (or unicode on Python 3) - """ - url = self._build_url('octocat') - req = self._get(url, params={'s': say}) - return req.text if req.ok else '' - - def organization(self, username): - """Returns a Organization object for the login name - - :param str username: (required), login name of the org - :returns: :class:`Organization ` - """ - url = self._build_url('orgs', username) - json = self._json(self._get(url), 200) - return self._instance_or_null(Organization, json) - - @requires_auth - def organization_issues(self, name, filter='', state='', labels='', - sort='', direction='', since=None, number=-1, - etag=None): - """Iterate over the organization's issues if the authenticated user - belongs to it. - - :param str name: (required), name of the organization - :param str filter: accepted values: - ('assigned', 'created', 'mentioned', 'subscribed') - api-default: 'assigned' - :param str state: accepted values: ('open', 'closed') - api-default: 'open' - :param str labels: comma-separated list of label names, e.g., - 'bug,ui,@high' - :param str sort: accepted values: ('created', 'updated', 'comments') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - api-default: desc - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :param int number: (optional), number of issues to return. Default: - -1, returns all available issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Issue ` - """ - url = self._build_url('orgs', name, 'issues') - # issue_params will handle the since parameter - params = issue_params(filter, state, labels, sort, direction, since) - return self._iter(int(number), url, Issue, params, etag) - - @requires_auth - def organizations(self, number=-1, etag=None): - """Iterate over all organizations the authenticated user belongs to. - - This will display both the private memberships and the publicized - memberships. - - :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 `\ s - """ - url = self._build_url('user', 'orgs') - return self._iter(int(number), url, Organization, etag=etag) - - def organizations_with(self, username, number=-1, etag=None): - """Iterate over organizations with ``username`` as a public member. - - .. versionadded:: 1.0.0 - - Replaces ``iter_orgs('sigmavirus24')``. - - :param str username: (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 `\ s - """ - if username: - url = self._build_url('users', username, 'orgs') - return self._iter(int(number), url, Organization, etag=etag) - return iter([]) - - def public_gists(self, number=-1, etag=None): - """Retrieve all public gists and iterate over them. - - .. versionadded:: 1.0 - - :param int number: (optional), number of gists to return. Default: -1 - returns all available gists - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Gist `\ s - """ - url = self._build_url('gists', 'public') - return self._iter(int(number), url, Gist, etag=etag) - - @requires_auth - def organization_memberships(self, state=None, number=-1, etag=None): - """List organizations of which the user is a current or pending member. - - :param str state: (option), state of the membership, i.e., active, - pending - :returns: iterator of :class:`Membership ` - """ - params = None - url = self._build_url('user', 'memberships', 'orgs') - if state is not None and state.lower() in ('active', 'pending'): - params = {'state': state.lower()} - return self._iter(int(number), url, Membership, - params=params, - etag=etag) - - @requires_auth - def pubsubhubbub(self, mode, topic, callback, secret=''): - """Create/update a pubsubhubbub hook. - - :param str mode: (required), accepted values: ('subscribe', - 'unsubscribe') - :param str topic: (required), form: - https://github.com/:user/:repo/events/:event - :param str callback: (required), the URI that receives the updates - :param str secret: (optional), shared secret key that generates a - SHA1 HMAC of the payload content. - :returns: bool - """ - from re import match - m = match('https?://[\w\d\-\.\:]+/\w+/[\w\._-]+/events/\w+', topic) - status = False - if mode and topic and callback and m: - data = [('hub.mode', mode), ('hub.topic', topic), - ('hub.callback', callback)] - if secret: - data.append(('hub.secret', secret)) - url = self._build_url('hub') - # This is not JSON data. It is meant to be form data - # application/x-www-form-urlencoded works fine here, no need for - # multipart/form-data - status = self._boolean(self._post(url, data=data, json=False), 204, - 404) - return status - - def pull_request(self, owner, repository, number): - """Fetch pull_request #:number: from :owner:/:repository - - :param str owner: (required), owner of the repository - :param str repository: (required), name of the repository - :param int number: (required), issue number - :return: :class:`~github.pulls.PullRequest` - """ - json = None - if int(number) > 0: - url = self._build_url('repos', owner, repository, 'pulls', - str(number)) - json = self._json(self._get(url), 200) - return self._instance_or_null(PullRequest, json) - - def rate_limit(self): - """Returns a dictionary with information from /rate_limit. - - The dictionary has two keys: ``resources`` and ``rate``. In - ``resources`` you can access information about ``core`` or ``search``. - - Note: the ``rate`` key will be deprecated before version 3 of the - GitHub API is finalized. Do not rely on that key. Instead, make your - code future-proof by using ``core`` in ``resources``, e.g., - - :: - - rates = g.rate_limit() - rates['resources']['core'] # => your normal ratelimit info - rates['resources']['search'] # => your search ratelimit info - - .. versionadded:: 0.8 - - :returns: dict - """ - url = self._build_url('rate_limit') - return self._json(self._get(url), 200) - - @requires_auth - def repositories(self, username='', type=None, sort=None, direction=None, - number=-1, etag=None): - """List repositories for the given user, filterable by ``type``. - - .. versionchanged:: 0.6 - - Removed the login parameter for correctness. Use repositories_by - instead - - :param str username: (optional), username - :param str type: (optional), accepted values: - ('all', 'owner', 'public', 'private', 'member') - API default: 'all' - :param str sort: (optional), accepted values: - ('created', 'updated', 'pushed', 'full_name') - API default: 'created' - :param str direction: (optional), accepted values: - ('asc', 'desc'), API default: 'asc' when using 'full_name', - 'desc' otherwise - :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 ` - objects - """ - if username: - url = self._build_url('users', username, 'repos') - else: - url = self._build_url('user', 'repos') - - params = {} - if type in ('all', 'owner', 'public', 'private', 'member'): - params.update(type=type) - if sort in ('created', 'updated', 'pushed', 'full_name'): - params.update(sort=sort) - if direction in ('asc', 'desc'): - params.update(direction=direction) - - return self._iter(int(number), url, Repository, params, etag) - - def repositories_by(self, username, type=None, sort=None, direction=None, - number=-1, etag=None): - """List public repositories for the specified ``username``. - - .. versionadded:: 0.6 - - :param str username: (required), username - :param str type: (optional), accepted values: - ('all', 'owner', 'member') - API default: 'all' - :param str sort: (optional), accepted values: - ('created', 'updated', 'pushed', 'full_name') - API default: 'created' - :param str direction: (optional), accepted values: - ('asc', 'desc'), API default: 'asc' when using 'full_name', - 'desc' otherwise - :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 ` - objects - """ - url = self._build_url('users', username, 'repos') - - params = {} - if type in ('all', 'owner', 'member'): - params.update(type=type) - if sort in ('created', 'updated', 'pushed', 'full_name'): - params.update(sort=sort) - if direction in ('asc', 'desc'): - params.update(direction=direction) - - return self._iter(int(number), url, Repository, params, etag) - - def repository(self, owner, repository): - """Returns a Repository object for the specified combination of - owner and repository - - :param str owner: (required) - :param str repository: (required) - :returns: :class:`Repository ` - """ - json = None - if owner and repository: - url = self._build_url('repos', owner, repository) - json = self._json(self._get(url), 200) - return self._instance_or_null(Repository, json) - - def repository_with_id(self, number): - """Returns the Repository with id ``number``. - - :param int number: id of the repository - :returns: :class:`Repository ` - """ - number = int(number) - json = None - if number > 0: - url = self._build_url('repositories', str(number)) - json = self._json(self._get(url), 200) - return self._instance_or_null(Repository, json) - - @requires_app_credentials - def revoke_authorization(self, access_token): - """Revoke specified authorization for an OAuth application. - - Revoke all authorization tokens created by your application. This will - only work if you have already called ``set_client_id``. - - :param str access_token: (required), the access_token to revoke - :returns: bool -- True if successful, False otherwise - """ - client_id, client_secret = self.session.retrieve_client_credentials() - url = self._build_url('applications', str(client_id), 'tokens', - access_token) - with self.session.temporary_basic_auth(client_id, client_secret): - response = self._delete(url, params={'client_id': None, - 'client_secret': None}) - - return self._boolean(response, 204, 404) - - @requires_app_credentials - def revoke_authorizations(self): - """Revoke all authorizations for an OAuth application. - - Revoke all authorization tokens created by your application. This will - only work if you have already called ``set_client_id``. - - :param str client_id: (required), the client_id of your application - :returns: bool -- True if successful, False otherwise - """ - client_id, client_secret = self.session.retrieve_client_credentials() - url = self._build_url('applications', str(client_id), 'tokens') - with self.session.temporary_basic_auth(client_id, client_secret): - response = self._delete(url, params={'client_id': None, - 'client_secret': None}) - - return self._boolean(response, 204, 404) - - def search_code(self, query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find code via the code search API. - - The query can contain any combination of the following supported - qualifiers: - - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the file contents, the file path, or - both. - - ``language`` Searches code based on the language it’s written in. - - ``fork`` Specifies that code from forked repositories should be - searched. Repository forks will not be searchable unless the fork - has more stars than the parent repository. - - ``size`` Finds files that match a certain size (in bytes). - - ``path`` Specifies the path that the resulting file must be at. - - ``extension`` Matches files with a certain extension. - - ``user`` or ``repo`` Limits searches to a specific user or - repository. - - For more information about these qualifiers, see: http://git.io/-DvAuA - - :param str query: (required), a valid query as described above, e.g., - ``addClass in:file language:js repo:jquery/jquery`` - :param str sort: (optional), how the results should be sorted; - option(s): ``indexed``; default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/iRmJxg for more information - :param int number: (optional), number of repositories to return. - Default: -1, returns all available repositories - :param str etag: (optional), previous ETag header value - :return: generator of :class:`CodeSearchResult - ` - """ - params = {'q': query} - headers = {} - - if sort == 'indexed': - params['sort'] = sort - - if sort and order in ('asc', 'desc'): - params['order'] = order - - if text_match: - headers = { - 'Accept': 'application/vnd.github.v3.full.text-match+json' - } - - url = self._build_url('search', 'code') - return SearchIterator(number, url, CodeSearchResult, self, params, - etag, headers) - - def search_issues(self, query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find issues by state and keyword - - The query can contain any combination of the following supported - qualifers: - - - ``type`` With this qualifier you can restrict the search to issues - or pull request only. - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the title, body, comments, or any - combination of these. - - ``author`` Finds issues created by a certain user. - - ``assignee`` Finds issues that are assigned to a certain user. - - ``mentions`` Finds issues that mention a certain user. - - ``commenter`` Finds issues that a certain user commented on. - - ``involves`` Finds issues that were either created by a certain user, - assigned to that user, mention that user, or were commented on by - that user. - - ``state`` Filter issues based on whether they’re open or closed. - - ``labels`` Filters issues based on their labels. - - ``language`` Searches for issues within repositories that match a - certain language. - - ``created`` or ``updated`` Filters issues based on times of creation, - or when they were last updated. - - ``comments`` Filters issues based on the quantity of comments. - - ``user`` or ``repo`` Limits searches to a specific user or - repository. - - For more information about these qualifiers, see: http://git.io/d1oELA - - :param str query: (required), a valid query as described above, e.g., - ``windows label:bug`` - :param str sort: (optional), how the results should be sorted; - options: ``created``, ``comments``, ``updated``; - default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/QLQuSQ for more information - :param int number: (optional), number of issues to return. - Default: -1, returns all available issues - :param str etag: (optional), previous ETag header value - :return: generator of :class:`IssueSearchResult - ` - """ - params = {'q': query} - headers = {} - - if sort in ('comments', 'created', 'updated'): - params['sort'] = sort - - if order in ('asc', 'desc'): - params['order'] = order - - if text_match: - headers = { - 'Accept': 'application/vnd.github.v3.full.text-match+json' - } - - url = self._build_url('search', 'issues') - return SearchIterator(number, url, IssueSearchResult, self, params, - etag, headers) - - def search_repositories(self, query, sort=None, order=None, - per_page=None, text_match=False, number=-1, - etag=None): - """Find repositories via various criteria. - - The query can contain any combination of the following supported - qualifers: - - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the repository name, description, - readme, or any combination of these. - - ``size`` Finds repositories that match a certain size (in - kilobytes). - - ``forks`` Filters repositories based on the number of forks, and/or - whether forked repositories should be included in the results at - all. - - ``created`` or ``pushed`` Filters repositories based on times of - creation, or when they were last updated. Format: ``YYYY-MM-DD``. - Examples: ``created:<2011``, ``pushed:<2013-02``, - ``pushed:>=2013-03-06`` - - ``user`` or ``repo`` Limits searches to a specific user or - repository. - - ``language`` Searches repositories based on the language they're - written in. - - ``stars`` Searches repositories based on the number of stars. - - For more information about these qualifiers, see: http://git.io/4Z8AkA - - :param str query: (required), a valid query as described above, e.g., - ``tetris language:assembly`` - :param str sort: (optional), how the results should be sorted; - options: ``stars``, ``forks``, ``updated``; default: best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/4ct1eQ for more information - :param int number: (optional), number of repositories to return. - Default: -1, returns all available repositories - :param str etag: (optional), previous ETag header value - :return: generator of :class:`Repository ` - """ - params = {'q': query} - headers = {} - - if sort in ('stars', 'forks', 'updated'): - params['sort'] = sort - - if order in ('asc', 'desc'): - params['order'] = order - - if text_match: - headers = { - 'Accept': 'application/vnd.github.v3.full.text-match+json' - } - - url = self._build_url('search', 'repositories') - return SearchIterator(number, url, RepositorySearchResult, self, - params, etag, headers) - - def search_users(self, query, sort=None, order=None, per_page=None, - text_match=False, number=-1, etag=None): - """Find users via the Search API. - - The query can contain any combination of the following supported - qualifers: - - - - ``type`` With this qualifier you can restrict the search to just - personal accounts or just organization accounts. - - ``in`` Qualifies which fields are searched. With this qualifier you - can restrict the search to just the username, public email, full - name, or any combination of these. - - ``repos`` Filters users based on the number of repositories they - have. - - ``location`` Filter users by the location indicated in their - profile. - - ``language`` Search for users that have repositories that match a - certain language. - - ``created`` Filter users based on when they joined. - - ``followers`` Filter users based on the number of followers they - have. - - For more information about these qualifiers see: http://git.io/wjVYJw - - :param str query: (required), a valid query as described above, e.g., - ``tom repos:>42 followers:>1000`` - :param str sort: (optional), how the results should be sorted; - options: ``followers``, ``repositories``, or ``joined``; default: - best match - :param str order: (optional), the direction of the sorted results, - options: ``asc``, ``desc``; default: ``desc`` - :param int per_page: (optional) - :param bool text_match: (optional), if True, return matching search - terms. See http://git.io/_V1zRwa for more information - :param int number: (optional), number of search results to return; - Default: -1 returns all available - :param str etag: (optional), ETag header value of the last request. - :return: generator of :class:`UserSearchResult - ` - """ - params = {'q': query} - headers = {} - - if sort in ('followers', 'repositories', 'joined'): - params['sort'] = sort - - if order in ('asc', 'desc'): - params['order'] = order - - if text_match: - headers = { - 'Accept': 'application/vnd.github.v3.full.text-match+json' - } - - url = self._build_url('search', 'users') - return SearchIterator(number, url, UserSearchResult, self, params, - etag, headers) - - def set_client_id(self, id, secret): - """Allows the developer to set their client_id and client_secret for - their OAuth application. - - :param str id: 20-character hexidecimal client_id provided by GitHub - :param str secret: 40-character hexidecimal client_secret provided by - GitHub - """ - self.session.params = {'client_id': id, 'client_secret': secret} - - def set_user_agent(self, user_agent): - """Allows the user to set their own user agent string to identify with - the API. - - :param str user_agent: String used to identify your application. - Library default: "github3.py/{version}", e.g., "github3.py/0.5" - """ - if not user_agent: - return - self.session.headers.update({'User-Agent': user_agent}) - - @requires_auth - def star(self, username, repo): - """Star to username/repo - - :param str username: (required), owner of the repo - :param str repo: (required), name of the repo - :return: bool - """ - resp = False - if username and repo: - url = self._build_url('user', 'starred', username, repo) - resp = self._boolean(self._put(url), 204, 404) - return resp - - @requires_auth - def starred(self, sort=None, direction=None, number=-1, etag=None): - """Iterate over repositories starred by the authenticated user. - - .. versionchanged:: 1.0 - - This was split from ``iter_starred`` and requires authentication. - - :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 ` - """ - params = {'sort': sort, 'direction': direction} - self._remove_none(params) - url = self._build_url('user', 'starred') - return self._iter(int(number), url, Repository, params, etag) - - 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 ` - """ - 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) - - @requires_auth - def subscriptions(self, number=-1, etag=None): - """Iterate over repositories subscribed to by the authenticated user. - - :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 ` - """ - url = self._build_url('user', 'subscriptions') - return self._iter(int(number), url, Repository, etag=etag) - - def subscriptions_for(self, username, number=-1, etag=None): - """Iterate over repositories subscribed to by ``username``. - - :param str username: , 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 ` - """ - url = self._build_url('users', str(username), 'subscriptions') - return self._iter(int(number), url, Repository, etag=etag) - - @requires_auth - def unfollow(self, username): - """Make the authenticated user stop following username - - :param str username: (required) - :returns: bool - """ - resp = False - if username: - url = self._build_url('user', 'following', username) - resp = self._boolean(self._delete(url), 204, 404) - return resp - - @requires_auth - def unstar(self, username, repo): - """Unstar username/repo. - - :param str username: (required), owner of the repo - :param str repo: (required), name of the repo - :return: bool - """ - resp = False - if username and repo: - url = self._build_url('user', 'starred', username, repo) - resp = self._boolean(self._delete(url), 204, 404) - return resp - - @requires_auth - def update_me(self, name=None, email=None, blog=None, company=None, - location=None, hireable=False, bio=None): - """Update the profile of the authenticated user. - - :param str name: e.g., 'John Smith', not login name - :param str email: e.g., 'john.smith@example.com' - :param str blog: e.g., 'http://www.example.com/jsmith/blog' - :param str company: - :param str location: - :param bool hireable: defaults to False - :param str bio: GitHub flavored markdown - :returns: whether the operation was successful or not - :rtype: bool - """ - user = {'name': name, 'email': email, 'blog': blog, - 'company': company, 'location': location, - 'hireable': hireable, 'bio': bio} - self._remove_none(user) - url = self._build_url('user') - _json = self._json(self._patch(url, data=json.dumps(user)), 200) - if _json: - self._update_attributes(_json) - return True - return False - - def user(self, username): - """Returns a User object for the specified user name. - - :param str username: name of the user - :returns: :class:`User ` - """ - url = self._build_url('users', username) - json = self._json(self._get(url), 200) - return self._instance_or_null(users.User, json) - - @requires_auth - def user_issues(self, filter='', state='', labels='', sort='', - direction='', since=None, per_page=None, number=-1, - etag=None): - """List only the authenticated user's issues. Will not list - organization's issues - - .. versionchanged:: 1.0 - - ``per_page`` parameter added before ``number`` - - .. versionchanged:: 0.9.0 - - - The ``state`` parameter now accepts 'all' in addition to 'open' - and 'closed'. - - :param str filter: accepted values: - ('assigned', 'created', 'mentioned', 'subscribed') - api-default: 'assigned' - :param str state: accepted values: ('all', 'open', 'closed') - api-default: 'open' - :param str labels: comma-separated list of label names, e.g., - 'bug,ui,@high' - :param str sort: accepted values: ('created', 'updated', 'comments') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - api-default: desc - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :param int number: (optional), number of issues to return. - Default: -1 returns all issues - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Issue ` - """ - url = self._build_url('user', 'issues') - # issue_params will handle the since parameter - params = issue_params(filter, state, labels, sort, direction, since) - params.update(per_page=per_page) - return self._iter(int(number), url, Issue, params, etag) - - @requires_auth - def user_teams(self, number=-1, etag=None): - """Gets the authenticated user's teams across all of organizations. - - List all of the teams across all of the organizations to which the - authenticated user belongs. This method requires user or repo scope - when authenticating via OAuth. - - :returns: generator of :class:`Team ` objects - """ - url = self._build_url('user', 'teams') - return self._iter(int(number), url, Team, etag=etag) - - def user_with_id(self, number): - """Get the user's information with id ``number``. - - :param int number: the user's id number - :returns: :class:`User ` - """ - number = int(number) - json = None - if number > 0: - url = self._build_url('user', str(number)) - json = self._json(self._get(url), 200) - return self._instance_or_null(users.User, json) - - def zen(self): - """Returns a quote from the Zen of GitHub. Yet another API Easter Egg - - :returns: str (on Python 3, unicode on Python 2) - """ - url = self._build_url('zen') - resp = self._get(url) - return resp.text if resp.status_code == 200 else b''.decode('utf-8') - - -class GitHubEnterprise(GitHub): - """For GitHub Enterprise users, this object will act as the public API to - your instance. You must provide the URL to your instance upon - initialization and can provide the rest of the login details just like in - the :class:`GitHub ` object. - - There is no need to provide the end of the url (e.g., /api/v3/), that will - be taken care of by us. - - If you have a self signed SSL for your local github enterprise you can - override the validation by passing `verify=False`. - """ - def __init__(self, url, username='', password='', token='', verify=True): - super(GitHubEnterprise, self).__init__(username, password, token) - self.session.base_url = url.rstrip('/') + '/api/v3' - self.session.verify = verify - self.url = url - - def _repr(self): - return ''.format(self) - - @requires_auth - def create_user(self, login, email): - """Create a new user. - This is only available for administrators of the instance. - - :param str login: (required), The user's username. - :param str email: (required), The user's email address. - - :returns: :class:`User `, if successful - """ - url = self._build_url('admin', 'users') - payload = {'login': login, 'email': email} - json_data = self._json(self._post(url, data=payload), 201) - return self._instance_or_null(users.User, json_data) - - @requires_auth - def admin_stats(self, option): - """This is a simple way to get statistics about your system. - - :param str option: (required), accepted values: ('all', 'repos', - 'hooks', 'pages', 'orgs', 'users', 'pulls', 'issues', - 'milestones', 'gists', 'comments') - :returns: dict - """ - stats = {} - if option.lower() in ('all', 'repos', 'hooks', 'pages', 'orgs', - 'users', 'pulls', 'issues', 'milestones', - 'gists', 'comments'): - url = self._build_url('enterprise', 'stats', option.lower()) - stats = self._json(self._get(url), 200) - return stats - - -class GitHubStatus(GitHubCore): - """A sleek interface to the GitHub System Status API. This will only ever - return the JSON objects returned by the API. - """ - def __init__(self): - super(GitHubStatus, self).__init__({}) - self.session.base_url = 'https://status.github.com' - - def _repr(self): - return '' - - def _recipe(self, *args): - url = self._build_url(*args) - resp = self._get(url) - return resp.json() if self._boolean(resp, 200, 404) else {} - - def api(self): - """GET /api.json""" - return self._recipe('api.json') - - def status(self): - """GET /api/status.json""" - return self._recipe('api', 'status.json') - - def last_message(self): - """GET /api/last-message.json""" - return self._recipe('api', 'last-message.json') - - def messages(self): - """GET /api/messages.json""" - return self._recipe('api', 'messages.json') diff --git a/gitsome/lib/github3/issues/__init__.py b/gitsome/lib/github3/issues/__init__.py deleted file mode 100644 index affc784..0000000 --- a/gitsome/lib/github3/issues/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -github3.issues -============== - -This module contains the classes related to issues. - -See also: http://developer.github.com/v3/issues/ -""" - -from ..utils import timestamp_parameter -from .issue import Issue - -__all__ = [Issue] - - -def issue_params(filter, state, labels, sort, direction, since): - params = {} - if filter in ('assigned', 'created', 'mentioned', 'subscribed', 'all'): - params['filter'] = filter - - if state in ('open', 'closed', 'all'): - params['state'] = state - - if labels: - params['labels'] = labels - - if sort in ('created', 'updated', 'comments'): - params['sort'] = sort - - if direction in ('asc', 'desc'): - params['direction'] = direction - - since = timestamp_parameter(since) - if since: - params['since'] = since - - return params diff --git a/gitsome/lib/github3/issues/comment.py b/gitsome/lib/github3/issues/comment.py deleted file mode 100644 index eb9d8d3..0000000 --- a/gitsome/lib/github3/issues/comment.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from ..utils import timestamp_parameter -from ..models import BaseComment -from ..users import User - - -class IssueComment(BaseComment): - """The :class:`IssueComment ` object. This structures and - handles the comments on issues specifically. - - Two comment instances can be checked like so:: - - c1 == c2 - c1 != c2 - - And is equivalent to:: - - c1.id == c2.id - c1.id != c2.id - - See also: http://developer.github.com/v3/issues/comments/ - """ - def _update_attributes(self, comment): - super(IssueComment, self)._update_attributes(comment) - - user = comment.get('user') - #: :class:`User ` who made the comment - self.user = User(user, self) if user else None - - #: Issue url (not a template) - self.issue_url = comment.get('issue_url') - - #: Html url (not a template) - self.html_url = comment.get('html_url') - - def _repr(self): - return ''.format(self.user.login) - - -def issue_comment_params(sort, direction, since): - params = {} - - if sort in ('created', 'updated'): - params['sort'] = sort - - if direction in ('asc', 'desc'): - params['direction'] = direction - - since = timestamp_parameter(since) - if since: - params['since'] = since - - return params diff --git a/gitsome/lib/github3/issues/event.py b/gitsome/lib/github3/issues/event.py deleted file mode 100644 index dfd2489..0000000 --- a/gitsome/lib/github3/issues/event.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from ..models import GitHubCore -from ..users import User - - -class IssueEvent(GitHubCore): - """The :class:`IssueEvent ` object. This specifically deals - with events described in the - `Issues\>Events `_ section of - the GitHub API. - - Two event instances can be checked like so:: - - e1 == e2 - e1 != e2 - - And is equivalent to:: - - e1.commit_id == e2.commit_id - e1.commit_id != e2.commit_id - - """ - def _update_attributes(self, event): - # The type of event: - # ('closed', 'reopened', 'subscribed', 'merged', 'referenced', - # 'mentioned', 'assigned') - #: The type of event, e.g., closed - self.event = event.get('event') - #: SHA of the commit. - self.commit_id = event.get('commit_id') - self._api = event.get('url', '') - - #: :class:`Issue ` where this comment was made. - self.issue = event.get('issue') - if self.issue: - from .issue import Issue - self.issue = Issue(self.issue, self) - - #: :class:`User ` who caused this event. - self.actor = event.get('actor') - if self.actor: - self.actor = User(self.actor, self) - - #: :class:`User ` that generated the event. - self.actor = event.get('actor') - if self.actor: - self.actor = User(self.actor, self) - - #: Number of comments - self.comments = event.get('comments', 0) - - #: datetime object representing when the event was created. - self.created_at = self._strptime(event.get('created_at')) - - #: Dictionary of links for the pull request - self.pull_request = event.get('pull_request', {}) - - #: Dictionary containing label details - self.label = event.get('label', {}) - - #: The integer ID of the event - self.id = event.get('id') - - #: :class:`User ` that is assigned - self.assignee = event.get('assignee') - if self.assignee: - self.assignee = User(self.assignee, self) - - #: Dictionary containing milestone details - self.milestone = event.get('milestone', {}) - - #: Dictionary containing to and from attributes - self.rename = event.get('rename', {}) - - self._uniq = self.commit_id - - def _repr(self): - return ''.format( - self.event, self.actor - ) diff --git a/gitsome/lib/github3/issues/issue.py b/gitsome/lib/github3/issues/issue.py deleted file mode 100644 index cd8afac..0000000 --- a/gitsome/lib/github3/issues/issue.py +++ /dev/null @@ -1,339 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from re import match -from json import dumps -from ..decorators import requires_auth -from .comment import IssueComment, issue_comment_params -from .event import IssueEvent -from .label import Label -from .milestone import Milestone -from ..models import GitHubCore -from ..users import User -from uritemplate import URITemplate - - -class Issue(GitHubCore): - - """The :class:`Issue ` object. It structures and handles the data - returned via the `Issues `_ section - of the GitHub API. - - Two issue instances can be checked like so:: - - i1 == i2 - i1 != i2 - - And is equivalent to:: - - i1.id == i2.id - i1.id != i2.id - - """ - - # The Accept header will likely be removable once the feature is out of - # preview mode. See: https://git.io/vgXmB - LOCKING_PREVIEW_HEADERS = { - 'Accept': 'application/vnd.github.the-key-preview+json' - } - - def _update_attributes(self, issue): - self._api = issue.get('url', '') - #: :class:`User ` representing the user the issue - #: was assigned to. - self.assignee = issue.get('assignee') - if self.assignee: - self.assignee = User(issue.get('assignee'), self) - #: Body (description) of the issue. - self.body = issue.get('body', '') - #: HTML formatted body of the issue. - self.body_html = issue.get('body_html', '') - #: Plain text formatted body of the issue. - self.body_text = issue.get('body_text', '') - - # If an issue is still open, this field will be None - #: datetime object representing when the issue was closed. - self.closed_at = self._strptime(issue.get('closed_at')) - - #: Number of comments on this issue. - self.comments_count = issue.get('comments') - #: Comments url (not a template) - self.comments_url = issue.get('comments_url') - #: datetime object representing when the issue was created. - self.created_at = self._strptime(issue.get('created_at')) - #: Events url (not a template) - self.events_url = issue.get('events_url') - #: URL to view the issue at GitHub. - self.html_url = issue.get('html_url') - #: Unique ID for the issue. - self.id = issue.get('id') - #: Returns the list of :class:`Label `\ s - #: on this issue. - self.original_labels = [ - Label(l, self) for l in issue.get('labels') - ] - labels_url = issue.get('labels_url') - #: Labels URL Template. Expand with ``name`` - self.labels_urlt = URITemplate(labels_url) if labels_url else None - #: Locked status - self.locked = issue.get('locked') - #: :class:`Milestone ` this - #: issue was assigned to. - self.milestone = None - if issue.get('milestone'): - self.milestone = Milestone(issue.get('milestone'), self) - #: Issue number (e.g. #15) - self.number = issue.get('number') - #: Dictionary URLs for the pull request (if they exist) - self.pull_request_urls = issue.get('pull_request', {}) - m = match('https?://[\w\d\-\.\:]+/(\S+)/(\S+)/(?:issues|pull)/\d+', - self.html_url) - #: Returns ('owner', 'repository') this issue was filed on. - self.repository = m.groups() - #: State of the issue, e.g., open, closed - self.state = issue.get('state') - #: Title of the issue. - self.title = issue.get('title') - #: datetime object representing the last time the issue was updated. - self.updated_at = self._strptime(issue.get('updated_at')) - #: :class:`User ` who opened the issue. - self.user = User(issue.get('user'), self) - - closed_by = issue.get('closed_by') - #: :class:`User ` who closed the issue. - self.closed_by = User(closed_by, self) if closed_by else None - - def _repr(self): - return ''.format(r=self.repository, - n=self.number) - - @requires_auth - def add_labels(self, *args): - """Add labels to this issue. - - :param str args: (required), names of the labels you wish to add - :returns: list of :class:`Label`\ s - """ - url = self._build_url('labels', base_url=self._api) - json = self._json(self._post(url, data=args), 200) - return [Label(l, self) for l in json] if json else [] - - @requires_auth - def assign(self, username): - """Assigns user ``username`` to this issue. This is a short cut for - ``issue.edit``. - - :param str username: username of the person to assign this issue to - :returns: bool - """ - if not username: - return False - number = self.milestone.number if self.milestone else None - labels = [str(l) for l in self.original_labels] - return self.edit(self.title, self.body, username, self.state, number, - labels) - - @requires_auth - def close(self): - """Close this issue. - - :returns: bool - """ - assignee = self.assignee.login if self.assignee else '' - number = self.milestone.number if self.milestone else None - labels = [str(l) for l in self.original_labels] - return self.edit(self.title, self.body, assignee, 'closed', - number, labels) - - def comment(self, id_num): - """Get a single comment by its id. - - The catch here is that id is NOT a simple number to obtain. If - you were to look at the comments on issue #15 in - sigmavirus24/Todo.txt-python, the first comment's id is 4150787. - - :param int id_num: (required), comment id, see example above - :returns: :class:`IssueComment ` - """ - json = None - if int(id_num) > 0: # Might as well check that it's positive - owner, repo = self.repository - url = self._build_url('repos', owner, repo, 'issues', 'comments', - str(id_num)) - json = self._json(self._get(url), 200) - return self._instance_or_null(IssueComment, json) - - def comments(self, number=-1, sort='', direction='', since=None): - """Iterate over the comments on this issue. - - :param int number: (optional), number of comments to iterate over - Default: -1 returns all comments - :param str sort: accepted valuees: ('created', 'updated') - api-default: created - :param str direction: accepted values: ('asc', 'desc') - Ignored without the sort parameter - :param since: (optional), Only issues after this date will - be returned. This can be a `datetime` or an ISO8601 formatted - date string, e.g., 2012-05-20T23:10:27Z - :type since: datetime or string - :returns: iterator of - :class:`IssueComment `\ s - """ - url = self._build_url('comments', base_url=self._api) - params = issue_comment_params(sort, direction, since) - return self._iter(int(number), url, IssueComment, params) - - @requires_auth - def create_comment(self, body): - """Create a comment on this issue. - - :param str body: (required), comment body - :returns: :class:`IssueComment ` - """ - json = None - if body: - url = self._build_url('comments', base_url=self._api) - json = self._json(self._post(url, data={'body': body}), - 201) - return self._instance_or_null(IssueComment, json) - - @requires_auth - def edit(self, title=None, body=None, assignee=None, state=None, - milestone=None, labels=None): - """Edit this issue. - - :param str title: Title of the issue - :param str body: markdown formatted body (description) of the issue - :param str assignee: login name of user the issue should be assigned - to - :param str state: accepted values: ('open', 'closed') - :param int milestone: the NUMBER (not title) of the milestone to - assign this to [1]_, or 0 to remove the milestone - :param list labels: list of labels to apply this to - :returns: bool - - .. [1] Milestone numbering starts at 1, i.e. the first milestone you - create is 1, the second is 2, etc. - """ - json = None - data = {'title': title, 'body': body, 'assignee': assignee, - 'state': state, 'milestone': milestone, 'labels': labels} - self._remove_none(data) - if data: - if 'milestone' in data and data['milestone'] == 0: - data['milestone'] = None - json = self._json(self._patch(self._api, data=dumps(data)), 200) - if json: - self._update_attributes(json) - return True - return False - - def events(self, number=-1): - """Iterate over events associated with this issue only. - - :param int number: (optional), number of events to return. Default: -1 - returns all events available. - :returns: generator of - :class:`IssueEvent `\ s - """ - url = self._build_url('events', base_url=self._api) - return self._iter(int(number), url, IssueEvent) - - def is_closed(self): - """Checks if the issue is closed. - - :returns: bool - """ - if self.closed_at or (self.state == 'closed'): - return True - return False - - def labels(self, number=-1, etag=None): - """Iterate over the labels associated with this issue. - - :param int number: (optional), number of labels to return. Default: -1 - returns all labels applied to this issue. - :param str etag: (optional), ETag from a previous request to the same - endpoint - :returns: generator of :class:`Label `\ s - """ - url = self._build_url('labels', base_url=self._api) - return self._iter(int(number), url, Label, etag=etag) - - @requires_auth - def lock(self): - """Lock an issue. - - :returns: bool - """ - headers = Issue.LOCKING_PREVIEW_HEADERS - - url = self._build_url('lock', base_url=self._api) - return self._boolean(self._put(url, headers=headers), 204, 404) - - def pull_request(self): - """Retrieve the pull request associated with this issue. - - :returns: :class:`~github3.pulls.PullRequest` - """ - from .. import pulls - json = None - pull_request_url = self.pull_request_urls.get('url') - if pull_request_url: - json = self._json(self._get(pull_request_url), 200) - return self._instance_or_null(pulls.PullRequest, json) - - @requires_auth - def remove_label(self, name): - """Removes label ``name`` from this issue. - - :param str name: (required), name of the label to remove - :returns: list of :class:`Label` - """ - url = self._build_url('labels', name, base_url=self._api) - json = self._json(self._delete(url), 200, 404) - labels = [Label(label, self) for label in json] if json else [] - return labels - - @requires_auth - def remove_all_labels(self): - """Remove all labels from this issue. - - :returns: an empty list if successful - """ - # Can either send DELETE or [] to remove all labels - return self.replace_labels([]) - - @requires_auth - def replace_labels(self, labels): - """Replace all labels on this issue with ``labels``. - - :param list labels: label names - :returns: list of :class:`Label` - """ - url = self._build_url('labels', base_url=self._api) - json = self._json(self._put(url, data=dumps(labels)), 200) - return [Label(l, self) for l in json] if json else [] - - @requires_auth - def reopen(self): - """Re-open a closed issue. - - :returns: bool - """ - assignee = self.assignee.login if self.assignee else '' - number = self.milestone.number if self.milestone else None - labels = [str(l) for l in self.original_labels] - return self.edit(self.title, self.body, assignee, 'open', - number, labels) - - @requires_auth - def unlock(self): - """Unlock an issue. - - :returns: bool - """ - headers = Issue.LOCKING_PREVIEW_HEADERS - - url = self._build_url('lock', base_url=self._api) - return self._boolean(self._delete(url, headers=headers), 204, 404) diff --git a/gitsome/lib/github3/issues/label.py b/gitsome/lib/github3/issues/label.py deleted file mode 100644 index cef5a07..0000000 --- a/gitsome/lib/github3/issues/label.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from json import dumps -from ..decorators import requires_auth -from ..models import GitHubCore - - -class Label(GitHubCore): - """The :class:`Label