Browse Source

Update the list command

Valentin Niess 8 months ago
parent
commit
eb05b77a85

+ 6 - 15
python_appimage/commands/build/manylinux.py

@@ -3,8 +3,7 @@ from pathlib import Path
 import shutil
 import shutil
 
 
 from ...appimage import build_appimage
 from ...appimage import build_appimage
-from ...manylinux import Arch, Downloader, ImageExtractor, LinuxTag, \
-                         PythonExtractor
+from ...manylinux import ensure_image, PythonExtractor
 from ...utils.tmp import TemporaryDirectory
 from ...utils.tmp import TemporaryDirectory
 
 
 
 
@@ -21,21 +20,13 @@ def execute(tag, abi):
     '''Build a Python AppImage using a Manylinux image
     '''Build a Python AppImage using a Manylinux image
     '''
     '''
 
 
-    tag, arch = tag.split('_', 1)
-    tag = LinuxTag.from_brief(tag)
-    arch = Arch.from_str(arch)
-
-    downloader = Downloader(tag=tag, arch=arch)
-    downloader.download()
-
-    image_extractor = ImageExtractor(downloader.default_destination())
-    image_extractor.extract()
+    image = ensure_image(tag)
 
 
     pwd = os.getcwd()
     pwd = os.getcwd()
     with TemporaryDirectory() as tmpdir:
     with TemporaryDirectory() as tmpdir:
         python_extractor = PythonExtractor(
         python_extractor = PythonExtractor(
-            arch = arch,
-            prefix = image_extractor.default_destination(),
+            arch = image.arch,
+            prefix = image.path,
             tag = abi
             tag = abi
         )
         )
         appdir = Path(tmpdir) / 'AppDir'
         appdir = Path(tmpdir) / 'AppDir'
@@ -44,7 +35,7 @@ def execute(tag, abi):
         fullname = '-'.join((
         fullname = '-'.join((
             f'{python_extractor.impl}{python_extractor.version.long()}',
             f'{python_extractor.impl}{python_extractor.version.long()}',
             abi,
             abi,
-            f'{tag}_{arch}'
+            f'{image.tag}_{image.arch}'
         ))
         ))
 
 
         destination = f'{fullname}.AppImage'
         destination = f'{fullname}.AppImage'
@@ -52,7 +43,7 @@ def execute(tag, abi):
             appdir = str(appdir),
             appdir = str(appdir),
             destination = destination
             destination = destination
         )
         )
-        shutil.move(
+        shutil.copy(
             Path(tmpdir) / destination,
             Path(tmpdir) / destination,
             Path(pwd) / destination
             Path(pwd) / destination
         )
         )

+ 16 - 26
python_appimage/commands/list.py

@@ -1,8 +1,8 @@
-import os
+import glob
+from pathlib import Path
 
 
-from ..utils.docker import docker_run
+from ..manylinux import ensure_image, PythonVersion
 from ..utils.log import log
 from ..utils.log import log
-from ..utils.tmp import TemporaryDirectory
 
 
 
 
 __all__ = ['execute']
 __all__ = ['execute']
@@ -18,26 +18,16 @@ def execute(tag):
     '''List python versions installed in a manylinux image
     '''List python versions installed in a manylinux image
     '''
     '''
 
 
-    with TemporaryDirectory() as tmpdir:
-        script = (
-            'for dir in $(ls /opt/python | grep "^cp[0-9]"); do',
-            '   version=$(/opt/python/$dir/bin/python -c "import sys; ' \
-                    'sys.stdout.write(sys.version.split()[0])")',
-            '   echo "$dir $version"',
-            'done',
-        )
-        if tag.startswith('2_'):
-            image = 'manylinux_' + tag
-        else:
-            image = 'manylinux' + tag
-        result = docker_run(
-            'quay.io/pypa/' + image,
-            script,
-            capture = True
-        )
-        pythons = [line.split() for line in result.split(os.linesep) if line]
-
-        for (abi, version) in pythons:
-            log('LIST', "{:7} ->  /opt/python/{:}".format(version, abi))
-
-        return pythons
+    image = ensure_image(tag)
+
+    pythons = []
+    for path in glob.glob(str(image.path / 'opt/python/cp*')):
+        path = Path(path)
+        version = PythonVersion.from_str(path.readlink().name[8:]).long()
+        pythons.append((path.name, version))
+    pythons = sorted(pythons)
+
+    for (abi, version) in pythons:
+        log('LIST', "{:8} ->  /opt/python/{:}".format(version, abi))
+
+    return pythons

+ 23 - 1
python_appimage/manylinux/__init__.py

@@ -1,7 +1,29 @@
+from types import SimpleNamespace
+
 from .config import Arch, LinuxTag, PythonImpl, PythonVersion
 from .config import Arch, LinuxTag, PythonImpl, PythonVersion
 from .download import Downloader
 from .download import Downloader
 from .extract import ImageExtractor, PythonExtractor
 from .extract import ImageExtractor, PythonExtractor
 
 
 
 
-__all__ = ['Arch', 'Downloader', 'ImageExtractor', 'LinuxTag',
+__all__ = ['Arch', 'Downloader', 'ensure_image', 'ImageExtractor', 'LinuxTag',
            'PythonExtractor', 'PythonImpl', 'PythonVersion']
            'PythonExtractor', 'PythonImpl', 'PythonVersion']
+
+
+def ensure_image(tag):
+    '''Extract a manylinux image to the cache'''
+
+    tag, arch = tag.split('_', 1)
+    tag = LinuxTag.from_brief(tag)
+    arch = Arch.from_str(arch)
+
+    downloader = Downloader(tag=tag, arch=arch)
+    downloader.download()
+
+    image_extractor = ImageExtractor(downloader.default_destination())
+    image_extractor.extract()
+
+    return SimpleNamespace(
+        arch = arch,
+        tag = tag,
+        path = image_extractor.default_destination(),
+    )

+ 1 - 4
python_appimage/manylinux/download.py

@@ -22,10 +22,6 @@ class DownloadError(Exception):
     pass
     pass
 
 
 
 
-class TarError(Exception):
-    pass
-
-
 @dataclass(frozen=True)
 @dataclass(frozen=True)
 class Downloader:
 class Downloader:
 
 
@@ -68,6 +64,7 @@ class Downloader:
 
 
         # Authenticate to quay.io.
         # Authenticate to quay.io.
         repository = f'pypa/{self.image}'
         repository = f'pypa/{self.image}'
+        log('PULL', f'{self.image}:{tag}')
         url = 'https://quay.io/v2/auth'
         url = 'https://quay.io/v2/auth'
         url = f'{url}?service=quay.io&scope=repository:{repository}:pull'
         url = f'{url}?service=quay.io&scope=repository:{repository}:pull'
         debug('GET', url)
         debug('GET', url)

+ 2 - 0
python_appimage/manylinux/extract.py

@@ -346,6 +346,8 @@ class ImageExtractor:
                 shutil.rmtree(destination, ignore_errors=True)
                 shutil.rmtree(destination, ignore_errors=True)
             atexit.register(cleanup, destination)
             atexit.register(cleanup, destination)
 
 
+        log('EXTRACT', f'{self.prefix.name}:{self.tag}')
+
         with open(self.prefix / f'tags/{self.tag}.json') as f:
         with open(self.prefix / f'tags/{self.tag}.json') as f:
             meta = json.load(f)
             meta = json.load(f)
         layers = meta['layers']
         layers = meta['layers']

+ 0 - 58
python_appimage/utils/docker.py

@@ -1,58 +0,0 @@
-import os
-import platform
-import stat
-import subprocess
-import sys
-
-from .compat import decode
-from .log import log
-from .system import system
-
-
-def docker_run(image, extra_cmds, capture=False):
-    '''Execute commands within a docker container
-    '''
-
-    ARCH = platform.machine()
-    if image.endswith(ARCH):
-        bash_arg = '/pwd/run.sh'
-    elif image.endswith('i686') and ARCH == 'x86_64':
-        bash_arg = '-c "linux32 /pwd/run.sh"'
-    elif image.endswith('x86_64') and ARCH == 'i686':
-        bash_arg = '-c "linux64 /pwd/run.sh"'
-    else:
-        raise ValueError('Unsupported Docker image: ' + image)
-
-    log('PULL', image)
-    system(('docker', 'pull', image))
-
-    script = [
-        'set -e',
-        'trap "chown -R {:}:{:} *" EXIT'.format(os.getuid(),
-                                                os.getgid()),
-        'cd /pwd'
-    ]
-
-    script += extra_cmds
-
-    with open('run.sh', 'w') as f:
-        f.write(os.linesep.join(script))
-    os.chmod('run.sh', stat.S_IRWXU)
-
-    cmd = ' '.join(('docker', 'run', '--mount',
-                    'type=bind,source={:},target=/pwd'.format(os.getcwd()),
-                    image, '/bin/bash', bash_arg))
-
-    if capture:
-        opts = {'stderr': subprocess.PIPE, 'stdout': subprocess.PIPE}
-    else:
-        opts = {}
-    log('RUN', image)
-    p = subprocess.Popen(cmd, shell=True, **opts)
-    r = p.communicate()
-    if p.returncode != 0:
-        if p.returncode == 139:
-            sys.stderr.write("segmentation fault when running Docker (139)\n")
-        sys.exit(p.returncode)
-    if capture:
-        return decode(r[0])

+ 0 - 14
python_appimage/utils/manylinux.py

@@ -1,14 +0,0 @@
-def format_appimage_name(abi, version, tag):
-    '''Format the Python AppImage name using the ABI, python version and OS tags
-    '''
-    return 'python{:}-{:}-{:}.AppImage'.format(
-        version, abi, format_tag(tag))
-
-
-def format_tag(tag):
-    '''Format Manylinux tag
-    '''
-    if tag.startswith('2_'):
-        return 'manylinux_' + tag
-    else:
-        return 'manylinux' + tag

+ 1 - 1
scripts/test-appimage.py

@@ -162,7 +162,7 @@ assert_eq(expected, sys.prefix)
 
 
         # Test SSL (see issue #24).
         # Test SSL (see issue #24).
         if version.major > 2:
         if version.major > 2:
-            Script(f'''
+            Script('''
 from http import HTTPStatus
 from http import HTTPStatus
 import urllib.request
 import urllib.request
 with urllib.request.urlopen('https://wikipedia.org') as r:
 with urllib.request.urlopen('https://wikipedia.org') as r: