Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions bitbucket/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import requests

from .issue import Issue
from .pullrequest import PullRequest
from .repository import Repository
from .service import Service
from .ssh import SSH
Expand All @@ -27,6 +28,7 @@
# ========
URLS = {
'BASE': 'https://bitbucket.org/!api/1.0/%s',
'BASE_V2': 'https://bitbucket.org/!api/2.0/%s',
# Get user profile and repos
'GET_USER': 'users/%(username)s/',
'GET_USER_PRIVILEGES': 'user/privileges',
Expand Down Expand Up @@ -55,6 +57,7 @@ def __init__(self, username='', password='', repo_name_or_slug=''):
self.service = Service(self)
self.ssh = SSH(self)
self.issue = Issue(self)
self.pullrequest = PullRequest(self)
self.deploy_key = DeployKey(self)

self.access_token = None
Expand Down Expand Up @@ -227,23 +230,26 @@ def dispatch(self, method, url, auth=None, params=None, **kwargs):
s = Session()
resp = s.send(r.prepare())
status = resp.status_code
text = resp.text
content = resp.content # Includes binary

error = resp.reason
if status >= 200 and status < 300:
if text:
if content:
try:
return (True, json.loads(text))
return (True, json.loads(content))
except TypeError:
pass
except ValueError:
pass
return (True, text)

return (True, content)
elif status >= 300 and status < 400:
return (
False,
'Unauthorized access, '
'please check your credentials.')
elif status >= 400 and status < 500:
import pdb; pdb.set_trace()
return (False, 'Service not found.')
elif status >= 500 and status < 600:
return (False, 'Server error.')
Expand All @@ -255,6 +261,11 @@ def url(self, action, **kwargs):
# TODO : should be static method ?
return self.URLS['BASE'] % self.URLS[action] % kwargs

def url_v2(self, action, **kwargs):
""" Construct and return the URL for a specific API service. """
# TODO : should be static method ?
return self.URLS['BASE_V2'] % self.URLS[action] % kwargs

# =====================
# = General functions =
# =====================
Expand Down Expand Up @@ -290,3 +301,42 @@ def get_privileges(self):
""" Get privledges for this user. """
url = self.url('GET_USER_PRIVILEGES')
return self.dispatch('GET', url, auth=self.auth)


class BitbucketTeam(Bitbucket):
""" This class lets you interact with the bitbucket public API. """
def __init__(self, username='', password='', repo_name_or_slug='', team=None):
self.team = team
super(self.__class__, self).__init__(username=username, password=password, repo_name_or_slug=repo_name_or_slug)

# ===================
# = Getters/Setters =
# ===================

@property
def auth(self):
""" Return credentials for current Bitbucket user. """
if self.oauth:
return self.oauth
return (self._username, self.password)

@property
def username(self):
"""Return your repository's username."""
return self.team or Bitbucket.username.fget(self)

@username.setter
def username(self, value):
try:
if isinstance(value, basestring):
self._username = unicode(value)
except NameError:
self._username = value

if value is None:
self._username = None

@username.deleter
def username(self):
del self._username

25 changes: 15 additions & 10 deletions bitbucket/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,23 @@ def issue_id(self, value):
def issue_id(self):
del self._issue_id

def all(self, repo_slug=None, params=None):
def all(self, repo_slug=None, params=None, owner=None):
""" Get issues from one of your repositories.
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('GET_ISSUES', username=self.bitbucket.username, repo_slug=repo_slug)
url = self.bitbucket.url('GET_ISSUES', username=owner, repo_slug=repo_slug)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth, params=params)

def get(self, issue_id, repo_slug=None):
def get(self, issue_id, repo_slug=None, owner=None):
""" Get an issue from one of your repositories.
"""
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('GET_ISSUE', username=self.bitbucket.username, repo_slug=repo_slug, issue_id=issue_id)
owner = owner or self.bitbucket.username
url = self.bitbucket.url('GET_ISSUE', username=owner, repo_slug=repo_slug, issue_id=issue_id)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth)

def create(self, repo_slug=None, **kwargs):
def create(self, repo_slug=None, owner=None, **kwargs):
"""
Add an issue to one of your repositories.
Each issue require a different set of attributes,
Expand All @@ -67,11 +69,12 @@ def create(self, repo_slug=None, **kwargs):
* status: The status of the issue (new, open, resolved, on hold, invalid, duplicate, or wontfix).
* kind: The kind of issue (bug, enhancement, or proposal).
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('CREATE_ISSUE', username=self.bitbucket.username, repo_slug=repo_slug)
url = self.bitbucket.url('CREATE_ISSUE', username=owner, repo_slug=repo_slug)
return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, **kwargs)

def update(self, issue_id, repo_slug=None, **kwargs):
def update(self, issue_id, repo_slug=None, owner=None, **kwargs):
"""
Update an issue to one of your repositories.
Each issue require a different set of attributes,
Expand All @@ -87,13 +90,15 @@ def update(self, issue_id, repo_slug=None, **kwargs):
* status: The status of the issue (new, open, resolved, on hold, invalid, duplicate, or wontfix).
* kind: The kind of issue (bug, enhancement, or proposal).
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('UPDATE_ISSUE', username=self.bitbucket.username, repo_slug=repo_slug, issue_id=issue_id)
url = self.bitbucket.url('UPDATE_ISSUE', username=owner, repo_slug=repo_slug, issue_id=issue_id)
return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs)

def delete(self, issue_id, repo_slug=None):
def delete(self, issue_id, repo_slug=None, owner=None):
""" Delete an issue from one of your repositories.
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('DELETE_ISSUE', username=self.bitbucket.username, repo_slug=repo_slug, issue_id=issue_id)
url = self.bitbucket.url('DELETE_ISSUE', username=owner, repo_slug=repo_slug, issue_id=issue_id)
return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth)
25 changes: 15 additions & 10 deletions bitbucket/issue_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,68 @@ def __init__(self, issue):
self.bitbucket.URLS.update(URLS)
self.issue_id = issue.issue_id

def all(self, issue_id=None, repo_slug=None):
def all(self, issue_id=None, repo_slug=None , owner=None):
""" Get issue comments from one of your repositories.
"""
issue_id = issue_id or self.issue_id
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
owner = owner or self.bitbucket.username
url = self.bitbucket.url('GET_COMMENTS',
username=self.bitbucket.username,
username=owner,
repo_slug=repo_slug,
issue_id=issue_id)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth)

def get(self, comment_id, issue_id=None, repo_slug=None):
def get(self, comment_id, issue_id=None, repo_slug=None, owner=None):
""" Get an issue from one of your repositories.
"""
issue_id = issue_id or self.issue_id
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url('GET_COMMENT',
username=self.bitbucket.username,
username=owner,
repo_slug=repo_slug,
issue_id=issue_id,
comment_id=comment_id)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth)

def create(self, issue_id=None, repo_slug=None, **kwargs):
def create(self, issue_id=None, repo_slug=None, owner=None, **kwargs):
""" Add an issue comment to one of your repositories.
Each issue comment require only the content data field
the system autopopulate the rest.
"""
issue_id = issue_id or self.issue_id
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
owner = owner or self.bitbucket.username
url = self.bitbucket.url('CREATE_COMMENT',
username=self.bitbucket.username,
username=owner,
repo_slug=repo_slug,
issue_id=issue_id)
return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, **kwargs)

def update(self, comment_id, issue_id=None, repo_slug=None, **kwargs):
def update(self, comment_id, issue_id=None, repo_slug=None, owner=None, **kwargs):
""" Update an issue comment in one of your repositories.
Each issue comment require only the content data field
the system autopopulate the rest.
"""
issue_id = issue_id or self.issue_id
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
owner = owner or self.bitbucket.username
url = self.bitbucket.url('UPDATE_COMMENT',
username=self.bitbucket.username,
username=owner,
repo_slug=repo_slug,
issue_id=issue_id,
comment_id=comment_id)
return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs)

def delete(self, comment_id, issue_id=None, repo_slug=None):
def delete(self, comment_id, issue_id=None, repo_slug=None, owner=None):
""" Delete an issue from one of your repositories.
"""
issue_id = issue_id or self.issue_id
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
owner = owner or self.bitbucket.username
url = self.bitbucket.url('DELETE_COMMENT',
username=self.bitbucket.username,
username=owner,
repo_slug=repo_slug,
issue_id=issue_id,
comment_id=comment_id)
Expand Down
104 changes: 104 additions & 0 deletions bitbucket/pullrequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
from .pullrequest_comment import PullRequestComment


URLS = {
# Issues
'GET_PULLREQUESTS': 'repositories/%(username)s/%(repo_slug)s/pullrequests/',
'GET_PULLREQUEST': 'repositories/%(username)s/%(repo_slug)s/pullrequests/%(issue_id)s/',
'CREATE_PULLREQUEST': 'repositories/%(username)s/%(repo_slug)s/pullrequests/',
'UPDATE_PULLREQUEST': 'repositories/%(username)s/%(repo_slug)s/pullrequests/%(issue_id)s/',
'DELETE_PULLREQUEST': 'repositories/%(username)s/%(repo_slug)s/pullrequests/%(issue_id)s/',
}


class PullRequest(object):
""" This class provide issue-related methods to Bitbucket objects."""

def __init__(self, bitbucket, pullrequest_id=None):
self.bitbucket = bitbucket
self.bitbucket.URLS.update(URLS)
self.pullrequest_id = pullrequest_id
self.comment = PullRequestComment(self)

@property
def pullrequest_id(self):
"""Your repository slug name."""
return self._pullrequest_id

@pullrequest_id.setter
def pullrequest_id(self, value):
if value:
self._pullrequest_id = int(value)
elif value is None:
self._pullrequest_id = None

@pullrequest_id.deleter
def pullrequest_id(self):
del self._pullrequest_id

def all(self, repo_slug=None, params=None, owner=None):
""" Get PullRequests from one of your repositories.
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url_v2('GET_PULLREQUESTS', username=owner, repo_slug=repo_slug)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth, params=params)

def get(self, pullrequest_id, repo_slug=None, owner=None):
""" Get a PullRequest from one of your repositories.
"""
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
owner = owner or self.bitbucket.username
url = self.bitbucket.url_v2('GET_PULLREQUEST', username=owner, repo_slug=repo_slug, issue_id=pullrequest_id)
return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth)

def create(self, repo_slug=None, owner=None, **kwargs):
"""
Add a PullRequest to one of your repositories.
Each issue require a different set of attributes,
you can pass them as keyword arguments (attributename='attributevalue').
Attributes are:

* title: A string representing the request title.
* description: The description of the pull request.
* name:
* milestone: The milestone associated with the issue.
* version: The version associated with the issue.
* responsible: The username of the person responsible for the issue.
* status: The status of the issue (new, open, resolved, on hold, invalid, duplicate, or wontfix).
* kind: The kind of issue (bug, enhancement, or proposal).
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url_v2('CREATE_PULLREQUEST', username=owner, repo_slug=repo_slug)
return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, **kwargs)

def update(self, issue_id, repo_slug=None, owner=None, **kwargs):
"""
Update an issue to one of your repositories.
Each issue require a different set of attributes,
you can pass them as keyword arguments (attributename='attributevalue').
Attributes are:

* title: The title of the new issue.
* content: The content of the new issue.
* component: The component associated with the issue.
* milestone: The milestone associated with the issue.
* version: The version associated with the issue.
* responsible: The username of the person responsible for the issue.
* status: The status of the issue (new, open, resolved, on hold, invalid, duplicate, or wontfix).
* kind: The kind of issue (bug, enhancement, or proposal).
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url_v2('UPDATE_PULLREQUEST', username=owner, repo_slug=repo_slug, issue_id=issue_id)
return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs)

def delete(self, issue_id, repo_slug=None, owner=None):
""" Delete an issue from one of your repositories.
"""
owner = owner or self.bitbucket.username
repo_slug = repo_slug or self.bitbucket.repo_slug or ''
url = self.bitbucket.url_v2('DELETE_PULLREQUEST', username=owner, repo_slug=repo_slug, issue_id=issue_id)
return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth)
Loading