1import os
2import re
3import zipfile
4from typing import Optional
5
6from pkginfo import distribution
7
8from twine import exceptions
9
10wininst_file_re = re.compile(r".*py(?P<pyver>\d+\.\d+)\.exe$")
11
12
13class WinInst(distribution.Distribution):
14    def __init__(self, filename: str, metadata_version: Optional[str] = None) -> None:
15        self.filename = filename
16        self.metadata_version = metadata_version
17        self.extractMetadata()
18
19    @property
20    def py_version(self) -> str:
21        m = wininst_file_re.match(self.filename)
22        if m is None:
23            return "any"
24        else:
25            return m.group("pyver")
26
27    def read(self) -> bytes:
28        fqn = os.path.abspath(os.path.normpath(self.filename))
29        if not os.path.exists(fqn):
30            raise exceptions.InvalidDistribution("No such file: %s" % fqn)
31
32        if fqn.endswith(".exe"):
33            archive = zipfile.ZipFile(fqn)
34            names = archive.namelist()
35
36            def read_file(name: str) -> bytes:
37                return archive.read(name)
38
39        else:
40            raise exceptions.InvalidDistribution(
41                "Not a known archive format for file: %s" % fqn
42            )
43
44        try:
45            tuples = [
46                x.split("/")
47                for x in names
48                if x.endswith(".egg-info") or x.endswith("PKG-INFO")
49            ]
50            schwarz = sorted([(len(x), x) for x in tuples])
51            for path in [x[1] for x in schwarz]:
52                candidate = "/".join(path)
53                data = read_file(candidate)
54                if b"Metadata-Version" in data:
55                    return data
56        finally:
57            archive.close()
58
59        raise exceptions.InvalidDistribution(
60            "No PKG-INFO/.egg-info in archive: %s" % fqn
61        )
62