PKKxvi;;flonda/__init__.py"""Build conda packages using flit""" __version__ = '0.2' PK >Hkflonda/__main__.pyfrom .cli import main main() PKښK@ flonda/cli.pyimport argparse import pathlib import subprocess import sys from . import __version__ from .flonda import PackageBuilder DEFAULT_PYTHON = ['{}.{}'.format(*sys.version_info[:2])] DEFAULT_PLATFORMS = ['linux-64', 'linux-32', 'osx-64', 'win-64', 'win-32'] def main(argv=None): ap = argparse.ArgumentParser(prog='flonda') ap.add_argument('--version', action='version', version='flonda '+__version__) ap.add_argument('--pythons', help='Comma separated Python versions to build for') ap.add_argument('--platforms', help='Comma separated conda platforms to build for') ap.add_argument('--ini-file', type=pathlib.Path, default='flit.ini') ap.add_argument('--dist-dir', type=pathlib.Path) subparsers = ap.add_subparsers(title='subcommands', dest='subcmd') parser_build = subparsers.add_parser('build', help='Build conda packages') parser_publish = subparsers.add_parser('publish', help='Build conda packages & publish to anaconda.org') args = ap.parse_args(argv) if args.pythons: pythons = args.pythons.split(',') else: pythons = DEFAULT_PYTHON if args.platforms: platforms = args.platforms.split(',') else: platforms = DEFAULT_PLATFORMS if args.dist_dir: dist_dir = args.dist_dir else: dist_dir = args.ini_file.parent / 'dist' if args.subcmd == 'build': build_multi(args.ini_file, dist_dir, pythons, platforms) elif args.subcmd == 'publish': files = build_multi(args.ini_file, dist_dir, pythons, platforms) publish_packages(files) else: ap.print_help() sys.exit(1) def build_multi(ini_path, dist_dir, pythons, platforms): built_packages = [] for plat in platforms: try: (dist_dir / plat).mkdir(parents=True) except FileExistsError: pass platform, bitness = plat.split('-') for py in pythons: pb = PackageBuilder(ini_path, py, platform, bitness) # TODO: Build numbers aren't always 0 filename = '{}-{}-py{}_0.tar.bz2'.format( pb.metadata.name, pb.metadata.version, py.replace('.', '')) pkg_path = dist_dir / plat / filename with pkg_path.open('wb') as f: pb.build(f) built_packages.append(pkg_path) # The filename should be the same for all of them filename = '{}-{}-*.tar.bz2'.format(pb.metadata.name, pb.metadata.version) print("Packages are now in", dist_dir / '*' / filename) return built_packages def publish_packages(package_files): print("Uploading {} files to anaconda.org...".format(len(package_files))) subprocess.run(['anaconda', 'upload'] + package_files, check=True) PKvKpflonda/flonda.pyimport json import os from pathlib import Path import posixpath from flit import inifile, common import tarfile from io import BytesIO pkgdir = Path(__file__).parent class PackageBuilder: def __init__(self, ini_path, python_version, platform, bitness): self.ini_path = ini_path self.directory = ini_path.parent self.ini_info = ini_info = inifile.read_pkg_ini(ini_path) self.module = common.Module(ini_info['module'], self.directory) self.metadata = common.make_metadata(self.module, ini_info) self.python_version = python_version self.platform = platform assert platform in {'osx', 'linux', 'win'}, platform self.bitness = bitness assert bitness in {'32', '64'}, bitness self.files = [] self.has_prefix_files = [] def record_file(self, arcname, has_prefix=False): self.files.append(arcname) if has_prefix: self.has_prefix_files.append(arcname) def site_packages_path(self): if self.platform == 'win': return 'Lib/site-packages/' else: return 'lib/python{}/site-packages/'.format(self.python_version) def scripts_path(self): if self.platform == 'win': return 'Scripts/' else: return 'bin/' def build(self, fileobj): with tarfile.open(fileobj=fileobj, mode='w:bz2') as tf: self.add_module(tf) self.create_scripts(tf) self.write_index(tf) self.write_has_prefix_list(tf) self.write_files_list(tf) def _include(self, path): name = os.path.basename(path) if (name == '__pycache__') or name.endswith('.pyc'): return False return True def add_module(self, tf): if self.module.is_package: for dirpath, dirs, files in os.walk(str(self.module.path)): reldir = os.path.relpath(dirpath, str(self.directory)) for f in sorted(files): full_path = os.path.join(dirpath, f) if self._include(full_path): in_archive = posixpath.join(self.site_packages_path(), reldir, f) tf.add(full_path, in_archive) self.record_file(in_archive) dirs[:] = [d for d in sorted(dirs) if self._include(d)] for d in dirs: full_path = os.path.join(dirpath, d) tf.add(full_path, posixpath.join(self.site_packages_path(), reldir, d), recursive=False) else: # Module is a single file src = str(self.module.path) dst = self.site_packages_path() + self.module.path.name tf.add(src, arcname=dst) self.record_file(dst) def _write_script_unix(self, tf, name, contents): ti = tarfile.TarInfo(self.scripts_path() + name) contents = contents.encode('utf-8') ti.size = len(contents) ti.mode = 0o755 # Set executable bit tf.addfile(ti, BytesIO(contents)) self.record_file(ti.name, has_prefix=True) def _write_script_windows(self, tf, name, contents): from win_cli_launchers import find_exe self._write_script_unix(tf, name+'-script.py', contents) src = find_exe(arch=('x86' if self.bitness == '32' else 'x64')) dst = self.scripts_path() + name + '.exe' tf.add(src, arcname=dst) self.record_file(dst) def create_scripts(self, tf): for name, (mod, func) in self.ini_info['scripts'].items(): s = common.script_template.format( module=mod, func=func, # This is replaced when the package is installed: interpreter='/opt/anaconda1anaconda2anaconda3/bin/python', ) if self.platform == 'win': self._write_script_windows(tf, name, s) else: self._write_script_unix(tf, name, s) def _find_license(self): if self.metadata.license: return self.metadata.license for cl in self.metadata.classifiers: if cl.startswith('License :: OSI Approved :: '): return cl[len('License :: OSI Approved :: '):] return '' def _get_dependencies(self): py = ["python {}*".format(self.python_version)] cfg = self.ini_info['raw_config'] if cfg.has_section('x-flonda') and ('requires' in cfg['x-flonda']): return py + cfg['x-flonda']['requires'].splitlines() else: from .requirements import requires_dist_to_conda_requirements return py + requires_dist_to_conda_requirements(self.metadata.requires_dist, self.python_version, self.platform, self.bitness) def write_index(self, tf): a = { "arch": ("x86_64" if self.bitness=='64' else 'x86'), "build": "py{}_0".format(self.python_version.replace('.', '')), "build_number": 0, "depends": self._get_dependencies(), "license": self._find_license(), "name": self.metadata.name, "platform": self.platform, "subdir": "{}-{}".format(self.platform, self.bitness), "version": self.metadata.version, } contents = json.dumps(a, indent=2, sort_keys=True).encode('utf-8') ti = tarfile.TarInfo('info/index.json') ti.size = len(contents) tf.addfile(ti, BytesIO(contents)) def write_has_prefix_list(self, tf): if not self.has_prefix_files: return contents = '\n'.join(self.has_prefix_files).encode('utf-8') ti = tarfile.TarInfo('info/has_prefix') ti.size = len(contents) tf.addfile(ti, BytesIO(contents)) def write_files_list(self, tf): contents = '\n'.join(self.files).encode('utf-8') ti = tarfile.TarInfo('info/files') ti.size = len(contents) tf.addfile(ti, BytesIO(contents)) PKj>HNݍ flonda/requirements.pyimport ast sys_platforms = { 'linux': 'linux', 'osx': 'darwin', 'win': 'win32', } os_names = { 'linux': 'posix', 'osx': 'posx', 'win': 'nt', } platform_machines = { '32': 'i386', '64': 'x86_64' } class EnvMarkerNameFiller(ast.NodeTransformer): def __init__(self, python_version, platform, bitness): self.python_version = python_version self.platform = platform self.bitness = bitness def visit_Name(self, node): if node.id == 'python_version': new = ast.Str(self.python_version) elif node.id == 'python_full_version': # We can't really match this exactly, because we're building for # e.g. any Python 3.5.x new = ast.Str(self.python_version + '.0') else: raise ValueError("Unexpected name: %s" % node.id) return ast.copy_location(new, node) def visit_Attribute(self, node): if node.attr == 'platform': assert node.value.id == 'sys' new = ast.Str(sys_platforms[self.platform]) elif node.attr == 'name': assert node.value.id == 'os' new = ast.Str(os_names[self.platform]) elif node.attr == 'version': assert node.value.id == 'platform' # Don't know new = ast.Str('') elif node.attr == 'machine': assert node.value.id == 'platform' new = ast.Str(platform_machines[self.bitness]) elif node.attr == 'python_implementation': assert node.value.id == 'platform' new = ast.Str('CPython') else: raise ValueError('Unknown attribute: %s' % node.attr) return ast.copy_location(new, node) def eval_env_marker(s, python_version, platform, bitness): expr = ast.parse(s, '', 'eval') filler = EnvMarkerNameFiller(python_version, platform, bitness) filler.visit(expr) codeobj = compile(expr, 'Hm 599flonda-0.2.dist-info/LICENSEThe MIT License (MIT) Copyright (c) 2016 Thomas Kluyver 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. PK!H١Wdflonda-0.2.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q0343 /, (-JLR()*M ILR(4KM̫#DPK!H>flonda-0.2.dist-info/METADATAmKo0#6 UUPUMM,06#omryŠJT 'k2$?j1T=ض \D%cthT'Sqݶtl͠Y}/+ uea4F]4A ۛVP[Cm[f_ tar~[jelĥVӁepy9xAPk_FL{'QK[xI6Erq!C,`-F"J2|4BH9Nrm]x'J ptaʘǀ|L[k0@옢H*`r ֨(bJ9qgٔ18/Jr=6ҜDHu$l‰Ӛz]ʱpyȝmмɑBPK!H`Qflonda-0.2.dist-info/RECORDmϹrP@2b ̎xl3 Qu3ssqTcoJ@F|!~ZLmUԑgɫ#zXݿ+tUЖ\tXVݎ Q.eH^\ Lo(ՃQ9PM4F1[xE!G:yte{|` FDd z2P|9 sUv'~m Z%kotqS}sP3e푙pv`(ap86i5x E/|Uo&qR}Mt:+W.Ľ` UA2]i%/gṔOf% TDy8*=aR)ʼn{i{yC2HhHAHkkflonda/__main__.pyPKښK@ flonda/cli.pyPKvKp flonda/flonda.pyPKj>HNݍ #flonda/requirements.pyPK!HSo]),%\-flonda-0.2.dist-info/entry_points.txtPK>Hm 599-flonda-0.2.dist-info/LICENSEPK!H١Wd;2flonda-0.2.dist-info/WHEELPK!H>2flonda-0.2.dist-info/METADATAPK!H`Q5flonda-0.2.dist-info/RECORDPK 7