Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 33 additions & 26 deletions emu/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import re
import subprocess
import sys
from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple


def get_keywords():
Expand All @@ -32,6 +33,12 @@ def get_keywords():

class VersioneerConfig:
"""Container for Versioneer configuration parameters."""
VCS: str
style: str
tag_prefix: str
parentdir_prefix: str
versionfile_source: str
verbose: bool


def get_config():
Expand All @@ -56,9 +63,9 @@ class NotThisMethod(Exception):
HANDLERS = {}


def register_vcs_handler(vcs, method): # decorator
def register_vcs_handler(vcs: str, method: str): # decorator
"""Decorator to mark a method as the handler for a particular VCS."""
def decorate(f):
def decorate(f: Any):
"""Store f in HANDLERS[vcs][method]."""
if vcs not in HANDLERS:
HANDLERS[vcs] = {}
Expand All @@ -67,11 +74,12 @@ def decorate(f):
return decorate


def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
env=None):
def run_command(commands: List[Any], args: List[str], cwd: Optional[str] = None, verbose: bool = False, hide_stderr: bool = False,
env: Optional[Mapping[str, Any]] = None):
"""Call the given command(s)."""
assert isinstance(commands, list)
p = None
dispcmd = None
for c in commands:
try:
dispcmd = str([c] + args)
Expand All @@ -81,8 +89,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
stderr=(subprocess.PIPE if hide_stderr
else None))
break
except EnvironmentError:
e = sys.exc_info()[1]
except EnvironmentError as e:
if e.errno == errno.ENOENT:
continue
if verbose:
Expand All @@ -104,16 +111,16 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
return stdout, p.returncode


def versions_from_parentdir(parentdir_prefix, root, verbose):
def versions_from_parentdir(parentdir_prefix: str, root: str, verbose: bool):
"""Try to determine the version from the parent directory name.

Source tarballs conventionally unpack into a directory that includes both
the project name and a version string. We will also support searching up
two directory levels for an appropriately named parent directory
"""
rootdirs = []
rootdirs: List[str] = []

for i in range(3):
for _ in range(3):
dirname = os.path.basename(root)
if dirname.startswith(parentdir_prefix):
return {"version": dirname[len(parentdir_prefix):],
Expand All @@ -130,13 +137,13 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):


@register_vcs_handler("git", "get_keywords")
def git_get_keywords(versionfile_abs):
def git_get_keywords(versionfile_abs: str):
"""Extract version information from the given file."""
# the code embedded in _version.py can just fetch the value of these
# keywords. When used from setup.py, we don't want to import _version.py,
# so we do it with a regexp instead. This function is not used from
# _version.py.
keywords = {}
keywords: Dict[str, str] = {}
try:
f = open(versionfile_abs, "r")
for line in f.readlines():
Expand All @@ -159,7 +166,7 @@ def git_get_keywords(versionfile_abs):


@register_vcs_handler("git", "keywords")
def git_versions_from_keywords(keywords, tag_prefix, verbose):
def git_versions_from_keywords(keywords: Mapping[str, Any], tag_prefix: str, verbose: bool):
"""Get version information from git keywords."""
if not keywords:
raise NotThisMethod("no keywords at all, weird")
Expand Down Expand Up @@ -214,7 +221,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):


@register_vcs_handler("git", "pieces_from_vcs")
def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
def git_pieces_from_vcs(tag_prefix: str, root: str, verbose: bool, run_command: Callable[..., Tuple[Any, Any]] = run_command):
"""Get version from 'git describe' in the root of the source tree.

This only gets called if the git-archive 'subst' keywords were *not*
Expand All @@ -225,8 +232,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
if sys.platform == "win32":
GITS = ["git.cmd", "git.exe"]

out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=True)
_, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=True)
if rc != 0:
if verbose:
print("Directory %s not under git control" % root)
Expand All @@ -247,7 +254,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
raise NotThisMethod("'git rev-parse' failed")
full_out = full_out.strip()

pieces = {}
pieces: Dict[str, Any] = {}
pieces["long"] = full_out
pieces["short"] = full_out[:7] # maybe improved later
pieces["error"] = None
Expand Down Expand Up @@ -305,14 +312,14 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
return pieces


def plus_or_dot(pieces):
def plus_or_dot(pieces: Mapping[str, Any]):
"""Return a + if we don't already have one, else return a ."""
if "+" in pieces.get("closest-tag", ""):
return "."
return "+"


def render_pep440(pieces):
def render_pep440(pieces: Mapping[str, Any]):
"""Build up version string, with post-release "local version identifier".

Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
Expand All @@ -337,7 +344,7 @@ def render_pep440(pieces):
return rendered


def render_pep440_pre(pieces):
def render_pep440_pre(pieces: Mapping[str, Any]):
"""TAG[.post.devDISTANCE] -- No -dirty.

Exceptions:
Expand All @@ -353,7 +360,7 @@ def render_pep440_pre(pieces):
return rendered


def render_pep440_post(pieces):
def render_pep440_post(pieces: Mapping[str, Any]):
"""TAG[.postDISTANCE[.dev0]+gHEX] .

The ".dev0" means dirty. Note that .dev0 sorts backwards
Expand All @@ -380,7 +387,7 @@ def render_pep440_post(pieces):
return rendered


def render_pep440_old(pieces):
def render_pep440_old(pieces: Mapping[str, Any]):
"""TAG[.postDISTANCE[.dev0]] .

The ".dev0" means dirty.
Expand All @@ -402,7 +409,7 @@ def render_pep440_old(pieces):
return rendered


def render_git_describe(pieces):
def render_git_describe(pieces: Mapping[str, Any]):
"""TAG[-DISTANCE-gHEX][-dirty].

Like 'git describe --tags --dirty --always'.
Expand All @@ -422,7 +429,7 @@ def render_git_describe(pieces):
return rendered


def render_git_describe_long(pieces):
def render_git_describe_long(pieces: Mapping[str, Any]):
"""TAG-DISTANCE-gHEX[-dirty].

Like 'git describe --tags --dirty --always -long'.
Expand All @@ -442,7 +449,7 @@ def render_git_describe_long(pieces):
return rendered


def render(pieces, style):
def render(pieces: Mapping[str, Any], style: str):
"""Render the given version pieces into the requested style."""
if pieces["error"]:
return {"version": "unknown",
Expand Down Expand Up @@ -482,7 +489,7 @@ def get_versions():
# case we can only use expanded keywords.

cfg = get_config()
verbose = cfg.verbose
verbose: bool = cfg.verbose

try:
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
Expand All @@ -495,7 +502,7 @@ def get_versions():
# versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert
# this to find the root from __file__.
for i in cfg.versionfile_source.split('/'):
for _ in cfg.versionfile_source.split('/'):
root = os.path.dirname(root)
except NameError:
return {"version": "0+unknown", "full-revisionid": None,
Expand Down
35 changes: 21 additions & 14 deletions emu/android_release_zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from _typeshed import StrPath
import collections
import logging
import os
import shutil
from typing import DefaultDict, Dict, Union
import zipfile

from tqdm import tqdm
Expand All @@ -30,28 +32,30 @@ class AndroidReleaseZip(object):
about the contents of the zip.
"""

def __init__(self, file_name):
def __init__(self, file_name: StrPath):
self.file_name = file_name
if not zipfile.is_zipfile(file_name):
raise Exception("{} is not a zipfile!".format(file_name))
with zipfile.ZipFile(file_name, "r") as zip_file:
self.props = collections.defaultdict(set)
files = [x for x in zip_file.infolist() if "source.properties" in x.filename or "build.prop" in x.filename]
self.props: DefaultDict[str, str
] = collections.defaultdict(str)
files = [x for x in zip_file.infolist(
) if "source.properties" in x.filename or "build.prop" in x.filename]
for file in files:
for key, value in self._unpack_properties(zip_file, file).items():
self.props[key] = value

def _unpack_properties(self, zip_file, zip_info):
def _unpack_properties(self, zip_file: zipfile.ZipFile, zip_info: zipfile.ZipInfo):
prop = zip_file.read(zip_info).decode("utf-8").splitlines()
res = dict([a.split("=") for a in prop if "=" in a])
res: Dict[str, str] = dict([a.split("=") for a in prop if "=" in a])
return res

def __str__(self):
return "{}-{}".format(self.description(), self.revision())

def description(self):
def description(self) -> Union[str, set[str]]:
"""Descripton of this release."""
return self.props.get("Pkg.Desc")
return self.props.get("Pkg.Desc", "")

def revision(self):
"""The revision of this release."""
Expand All @@ -71,7 +75,7 @@ def is_emulator(self):
"""True if this zip file contains the android emulator."""
return "Android Emulator" in self.description()

def copy(self, destination):
def copy(self, destination: str):
"""Copy the zipfile to the given destination.

If the destination is the same as this zipfile the current path
Expand All @@ -89,7 +93,7 @@ def copy(self, destination):
logging.warning("Will not copy to itself, ignoring..")
return self.file_name

def extract(self, destination):
def extract(self, destination: str):
"""Extract this release zip to the given destination

Args:
Expand All @@ -100,16 +104,18 @@ def extract(self, destination):
print("Extracting: {} -> {}".format(self.file_name, destination))
for info in tqdm(iterable=zip_file.infolist(), total=len(zip_file.infolist())):
filename = zip_file.extract(info, path=destination)
mode = info.external_attr >> 16
mode: int = info.external_attr >> 16
if mode:
os.chmod(filename, mode)


class SystemImageReleaseZip(AndroidReleaseZip):
"""An Android Release Zipfile containing an emulator system image."""

ABI_CPU_MAP = {"armeabi-v7a": "arm", "arm64-v8a": "arm64", "x86_64": "x86_64", "x86": "x86"}
SHORT_MAP = {"armeabi-v7a": "a32", "arm64-v8a": "a64", "x86_64": "x64", "x86": "x86"}
ABI_CPU_MAP = {"armeabi-v7a": "arm", "arm64-v8a": "arm64",
"x86_64": "x86_64", "x86": "x86"}
SHORT_MAP = {"armeabi-v7a": "a32",
"arm64-v8a": "a64", "x86_64": "x64", "x86": "x86"}
SHORT_TAG = {
"android": "aosp",
"google_apis": "google",
Expand All @@ -118,10 +124,11 @@ class SystemImageReleaseZip(AndroidReleaseZip):
"android-tv": "tv",
}

def __init__(self, file_name):
def __init__(self, file_name: str):
super().__init__(file_name)
if not self.is_system_image():
raise Exception("{} is not a zip file with a system image".format(file_name))
raise Exception(
"{} is not a zip file with a system image".format(file_name))

self.props["qemu.cpu"] = self.qemu_cpu()
self.props["qemu.tag"] = self.tag()
Expand Down
Loading