1import io
2import os
3import tarfile
4import zipfile
5
6from .distribution import Distribution
7
8class SDist(Distribution):
9
10    def __init__(self, filename, metadata_version=None):
11        self.filename = filename
12        self.metadata_version = metadata_version
13        self.extractMetadata()
14
15    @classmethod
16    def _get_archive(cls, fqn):
17        if not os.path.exists(fqn):
18            raise ValueError('No such file: %s' % fqn)
19
20        if zipfile.is_zipfile(fqn):
21            archive = zipfile.ZipFile(fqn)
22            names = archive.namelist()
23            def read_file(name):
24                return archive.read(name)
25        elif tarfile.is_tarfile(fqn):
26            archive = tarfile.TarFile.open(fqn)
27            names = archive.getnames()
28            def read_file(name):
29                return archive.extractfile(name).read()
30        else:
31            raise ValueError('Not a known archive format: %s' % fqn)
32
33        return archive, names, read_file
34
35
36    def read(self):
37        fqn = os.path.abspath(
38                os.path.normpath(self.filename))
39
40        archive, names, read_file = self._get_archive(fqn)
41
42        try:
43            tuples = [x.split('/') for x in names if 'PKG-INFO' in x]
44            schwarz = sorted([(len(x), x) for x in tuples])
45            for path in [x[1] for x in schwarz]:
46                candidate = '/'.join(path)
47                data = read_file(candidate)
48                if b'Metadata-Version' in data:
49                    return data
50        finally:
51            archive.close()
52
53        raise ValueError('No PKG-INFO in archive: %s' % fqn)
54
55
56class UnpackedSDist(SDist):
57    def __init__(self, filename, metadata_version=None):
58        if os.path.isdir(filename):
59            pass
60        elif os.path.isfile(filename):
61            filename = os.path.dirname(filename)
62        else:
63            raise ValueError('No such file: %s' % filename)
64
65        super(UnpackedSDist, self).__init__(
66                filename, metadata_version=metadata_version)
67
68    def read(self):
69        try:
70            pkg_info = os.path.join(self.filename, 'PKG-INFO')
71            with io.open(pkg_info, errors='ignore') as f:
72                return f.read()
73        except Exception as e:
74            raise ValueError('Could not load %s as an unpacked sdist: %s'
75                                % (self.filename, e))
76