Changeset 11655:50d8dd042ab3 in orange


Ignore:
Timestamp:
08/12/13 18:56:27 (8 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Added support for installing from .zip source packages.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/utils/addons.py

    r11654 r11655  
    1818#TODO Document this module. 
    1919 
    20 import socket 
    2120import shelve 
    2221import xmlrpclib 
     
    2625import tempfile 
    2726import tarfile 
     27import zipfile 
    2828import shutil 
    2929import os 
     
    3535import posixpath 
    3636import site 
     37import itertools 
    3738 
    3839from collections import namedtuple, defaultdict 
     
    183184 
    184185 
     186def open_archive(path, mode="r"): 
     187    """ 
     188    Return an open archive file object (zipfile.ZipFile or tarfile.TarFile). 
     189    """ 
     190    _, ext = os.path.splitext(path) 
     191    if ext == ".zip": 
     192        # TODO: should it also open .egg, ... 
     193        archive = zipfile.ZipFile(path, mode) 
     194 
     195    elif ext in (".tar", ".gz", ".bz2", ".tgz", ".tbz2", ".tb2"): 
     196        archive = tarfile.open(path, mode) 
     197 
     198    return archive 
     199 
     200 
     201member_info = namedtuple( 
     202    "member_info", 
     203    ["info",  # original info object (Tar/ZipInfo) 
     204     "path",  # filename inside the archive 
     205     "linkname",  # linkname if applicable 
     206     "issym",  # True if sym link 
     207     "islnk",  # True if hardlink 
     208     ] 
     209) 
     210 
     211 
     212def archive_members(archive): 
     213    """ 
     214    Given an open archive return an iterator of `member_info` instances. 
     215    """ 
     216    if isinstance(archive, zipfile.ZipFile): 
     217        def converter(info): 
     218            return member_info(info, info.filename, None, False, False) 
     219 
     220        return itertools.imap(converter, archive.infolist()) 
     221    elif isinstance(archive, tarfile.TarFile): 
     222        def converter(info): 
     223            return member_info(info, info.name, info.linkname, 
     224                               info.issym(), info.islnk()) 
     225        return itertools.imap(converter, archive.getmembers()) 
     226    else: 
     227        raise TypeError 
     228 
     229 
     230def resolve_path(path): 
     231    """ 
     232    Return a normalized real path. 
     233    """ 
     234    return os.path.normpath(os.path.realpath(os.path.abspath(path))) 
     235 
     236 
     237def is_badfile(member, base_dir): 
     238    """ 
     239    Would extracting `member_info` instance write outside of `base_dir`. 
     240    """ 
     241    path = member.path 
     242    full_path = resolve_path(os.path.join(base_dir, path)) 
     243    return not full_path.startswith(base_dir) 
     244 
     245 
     246def is_badlink(member, base_dir): 
     247    """ 
     248    Would extracting `member_info` instance create a link to outside 
     249    of `base_dir`. 
     250 
     251    """ 
     252    if member.issym or member.islnk: 
     253        dirname = os.path.dirname(member.path) 
     254        full_path = resolve_path(os.path.join(dirname, member.linkname)) 
     255        return not full_path.startswith(base_dir) 
     256    else: 
     257        return False 
     258 
     259 
     260def check_safe(member, base_dir): 
     261    """ 
     262    Check if member is safe to extract to base_dir or raise an exception. 
     263    """ 
     264    path = member.path 
     265    drive, path = os.path.splitdrive(path) 
     266 
     267    if drive != "": 
     268        raise ValueError("Absolute path in archive") 
     269 
     270    if path.startswith("/"): 
     271        raise ValueError("Absolute path in archive") 
     272 
     273    base_dir = resolve_path(base_dir) 
     274 
     275    if is_badfile(member, base_dir): 
     276        raise ValueError("Extract outside %r" % base_dir) 
     277    if is_badlink(member, base_dir): 
     278        raise ValueError("Link outside %r" % base_dir) 
     279 
     280    return True 
     281 
     282 
     283def extract_archive(archive, path="."): 
     284    """ 
     285    Extract the contents of `archive` to `path`. 
     286    """ 
     287    if isinstance(archive, basestring): 
     288        archive = open_archive(archive) 
     289 
     290    members = archive_members(archive) 
     291 
     292    for member in members: 
     293        if check_safe(member, path): 
     294            archive.extract(member.info, path) 
     295 
     296 
    185297def run_setup(setup_script, args): 
    186298    """ 
     
    230342                stream, package_file, progress=progress_cb) 
    231343 
    232         egg_contents = tarfile.open(package_path) 
    233         egg_contents.extractall(tmpdir) 
     344        extract_archive(package_path, tmpdir) 
     345 
    234346        setup_py = os.path.join(tmpdir, name + '-' + addon.available_version, 
    235347                                'setup.py') 
Note: See TracChangeset for help on using the changeset viewer.