Source code for bumper.utils
import logging
import pkg_resources
import re
import sys
from brownie.caching import memoize
import requests
import simplejson
log = logging.getLogger(__name__)
[docs]def parse_requirements(requirements, in_file=None):
""" Parse string requirements into list of :class:`pkg_resources.Requirement` instances """
try:
return list(pkg_resources.parse_requirements(requirements))
except Exception as e:
in_file = ' in %s' % in_file if in_file else ''
log.error(' '.join(e) + in_file)
sys.exit(1)
[docs]class PyPI(object):
""" Helper functions to get package info from PyPI """
@staticmethod
@memoize
[docs] def package_info(package):
""" All package info for given package """
package_json_url = 'https://pypi.python.org/pypi/%s/json' % package
try:
logging.getLogger('requests').setLevel(logging.WARN)
response = requests.get(package_json_url)
response.raise_for_status()
return simplejson.loads(response.text)
except Exception as e:
log.debug('Could not get package info from %s: %s', package_json_url, e)
@staticmethod
[docs] def latest_package_version(package):
""" Latest version for package """
info = PyPI.package_info(package)
return info and info['info']['version']
@staticmethod
[docs] def all_package_versions(package):
""" All versions for package """
info = PyPI.package_info(package)
return info and sorted(info['releases'].keys(), key=lambda x: x.split(), reverse=True) or []
@staticmethod
def changes(package, current_version, new_version):
changes = []
if not current_version:
return changes
parsed_current_version = pkg_resources.parse_version(current_version)
parsed_new_version = pkg_resources.parse_version(new_version)
try:
package_info = PyPI.package_info(package)
repo_url = None
repo_re = re.compile('https?://(?:github.com|bitbucket.org)/[\w\-]+/' + package)
for url_name in ['home_page', 'docs_url']:
if package_info['info'].get(url_name):
match = repo_re.match(package_info['info'][url_name])
if match:
repo_url = match.group(0)
if not repo_url and package_info['info'].get('description'):
match = repo_re.search(package_info['info']['description'])
if match:
repo_url = match.group(0)
if not repo_url:
log.debug('Could not find repo url for %s to get changelog', package)
return changes
changelog = PyPI._changelog(repo_url)
version_re = re.compile('^(?:Version )?(\d+(?:\.\d+)+)', flags=re.IGNORECASE)
hr_re = re.compile('^\s*(?:[\-=~+]+)\s*$')
if changelog:
version = None
for line in changelog.split('\n'):
line = line.rstrip()
if not line or hr_re.match(line):
continue
match = version_re.match(line)
if match:
version = match.group(1)
parsed_version = pkg_resources.parse_version(version)
if parsed_version <= parsed_current_version:
break
if parsed_version <= parsed_new_version:
changes.append(version)
else:
version = None
continue
if version:
if line.startswith('- '):
line = '+' + line.lstrip('-')
changes.append(' ' + line)
except Exception as e:
log.debug(e)
return changes
@staticmethod
def _changelog(repo_url):
if 'github.com' in repo_url:
repo_url = repo_url.replace('github.com', 'raw.githubusercontent.com').replace('http:', 'https:')
repo_url += '/master'
for change_ext in ['rst', 'md', 'txt']:
for change_name in ['CHANGELOG', 'HISTORY', 'CHANGES', 'changes']:
for subfolder in ['', 'docs']:
changelog_url = '%s/%s/%s.%s' % (repo_url, subfolder, change_name, change_ext)
log.debug('Trying %s', changelog_url)
try:
response = requests.get(changelog_url, timeout=5)
response.raise_for_status()
return response.text
except Exception:
pass