[Python-modules-commits] [python-bitbucket-api] 01/03: New upstream version 0.5.0

ChangZhuo Chen czchen at moszumanska.debian.org
Sat Oct 15 08:35:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

czchen pushed a commit to branch master
in repository python-bitbucket-api.

commit 770624d03ca1f01b954a5844f56d9f3c4283c7cc
Author: ChangZhuo Chen (陳昌倬) <czchen at debian.org>
Date:   Sat Oct 15 16:24:59 2016 +0800

    New upstream version 0.5.0
---
 LICENSE                     |  13 ++
 PKG-INFO                    |  63 ++++++++++
 README                      |  33 +++++
 bitbucket/__init__.py       |  76 ++++++++++++
 bitbucket/bitbucket.py      | 292 ++++++++++++++++++++++++++++++++++++++++++++
 bitbucket/deploy_key.py     |  60 +++++++++
 bitbucket/issue.py          |  99 +++++++++++++++
 bitbucket/issue_comment.py  |  81 ++++++++++++
 bitbucket/repository.py     | 114 +++++++++++++++++
 bitbucket/service.py        |  75 ++++++++++++
 bitbucket/ssh.py            |  42 +++++++
 bitbucket/tests/__init__.py |   5 +
 bitbucket/tests/public.py   | 140 +++++++++++++++++++++
 setup.py                    |  30 +++++
 14 files changed, 1123 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5810e5a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+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.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..6f505ae
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,63 @@
+Metadata-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 at 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
diff --git a/README b/README
new file mode 100644
index 0000000..052313a
--- /dev/null
+++ b/README
@@ -0,0 +1,33 @@
+#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).
diff --git a/bitbucket/__init__.py b/bitbucket/__init__.py
new file mode 100644
index 0000000..fa8be13
--- /dev/null
+++ b/bitbucket/__init__.py
@@ -0,0 +1,76 @@
+# -*- 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)
+
+"""
diff --git a/bitbucket/bitbucket.py b/bitbucket/bitbucket.py
new file mode 100644
index 0000000..e7d7b2b
--- /dev/null
+++ b/bitbucket/bitbucket.py
@@ -0,0 +1,292 @@
+# -*- 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)
diff --git a/bitbucket/deploy_key.py b/bitbucket/deploy_key.py
new file mode 100644
index 0000000..285aa17
--- /dev/null
+++ b/bitbucket/deploy_key.py
@@ -0,0 +1,60 @@
+# -*- 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)
diff --git a/bitbucket/issue.py b/bitbucket/issue.py
new file mode 100644
index 0000000..40be84b
--- /dev/null
+++ b/bitbucket/issue.py
@@ -0,0 +1,99 @@
+# -*- 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)
diff --git a/bitbucket/issue_comment.py b/bitbucket/issue_comment.py
new file mode 100644
index 0000000..f7c4661
--- /dev/null
+++ b/bitbucket/issue_comment.py
@@ -0,0 +1,81 @@
+# -*- 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)
diff --git a/bitbucket/repository.py b/bitbucket/repository.py
new file mode 100644
index 0000000..b900b9b
--- /dev/null
+++ b/bitbucket/repository.py
@@ -0,0 +1,114 @@
+# -*- 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.')
diff --git a/bitbucket/service.py b/bitbucket/service.py
new file mode 100644
index 0000000..843f9ca
--- /dev/null
+++ b/bitbucket/service.py
@@ -0,0 +1,75 @@
+# -*- 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,),
+# }
diff --git a/bitbucket/ssh.py b/bitbucket/ssh.py
new file mode 100644
index 0000000..7e0ee79
--- /dev/null
+++ b/bitbucket/ssh.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+URLS = {
+    # SSH keys
+    'GET_SSH_KEYS': 'ssh-keys/',
+    'GET_SSH_KEY': 'ssh-keys/%(key_id)s',
+    'SET_SSH_KEY': 'ssh-keys/',
... 229 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-bitbucket-api.git



More information about the Python-modules-commits mailing list