bitbucket-api-0.5.0/0000755000076500000240000000000012304164207014653 5ustar baptoustaff00000000000000bitbucket-api-0.5.0/bitbucket/0000755000076500000240000000000012304164207016627 5ustar baptoustaff00000000000000bitbucket-api-0.5.0/bitbucket/__init__.py0000644000076500000240000000536312304157005020745 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- __version__ = '0.5.0' __doc__ = """ Bitbucket has a REST API publicly available, this package provide methods to interact with it. It allows you to access repositories and perform various actions on them. Various usages : :: from bitbucket.bitbucket import Bitbucket # Access a public repository bb = Bitbucket(USERNAME, repo_name_or_slug="public_repository") # Access a private repository bb = Bitbucket(USERNAME, PASSWORD, repo_name_or_slug="private_repository") # Access a private repository through oauth bb = Bitbucket(USERNAME, repo_name_or_slug="public_repository") bb.authorize(CONSUMER_KEY, CONSUMER_SECRET, 'http://localhost/') # Access your working repository success, result = bb.repository.get() # Create a repository, and define it as your working repository success, result = bb.repository.create("repository_slug") bb.repo_slug = "repository_slug" # Update your working repository success, result = bb.repository.update(description='new description') # Delete a repository success, result = bb.repository.delete("repository_slug") # Download a repository as an archive success, archive_path = bb.repository.archive() # Access user informations success, result = bb.get_user(username=USERNAME) # Access tags and branches success, result = bb.get_tags() success, result = bb.get_branches() # Access, create, update or delete a service (hook) success, result = bb.service.get(service_id=SERVICE_ID) success, result = bb.service.create(service=u'POST', URL='http://httpbin.org/') success, result = bb.service.update(service_id=SERVICE_ID, URL='http://google.com') success, result = bb.service.delete(service_id=SERVICE_ID) # Access, create or delete an SSH key success, result = bb.ssh.get(key_id=SSH_ID) success, result = bb.ssh.create(key=r'ssh-rsa a1b2c3d4e5', label=u'my key') success, result = bb.ssh.delete(key_id=SSH_ID) # Access, create, update or delete an issue success, result = bb.issue.get(issue_id=ISSUE_ID) success, result = bb.issue.create( title=u'Issue title', content=u'Issue content', responsible=bb.username, status=u'new', kind=u'bug') success, result = bb.issue.update(issue_id=ISSUE_ID, content='New content') success, result = bb.issue.delete(issue_id=ISSUE_ID) # Access, create, update or delete an issue comment success, result = bb.issue.comment.get(comment_id=COMMENT_ID) success, result = bb.issue.comment.create(content='Content') success, result = bb.issue.comment.update( comment_id=COMMENT_ID, content='New content') success, result = bb.issue.comment.delete(comment_id=COMMENT_ID) """ bitbucket-api-0.5.0/bitbucket/bitbucket.py0000644000076500000240000002222112304156623021157 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- # git+git://github.com/Sheeprider/BitBucket-api.git __all__ = ['Bitbucket', ] try: from urlparse import parse_qs except ImportError: from urllib.parse import parse_qs import json import re from requests import Request, Session from requests_oauthlib import OAuth1 import requests from .issue import Issue from .repository import Repository from .service import Service from .ssh import SSH from .deploy_key import DeployKey # ======== # = URLs = # ======== URLS = { 'BASE': 'https://bitbucket.org/!api/1.0/%s', # Get user profile and repos 'GET_USER': 'users/%(username)s/', 'GET_USER_PRIVILEGES': 'user/privileges', # Search repo # 'SEARCH_REPO': 'repositories/?name=%(search)s', # Get tags & branches 'GET_TAGS': 'repositories/%(username)s/%(repo_slug)s/tags/', 'GET_BRANCHES': 'repositories/%(username)s/%(repo_slug)s/branches/', 'REQUEST_TOKEN': 'oauth/request_token/', 'AUTHENTICATE': 'oauth/authenticate?oauth_token=%(token)s', 'ACCESS_TOKEN': 'oauth/access_token/' } class Bitbucket(object): """ This class lets you interact with the bitbucket public API. """ def __init__(self, username='', password='', repo_name_or_slug=''): self.username = username self.password = password self.repo_slug = repo_name_or_slug self.repo_tree = {} self.URLS = URLS self.repository = Repository(self) self.service = Service(self) self.ssh = SSH(self) self.issue = Issue(self) self.deploy_key = DeployKey(self) self.access_token = None self.access_token_secret = None self.consumer_key = None self.consumer_secret = None self.oauth = None # =================== # = 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._username @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 @property def password(self): """Return your repository's password.""" return self._password @password.setter def password(self, value): try: if isinstance(value, basestring): self._password = unicode(value) except NameError: self._password = value if value is None: self._password = None @password.deleter def password(self): del self._password @property def repo_slug(self): """Return your repository's slug name.""" return self._repo_slug @repo_slug.setter def repo_slug(self, value): if value is None: self._repo_slug = None else: try: if isinstance(value, basestring): value = unicode(value) except NameError: pass value = value.lower() self._repo_slug = re.sub(r'[^a-z0-9_-]+', '-', value) @repo_slug.deleter def repo_slug(self): del self._repo_slug # ======================== # = Oauth authentication = # ======================== def authorize(self, consumer_key, consumer_secret, callback_url=None, access_token=None, access_token_secret=None): """ Call this with your consumer key, secret and callback URL, to generate a token for verification. """ self.consumer_key = consumer_key self.consumer_secret = consumer_secret if not access_token and not access_token_secret: if not callback_url: return (False, "Callback URL required") oauth = OAuth1( consumer_key, client_secret=consumer_secret, callback_uri=callback_url) r = requests.post(self.url('REQUEST_TOKEN'), auth=oauth) if r.status_code == 200: creds = parse_qs(r.content) self.access_token = creds.get('oauth_token')[0] self.access_token_secret = creds.get('oauth_token_secret')[0] else: return (False, r.content) else: self.finalize_oauth(access_token, access_token_secret) return (True, None) def verify(self, verifier, consumer_key=None, consumer_secret=None, access_token=None, access_token_secret=None): """ After converting the token into verifier, call this to finalize the authorization. """ # Stored values can be supplied to verify self.consumer_key = consumer_key or self.consumer_key self.consumer_secret = consumer_secret or self.consumer_secret self.access_token = access_token or self.access_token self.access_token_secret = access_token_secret or self.access_token_secret oauth = OAuth1( self.consumer_key, client_secret=self.consumer_secret, resource_owner_key=self.access_token, resource_owner_secret=self.access_token_secret, verifier=verifier) r = requests.post(self.url('ACCESS_TOKEN'), auth=oauth) if r.status_code == 200: creds = parse_qs(r.content) else: return (False, r.content) self.finalize_oauth(creds.get('oauth_token')[0], creds.get('oauth_token_secret')[0]) return (True, None) def finalize_oauth(self, access_token, access_token_secret): """ Called internally once auth process is complete. """ self.access_token = access_token self.access_token_secret = access_token_secret # Final OAuth object self.oauth = OAuth1( self.consumer_key, client_secret=self.consumer_secret, resource_owner_key=self.access_token, resource_owner_secret=self.access_token_secret) # ====================== # = High lvl functions = # ====================== def dispatch(self, method, url, auth=None, params=None, **kwargs): """ Send HTTP request, with given method, credentials and data to the given URL, and return the success and the result on success. """ r = Request( method=method, url=url, auth=auth, params=params, data=kwargs) s = Session() resp = s.send(r.prepare()) status = resp.status_code text = resp.text error = resp.reason if status >= 200 and status < 300: if text: try: return (True, json.loads(text)) except TypeError: pass except ValueError: pass return (True, text) elif status >= 300 and status < 400: return ( False, 'Unauthorized access, ' 'please check your credentials.') elif status >= 400 and status < 500: return (False, 'Service not found.') elif status >= 500 and status < 600: return (False, 'Server error.') else: return (False, error) def url(self, action, **kwargs): """ Construct and return the URL for a specific API service. """ # TODO : should be static method ? return self.URLS['BASE'] % self.URLS[action] % kwargs # ===================== # = General functions = # ===================== def get_user(self, username=None): """ Returns user informations. If username is not defined, tries to return own informations. """ username = username or self.username or '' url = self.url('GET_USER', username=username) response = self.dispatch('GET', url) try: return (response[0], response[1]['user']) except TypeError: pass return response def get_tags(self, repo_slug=None): """ Get a single repository on Bitbucket and return its tags.""" repo_slug = repo_slug or self.repo_slug or '' url = self.url('GET_TAGS', username=self.username, repo_slug=repo_slug) return self.dispatch('GET', url, auth=self.auth) def get_branches(self, repo_slug=None): """ Get a single repository on Bitbucket and return its branches.""" repo_slug = repo_slug or self.repo_slug or '' url = self.url('GET_BRANCHES', username=self.username, repo_slug=repo_slug) return self.dispatch('GET', url, auth=self.auth) def get_privileges(self): """ Get privledges for this user. """ url = self.url('GET_USER_PRIVILEGES') return self.dispatch('GET', url, auth=self.auth) bitbucket-api-0.5.0/bitbucket/deploy_key.py0000644000076500000240000000524712304156623021360 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- URLS = { # deploy keys 'GET_DEPLOY_KEYS': 'repositories/%(username)s/%(repo_slug)s/deploy-keys', 'SET_DEPLOY_KEY': 'repositories/%(username)s/%(repo_slug)s/deploy-keys', 'GET_DEPLOY_KEY': 'repositories/%(username)s/%(repo_slug)s/deploy-keys/%(key_id)s', 'DELETE_DEPLOY_KEY': 'repositories/%(username)s/%(repo_slug)s/deploy-keys/%(key_id)s', } class DeployKey(object): """ This class provide services-related methods to Bitbucket objects.""" def __init__(self, bitbucket): self.bitbucket = bitbucket self.bitbucket.URLS.update(URLS) def all(self, repo_slug=None): """ Get all ssh keys associated with a repo """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_DEPLOY_KEYS', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def get(self, repo_slug=None, key_id=None): """ Get one of the ssh keys associated with this repo """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_DEPLOY_KEY', key_id=key_id, username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def create(self, repo_slug=None, key=None, label=None): """ Associate an ssh key with your repo and return it. """ key = '%s' % key repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('SET_DEPLOY_KEY', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, key=key, label=label) def delete(self, repo_slug=None, key_id=None): """ Delete one of the ssh keys associated with your repo. Please use with caution as there is NO confimation and NO undo. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('DELETE_DEPLOY_KEY', key_id=key_id, username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) bitbucket-api-0.5.0/bitbucket/issue.py0000644000076500000240000001062712304156623020342 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- from .issue_comment import IssueComment URLS = { # Issues 'GET_ISSUES': 'repositories/%(username)s/%(repo_slug)s/issues/', 'GET_ISSUE': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/', 'CREATE_ISSUE': 'repositories/%(username)s/%(repo_slug)s/issues/', 'UPDATE_ISSUE': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/', 'DELETE_ISSUE': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/', } class Issue(object): """ This class provide issue-related methods to Bitbucket objects.""" def __init__(self, bitbucket, issue_id=None): self.bitbucket = bitbucket self.bitbucket.URLS.update(URLS) self.issue_id = issue_id self.comment = IssueComment(self) @property def issue_id(self): """Your repository slug name.""" return self._issue_id @issue_id.setter def issue_id(self, value): if value: self._issue_id = int(value) elif value is None: self._issue_id = None @issue_id.deleter def issue_id(self): del self._issue_id def all(self, repo_slug=None, params=None): """ Get issues from one of your repositories. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_ISSUES', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth, params=params) def get(self, issue_id, repo_slug=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) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def create(self, repo_slug=None, **kwargs): """ Add 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). """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('CREATE_ISSUE', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, **kwargs) def update(self, issue_id, repo_slug=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). """ 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) return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs) def delete(self, issue_id, repo_slug=None): """ Delete an issue from one of your repositories. """ 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) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) bitbucket-api-0.5.0/bitbucket/issue_comment.py0000644000076500000240000000766212304156623022071 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- URLS = { # Issue comments 'GET_COMMENTS': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/comments/', 'GET_COMMENT': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/comments/%(comment_id)s/', 'CREATE_COMMENT': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/comments/', 'UPDATE_COMMENT': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/comments/%(comment_id)s/', 'DELETE_COMMENT': 'repositories/%(username)s/%(repo_slug)s/issues/%(issue_id)s/comments/%(comment_id)s/', } class IssueComment(object): """ This class provide issue's comments related methods to Bitbucket objects.""" def __init__(self, issue): self.issue = issue self.bitbucket = self.issue.bitbucket self.bitbucket.URLS.update(URLS) self.issue_id = issue.issue_id def all(self, issue_id=None, repo_slug=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 '' url = self.bitbucket.url('GET_COMMENTS', username=self.bitbucket.username, 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): """ Get 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 '' url = self.bitbucket.url('GET_COMMENT', username=self.bitbucket.username, 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): """ 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 '' url = self.bitbucket.url('CREATE_COMMENT', username=self.bitbucket.username, 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): """ 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 '' url = self.bitbucket.url('UPDATE_COMMENT', username=self.bitbucket.username, 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): """ 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 '' url = self.bitbucket.url('DELETE_COMMENT', username=self.bitbucket.username, repo_slug=repo_slug, issue_id=issue_id, comment_id=comment_id) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) bitbucket-api-0.5.0/bitbucket/repository.py0000644000076500000240000001201712304143747021427 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- from tempfile import NamedTemporaryFile from zipfile import ZipFile URLS = { 'CREATE_REPO': 'repositories/', 'GET_REPO': 'repositories/%(username)s/%(repo_slug)s/', 'UPDATE_REPO': 'repositories/%(username)s/%(repo_slug)s/', 'DELETE_REPO': 'repositories/%(username)s/%(repo_slug)s/', # Get archive 'GET_ARCHIVE': 'repositories/%(username)s/%(repo_slug)s/%(format)s/master/', } class Repository(object): """ This class provide repository-related methods to Bitbucket objects.""" def __init__(self, bitbucket): self.bitbucket = bitbucket self.bitbucket.URLS.update(URLS) def _get_files_in_dir(self, repo_slug=None, dir='/'): repo_slug = repo_slug or self.bitbucket.repo_slug or '' dir = dir.lstrip('/') url = self.bitbucket.url( 'GET_ARCHIVE', username=self.bitbucket.username, repo_slug=repo_slug, format='src') dir_url = url + dir response = self.bitbucket.dispatch('GET', dir_url, auth=self.bitbucket.auth) if response[0] and isinstance(response[1], dict): repo_tree = response[1] url = self.bitbucket.url( 'GET_ARCHIVE', username=self.bitbucket.username, repo_slug=repo_slug, format='raw') # Download all files in dir for file in repo_tree['files']: file_url = url + '/'.join((file['path'],)) response = self.bitbucket.dispatch('GET', file_url, auth=self.bitbucket.auth) self.bitbucket.repo_tree[file['path']] = response[1] # recursively download in dirs for directory in repo_tree['directories']: dir_path = '/'.join((dir, directory)) self._get_files_in_dir(repo_slug=repo_slug, dir=dir_path) def public(self, username=None): """ Returns all public repositories from an user. If username is not defined, tries to return own public repos. """ username = username or self.bitbucket.username or '' url = self.bitbucket.url('GET_USER', username=username) response = self.bitbucket.dispatch('GET', url) try: return (response[0], response[1]['repositories']) except TypeError: pass return response def all(self): """ Return own repositories.""" url = self.bitbucket.url('GET_USER', username=self.bitbucket.username) response = self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) try: return (response[0], response[1]['repositories']) except TypeError: pass return response def get(self, repo_slug=None): """ Get a single repository on Bitbucket and return it.""" repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_REPO', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def create(self, repo_name, scm='git', private=True, **kwargs): """ Creates a new repository on own Bitbucket account and return it.""" url = self.bitbucket.url('CREATE_REPO') return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, name=repo_name, scm=scm, is_private=private, **kwargs) def update(self, repo_slug=None, **kwargs): """ Updates repository on own Bitbucket account and return it.""" repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('UPDATE_REPO', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs) def delete(self, repo_slug=None): """ Delete a repository on own Bitbucket account. Please use with caution as there is NO confimation and NO undo. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('DELETE_REPO', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) def archive(self, repo_slug=None, format='zip', prefix=''): """ Get one of your repositories and compress it as an archive. Return the path of the archive. format parameter is curently not supported. """ prefix = '%s'.lstrip('/') % prefix self._get_files_in_dir(repo_slug=repo_slug, dir='/') if self.bitbucket.repo_tree: with NamedTemporaryFile(delete=False) as archive: with ZipFile(archive, 'w') as zip_archive: for name, file in self.bitbucket.repo_tree.items(): with NamedTemporaryFile(delete=False) as temp_file: temp_file.write(file.encode('utf-8')) zip_archive.write(temp_file.name, prefix + name) return (True, archive.name) return (False, 'Could not archive your project.') bitbucket-api-0.5.0/bitbucket/service.py0000644000076500000240000000664512304143755020661 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- URLS = { # Get services (hooks) 'GET_SERVICE': 'repositories/%(username)s/%(repo_slug)s/services/%(service_id)s/', 'GET_SERVICES': 'repositories/%(username)s/%(repo_slug)s/services/', # Set services (hooks) 'SET_SERVICE': 'repositories/%(username)s/%(repo_slug)s/services/', 'UPDATE_SERVICE': 'repositories/%(username)s/%(repo_slug)s/services/%(service_id)s/', 'DELETE_SERVICE': 'repositories/%(username)s/%(repo_slug)s/services/%(service_id)s/', } class Service(object): """ This class provide services-related methods to Bitbucket objects.""" def __init__(self, bitbucket): self.bitbucket = bitbucket self.bitbucket.URLS.update(URLS) def create(self, service, repo_slug=None, **kwargs): """ Add a service (hook) to one of your repositories. Each type of service require a different set of additionnal fields, you can pass them as keyword arguments (fieldname='fieldvalue'). """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('SET_SERVICE', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, type=service, **kwargs) def get(self, service_id, repo_slug=None): """ Get a service (hook) from one of your repositories. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_SERVICE', username=self.bitbucket.username, repo_slug=repo_slug, service_id=service_id) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def update(self, service_id, repo_slug=None, **kwargs): """ Update a service (hook) from one of your repositories. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('UPDATE_SERVICE', username=self.bitbucket.username, repo_slug=repo_slug, service_id=service_id) return self.bitbucket.dispatch('PUT', url, auth=self.bitbucket.auth, **kwargs) def delete(self, service_id, repo_slug=None): """ Delete a service (hook) from one of your repositories. Please use with caution as there is NO confimation and NO undo. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('DELETE_SERVICE', username=self.bitbucket.username, repo_slug=repo_slug, service_id=service_id) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) def all(self, repo_slug=None): """ Get all services (hook) from one of your repositories. """ repo_slug = repo_slug or self.bitbucket.repo_slug or '' url = self.bitbucket.url('GET_SERVICES', username=self.bitbucket.username, repo_slug=repo_slug) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) # ============ # = Services = # ============ # SERVICES = { # 'Basecamp': ('Username', 'Password', 'Discussion URL',), # 'CIA.vc': ('Module', 'Project',), # 'Email Diff': ('Email',), # 'Email': ('Email',), # 'FogBugz': ('Repository ID', 'CVSSubmit URL',), # 'FriendFeed': ('Username', 'Remote Key', 'Format',), # 'Geocommit': (None,), # 'Issues': (None,), # 'Lighthouse': ('Project ID', 'API Key', 'Subdomain',), # 'Pivotal Tracker': ('Token',), # 'POST': ('URL',), # 'Rietveld': ('Email', 'Password', 'URL',), # 'Superfeedr': (None,), # } bitbucket-api-0.5.0/bitbucket/ssh.py0000644000076500000240000000274612304143761020011 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- URLS = { # SSH keys 'GET_SSH_KEYS': 'ssh-keys/', 'GET_SSH_KEY': 'ssh-keys/%(key_id)s', 'SET_SSH_KEY': 'ssh-keys/', 'DELETE_SSH_KEY': 'ssh-keys/%(key_id)s', } class SSH(object): """ This class provide ssh-related methods to Bitbucket objects.""" def __init__(self, bitbucket): self.bitbucket = bitbucket self.bitbucket.URLS.update(URLS) def all(self): """ Get all ssh keys associated with your account. """ url = self.bitbucket.url('GET_SSH_KEYS') return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def get(self, key_id=None): """ Get one of the ssh keys associated with your account. """ url = self.bitbucket.url('GET_SSH_KEY', key_id=key_id) return self.bitbucket.dispatch('GET', url, auth=self.bitbucket.auth) def create(self, key=None, label=None): """ Associate an ssh key with your account and return it. """ key = '%s' % key url = self.bitbucket.url('SET_SSH_KEY') return self.bitbucket.dispatch('POST', url, auth=self.bitbucket.auth, key=key, label=label) def delete(self, key_id=None): """ Delete one of the ssh keys associated with your account. Please use with caution as there is NO confimation and NO undo. """ url = self.bitbucket.url('DELETE_SSH_KEY', key_id=key_id) return self.bitbucket.dispatch('DELETE', url, auth=self.bitbucket.auth) bitbucket-api-0.5.0/bitbucket/tests/0000755000076500000240000000000012304164207017771 5ustar baptoustaff00000000000000bitbucket-api-0.5.0/bitbucket/tests/__init__.py0000644000076500000240000000015512121366433022105 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- from bitbucket.tests.public import * if __name__ == "__main__": unittest.main() bitbucket-api-0.5.0/bitbucket/tests/public.py0000755000076500000240000001075012304156623021632 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- # run with `site-packages$> python -m bitbucket.tests.public` import unittest from bitbucket.bitbucket import Bitbucket httpbin = 'http://httpbin.org/' foo = u'foo' bar = u'bar' username = 'baptistemillou' class AnonymousBitbucketTest(unittest.TestCase): """ Bitbucket test base class.""" def setUp(self): """Create a new annonymous Bitbucket...""" self.bb = Bitbucket() def tearDown(self): """Destroy the Bitbucket...""" self.bb = None class BitbucketUtilitiesTest(AnonymousBitbucketTest): """ Test Bitbucket utilities functions.""" def test_default_credential(self): self.assertEqual(self.bb.username, '') self.assertEqual(self.bb.password, '') self.assertEqual(self.bb.repo_slug, '') def test_dispatch_get(self): success, result = self.bb.dispatch('GET', httpbin + 'get') self.assertTrue(success) self.assertIsInstance(result, dict) def test_dispatch_post(self): success, result = self.bb.dispatch('POST', httpbin + 'post', foo='bar') self.assertTrue(success) self.assertIsInstance(result, dict) self.assertEqual(result['form'], {foo: bar}) def test_dispatch_put(self): success, result = self.bb.dispatch('PUT', httpbin + 'put', foo='bar') self.assertTrue(success) self.assertIsInstance(result, dict) self.assertEqual(result['form'], {foo: bar}) def test_dispatch_delete(self): success, result = self.bb.dispatch('DELETE', httpbin + 'delete') self.assertTrue(success) self.assertIsInstance(result, dict) def test_url_simple(self): base = self.bb.URLS['BASE'] create_repo = self.bb.URLS['CREATE_REPO'] self.assertEqual(self.bb.url('CREATE_REPO'), base % create_repo) def test_url_complex(self): base = self.bb.URLS['BASE'] get_branches = self.bb.URLS['GET_BRANCHES'] self.assertEqual( self.bb.url('GET_BRANCHES', username=self.bb.username, repo_slug=self.bb.repo_slug), base % get_branches % {'username': '', 'repo_slug': ''}) def test_auth(self): self.assertEqual(self.bb.auth, (self.bb.username, self.bb.password)) def test_username(self): self.bb.username = foo self.assertEqual(self.bb.username, foo) del self.bb.username with self.assertRaises(AttributeError): self.bb.username def test_password(self): self.bb.password = foo self.assertEqual(self.bb.password, foo) del self.bb.password with self.assertRaises(AttributeError): self.bb.password def test_repo_slug(self): self.bb.repo_slug = foo self.assertEqual(self.bb.repo_slug, foo) del self.bb.repo_slug with self.assertRaises(AttributeError): self.bb.repo_slug class BitbucketAnnonymousMethodsTest(AnonymousBitbucketTest): """ Test Bitbucket annonymous methods.""" def test_get_user(self): """ Test get_user on specific user.""" success, result = self.bb.get_user(username=username) self.assertTrue(success) self.assertIsInstance(result, dict) def test_get_self_user(self): """ Test get_user on self username.""" self.bb.username = username success, result = self.bb.get_user() self.assertTrue(success) self.assertIsInstance(result, dict) def test_get_none_user(self): """ Test get_user with no username.""" self.bb.username = None success, result = self.bb.get_user() self.assertFalse(success) self.assertEqual(result, 'Service not found.') def test_get_public_repos(self): """ Test public_repos on specific user.""" success, result = self.bb.repository.public(username=username) self.assertTrue(success) self.assertIsInstance(result, (dict, list)) def test_get_self_public_repos(self): """ Test public_repos on specific user.""" self.bb.username = username success, result = self.bb.repository.public() self.assertTrue(success) self.assertIsInstance(result, (dict, list)) def test_get_none_public_repos(self): """ Test public_repos on specific user.""" self.bb.username = None success, result = self.bb.repository.public() self.assertFalse(success) self.assertEqual(result, 'Service not found.') if __name__ == "__main__": unittest.main() bitbucket-api-0.5.0/LICENSE0000644000076500000240000000133512121366433015664 0ustar baptoustaff00000000000000Copyright (c) 2012 Millou Baptiste Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. bitbucket-api-0.5.0/PKG-INFO0000644000076500000240000000541012304164207015750 0ustar baptoustaff00000000000000Metadata-Version: 1.1 Name: bitbucket-api Version: 0.5.0 Summary: Bitbucket API Home-page: https://github.com/Sheeprider/BitBucket-api Author: Baptiste Millou Author-email: baptiste@smoothie-creative.com License: Copyright (c) 2012 Millou Baptiste Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Description: #BitBucket-API [![Build Status](https://secure.travis-ci.org/Sheeprider/BitBucket-api.png)](http://travis-ci.org/Sheeprider/BitBucket-api) BitBucket-api is an ISC Licensed library, written in Python. Bitbucket has a REST API publicly available, this package provide methods to interact with it. It allows you to access most repositories, services (hooks) and ssh keys related functionalities. ##Features * Access public user informations * Access public or private repositories, tags or branches * Create, update or delete one of your repository * Access, create, update or delete a service (hook) * Access, create or delete an SSH key * Download a repository as an archive * Access, create, update or delete an issue * Access, create, update or delete an issue comment ##Installation To install bitbucket-api, simply: $ pip install bitbucket-api ##Requirements Bitbucket-api require [requests](https://github.com/kennethreitz/requests), [sh](https://github.com/amoffat/sh) and [requests-oauthlib](https://github.com/requests/requests-oauthlib)to work, but dependencies should be handled by pip. ##Documentation Documentation is available on [Read The Docs](https://bitbucket-api.readthedocs.org/en/latest/index.html). Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: ISC License (ISCL) Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules bitbucket-api-0.5.0/README0000644000076500000240000000232012304143405015525 0ustar baptoustaff00000000000000#BitBucket-API [![Build Status](https://secure.travis-ci.org/Sheeprider/BitBucket-api.png)](http://travis-ci.org/Sheeprider/BitBucket-api) BitBucket-api is an ISC Licensed library, written in Python. Bitbucket has a REST API publicly available, this package provide methods to interact with it. It allows you to access most repositories, services (hooks) and ssh keys related functionalities. ##Features * Access public user informations * Access public or private repositories, tags or branches * Create, update or delete one of your repository * Access, create, update or delete a service (hook) * Access, create or delete an SSH key * Download a repository as an archive * Access, create, update or delete an issue * Access, create, update or delete an issue comment ##Installation To install bitbucket-api, simply: $ pip install bitbucket-api ##Requirements Bitbucket-api require [requests](https://github.com/kennethreitz/requests), [sh](https://github.com/amoffat/sh) and [requests-oauthlib](https://github.com/requests/requests-oauthlib)to work, but dependencies should be handled by pip. ##Documentation Documentation is available on [Read The Docs](https://bitbucket-api.readthedocs.org/en/latest/index.html). bitbucket-api-0.5.0/setup.py0000644000076500000240000000177112304143405016370 0ustar baptoustaff00000000000000# -*- coding: utf-8 -*- from distutils.core import setup import bitbucket if __name__ == "__main__": setup( name='bitbucket-api', version=bitbucket.__version__, description='Bitbucket API', long_description=open('README').read(), author='Baptiste Millou', author_email='baptiste@smoothie-creative.com', url='https://github.com/Sheeprider/BitBucket-api', packages=[ 'bitbucket', 'bitbucket.tests', ], license=open('LICENSE').read(), install_requires=['requests', 'sh', 'requests-oauthlib'], classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: ISC License (ISCL)', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', ], )