mirror of
https://github.com/captn3m0/Scripts.git
synced 2024-09-27 22:22:53 +00:00
252 lines
9.8 KiB
Python
Executable File
252 lines
9.8 KiB
Python
Executable File
#!/usr/bin/python
|
|
from __future__ import unicode_literals
|
|
from __future__ import with_statement
|
|
|
|
import sys
|
|
import datetime
|
|
import logging
|
|
import argparse
|
|
import re
|
|
|
|
# make this script work for python2 and python3
|
|
# the try will fail on python3
|
|
try:
|
|
input = raw_input
|
|
from xmlrpclib import ServerProxy
|
|
except NameError:
|
|
from xmlrpc.client import ServerProxy
|
|
|
|
BLANK_PKGBUILD = """\
|
|
#Automatically generated by pip2arch on {date}
|
|
pkgname={pkg.outname}
|
|
pkgver={pkg.version}
|
|
pkgrel=1
|
|
pkgdesc="{pkg.description}"
|
|
url="{pkg.url}"
|
|
depends=('{pkg.pyversion}' {depends})
|
|
makedepends=('{pkg.distributepackage}' {makedepends})
|
|
license=('{pkg.license}')
|
|
arch=('any')
|
|
source=('{pkg.download_url}')
|
|
md5sums=('{pkg.md5}')
|
|
build() {{
|
|
cd $srcdir/{pkg.name}-{pkg.version}
|
|
{pkg.pyversion} setup.py build
|
|
}}
|
|
package() {{
|
|
cd $srcdir/{pkg.name}-{pkg.version}
|
|
{pkg.pyversion} setup.py install --root="$pkgdir" --optimize=1 {pkg.setup_args}
|
|
}}
|
|
"""
|
|
|
|
SOURCEFILE_TYPE_RE = re.compile(".*\.(tar|zip|gz|z|bz2?|xz)", re.IGNORECASE)
|
|
|
|
class pip2archException(Exception): pass
|
|
class VersionNotFound(pip2archException): pass
|
|
class LackOfInformation(pip2archException): pass
|
|
|
|
class Package(object):
|
|
logging.info('Creating Server Proxy object')
|
|
client = ServerProxy('https://pypi.python.org/pypi')
|
|
depends = []
|
|
makedepends = []
|
|
data_received = False
|
|
setup_args = ''
|
|
|
|
def get_package(self, name, outname, pyversion ,version=None):
|
|
if version is None:
|
|
versions = self.client.package_releases(name)
|
|
if len(versions) > 1:
|
|
version = self.choose_version(versions)
|
|
else:
|
|
logging.info('Using version %s' % versions[0])
|
|
version = versions[0]
|
|
self.version = version
|
|
self.pyversion = pyversion
|
|
|
|
data = self.client.release_data(name, version)
|
|
logging.info('Got release_data from PyPi')
|
|
|
|
raw_urls = self.client.release_urls(name, version)
|
|
logging.info('Got release_urls from PyPi')
|
|
if not len(data):
|
|
raise VersionNotFound('PyPi did not return any information for version {0}'.format(self.version))
|
|
elif not len(raw_urls):
|
|
if 'download_url' in data:
|
|
download_url = data['download_url']
|
|
if SOURCEFILE_TYPE_RE.match(data['download_url']) is None:
|
|
raise LackOfInformation("Couldn't find any suitable source")
|
|
else:
|
|
urls = {'url': download_url}
|
|
logging.warning('Got download link but no md5, you may have to search it by youself or generate it')
|
|
else:
|
|
raise LackOfInformation('PyPi did not return the necessary information to create the PKGBUILD')
|
|
else:
|
|
urls = {}
|
|
for url in raw_urls:
|
|
if SOURCEFILE_TYPE_RE.match(url['filename']):
|
|
urls = url
|
|
if not urls:
|
|
raise pip2archException
|
|
('Selected package version had no suitable sources')
|
|
logging.info('Parsed release_urls data')
|
|
|
|
|
|
self.distributepackage = 'python2-distribute' if\
|
|
self.pyversion != 'python' else 'python3'
|
|
logging.info("Set distribute package as {0}".format(self.distributepackage))
|
|
|
|
if outname is not None:
|
|
self.outname = outname.lower()
|
|
elif any(re.search(r'Librar(ies|y)', item) for item in data['classifiers']):
|
|
#if this is a library
|
|
self.outname = '{pyversion}-{pkgname}'.format(
|
|
pyversion=self.pyversion, pkgname=name).lower()
|
|
logging.info('Automaticly added {0} to the front of the package'.format(self.pyversion))
|
|
else:
|
|
self.outname = name.lower()
|
|
|
|
#check for licenes texts
|
|
if len(data.get('license', '')) > 10:
|
|
self.license = 'CUSTOM'
|
|
else:
|
|
self.license = data.get('license', 'UNKNOWN')
|
|
|
|
try:
|
|
self.name = data['name']
|
|
self.description = data['summary']
|
|
self.download_url = urls.get('url', '')
|
|
self.md5 = urls.get('md5_digest', '')
|
|
self.url = data.get('home_page', '')
|
|
self.depends = data.get('requires', [])
|
|
except KeyError:
|
|
raise pip2archException('PyPi did not return needed information')
|
|
logging.info('Parsed other data')
|
|
self.data_received = True
|
|
|
|
def search(self, term, interactive=False):
|
|
results = self.client.search({'description': term[1:],
|
|
'name': term[1:]},
|
|
'or')
|
|
logging.info('Got search results for term {term} from PyPi server'.format(term=term))
|
|
|
|
#If no results
|
|
if not results:
|
|
print ('No packages found')
|
|
return
|
|
for i, result in enumerate(results):
|
|
i += 1
|
|
print ('{index}. {name} - {summary}'.format(index=i, name=result['name'], summary=result['summary']))
|
|
|
|
#If we don't want talking, exit here
|
|
if not interactive:
|
|
#self.data_received = False
|
|
return
|
|
|
|
selection = raw_input('Enter the number of the PyPi package you would like to process\n')
|
|
|
|
try:
|
|
selection = int(selection.strip())
|
|
selection -= 1
|
|
chosen = results[selection]
|
|
except (TypeError, IndexError):
|
|
print ('Not a valid selection. Must be integer in range 1 - {length}'.format(length=len(results)))
|
|
retry = raw_input('Retry? [Y/n]\n')
|
|
if retry.strip()[0].lower() != 'n':
|
|
#offer recurse on failure, maybe user will be smarter this time -.-
|
|
return self.search(term)
|
|
else:
|
|
return
|
|
|
|
name = chosen['name']
|
|
outname = chosen['name']
|
|
|
|
return self.get_package(name, outname)
|
|
|
|
def choose_version(self, versions):
|
|
print ('Multiple versions found:')
|
|
print (', '.join(versions))
|
|
ver = raw_input('Which version would you like to use? ')
|
|
if ver in versions:
|
|
return ver
|
|
else:
|
|
print ('That was NOT one of the choices...')
|
|
print ('Try again')
|
|
return self.choose_version(versions)
|
|
|
|
def add_depends(self, depends):
|
|
self.depends += depends
|
|
|
|
def add_makedepends(self, makedepends):
|
|
self.makedepends += makedepends
|
|
|
|
def render(self):
|
|
depends = "'" + "' '".join(d for d in self.depends) + "'" if self.depends else ''
|
|
makedepends = "'" + "' '".join(d for d in self.makedepends) + "'" if self.makedepends else ''
|
|
return BLANK_PKGBUILD.format(pkg=self,
|
|
date=datetime.date.today(),
|
|
depends=depends,
|
|
makedepends=makedepends)
|
|
|
|
def set_logging_level(level_str):
|
|
level = getattr(logging, level_str.upper())
|
|
logging.root.setLevel(level)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Convert a PyPi package into an Arch Linux PKGBUILD.')
|
|
parser.add_argument('pkgname', metavar='PKGNAME', action='store',
|
|
help='Name of PyPi package for pip2arch to process')
|
|
parser.add_argument('-v', '--version', dest='version', action='store',
|
|
help='The version of the speciied PyPi package to process')
|
|
parser.add_argument('-p', '--python-version', dest='pyversion', action='store',
|
|
default='python', choices=('python','python2'),
|
|
help='The python version to build and install the package with')
|
|
parser.add_argument('-o', '--output', dest='outfile', action='store',
|
|
default='PKGBUILD',
|
|
help='The file to output the generated PKGBUILD to')
|
|
parser.add_argument('-s', '--search', dest='search', action='store_true',
|
|
help="Search for given package name, instead of building PKGBUILD")
|
|
parser.add_argument('-i', '--interactive', dest='interactive', action='store_true',
|
|
help="Makes all commands interactive, prompting user for input.")
|
|
parser.add_argument('-d', '--dependencies', dest='depends', action='append',
|
|
help="The name of a package that should be added to the depends array")
|
|
parser.add_argument('-m', '--make-dependencies', dest='makedepends', action='append',
|
|
help="The name of a package that should be added to the makedepends array")
|
|
parser.add_argument('-n', '--output-package-name', dest='outname', action='store', default=None,
|
|
help='The name of the package that pip2arch will generate')
|
|
parser.add_argument('--logging-level', dest='logging_level', action='store', default='warning', choices=('warning', 'info', 'debug'),
|
|
help='The level of logging messages to show')
|
|
parser.add_argument('-b', '--build-args', dest='build_args', action='store',
|
|
help='Custom arguments for the python install setup.py file')
|
|
|
|
args = parser.parse_args()
|
|
|
|
set_logging_level(args.logging_level)
|
|
|
|
p = Package()
|
|
|
|
if args.search:
|
|
p.search(args.pkgname, interactive=args.interactive)
|
|
else:
|
|
p.get_package(name=args.pkgname, pyversion= args.pyversion,
|
|
version=args.version, outname=args.outname)
|
|
|
|
if args.depends:
|
|
p.add_depends(args.depends)
|
|
if args.makedepends:
|
|
p.add_makedepends(args.makedepends)
|
|
if args.build_args:
|
|
p.setup_args = args.build_args
|
|
if p.data_received:
|
|
print ('Got package information')
|
|
with open(args.outfile, 'w') as f:
|
|
f.write(p.render())
|
|
print ('PKGBUILD written')
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
main()
|
|
except pip2archException as e:
|
|
sys.exit('Pip2Arch error: {0}'.format(e))
|
|
else:
|
|
sys.exit(0) |