16
16
import os
17
17
import shutil
18
18
import zipfile
19
+ from typing import Dict , Set , Union
19
20
20
21
from tqdm import tqdm
21
22
22
23
from emu .utils import api_codename
23
24
24
25
26
+ class NotAZipfile (Exception ):
27
+ pass
28
+
29
+
25
30
class AndroidReleaseZip (object ):
26
31
"""Provides information of released android products.
27
32
@@ -30,74 +35,84 @@ class AndroidReleaseZip(object):
30
35
about the contents of the zip.
31
36
"""
32
37
33
- def __init__ (self , file_name ):
34
- self .file_name = file_name
38
+ def __init__ (self , file_name : str ):
39
+ self .file_name : str = file_name
35
40
if not zipfile .is_zipfile (file_name ):
36
- raise Exception ("{} is not a zipfile!" .format (file_name ))
41
+ raise NotAZipfile (f"{ file_name } is not a zipfile!" )
42
+
37
43
with zipfile .ZipFile (file_name , "r" ) as zip_file :
38
- self .props = collections .defaultdict (set )
39
- files = [x for x in zip_file .infolist () if "source.properties" in x .filename or "build.prop" in x .filename ]
44
+ self .props : Dict [str , Set [str ]] = collections .defaultdict (set )
45
+ files = [
46
+ x
47
+ for x in zip_file .infolist ()
48
+ if "source.properties" in x .filename or "build.prop" in x .filename
49
+ ]
40
50
for file in files :
41
51
for key , value in self ._unpack_properties (zip_file , file ).items ():
42
52
self .props [key ] = value
43
53
44
- def _unpack_properties (self , zip_file , zip_info ):
54
+ def _unpack_properties (
55
+ self , zip_file : zipfile .ZipFile , zip_info : zipfile .ZipInfo
56
+ ) -> Dict [str , str ]:
45
57
prop = zip_file .read (zip_info ).decode ("utf-8" ).splitlines ()
46
58
res = dict ([a .split ("=" ) for a in prop if "=" in a ])
47
59
return res
48
60
49
- def __str__ (self ):
50
- return "{}-{}" . format ( self .description (), self .revision ())
61
+ def __str__ (self ) -> str :
62
+ return f" { self .description ()} - { self .revision ()} "
51
63
52
- def description (self ):
64
+ def description (self ) -> Union [ str , None ] :
53
65
"""Descripton of this release."""
54
66
return self .props .get ("Pkg.Desc" )
55
67
56
- def revision (self ):
68
+ def revision (self ) -> Union [ str , None ] :
57
69
"""The revision of this release."""
58
70
return self .props .get ("Pkg.Revision" )
59
71
60
- def build_id (self ):
72
+ def build_id (self ) -> str :
61
73
"""The Pkg.BuildId or revision if build id is not available."""
62
74
if "Pkg.BuildId" in self .props :
63
75
return self .props .get ("Pkg.BuildId" )
64
76
return self .revision ()
65
77
66
- def is_system_image (self ):
78
+ def is_system_image (self ) -> bool :
67
79
"""True if this zip file contains a system image."""
68
- return "System Image" in self .description () or "Android SDK Platform" in self .description ()
80
+ return (
81
+ "System Image" in self .description ()
82
+ or "Android SDK Platform" in self .description ()
83
+ )
69
84
70
- def is_emulator (self ):
85
+ def is_emulator (self ) -> bool :
71
86
"""True if this zip file contains the android emulator."""
72
87
return "Android Emulator" in self .description ()
73
88
74
- def copy (self , destination ) :
89
+ def copy (self , destination : str ) -> str :
75
90
"""Copy the zipfile to the given destination.
76
91
77
92
If the destination is the same as this zipfile the current path
78
93
will be returned a no copy is made.
79
94
80
95
Args:
81
- destination ({string} ): The destination to copy this zip to.
96
+ destination (str ): The destination to copy this zip to.
82
97
83
98
Returns:
84
- {string} : The path where this zip file was copied to
99
+ str : The path where this zip file was copied to.
85
100
"""
86
101
try :
87
102
return shutil .copy2 (self .file_name , destination )
88
103
except shutil .SameFileError :
89
104
logging .warning ("Will not copy to itself, ignoring.." )
90
105
return self .file_name
91
106
92
- def extract (self , destination ) :
107
+ def extract (self , destination : str ) -> None :
93
108
"""Extract this release zip to the given destination
94
109
95
110
Args:
96
- destination ({string} ): The destination to extract the zipfile to.
111
+ destination (str ): The destination to extract the zipfile to.
97
112
"""
98
113
99
114
zip_file = zipfile .ZipFile (self .file_name )
100
- print ("Extracting: {} -> {}" . format ( self . file_name , destination ) )
115
+ print (f "Extracting: { self . file_name } -> { destination } " )
101
116
for info in tqdm (iterable = zip_file .infolist (), total = len (zip_file .infolist ())):
102
117
filename = zip_file .extract (info , path = destination )
103
118
mode = info .external_attr >> 16
@@ -108,61 +123,71 @@ def extract(self, destination):
108
123
class SystemImageReleaseZip (AndroidReleaseZip ):
109
124
"""An Android Release Zipfile containing an emulator system image."""
110
125
111
- ABI_CPU_MAP = {"armeabi-v7a" : "arm" , "arm64-v8a" : "arm64" , "x86_64" : "x86_64" , "x86" : "x86" }
112
- SHORT_MAP = {"armeabi-v7a" : "a32" , "arm64-v8a" : "a64" , "x86_64" : "x64" , "x86" : "x86" }
113
- SHORT_TAG = {
126
+ ABI_CPU_MAP : Dict [str , str ] = {
127
+ "armeabi-v7a" : "arm" ,
128
+ "arm64-v8a" : "arm64" ,
129
+ "x86_64" : "x86_64" ,
130
+ "x86" : "x86" ,
131
+ }
132
+ SHORT_MAP : Dict [str , str ] = {
133
+ "armeabi-v7a" : "a32" ,
134
+ "arm64-v8a" : "a64" ,
135
+ "x86_64" : "x64" ,
136
+ "x86" : "x86" ,
137
+ }
138
+ SHORT_TAG : Dict [str , str ] = {
114
139
"android" : "aosp" ,
115
140
"google_apis" : "google" ,
116
141
"google_apis_playstore" : "playstore" ,
117
142
"google_ndk_playstore" : "ndk_playstore" ,
118
143
"android-tv" : "tv" ,
119
144
}
120
145
121
- def __init__ (self , file_name ):
146
+ def __init__ (self , file_name : str ):
122
147
super ().__init__ (file_name )
123
148
if not self .is_system_image ():
124
- raise Exception ( "{ } is not a zip file with a system image". format ( file_name ) )
149
+ raise NotAZipfile ( f" { file_name } is not a zip file with a system image" )
125
150
126
151
self .props ["qemu.cpu" ] = self .qemu_cpu ()
127
152
self .props ["qemu.tag" ] = self .tag ()
128
153
self .props ["qemu.short_tag" ] = self .short_tag ()
129
154
self .props ["qemu.short_abi" ] = self .short_abi ()
130
155
131
- def api (self ):
156
+ def api (self ) -> str :
132
157
"""The api level, if any."""
133
158
return self .props .get ("AndroidVersion.ApiLevel" , "" )
134
159
135
- def codename (self ):
160
+ def codename (self ) -> str :
136
161
"""First letter of the desert, if any."""
137
162
if "AndroidVersion.CodeName" in self .props :
138
163
return self .props ["AndroidVersion.CodeName" ]
139
164
return api_codename (self .api ())
140
165
141
- def abi (self ):
166
+ def abi (self ) -> str :
142
167
"""The abi if any."""
143
168
return self .props .get ("SystemImage.Abi" , "" )
144
169
145
- def short_abi (self ):
170
+ def short_abi (self ) -> str :
146
171
"""Shortened version of the ABI string."""
147
172
if self .abi () not in self .SHORT_MAP :
148
173
logging .error ("%s not in short map" , self )
149
174
return self .SHORT_MAP [self .abi ()]
150
175
151
- def qemu_cpu (self ):
176
+ def qemu_cpu (self ) -> str :
152
177
"""Returns the cpu architecture, derived from the abi."""
153
178
return self .ABI_CPU_MAP .get (self .abi (), "None" )
154
179
155
- def gpu (self ):
180
+ def gpu (self ) -> str :
156
181
"""Returns whether or not the system has gpu support."""
157
182
return self .props .get ("SystemImage.GpuSupport" )
158
183
159
- def tag (self ):
184
+ def tag (self ) -> str :
160
185
"""The tag associated with this release."""
161
186
tag = self .props .get ("SystemImage.TagId" , "" )
162
187
if tag == "default" or tag .strip () == "" :
163
188
tag = "android"
164
189
return tag
165
190
166
- def short_tag (self ):
191
+ def short_tag (self ) -> str :
167
192
"""A shorthand tag."""
168
193
return self .SHORT_TAG [self .tag ()]
0 commit comments