# -*- coding: utf-8 -*- # (The MIT License) # # Copyright (c) 2014 Kura # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the 'Software'), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. try: import simplejson as json except ImportError: import json from collections import namedtuple import re from .release import Release class Package(object): """ A PyPI package. :param pypi_dict: A dictionary retrieved from the PyPI server. """ def __init__(self, pypi_dict): self._package = pypi_dict['info'] self._releases = pypi_dict['releases'] def __repr__(self): return "".format(self.name) @property def name(self): """ >>> package = yarg.get('yarg') >>> package.name u'yarg' """ return self._package['name'] @property def pypi_url(self): """ >>> package = yarg.get('yarg') >>> package.url u'https://pypi.python.org/pypi/yarg' """ return self._package['package_url'] @property def summary(self): """ >>> package = yarg.get('yarg') >>> package.summary u'Some random summary stuff' """ return self._package['summary'] @property def description(self): """ >>> package = yarg.get('yarg') >>> package.description u'A super long description, usually uploaded from the README' """ return self._package['description'] @property def homepage(self): """ >>> package = yarg.get('yarg') >>> package.homepage u'https://kura.io/yarg/' """ if ('home_page' not in self._package or self._package['home_page'] == ""): return None return self._package['home_page'] @property def bugtracker(self): """ >>> package = yarg.get('yarg') >>> package.bugtracker u'https://github.com/kura/yarg/issues' """ if ('bugtrack_url' not in self._package or self._package['bugtrack_url'] == ""): return None return self._package['bugtrack_url'] @property def docs(self): """ >>> package = yarg.get('yarg') >>> package.docs u'https://yarg.readthedocs.org/en/latest' """ if ('docs_url' not in self._package or self._package['docs_url'] == ""): return None return self._package['docs_url'] @property def author(self): """ >>> package = yarg.get('yarg') >>> package.author Author(name=u'Kura', email=u'kura@kura.io') """ author = namedtuple('Author', 'name email') return author(name=self._package['author'], email=self._package['author_email']) @property def maintainer(self): """ >>> package = yarg.get('yarg') >>> package.maintainer Maintainer(name=u'Kura', email=u'kura@kura.io') """ maintainer = namedtuple('Maintainer', 'name email') return maintainer(name=self._package['maintainer'], email=self._package['maintainer_email']) @property def license(self): """ >>> package = yarg.get('yarg') >>> package.license u'MIT' """ return self._package['license'] @property def license_from_classifiers(self): """ >>> package = yarg.get('yarg') >>> package.license_from_classifiers u'MIT License' """ if len(self.classifiers) > 0: for c in self.classifiers: if c.startswith("License"): return c.split(" :: ")[-1] @property def downloads(self): """ >>> package = yarg.get('yarg') >>> package.downloads Downloads(day=50100, week=367941, month=1601938) # I wish """ _downloads = self._package['downloads'] downloads = namedtuple('Downloads', 'day week month') return downloads(day=_downloads['last_day'], week=_downloads['last_week'], month=_downloads['last_month']) @property def classifiers(self): """ >>> package = yarg.get('yarg') >>> package.classifiers [u'License :: OSI Approved :: MIT License', u'Programming Language :: Python :: 2.7', u'Programming Language :: Python :: 3.4'] """ return self._package['classifiers'] @property def python_versions(self): """ Returns a list of Python version strings that the package has listed in :attr:`yarg.Release.classifiers`. >>> package = yarg.get('yarg') >>> package.python_versions [u'2.6', u'2.7', u'3.3', u'3.4'] """ version_re = re.compile(r"""Programming Language \:\: """ """Python \:\: \d\.\d""") return [c.split(' :: ')[-1] for c in self.classifiers if version_re.match(c)] @property def python_implementations(self): """ Returns a list of Python implementation strings that the package has listed in :attr:`yarg.Release.classifiers`. >>> package = yarg.get('yarg') >>> package.python_implementations [u'CPython', u'PyPy'] """ return [c.split(' :: ')[-1] for c in self.classifiers if c.startswith("""Programming Language :: """ """Python :: Implementation""")] @property def latest_release_id(self): """ >>> package = yarg.get('yarg') >>> package.latest_release_id u'0.1.0' """ return self._package['version'] @property def latest_release(self): """ A list of :class:`yarg.release.Release` objects for each file in the latest release. >>> package = yarg.get('yarg') >>> package.latest_release [, ] """ release_id = self.latest_release_id return self.release(release_id) @property def has_wheel(self): """ Returns `True` if one of the :class:`yarg.release.Release` objects in the latest set of release files is `wheel` format. Returns `False` if not. >>> package = yarg.get('yarg') >>> package.has_wheel True """ for release in self.latest_release: if release.package_type in ('wheel', 'bdist_wheel'): return True return False @property def has_egg(self): """ Returns `True` if one of the :class:`yarg.release.Release` objects in the latest set of release files is `egg` format. Returns `False` if not. >>> package = yarg.get('yarg') >>> package.has_egg False """ for release in self.latest_release: if release.package_type in ('egg', 'bdist_egg'): return True return False @property def has_source(self): """ Returns `True` if one of the :class:`yarg.release.Release` objects in the latest set of release files is `source` format. Returns `False` if not. >>> package = yarg.get('yarg') >>> package.has_source True """ for release in self.latest_release: if release.package_type in ('source', 'sdist'): return True return False @property def release_ids(self): """ >>> package = yarg.get('yarg') >>> package.release_ids [u'0.0.1', u'0.0.5', u'0.1.0'] """ r = [(k, self._releases[k][0]['upload_time']) for k in self._releases.keys() if len(self._releases[k]) > 0] return [k[0] for k in sorted(r, key=lambda k: k[1])] def release(self, release_id): """ A list of :class:`yarg.release.Release` objects for each file in a release. :param release_id: A pypi release id. >>> package = yarg.get('yarg') >>> last_release = yarg.releases[-1] >>> package.release(last_release) [, ] """ if release_id not in self.release_ids: return None return [Release(release_id, r) for r in self._releases[release_id]] def json2package(json_content): """ Returns a :class:`yarg.release.Release` object from JSON content from the PyPI server. :param json_content: JSON encoded content from the PyPI server. """ return Package(json.loads(json_content))