def test_default_value(self):
     uri = 'https://api.github.com/user{/user=sigmavirus24}'
     t = URITemplate(uri)
     self.assertEqual(t.expand(),
                      'https://api.github.com/user/sigmavirus24')
     self.assertEqual(t.expand(user='******'),
                      'https://api.github.com/user/lukasa')
 def test_no_variables_in_uri(self):
     """
     This test ensures that if there are no variables present, the
     template evaluates to itself.
     """
     uri = 'https://api.github.com/users'
     t = URITemplate(uri)
     self.assertEqual(t.expand(), uri)
     self.assertEqual(t.expand(users='foo'), uri)
    def test_expand(self):
        """
        This test ensures that expansion works as expected.
        """
        # Single
        t = URITemplate("https://api.github.com/users{/user}")
        expanded = "https://api.github.com/users/sigmavirus24"
        self.assertEqual(t.expand(user="******"), expanded)
        v = t.variables[0]
        self.assertEqual(v.expand({"user": None}), {"/user": ""})

        # Multiple
        t = URITemplate("https://api.github.com/users{/user}{/repo}")
        expanded = "https://api.github.com/users/sigmavirus24/github3.py"
        self.assertEqual(t.expand({"repo": "github3.py"}, user="******"), expanded)
    def test_expand(self):
        """
        This test ensures that expansion works as expected.
        """
        # Single
        t = URITemplate('https://api.github.com/users{/user}')
        expanded = 'https://api.github.com/users/sigmavirus24'
        self.assertEqual(t.expand(user='******'), expanded)
        v = t.variables[0]
        self.assertEqual(v.expand({'user': None}), {'/user': ''})

        # Multiple
        t = URITemplate('https://api.github.com/users{/user}{/repo}')
        expanded = 'https://api.github.com/users/sigmavirus24/github3.py'
        self.assertEqual(
            t.expand({'repo': 'github3.py'}, user='******'),
            expanded
        )
Example #5
0
class PriceURL:
    """ PriceURl implements an easily readible, funcational, and modifiable URL for retreiving prices
    :param item_type:
    :param item_number:
    :param  =color_id:
    
    Usage:
        url_template = PriceURL()
        uri = url_template.expand(item_type=itemtypeID, item_number=itemID, color_id=itemColorID)
        'https://www.bricklink.com/catalogPG.asp?P=3004&colorID=8'
    """

    url = ('https://www.bricklink.com/catalogPG.asp?'
           '{item_type} = {item_number} &'
           'colorID = {color_id}'
           )

    def __init__(self):
        self.raw_url = PriceURL.url.replace(" ", "")  # Spaces improved readability
        self.url_template = URITemplate(self.raw_url)

    def expand(self, itemtypeID, itemID, itemColorID):
        self.url = self.url_template.expand(item_type=itemtypeID, item_number=itemID, color_id=itemColorID)
        return self.url
 def _test_(self):
     for k, v in d.items():
         t = URITemplate(k)
         self.assertEqual(t.expand(v['expansion']), v['expected'])
Example #7
0
	def __get_url(self, path, id):
		rootUrl = self.hal.get_root_url()
		template = URITemplate(rootUrl + path);

		return template.expand(id= str(id) if id is not None else "")
 def test_default_value(self):
     uri = "https://api.github.com/user{/user=sigmavirus24}"
     t = URITemplate(uri)
     self.assertEqual(t.expand(), "https://api.github.com/user/sigmavirus24")
     self.assertEqual(t.expand(user="******"), "https://api.github.com/user/lukasa")
Example #9
0
class _User(models.GitHubCore):
    """The :class:`User <User>` object.

    This handles and structures information in the `User section`_.

    Two user instances can be checked like so::

        u1 == u2
        u1 != u2

    And is equivalent to::

        u1.id == u2.id
        u1.id != u2.id

    .. _User section:
        http://developer.github.com/v3/users/
    """

    class_name = "_User"

    def _update_attributes(self, user):
        self.avatar_url = user["avatar_url"]
        self.events_urlt = URITemplate(user["events_url"])
        self.followers_url = user["followers_url"]
        self.following_urlt = URITemplate(user["following_url"])
        self.gists_urlt = URITemplate(user["gists_url"])
        self.gravatar_id = user["gravatar_id"]
        self.html_url = user["html_url"]
        self.id = user["id"]
        self.login = user["login"]
        self.organizations_url = user["organizations_url"]
        self.received_events_url = user["received_events_url"]
        self.repos_url = user["repos_url"]
        self.site_admin = user.get("site_admin")
        self.starred_urlt = URITemplate(user["starred_url"])
        self.subscriptions_url = user["subscriptions_url"]
        self.type = user["type"]
        self.url = self._api = user["url"]
        self._uniq = self.id

    def __str__(self):
        return self.login

    def _repr(self):
        full_name = ""
        name = getattr(self, "name", None)
        if name is not None:
            full_name = ":{}".format(name)
        return "<{s.class_name} [{s.login}{full_name}]>".format(
            s=self, full_name=full_name)

    def is_assignee_on(self, username, repository):
        """Check if this user can be assigned to issues on username/repository.

        :param str username: owner's username of the repository
        :param str repository: name of the repository
        :returns: True if the use can be assigned, False otherwise
        :rtype: :class:`bool`
        """
        url = self._build_url("repos", username, repository, "assignees",
                              self.login)
        return self._boolean(self._get(url), 204, 404)

    def is_following(self, username):
        """Check if this user is following ``username``.

        :param str username: (required)
        :returns: bool

        """
        url = self.following_urlt.expand(other_user=username)
        return self._boolean(self._get(url), 204, 404)

    def events(self, public=False, number=-1, etag=None):
        r"""Iterate over events performed by this user.

        :param bool public: (optional), only list public events for the
            authenticated user
        :param int number: (optional), number of events to return. Default: -1
            returns all available events.
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ["events"]
        if public:
            path.append("public")
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def followers(self, number=-1, etag=None):
        r"""Iterate over the followers of this user.

        :param int number: (optional), number of followers to return. Default:
            -1 returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url("followers", base_url=self._api)
        return self._iter(int(number), url, ShortUser, etag=etag)

    def following(self, number=-1, etag=None):
        r"""Iterate over the users being followed by this user.

        :param int number: (optional), number of users to return. Default: -1
            returns all available users
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url("following", base_url=self._api)
        return self._iter(int(number), url, ShortUser, etag=etag)

    def gpg_keys(self, number=-1, etag=None):
        r"""Iterate over the GPG keys of this user.

        .. versionadded:: 1.2.0

        :param int number: (optional), number of GPG keys to return. Default:
            -1 returns all available GPG keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`GPGKey <GPGKey>`\ s
        """
        url = self._build_url("gpg_keys", base_url=self._api)
        return self._iter(int(number), url, GPGKey, etag=etag)

    def keys(self, number=-1, etag=None):
        r"""Iterate over the public keys of this user.

        .. versionadded:: 0.5

        :param int number: (optional), number of keys to return. Default: -1
            returns all available keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Key <Key>`\ s
        """
        url = self._build_url("keys", base_url=self._api)
        return self._iter(int(number), url, Key, etag=etag)

    @requires_auth
    def organization_events(self, org, number=-1, etag=None):
        r"""Iterate over events from the user's organization dashboard.

        .. note:: You must be authenticated to view this.

        :param str org: (required), name of the organization
        :param int number: (optional), number of events to return. Default: -1
            returns all available events
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        url = ""
        if org:
            url = self._build_url("events", "orgs", org, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def received_events(self, public=False, number=-1, etag=None):
        r"""Iterate over events that the user has received.

        If the user is the authenticated user, you will see private and public
        events, otherwise you will only see public events.

        :param bool public: (optional), determines if the authenticated user
            sees both private and public or just public
        :param int number: (optional), number of events to return. Default: -1
            returns all events available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ["received_events"]
        if public:
            path.append("public")
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def organizations(self, number=-1, etag=None):
        r"""Iterate over organizations the user is member of.

        :param int number: (optional), number of organizations to return.
            Default: -1 returns all available organization
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of
            :class:`ShortOrganization <github3.orgs.ShortOrganization>`\ s
        """
        # Import here, because a toplevel import causes an import loop
        from .orgs import ShortOrganization

        url = self._build_url("orgs", base_url=self._api)
        return self._iter(int(number), url, ShortOrganization, etag=etag)

    def starred_repositories(self,
                             sort=None,
                             direction=None,
                             number=-1,
                             etag=None):
        """Iterate over repositories starred by this user.

        .. versionchanged:: 0.5
           Added sort and direction parameters (optional) as per the change in
           GitHub's API.

        :param int number: (optional), number of starred repos to return.
            Default: -1, returns all available repos
        :param str sort: (optional), either 'created' (when the star was
            created) or 'updated' (when the repository was last pushed to)
        :param str direction: (optional), either 'asc' or 'desc'. Default:
            'desc'
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`~github3.repos.repo.StarredRepository`
        """
        from .repos import Repository, StarredRepository

        params = {"sort": sort, "direction": direction}
        self._remove_none(params)
        url = self.starred_urlt.expand(owner=None, repo=None)
        return self._iter(
            int(number),
            url,
            StarredRepository,
            params,
            etag,
            headers=Repository.STAR_HEADERS,
        )

    def subscriptions(self, number=-1, etag=None):
        """Iterate over repositories subscribed to by this user.

        :param int number: (optional), number of subscriptions to return.
            Default: -1, returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from .repos import ShortRepository

        url = self._build_url("subscriptions", base_url=self._api)
        return self._iter(int(number), url, ShortRepository, etag=etag)

    @requires_auth
    def rename(self, login):
        """Rename the user.

        .. note::

            This is only available for administrators of a GitHub Enterprise
            instance.

        :param str login: (required), new name of the user
        :returns: bool
        """
        url = self._build_url("admin", "users", self.login)
        payload = {"login": login}
        resp = self._boolean(self._patch(url, data=payload), 202, 403)
        return resp

    @requires_auth
    def impersonate(self, scopes=None):
        """Obtain an impersonation token for the user.

        The retrieved token will allow impersonation of the user.
        This is only available for admins of a GitHub Enterprise instance.

        :param list scopes: (optional), areas you want this token to apply to,
            i.e., 'gist', 'user'
        :returns: :class:`Authorization <Authorization>`
        """
        url = self._build_url("admin", "users", self.login, "authorizations")
        data = {}

        if scopes:
            data["scopes"] = scopes

        json = self._json(self._post(url, data=data), 201)

        return self._instance_or_null(Authorization, json)

    @requires_auth
    def revoke_impersonation(self):
        """Revoke all impersonation tokens for the current user.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("admin", "users", self.login, "authorizations")

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def promote(self):
        """Promote a user to site administrator.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("site_admin", base_url=self._api)

        return self._boolean(self._put(url), 204, 403)

    @requires_auth
    def demote(self):
        """Demote a site administrator to simple user.

        You can demote any user account except your own.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("site_admin", base_url=self._api)

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def suspend(self):
        """Suspend the user.

        This is only available for admins of a GitHub Enterprise instance.

        This API is disabled if you use LDAP, check the GitHub API dos for more
        information.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("suspended", base_url=self._api)

        return self._boolean(self._put(url), 204, 403)

    @requires_auth
    def unsuspend(self):
        """Unsuspend the user.

        This is only available for admins of a GitHub Enterprise instance.

        This API is disabled if you use LDAP, check the GitHub API dos for more
        information.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("suspended", base_url=self._api)

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def delete(self):
        """Delete the user.

        Per GitHub API documentation, it is often preferable to suspend the
        user.

        .. note::

            This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("admin", "users", self.login)
        return self._boolean(self._delete(url), 204, 403)
Example #10
0
class Release(models.GitHubCore):
    """Representation of a GitHub release.

    It holds the information GitHub returns about a release from a
    :class:`Repository <github3.repos.repo.Repository>`.

    Please see GitHub's `Releases Documentation`_ for more information.

    This object has the following attributes:

    .. attribute:: original_assets

        A list of :class:`~github3.repos.release.Asset` objects representing
        the assets uploaded for this relesae.

    .. attribute:: assets_url

        The URL to retrieve the assets from the API.

    .. attribute:: author

        A :class:`~github3.users.ShortUser` representing the creator of this
        release.

    .. attribute:: body

        The description of this release as written by the release creator.

    .. attribute:: created_at

        A :class:`~datetime.datetime` object representing the date and time
        when this release was created.

    .. attribute:: draft

        A boolean attribute describing whether this release is a draft.

    .. attribute:: html_url

        The URL to view this release in a browser.

    .. attribute:: id

        The unique identifier of this release.

    .. attribute:: name

        The name given to this release by the :attr:`author`.

    .. attribute:: prerelease

        A boolean attribute indicating whether the release is a pre-release.

    .. attribute:: published_at

        A :class:`~datetime.datetime` object representing the date and time
        when this release was publisehd.

    .. attribute:: tag_name

        The name of the tag associated with this release.

    .. attribute:: tarball_url

        The URL to retrieve a GitHub generated tarball for this release from
        the API.

    .. attribute:: target_commitish

        The reference (usually a commit) that is targetted by this release.

    .. attribute:: upload_urlt

        A :class:`~uritemplate.URITemplate` object that expands to form the
        URL to upload assets to.

    .. attribute:: zipball_url

        The URL to retrieve a GitHub generated zipball for this release from
        the API.

    .. _Releases Documentation:
        https://developer.github.com/v3/repos/releases/
    """

    def _update_attributes(self, release):
        self._api = self.url = release["url"]
        self.original_assets = [Asset(i, self) for i in release["assets"]]
        self.assets_url = release["assets_url"]
        self.author = users.ShortUser(release["author"], self)
        self.body = release["body"]
        self.created_at = self._strptime(release["created_at"])
        self.draft = release["draft"]
        self.html_url = release["html_url"]
        self.id = release["id"]
        self.name = release["name"]
        self.prerelease = release["prerelease"]
        self.published_at = self._strptime(release["published_at"])
        self.tag_name = release["tag_name"]
        self.tarball_url = release["tarball_url"]
        self.target_commitish = release["target_commitish"]
        self.upload_urlt = URITemplate(release["upload_url"])
        self.zipball_url = release["zipball_url"]

    def _repr(self):
        return "<Release [{0}]>".format(self.name)

    def archive(self, format, path=""):
        """Get the tarball or zipball archive for this release.

        :param str format:
            (required), accepted values: ('tarball', 'zipball')
        :param path:
            (optional), path where the file should be saved to, default is the
            filename provided in the headers and will be written in the current
            directory. It can take a file-like object as well
        :type path:
            str, file
        :returns:
            True if successful, False otherwise
        :rtype:
            bool
        """
        resp = None
        if format in ("tarball", "zipball"):
            repo_url = self._api[: self._api.rfind("/releases")]
            url = self._build_url(format, self.tag_name, base_url=repo_url)
            resp = self._get(url, allow_redirects=True, stream=True)

        if resp and self._boolean(resp, 200, 404):
            utils.stream_response_to_file(resp, path)
            return True
        return False

    def asset(self, asset_id):
        """Retrieve the asset from this release with ``asset_id``.

        :param int asset_id:
            ID of the Asset to retrieve
        :returns:
            the specified asset, if it exists
        :rtype:
            :class:`~github3.repos.release.Asset`
        """
        json = None
        if int(asset_id) > 0:
            i = self._api.rfind("/")
            url = self._build_url(
                "assets", str(asset_id), base_url=self._api[:i]
            )
            json = self._json(self._get(url), 200)
        return self._instance_or_null(Asset, json)

    def assets(self, number=-1, etag=None):
        """Iterate over the assets available for this release.

        :param int number:
            (optional), Number of assets to return
        :param str etag:
            (optional), last ETag header sent
        :returns:
            generator of asset objects
        :rtype:
            :class:`~github3.repos.release.Asset`
        """
        url = self._build_url("assets", base_url=self._api)
        return self._iter(number, url, Asset, etag=etag)

    @requires_auth
    def delete(self):
        """Delete this release.

        Only users with push access to the repository can delete a release.

        :returns:
            True if successful; False if not successful
        :rtype:
            bool
        """
        url = self._api
        return self._boolean(self._delete(url), 204, 404)

    @requires_auth
    def edit(
        self,
        tag_name=None,
        target_commitish=None,
        name=None,
        body=None,
        draft=None,
        prerelease=None,
    ):
        """Edit this release.

        Only users with push access to the repository can edit a release.

        If the edit is successful, this object will update itself.

        :param str tag_name:
            (optional), Name of the tag to use
        :param str target_commitish:
            (optional), The "commitish" value that determines where the Git tag
            is created from. Defaults to the repository's default branch.
        :param str name:
            (optional), Name of the release
        :param str body:
            (optional), Description of the release
        :param boolean draft:
            (optional), True => Release is a draft
        :param boolean prerelease:
            (optional), True => Release is a prerelease
        :returns:
            True if successful; False if not successful
        :rtype:
            bool
        """
        url = self._api
        data = {
            "tag_name": tag_name,
            "target_commitish": target_commitish,
            "name": name,
            "body": body,
            "draft": draft,
            "prerelease": prerelease,
        }
        self._remove_none(data)

        r = self.session.patch(url, data=json.dumps(data))

        successful = self._boolean(r, 200, 404)
        if successful:
            # If the edit was successful, let's update the object.
            self._update_attributes(r.json())

        return successful

    @requires_auth
    def upload_asset(self, content_type, name, asset, label=None):
        """Upload an asset to this release.

        .. note:: All parameters are required.

        :param str content_type:
            The content type of the asset. Wikipedia has a list of common media
            types
        :param str name:
            The name of the file
        :param asset:
            The file or bytes object to upload.
        :param label:
            (optional), An alternate short description of the asset.
        :returns:
            the created asset
        :rtype:
            :class:`~github3.repos.release.Asset`
        """
        headers = {"Content-Type": content_type}
        params = {"name": name, "label": label}
        self._remove_none(params)
        url = self.upload_urlt.expand(params)
        r = self._post(url, data=asset, json=False, headers=headers)
        if r.status_code in (201, 202):
            return Asset(r.json(), self)
        raise error_for(r)
Example #11
0
class User(BaseAccount):

    """The :class:`User <User>` object. This handles and structures information
    in the `User section <http://developer.github.com/v3/users/>`_.

    Two user instances can be checked like so::

        u1 == u2
        u1 != u2

    And is equivalent to::

        u1.id == u2.id
        u1.id != u2.id

    """

    def _update_attributes(self, user):
        super(User, self)._update_attributes(user)
        if not self.type:
            self.type = 'User'

        #: ID of the user's image on Gravatar
        self.gravatar_id = user.get('gravatar_id', '')
        #: True -- for hire, False -- not for hire
        self.hireable = user.get('hireable', False)

        # The number of public_gists
        #: Number of public gists
        self.public_gists = user.get('public_gists', 0)

        # Private information
        #: How much disk consumed by the user
        self.disk_usage = user.get('disk_usage', 0)

        #: Number of private repos owned by this user
        self.owned_private_repos = user.get('owned_private_repos', 0)
        #: Number of private gists owned by this user
        self.total_private_gists = user.get('total_private_gists', 0)
        #: Total number of private repos
        self.total_private_repos = user.get('total_private_repos', 0)

        #: Which plan this user is on
        self.plan = Plan(user.get('plan', {}))

        events_url = user.get('events_url', '')
        #: Events URL Template. Expands with ``privacy``
        self.events_urlt = URITemplate(events_url) if events_url else None

        #: Followers URL (not a template)
        self.followers_url = user.get('followers_url', '')

        furl = user.get('following_url', '')
        #: Following URL Template. Expands with ``other_user``
        self.following_urlt = URITemplate(furl) if furl else None

        gists_url = user.get('gists_url', '')
        #: Gists URL Template. Expands with ``gist_id``
        self.gists_urlt = URITemplate(gists_url) if gists_url else None

        #: Organizations URL (not a template)
        self.organizations_url = user.get('organizations_url', '')

        #: Received Events URL (not a template)
        self.received_events_url = user.get('received_events_url', '')

        #: Repostories URL (not a template)
        self.repos_url = user.get('repos_url', '')

        starred_url = user.get('starred_url', '')
        #: Starred URL Template. Expands with ``owner`` and ``repo``
        self.starred_urlt = URITemplate(starred_url) if starred_url else None

        #: Subscriptions URL (not a template)
        self.subscriptions_url = user.get('subscriptions_url', '')

        #: Number of repo contributions. Only appears in ``repo.contributors``
        contributions = user.get('contributions')
        # The refresh method uses __init__ to replace the attributes on the
        # instance with what it receives from the /users/:username endpoint.
        # What that means is that contributions is no longer returned and as
        # such is changed because it doesn't exist. This guards against that.
        if contributions is not None:
            self.contributions = contributions

        self._uniq = user.get('id', None)

    def __str__(self):
        return self.login

    @requires_auth
    def add_email_address(self, address):
        """Add the single email address to the authenticated user's
        account.

        :param str address: (required), email address to add
        :returns: list of email addresses
        """
        return self.add_email_addresses([address])

    @requires_auth
    def add_email_addresses(self, addresses=[]):
        """Add the email addresses in ``addresses`` to the authenticated
        user's account.

        :param list addresses: (optional), email addresses to be added
        :returns: list of email addresses
        """
        json = []
        if addresses:
            url = self._build_url('user', 'emails')
            json = self._json(self._post(url, data=addresses), 201)
        return json

    @requires_auth
    def delete_email_address(self, address):
        """Delete the email address from the user's account.

        :param str address: (required), email address to delete
        :returns: bool
        """
        return self.delete_email_addresses([address])

    @requires_auth
    def delete_email_addresses(self, addresses=[]):
        """Delete the email addresses in ``addresses`` from the
        authenticated user's account.

        :param list addresses: (optional), email addresses to be removed
        :returns: bool
        """
        url = self._build_url('user', 'emails')
        return self._boolean(self._delete(url, data=dumps(addresses)),
                             204, 404)

    def is_assignee_on(self, username, repository):
        """Check if this user can be assigned to issues on username/repository.

        :param str username: owner's username of the repository
        :param str repository: name of the repository
        :returns: True if the use can be assigned, False otherwise
        :rtype: :class:`bool`
        """
        url = self._build_url('repos', username, repository, 'assignees',
                              self.login)
        return self._boolean(self._get(url), 204, 404)

    def is_following(self, username):
        """Checks if this user is following ``username``.

        :param str username: (required)
        :returns: bool

        """
        url = self.following_urlt.expand(other_user=username)
        return self._boolean(self._get(url), 204, 404)

    def events(self, public=False, number=-1, etag=None):
        """Iterate over events performed by this user.

        :param bool public: (optional), only list public events for the
            authenticated user
        :param int number: (optional), number of events to return. Default: -1
            returns all available events.
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ['events']
        if public:
            path.append('public')
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def followers(self, number=-1, etag=None):
        """Iterate over the followers of this user.

        :param int number: (optional), number of followers to return. Default:
            -1 returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url('followers', base_url=self._api)
        return self._iter(int(number), url, User, etag=etag)

    def following(self, number=-1, etag=None):
        """Iterate over the users being followed by this user.

        :param int number: (optional), number of users to return. Default: -1
            returns all available users
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url('following', base_url=self._api)
        return self._iter(int(number), url, User, etag=etag)

    def keys(self, number=-1, etag=None):
        """Iterate over the public keys of this user.

        .. versionadded:: 0.5

        :param int number: (optional), number of keys to return. Default: -1
            returns all available keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Key <Key>`\ s
        """
        url = self._build_url('keys', base_url=self._api)
        return self._iter(int(number), url, Key, etag=etag)

    @requires_auth
    def organization_events(self, org, number=-1, etag=None):
        """Iterate over events as they appear on the user's organization
        dashboard. You must be authenticated to view this.

        :param str org: (required), name of the organization
        :param int number: (optional), number of events to return. Default: -1
            returns all available events
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        url = ''
        if org:
            url = self._build_url('events', 'orgs', org, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def received_events(self, public=False, number=-1, etag=None):
        """Iterate over events that the user has received. If the user is the
        authenticated user, you will see private and public events, otherwise
        you will only see public events.

        :param bool public: (optional), determines if the authenticated user
            sees both private and public or just public
        :param int number: (optional), number of events to return. Default: -1
            returns all events available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ['received_events']
        if public:
            path.append('public')
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def organizations(self, number=-1, etag=None):
        """Iterate over organizations the user is member of

        :param int number: (optional), number of organizations to return.
            Default: -1 returns all available organization
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.orgs.Organization>`\ s
        """
        # Import here, because a toplevel import causes an import loop
        from .orgs import Organization
        url = self._build_url('orgs', base_url=self._api)
        return self._iter(int(number), url, Organization, etag=etag)

    def starred_repositories(self, sort=None, direction=None, number=-1,
                             etag=None):
        """Iterate over repositories starred by this user.

        .. versionchanged:: 0.5
           Added sort and direction parameters (optional) as per the change in
           GitHub's API.

        :param int number: (optional), number of starred repos to return.
            Default: -1, returns all available repos
        :param str sort: (optional), either 'created' (when the star was
            created) or 'updated' (when the repository was last pushed to)
        :param str direction: (optional), either 'asc' or 'desc'. Default:
            'desc'
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from .repos import Repository

        params = {'sort': sort, 'direction': direction}
        self._remove_none(params)
        url = self.starred_urlt.expand(owner=None, repo=None)
        return self._iter(int(number), url, Repository, params, etag)

    def subscriptions(self, number=-1, etag=None):
        """Iterate over repositories subscribed to by this user.

        :param int number: (optional), number of subscriptions to return.
            Default: -1, returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from .repos import Repository
        url = self._build_url('subscriptions', base_url=self._api)
        return self._iter(int(number), url, Repository, etag=etag)
Example #12
0
class Release(GitHubCore):

    """The :class:`Release <Release>` object.

    It holds the information GitHub returns about a release from a
    :class:`Repository <github3.repos.repo.Repository>`.

    """

    CUSTOM_HEADERS = {'Accept': 'application/vnd.github.manifold-preview'}

    def __init__(self, release, session=None):
        super(Release, self).__init__(release, session)
        self._api = release.get('url')
        #: List of :class:`Asset <Asset>` objects for this release
        self.assets = [Asset(i, self) for i in release.get('assets', [])]
        #: URL for uploaded assets
        self.assets_url = release.get('assets_url')
        #: Body of the release (the description)
        self.body = release.get('body')
        #: Date the release was created
        self.created_at = self._strptime(release.get('created_at'))
        #: Boolean whether value is True or False
        self.draft = release.get('draft')
        #: HTML URL of the release
        self.html_url = release.get('html_url')
        #: GitHub id
        self.id = release.get('id')
        #: Name given to the release
        self.name = release.get('name')
        #; Boolean whether release is a prerelease
        self.prerelease = release.get('prerelease')
        #: Date the release was published
        self.published_at = self._strptime(release.get('published_at'))
        #: Name of the tag
        self.tag_name = release.get('tag_name')
        #: "Commit" that this release targets
        self.target_commitish = release.get('target_commitish')
        upload_url = release.get('upload_url')
        #: URITemplate to upload an asset with
        self.upload_urlt = URITemplate(upload_url) if upload_url else None

    def _repr(self):
        return '<Release [{0}]>'.format(self.name)

    @requires_auth
    def delete(self):
        """Users with push access to the repository can delete a release.

        :returns: True if successful; False if not successful
        """
        url = self._api
        return self._boolean(
            self._delete(url, headers=Release.CUSTOM_HEADERS),
            204,
            404
        )

    @requires_auth
    def edit(self, tag_name=None, target_commitish=None, name=None, body=None,
             draft=None, prerelease=None):
        """Users with push access to the repository can edit a release.

        If the edit is successful, this object will update itself.

        :param str tag_name: (optional), Name of the tag to use
        :param str target_commitish: (optional), The "commitish" value that
            determines where the Git tag is created from. Defaults to the
            repository's default branch.
        :param str name: (optional), Name of the release
        :param str body: (optional), Description of the release
        :param boolean draft: (optional), True => Release is a draft
        :param boolean prerelease: (optional), True => Release is a prerelease
        :returns: True if successful; False if not successful
        """
        url = self._api
        data = {
            'tag_name': tag_name,
            'target_commitish': target_commitish,
            'name': name,
            'body': body,
            'draft': draft,
            'prerelease': prerelease,
        }
        self._remove_none(data)

        r = self._session.patch(
            url, data=json.dumps(data), headers=Release.CUSTOM_HEADERS
        )

        successful = self._boolean(r, 200, 404)
        if successful:
            # If the edit was successful, let's update the object.
            self.__init__(r.json(), self)

        return successful

    def iter_assets(self, number=-1, etag=None):
        """Iterate over the assets available for this release.

        :param int number: (optional), Number of assets to return
        :param str etag: (optional), last ETag header sent
        :returns: generator of :class:`Asset <Asset>` objects
        """
        url = self._build_url('assets', base_url=self._api)
        return self._iter(number, url, Asset, etag=etag)

    @requires_auth
    def upload_asset(self, content_type, name, asset):
        """Upload an asset to this release.

        All parameters are required.

        :param str content_type: The content type of the asset. Wikipedia has
            a list of common media types
        :param str name: The name of the file
        :param asset: The file or bytes object to upload.
        :returns: :class:`Asset <Asset>`
        """
        headers = Release.CUSTOM_HEADERS.copy()
        headers.update({'Content-Type': content_type})
        url = self.upload_urlt.expand({'name': name})
        r = self._post(url, data=asset, json=False, headers=headers,
                       verify=False)
        if r.status_code in (201, 202):
            return Asset(r.json(), self)
        raise GitHubError(r)
Example #13
0
class _User(models.GitHubCore):
    """The :class:`User <User>` object.

    This handles and structures information in the `User section`_.

    Two user instances can be checked like so::

        u1 == u2
        u1 != u2

    And is equivalent to::

        u1.id == u2.id
        u1.id != u2.id

    .. _User section:
        http://developer.github.com/v3/users/
    """

    class_name = "_User"

    def _update_attributes(self, user):
        self.avatar_url = user["avatar_url"]
        self.events_urlt = URITemplate(user["events_url"])
        self.followers_url = user["followers_url"]
        self.following_urlt = URITemplate(user["following_url"])
        self.gists_urlt = URITemplate(user["gists_url"])
        self.gravatar_id = user["gravatar_id"]
        self.html_url = user["html_url"]
        self.id = user["id"]
        self.login = user["login"]
        self.organizations_url = user["organizations_url"]
        self.received_events_url = user["received_events_url"]
        self.repos_url = user["repos_url"]
        self.site_admin = user.get("site_admin")
        self.starred_urlt = URITemplate(user["starred_url"])
        self.subscriptions_url = user["subscriptions_url"]
        self.type = user["type"]
        self.url = self._api = user["url"]
        self._uniq = self.id

    def __str__(self):
        return self.login

    def _repr(self):
        full_name = ""
        name = getattr(self, "name", None)
        if name is not None:
            full_name = ":{}".format(name)
        return "<{s.class_name} [{s.login}{full_name}]>".format(
            s=self, full_name=full_name
        )

    def is_assignee_on(self, username, repository):
        """Check if this user can be assigned to issues on username/repository.

        :param str username: owner's username of the repository
        :param str repository: name of the repository
        :returns: True if the use can be assigned, False otherwise
        :rtype: :class:`bool`
        """
        url = self._build_url(
            "repos", username, repository, "assignees", self.login
        )
        return self._boolean(self._get(url), 204, 404)

    def is_following(self, username):
        """Check if this user is following ``username``.

        :param str username: (required)
        :returns: bool

        """
        url = self.following_urlt.expand(other_user=username)
        return self._boolean(self._get(url), 204, 404)

    def events(self, public=False, number=-1, etag=None):
        r"""Iterate over events performed by this user.

        :param bool public: (optional), only list public events for the
            authenticated user
        :param int number: (optional), number of events to return. Default: -1
            returns all available events.
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ["events"]
        if public:
            path.append("public")
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def followers(self, number=-1, etag=None):
        r"""Iterate over the followers of this user.

        :param int number: (optional), number of followers to return. Default:
            -1 returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url("followers", base_url=self._api)
        return self._iter(int(number), url, ShortUser, etag=etag)

    def following(self, number=-1, etag=None):
        r"""Iterate over the users being followed by this user.

        :param int number: (optional), number of users to return. Default: -1
            returns all available users
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url("following", base_url=self._api)
        return self._iter(int(number), url, ShortUser, etag=etag)

    def gpg_keys(self, number=-1, etag=None):
        r"""Iterate over the GPG keys of this user.

        .. versionadded:: 1.2.0

        :param int number: (optional), number of GPG keys to return. Default:
            -1 returns all available GPG keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`GPGKey <GPGKey>`\ s
        """
        url = self._build_url("gpg_keys", base_url=self._api)
        return self._iter(int(number), url, GPGKey, etag=etag)

    def keys(self, number=-1, etag=None):
        r"""Iterate over the public keys of this user.

        .. versionadded:: 0.5

        :param int number: (optional), number of keys to return. Default: -1
            returns all available keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Key <Key>`\ s
        """
        url = self._build_url("keys", base_url=self._api)
        return self._iter(int(number), url, Key, etag=etag)

    @requires_auth
    def organization_events(self, org, number=-1, etag=None):
        r"""Iterate over events from the user's organization dashboard.

        .. note:: You must be authenticated to view this.

        :param str org: (required), name of the organization
        :param int number: (optional), number of events to return. Default: -1
            returns all available events
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        url = ""
        if org:
            url = self._build_url("events", "orgs", org, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def received_events(self, public=False, number=-1, etag=None):
        r"""Iterate over events that the user has received.

        If the user is the authenticated user, you will see private and public
        events, otherwise you will only see public events.

        :param bool public: (optional), determines if the authenticated user
            sees both private and public or just public
        :param int number: (optional), number of events to return. Default: -1
            returns all events available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ["received_events"]
        if public:
            path.append("public")
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def organizations(self, number=-1, etag=None):
        r"""Iterate over organizations the user is member of.

        :param int number: (optional), number of organizations to return.
            Default: -1 returns all available organization
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of
            :class:`ShortOrganization <github3.orgs.ShortOrganization>`\ s
        """
        # Import here, because a toplevel import causes an import loop
        from .orgs import ShortOrganization

        url = self._build_url("orgs", base_url=self._api)
        return self._iter(int(number), url, ShortOrganization, etag=etag)

    def starred_repositories(
        self, sort=None, direction=None, number=-1, etag=None
    ):
        """Iterate over repositories starred by this user.

        .. versionchanged:: 0.5
           Added sort and direction parameters (optional) as per the change in
           GitHub's API.

        :param int number: (optional), number of starred repos to return.
            Default: -1, returns all available repos
        :param str sort: (optional), either 'created' (when the star was
            created) or 'updated' (when the repository was last pushed to)
        :param str direction: (optional), either 'asc' or 'desc'. Default:
            'desc'
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`~github3.repos.repo.StarredRepository`
        """
        from .repos import Repository, StarredRepository

        params = {"sort": sort, "direction": direction}
        self._remove_none(params)
        url = self.starred_urlt.expand(owner=None, repo=None)
        return self._iter(
            int(number),
            url,
            StarredRepository,
            params,
            etag,
            headers=Repository.STAR_HEADERS,
        )

    def subscriptions(self, number=-1, etag=None):
        """Iterate over repositories subscribed to by this user.

        :param int number: (optional), number of subscriptions to return.
            Default: -1, returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from .repos import ShortRepository

        url = self._build_url("subscriptions", base_url=self._api)
        return self._iter(int(number), url, ShortRepository, etag=etag)

    @requires_auth
    def rename(self, login):
        """Rename the user.

        .. note::

            This is only available for administrators of a GitHub Enterprise
            instance.

        :param str login: (required), new name of the user
        :returns: bool
        """
        url = self._build_url("admin", "users", self.login)
        payload = {"login": login}
        resp = self._boolean(self._patch(url, data=payload), 202, 403)
        return resp

    @requires_auth
    def impersonate(self, scopes=None):
        """Obtain an impersonation token for the user.

        The retrieved token will allow impersonation of the user.
        This is only available for admins of a GitHub Enterprise instance.

        :param list scopes: (optional), areas you want this token to apply to,
            i.e., 'gist', 'user'
        :returns: :class:`Authorization <Authorization>`
        """
        url = self._build_url("admin", "users", self.login, "authorizations")
        data = {}

        if scopes:
            data["scopes"] = scopes

        json = self._json(self._post(url, data=data), 201)

        return self._instance_or_null(Authorization, json)

    @requires_auth
    def revoke_impersonation(self):
        """Revoke all impersonation tokens for the current user.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("admin", "users", self.login, "authorizations")

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def promote(self):
        """Promote a user to site administrator.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("site_admin", base_url=self._api)

        return self._boolean(self._put(url), 204, 403)

    @requires_auth
    def demote(self):
        """Demote a site administrator to simple user.

        You can demote any user account except your own.

        This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("site_admin", base_url=self._api)

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def suspend(self):
        """Suspend the user.

        This is only available for admins of a GitHub Enterprise instance.

        This API is disabled if you use LDAP, check the GitHub API dos for more
        information.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("suspended", base_url=self._api)

        return self._boolean(self._put(url), 204, 403)

    @requires_auth
    def unsuspend(self):
        """Unsuspend the user.

        This is only available for admins of a GitHub Enterprise instance.

        This API is disabled if you use LDAP, check the GitHub API dos for more
        information.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("suspended", base_url=self._api)

        return self._boolean(self._delete(url), 204, 403)

    @requires_auth
    def delete(self):
        """Delete the user.

        Per GitHub API documentation, it is often preferable to suspend the
        user.

        .. note::

            This is only available for admins of a GitHub Enterprise instance.

        :returns: bool -- True if successful, False otherwise
        """
        url = self._build_url("admin", "users", self.login)
        return self._boolean(self._delete(url), 204, 403)
Example #14
0
class Release(GitHubCore):

    """The :class:`Release <Release>` object.

    It holds the information GitHub returns about a release from a
    :class:`Repository <github3.repos.repo.Repository>`.

    """

    CUSTOM_HEADERS = {'Accept': 'application/vnd.github.manifold-preview'}

    def _update_attributes(self, release):
        self._api = release.get('url')
        #: List of :class:`Asset <Asset>` objects for this release
        self.original_assets = [
            Asset(i, self) for i in release.get('assets', [])
        ]
        #: URL for uploaded assets
        self.assets_url = release.get('assets_url')
        #: Body of the release (the description)
        self.body = release.get('body')
        #: Date the release was created
        self.created_at = self._strptime(release.get('created_at'))
        #: Boolean whether value is True or False
        self.draft = release.get('draft')
        #: HTML URL of the release
        self.html_url = release.get('html_url')
        #: GitHub id
        self.id = release.get('id')
        #: Name given to the release
        self.name = release.get('name')
        #: Boolean whether release is a prerelease
        self.prerelease = release.get('prerelease')
        #: Date the release was published
        self.published_at = self._strptime(release.get('published_at'))
        #: Name of the tag
        self.tag_name = release.get('tag_name')
        #: URL to download a tarball of the release
        self.tarball_url = release.get('tarball_url')
        #: "Commit" that this release targets
        self.target_commitish = release.get('target_commitish')
        upload_url = release.get('upload_url')
        #: URITemplate to upload an asset with
        self.upload_urlt = URITemplate(upload_url) if upload_url else None
        #: URL to download a zipball of the release
        self.zipball_url = release.get('zipball_url')

    def _repr(self):
        return '<Release [{0}]>'.format(self.name)

    def archive(self, format, path=''):
        """Get the tarball or zipball archive for this release.

        :param str format: (required), accepted values: ('tarball',
            'zipball')
        :param path: (optional), path where the file should be saved
            to, default is the filename provided in the headers and will be
            written in the current directory.
            it can take a file-like object as well
        :type path: str, file
        :returns: bool -- True if successful, False otherwise

        """
        resp = None
        if format in ('tarball', 'zipball'):
            repo_url = self._api[:self._api.rfind('/releases')]
            url = self._build_url(format, self.tag_name, base_url=repo_url)
            resp = self._get(url, allow_redirects=True, stream=True)

        if resp and self._boolean(resp, 200, 404):
            utils.stream_response_to_file(resp, path)
            return True
        return False

    def asset(self, asset_id):
        """Retrieve the asset from this release with ``asset_id``.

        :param int asset_id: ID of the Asset to retrieve
        :returns: :class:`~github3.repos.release.Asset`
        """
        json = None
        if int(asset_id) > 0:
            i = self._api.rfind('/')
            url = self._build_url('assets', str(asset_id),
                                  base_url=self._api[:i])
            json = self._json(self._get(url), 200)
        return self._instance_or_null(Asset, json)

    def assets(self, number=-1, etag=None):
        """Iterate over the assets available for this release.

        :param int number: (optional), Number of assets to return
        :param str etag: (optional), last ETag header sent
        :returns: generator of :class:`Asset <Asset>` objects
        """
        url = self._build_url('assets', base_url=self._api)
        return self._iter(number, url, Asset, etag=etag)

    @requires_auth
    def delete(self):
        """Users with push access to the repository can delete a release.

        :returns: True if successful; False if not successful
        """
        url = self._api
        return self._boolean(
            self._delete(url, headers=Release.CUSTOM_HEADERS),
            204,
            404
        )

    @requires_auth
    def edit(self, tag_name=None, target_commitish=None, name=None, body=None,
             draft=None, prerelease=None):
        """Users with push access to the repository can edit a release.

        If the edit is successful, this object will update itself.

        :param str tag_name: (optional), Name of the tag to use
        :param str target_commitish: (optional), The "commitish" value that
            determines where the Git tag is created from. Defaults to the
            repository's default branch.
        :param str name: (optional), Name of the release
        :param str body: (optional), Description of the release
        :param boolean draft: (optional), True => Release is a draft
        :param boolean prerelease: (optional), True => Release is a prerelease
        :returns: True if successful; False if not successful
        """
        url = self._api
        data = {
            'tag_name': tag_name,
            'target_commitish': target_commitish,
            'name': name,
            'body': body,
            'draft': draft,
            'prerelease': prerelease,
        }
        self._remove_none(data)

        r = self.session.patch(
            url, data=json.dumps(data), headers=Release.CUSTOM_HEADERS
        )

        successful = self._boolean(r, 200, 404)
        if successful:
            # If the edit was successful, let's update the object.
            self._update_attributes(r.json())

        return successful

    @requires_auth
    def upload_asset(self, content_type, name, asset, label=None):
        """Upload an asset to this release.

        All parameters are required.

        :param str content_type: The content type of the asset. Wikipedia has
            a list of common media types
        :param str name: The name of the file
        :param asset: The file or bytes object to upload.
        :param label: (optional), An alternate short description of the asset.
        :returns: :class:`Asset <Asset>`
        """
        headers = {'Content-Type': content_type}
        params = {'name': name, 'label': label}
        self._remove_none(params)
        url = self.upload_urlt.expand(params)
        r = self._post(url, data=asset, json=False, headers=headers)
        if r.status_code in (201, 202):
            return Asset(r.json(), self)
        raise error_for(r)
Example #15
0
def main(argv):
    try:
        parser = argparse.ArgumentParser(description="Arma Automatic Publishing Script")
        parser.add_argument('manifest', type=argparse.FileType('r'), help='manifest json file')
        parser.add_argument('-r', '--release_target', type=str, help="the name of the release target in the manifest file.", default="release")
        parser.add_argument('-v', '--version', type=str, help="the version of the release archive.", default="")

        args = parser.parse_args()

        manifest_file = args.manifest
        release_target = args.release_target
        version = args.version.split(".")

        manifest = json.load(manifest_file)
        if version == "":
            version = get_project_version("..\\addons\\\main\\script_version.hpp")

        if(not "CBA_PUBLISH_CREDENTIALS_PATH" in os.environ):
            raise Exception("CBA_PUBLISH_CREDENTIALS_PATH is not set in the environment")

        credentials_path = os.environ["CBA_PUBLISH_CREDENTIALS_PATH"]


        for destination in manifest['publish'][release_target]['destinations']:

            if(destination["type"] == "steam"):
                cred_file = json.load(open(os.path.join(credentials_path, destination["cred_file"])))
                if("username" in cred_file and "password" in cred_file):
                    steam_username = cred_file["username"]
                    steam_password = cred_file["password"]

                    start_steam_with_user(steam_username, steam_password)
                else:
                    raise Exception("Credentials file did not specify a username and password for Steam login")
                if(not "project_id" in destination):
                    raise Exception("Steam Publish","No project ID defined in manifest for Steam publish")
                project_id = destination["project_id"]

                if(not "release_dir" in destination):
                    raise Exception("Steam Publish","No release directory defined in manifest for Steam publish")
                release_dir = destination["release_dir"]

                if(not "steam_changelog" in destination):
                    raise Exception("Steam Publish","No steam changelog defined in manifest for Steam publish")
                steam_changelog = destination["steam_changelog"]

                steam_publish_folder(release_dir, project_id, version, steam_changelog)
                close_steam()
            if(destination["type"] == "sftp"):
                cred_file = json.load(open(os.path.join(credentials_path, destination["cred_file"])))
                if("username" in cred_file and "password" in cred_file):
                    sftp_username = cred_file["username"]
                    sftp_password = cred_file["password"]
                else:
                    raise Exception("Credentials file did not specify a username and password for SFTP login")

                if(not "hostname" in destination):
                    raise Exception("SFTP Publish","No hostname was defined for the SFTP site.")
                hostname = destination["hostname"]

                if(not "local_path" in destination):
                    raise Exception("SFTP Publish","No local path was defined for the SFTP upload.")
                local_path = destination["local_path"]

                if(not "remote_path" in destination):
                    raise Exception("SFTP Publish","No remote path was defined for the SFTP upload.")
                remote_path = destination["remote_path"]


                cnopts = pysftp.CnOpts()
                cnopts.hostkeys = None
                sftp = pysftp.Connection(host=hostname, username=sftp_username, password=sftp_password, cnopts=cnopts)

                local_path = local_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3])
                remote_path = remote_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3])

                print("SFTP: Publishing {} to remote {}:{}".format(local_path, hostname, remote_path))

                sftp.put(local_path, remotepath=remote_path)
                print("SFTP: Upload Complete!")
            if(destination["type"] == "github"):

                account = destination["account"]
                tag_name = destination["tag_name"]
                branch = destination["branch"]
                name = destination["name"]
                body_file = destination["body_file"]
                local_path = destination["local_path"]
                prerelease = destination["prerelease"]
                asset_name = destination["asset_name"]

                tag_name = tag_name.format(major=version[0], minor=version[1], patch=version[2], build=version[3])
                name = name.format(major=version[0], minor=version[1], patch=version[2], build=version[3])
                asset_name = asset_name.format(major=version[0], minor=version[1], patch=version[2], build=version[3])
                local_path = local_path.format(major=version[0], minor=version[1], patch=version[2], build=version[3])

                release_text_file = open(body_file, mode='r')
                release_text = release_text_file.read()
                release_text_file.close()


                create_request = {
                    "tag_name": tag_name,
                    "target_commitish": branch,
                    "name": name,
                    "body": release_text,
                    "draft": False,
                    "prerelease": prerelease
                }

                github_token = os.environ["IDI_GITHUB_TOKEN"]

                release_string = json.dumps(create_request, separators=(',',':'))

                temp_dir = tempfile.mkdtemp()
                tmpname = os.path.join(temp_dir,"jsonpost")
                temp_file = open(tmpname, 'w')
                temp_file.write(release_string)
                temp_file.close()
                curl_string = ' '.join(["curl", '-s', '-H "Authorization: token {}"'.format(github_token), '-H "Content-Type: application/json"', "--request POST", "--data", '"@{}"'.format(tmpname).replace('\\','\\\\'), "https://api.github.com/repos/{}/releases".format(account)])

                print("Creating Github Release...")
                response = subprocess.check_output(curl_string)
                response_json = json.loads(response.decode("ascii"))
                shutil.rmtree(temp_dir)
                if("id" in response_json):
                    print("Github Release Created @ {}".format(response_json["url"]))
                    release_id = response_json["id"]
                    upload_url = response_json["upload_url"]

                    t = URITemplate(upload_url)
                    upload_url = t.expand(name=asset_name)


                    curl_string = ' '.join(["curl", '-s', '-H "Authorization: token {}"'.format(github_token),
                        '-H "Content-Type: application/zip"',
                        "--data-binary",
                        '"@{}"'.format(local_path),
                        upload_url])
                    print("Attaching Asset...")
                    response = subprocess.check_output(curl_string)
                    response_json = json.loads(response.decode("ascii"))
                    if("browser_download_url" in response_json):
                        print("Asset Attached @ {}".format(response_json["browser_download_url"]))
                    else:
                        print(response_json)
                        raise Exception("Github Publish","Failed to Attach Asset")

                else:
                    print(response_json)
                    raise Exception("Github Publish","Failed to Create Release")

    except Exception as e:
        print(e)
        sys.exit(1)
Example #16
0
class User(BaseAccount):

    """The :class:`User <User>` object. This handles and structures information
    in the `User section <http://developer.github.com/v3/users/>`_.

    Two user instances can be checked like so::

        u1 == u2
        u1 != u2

    And is equivalent to::

        u1.id == u2.id
        u1.id != u2.id

    """

    def __init__(self, user, session=None):
        super(User, self).__init__(user, session)
        if not self.type:
            self.type = 'User'

        #: ID of the user's image on Gravatar
        self.gravatar_id = user.get('gravatar_id', '')
        #: True -- for hire, False -- not for hire
        self.hireable = user.get('hireable', False)

        ## The number of public_gists
        #: Number of public gists
        self.public_gists = user.get('public_gists', 0)

        # Private information
        #: How much disk consumed by the user
        self.disk_usage = user.get('disk_usage', 0)

        #: Number of private repos owned by this user
        self.owned_private_repos = user.get('owned_private_repos', 0)
        #: Number of private gists owned by this user
        self.total_private_gists = user.get('total_private_gists', 0)
        #: Total number of private repos
        self.total_private_repos = user.get('total_private_repos', 0)

        #: Which plan this user is on
        self.plan = Plan(user.get('plan', {}))

        events_url = user.get('events_url', '')
        #: Events URL Template. Expands with ``privacy``
        self.events_urlt = URITemplate(events_url) if events_url else None

        #: Followers URL (not a template)
        self.followers_url = user.get('followers_url', '')

        furl = user.get('following_url', '')
        #: Following URL Template. Expands with ``other_user``
        self.following_urlt = URITemplate(furl) if furl else None

        gists_url = user.get('gists_url', '')
        #: Gists URL Template. Expands with ``gist_id``
        self.gists_urlt = URITemplate(gists_url) if gists_url else None

        #: Organizations URL (not a template)
        self.organizations_url = user.get('organizations_url', '')

        #: Received Events URL (not a template)
        self.received_events_url = user.get('received_events_url', '')

        #: Repostories URL (not a template)
        self.repos_url = user.get('repos_url', '')

        starred_url = user.get('starred_url', '')
        #: Starred URL Template. Expands with ``owner`` and ``repo``
        self.starred_urlt = URITemplate(starred_url) if starred_url else None

        #: Subscriptions URL (not a template)
        self.subscriptions_url = user.get('subscriptions_url', '')

    def __str__(self):
        return self.login

    def _update_(self, user):
        self.__init__(user, self._session)

    @requires_auth
    def add_email_address(self, address):
        """Add the single email address to the authenticated user's
        account.

        :param str address: (required), email address to add
        :returns: list of email addresses
        """
        return self.add_email_addresses([address])

    @requires_auth
    def add_email_addresses(self, addresses=[]):
        """Add the email addresses in ``addresses`` to the authenticated
        user's account.

        :param list addresses: (optional), email addresses to be added
        :returns: list of email addresses
        """
        json = []
        if addresses:
            url = self._build_url('user', 'emails')
            json = self._json(self._post(url, data=addresses), 201)
        return json

    @requires_auth
    def delete_email_address(self, address):
        """Delete the email address from the user's account.

        :param str address: (required), email address to delete
        :returns: bool
        """
        return self.delete_email_addresses([address])

    @requires_auth
    def delete_email_addresses(self, addresses=[]):
        """Delete the email addresses in ``addresses`` from the
        authenticated user's account.

        :param list addresses: (optional), email addresses to be removed
        :returns: bool
        """
        url = self._build_url('user', 'emails')
        return self._boolean(self._delete(url, data=dumps(addresses)),
                             204, 404)

    def is_assignee_on(self, login, repository):
        """Checks if this user can be assigned to issues on login/repository.

        :returns: :class:`bool`
        """
        url = self._build_url('repos', login, repository, 'assignees',
                              self.login)
        return self._boolean(self._get(url), 204, 404)

    def is_following(self, login):
        """Checks if this user is following ``login``.

        :param str login: (required)
        :returns: bool

        """
        url = self.following_urlt.expand(other_user=login)
        return self._boolean(self._get(url), 204, 404)

    def iter_events(self, public=False, number=-1, etag=None):
        """Iterate over events performed by this user.

        :param bool public: (optional), only list public events for the
            authenticated user
        :param int number: (optional), number of events to return. Default: -1
            returns all available events.
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: list of :class:`Event <github3.events.Event>`\ s
        """
        path = ['events']
        if public:
            path.append('public')
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def iter_followers(self, number=-1, etag=None):
        """Iterate over the followers of this user.

        :param int number: (optional), number of followers to return. Default:
            -1 returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url('followers', base_url=self._api)
        return self._iter(int(number), url, User, etag=etag)

    def iter_following(self, number=-1, etag=None):
        """Iterate over the users being followed by this user.

        :param int number: (optional), number of users to return. Default: -1
            returns all available users
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`User <User>`\ s
        """
        url = self._build_url('following', base_url=self._api)
        return self._iter(int(number), url, User, etag=etag)

    def iter_keys(self, number=-1, etag=None):
        """Iterate over the public keys of this user.

        .. versionadded:: 0.5

        :param int number: (optional), number of keys to return. Default: -1
            returns all available keys
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Key <Key>`\ s
        """
        url = self._build_url('keys', base_url=self._api)
        return self._iter(int(number), url, Key, etag=etag)

    def iter_org_events(self, org, number=-1, etag=None):
        """Iterate over events as they appear on the user's organization
        dashboard. You must be authenticated to view this.

        :param str org: (required), name of the organization
        :param int number: (optional), number of events to return. Default: -1
            returns all available events
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: list of :class:`Event <github3.events.Event>`\ s
        """
        url = ''
        if org:
            url = self._build_url('events', 'orgs', org, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def iter_received_events(self, public=False, number=-1, etag=None):
        """Iterate over events that the user has received. If the user is the
        authenticated user, you will see private and public events, otherwise
        you will only see public events.

        :param bool public: (optional), determines if the authenticated user
            sees both private and public or just public
        :param int number: (optional), number of events to return. Default: -1
            returns all events available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Event <github3.events.Event>`\ s
        """
        path = ['received_events']
        if public:
            path.append('public')
        url = self._build_url(*path, base_url=self._api)
        return self._iter(int(number), url, Event, etag=etag)

    def iter_starred(self, sort=None, direction=None, number=-1, etag=None):
        """Iterate over repositories starred by this user.

        .. versionchanged:: 0.5
           Added sort and direction parameters (optional) as per the change in
           GitHub's API.

        :param int number: (optional), number of starred repos to return.
            Default: -1, returns all available repos
        :param str sort: (optional), either 'created' (when the star was
            created) or 'updated' (when the repository was last pushed to)
        :param str direction: (optional), either 'asc' or 'desc'. Default:
            'desc'
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from github3.repos import Repository

        params = {'sort': sort, 'direction': direction}
        self._remove_none(params)
        url = self.starred_urlt.expand(owner=None, repo=None)
        return self._iter(int(number), url, Repository, params, etag)

    def iter_subscriptions(self, number=-1, etag=None):
        """Iterate over repositories subscribed to by this user.

        :param int number: (optional), number of subscriptions to return.
            Default: -1, returns all available
        :param str etag: (optional), ETag from a previous request to the same
            endpoint
        :returns: generator of :class:`Repository <github3.repos.Repository>`
        """
        from github3.repos import Repository
        url = self._build_url('subscriptions', base_url=self._api)
        return self._iter(int(number), url, Repository, etag=etag)

    @requires_auth
    def update(self, name=None, email=None, blog=None, company=None,
               location=None, hireable=False, bio=None):
        """If authenticated as this user, update the information with
        the information provided in the parameters.

        :param str name: e.g., 'John Smith', not login name
        :param str email: e.g., '*****@*****.**'
        :param str blog: e.g., 'http://www.example.com/jsmith/blog'
        :param str company:
        :param str location:
        :param bool hireable: defaults to False
        :param str bio: GitHub flavored markdown
        :returns: bool
        """
        user = {'name': name, 'email': email, 'blog': blog,
                'company': company, 'location': location,
                'hireable': hireable, 'bio': bio}
        self._remove_none(user)
        url = self._build_url('user')
        json = self._json(self._patch(url, data=dumps(user)), 200)
        if json:
            self._update_(json)
            return True
        return False
 def test_no_mutate(self):
     args = {}
     t = URITemplate('')
     t.expand(args, key=1)
     self.assertEqual(args, {})
Example #18
0
class ComponentStore:
    """Component store.

    Enables external components to be loaded by name and digest/tag.

    Attributes:

        local_search_paths: A list of local directories to include in the search.
        url_seach_prefixes: A list of URL prefixes to include in the search.
        uri_search_template: A URI template for components, which may include {name}, {digest} and {tag} variables.
    """

    def __init__(self,
                 local_search_paths=None,
                 url_search_prefixes=None,
                 auth=None,
                 uri_search_template=None):
        """Instantiates a ComponentStore including the specified locations.

        Args:

            local_search_paths: A list of local directories to include in the search.
            url_seach_prefixes: A list of URL prefixes to include in the search.
            auth: Auth object for the requests library. See https://requests.readthedocs.io/en/master/user/authentication/
            uri_search_template: A URI template for components, which may include {name}, {digest} and {tag} variables.
        """
        self.local_search_paths = local_search_paths or ['.']
        if uri_search_template:
            self.uri_search_template = URITemplate(uri_search_template)
        self.url_search_prefixes = url_search_prefixes or []
        self._auth = auth

        self._component_file_name = 'component.yaml'
        self._digests_subpath = 'versions/sha256'
        self._tags_subpath = 'versions/tags'

        cache_base_dir = Path(tempfile.gettempdir()) / '.kfp_components'
        self._git_blob_hash_to_data_db = KeyValueStore(
            cache_dir=cache_base_dir / 'git_blob_hash_to_data')
        self._url_to_info_db = KeyValueStore(cache_dir=cache_base_dir /
                                             'url_to_info')

    def load_component_from_url(self, url):
        """Loads a component from a URL.

        Args:
            url: The url of the component specification.

        Returns:
            A factory function with a strongly-typed signature.
        """
        return comp.load_component_from_url(url=url, auth=self._auth)

    def load_component_from_file(self, path):
        """Loads a component from a path.

        Args:
            path: The path of the component specification.

        Returns:
            A factory function with a strongly-typed signature.
        """
        return comp.load_component_from_file(path)

    def load_component(self, name, digest=None, tag=None):
        """Loads component local file or URL and creates a task factory
        function.

        Search locations:

        * :code:`<local-search-path>/<name>/component.yaml`
        * :code:`<url-search-prefix>/<name>/component.yaml`

        If the digest is specified, then the search locations are:

        * :code:`<local-search-path>/<name>/versions/sha256/<digest>`
        * :code:`<url-search-prefix>/<name>/versions/sha256/<digest>`

        If the tag is specified, then the search locations are:

        * :code:`<local-search-path>/<name>/versions/tags/<digest>`
        * :code:`<url-search-prefix>/<name>/versions/tags/<digest>`

        Args:
            name:   Component name used to search and load the component artifact containing the component definition.
                    Component name usually has the following form: group/subgroup/component
            digest: Strict component version. SHA256 hash digest of the component artifact file. Can be used to load a specific component version so that the pipeline is reproducible.
            tag:    Version tag. Can be used to load component version from a specific branch. The version of the component referenced by a tag can change in future.

        Returns:
            A factory function with a strongly-typed signature.
            Once called with the required arguments, the factory constructs a pipeline task instance (ContainerOp).
        """
        #This function should be called load_task_factory since it returns a factory function.
        #The real load_component function should produce an object with component properties (e.g. name, description, inputs/outputs).
        #TODO: Change this function to return component spec object but it should be callable to construct tasks.
        component_ref = ComponentReference(name=name, digest=digest, tag=tag)
        component_ref = self._load_component_spec_in_component_ref(
            component_ref)
        return comp._create_task_factory_from_component_spec(
            component_spec=component_ref.spec,
            component_ref=component_ref,
        )

    def _load_component_spec_in_component_ref(
        self,
        component_ref: ComponentReference,
    ) -> ComponentReference:
        """Takes component_ref, finds the component spec and returns
        component_ref with .spec set to the component spec.

        See ComponentStore.load_component for the details of the search
        logic.
        """
        if component_ref.spec:
            return component_ref

        component_ref = copy.copy(component_ref)
        if component_ref.url:
            component_ref.spec = comp._load_component_spec_from_url(
                url=component_ref.url, auth=self._auth)
            return component_ref

        name = component_ref.name
        if not name:
            raise TypeError("name is required")
        if name.startswith('/') or name.endswith('/'):
            raise ValueError(
                'Component name should not start or end with slash: "{}"'
                .format(name))

        digest = component_ref.digest
        tag = component_ref.tag

        tried_locations = []

        if digest is not None and tag is not None:
            raise ValueError('Cannot specify both tag and digest')

        if digest is not None:
            path_suffix = name + '/' + self._digests_subpath + '/' + digest
        elif tag is not None:
            path_suffix = name + '/' + self._tags_subpath + '/' + tag
            #TODO: Handle symlinks in GIT URLs
        else:
            path_suffix = name + '/' + self._component_file_name

        #Trying local search paths
        for local_search_path in self.local_search_paths:
            component_path = Path(local_search_path, path_suffix)
            tried_locations.append(str(component_path))
            if component_path.is_file():
                # TODO: Verify that the content matches the digest (if specified).
                component_ref._local_path = str(component_path)
                component_ref.spec = comp._load_component_spec_from_file(
                    str(component_path))
                return component_ref

        #Trying URI template
        if self.uri_search_template:
            url = self.uri_search_template.expand(
                name=name, digest=digest, tag=tag)
            tried_locations.append(url)
            if self._load_component_spec_from_url(component_ref, url):
                return component_ref

        #Trying URL prefixes
        for url_search_prefix in self.url_search_prefixes:
            url = url_search_prefix + path_suffix
            tried_locations.append(url)
            if self._load_component_spec_from_url(component_ref, url):
                return component_ref

        raise RuntimeError(
            'Component {} was not found. Tried the following locations:\n{}'
            .format(name, '\n'.join(tried_locations)))

    def _load_component_spec_from_url(self, component_ref, url) -> bool:
        """Loads component spec from a URL.

        On success, the url and spec attributes of the component_ref arg will be populated.

        Args:
            component_ref: the component whose spec to load.
            url: the location from which to obtain the component spec.

        Returns:
            True if the component was retrieved and non-empty; otherwise False.
        """

        try:
            response = requests.get(
                url, auth=self._auth
            )  #Does not throw exceptions on bad status, but throws on dead domains and malformed URLs. Should we log those cases?
            response.raise_for_status()
        except:
            return False

        if response.content:
            # TODO: Verify that the content matches the digest (if specified).
            component_ref.url = url
            component_ref.spec = comp._load_component_spec_from_yaml_or_zip_bytes(
                response.content)
            return True

        return False

    def _load_component_from_ref(self,
                                 component_ref: ComponentReference) -> Callable:
        component_ref = self._load_component_spec_in_component_ref(
            component_ref)
        return comp._create_task_factory_from_component_spec(
            component_spec=component_ref.spec, component_ref=component_ref)

    def search(self, name: str):
        """Searches for components by name in the configured component store.

        Prints the component name and URL for components that match the given name.
        Only components on GitHub are currently supported.

        Example::

            kfp.components.ComponentStore.default_store.search('xgboost')

            # Returns results:
            #     Xgboost train   https://raw.githubusercontent.com/.../components/XGBoost/Train/component.yaml
            #     Xgboost predict https://raw.githubusercontent.com/.../components/XGBoost/Predict/component.yaml
        """
        self._refresh_component_cache()
        for url in self._url_to_info_db.keys():
            component_info = json.loads(
                self._url_to_info_db.try_get_value_bytes(url))
            component_name = component_info['name']
            if name.casefold() in component_name.casefold():
                print('\t'.join([
                    component_name,
                    url,
                ]))

    def list(self):
        self.search('')

    def _refresh_component_cache(self):
        for url_search_prefix in self.url_search_prefixes:
            if url_search_prefix.startswith(
                    'https://raw.githubusercontent.com/'):
                logging.info('Searching for components in "{}"'.format(
                    url_search_prefix))
                for candidate in _list_candidate_component_uris_from_github_repo(
                        url_search_prefix, auth=self._auth):
                    component_url = candidate['url']
                    if self._url_to_info_db.exists(component_url):
                        continue

                    logging.debug(
                        'Found new component URL: "{}"'.format(component_url))

                    blob_hash = candidate['git_blob_hash']
                    if not self._git_blob_hash_to_data_db.exists(blob_hash):
                        logging.debug(
                            'Downloading component spec from "{}"'.format(
                                component_url))
                        response = _get_request_session().get(
                            component_url, auth=self._auth)
                        response.raise_for_status()
                        component_data = response.content

                        # Verifying the hash
                        received_data_hash = _calculate_git_blob_hash(
                            component_data)
                        if received_data_hash.lower() != blob_hash.lower():
                            raise RuntimeError(
                                'The downloaded component ({}) has incorrect hash: "{}" != "{}"'
                                .format(
                                    component_url,
                                    received_data_hash,
                                    blob_hash,
                                ))

                        # Verifying that the component is loadable
                        try:
                            component_spec = comp._load_component_spec_from_component_text(
                                component_data)
                        except:
                            continue
                        self._git_blob_hash_to_data_db.store_value_bytes(
                            blob_hash, component_data)
                    else:
                        component_data = self._git_blob_hash_to_data_db.try_get_value_bytes(
                            blob_hash)
                        component_spec = comp._load_component_spec_from_component_text(
                            component_data)

                    component_name = component_spec.name
                    self._url_to_info_db.store_value_text(
                        component_url,
                        json.dumps(
                            dict(
                                name=component_name,
                                url=component_url,
                                git_blob_hash=blob_hash,
                                digest=_calculate_component_digest(
                                    component_data),
                            )))