Skip to content

Commit c70f927

Browse files
author
Erwin Jansen
committed
Python 3 version fixes
This migrates the build system to poetry, and removes support for python2. It contains a series of small fixes: - Improve launcher logging - Remove python2 support - Add some typing information - Split build into sys and emu directories
1 parent 867208b commit c70f927

File tree

13 files changed

+367
-378
lines changed

13 files changed

+367
-378
lines changed

configure.sh

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ if [ "${BASH_SOURCE-}" = "$0" ]; then
1818
exit 33
1919
fi
2020

21-
PYTHON=python
21+
PYTHON=python3
22+
VENV=.venv
2223

23-
if [ ! -f "./venv/bin/activate" ]; then
24+
if [ ! -f "./$VENV/bin/activate" ]; then
2425
# Prefer python3 if it is available.
2526
if command -v python3 &>/dev/null; then
2627
echo "Using python 3"
27-
PYTHON=python3
28-
$PYTHON -m venv venv
29-
[ -e ./venv/bin/pip ] && ./venv/bin/pip install --upgrade pip
30-
[ -e ./venv/bin/pip ] && ./venv/bin/pip install --upgrade setuptools
28+
$PYTHON -m venv $VENV
29+
[ -e ./$VENV/bin/pip ] && ./$VENV/bin/pip install --upgrade pip
30+
[ -e ./$VENV/bin/pip ] && ./$VENV/bin/pip install --upgrade setuptools
3131
else
3232
echo "Using python 2 ----<< Deprecated! See: https://python3statement.org/.."
33-
$PYTHON -m virtualenv &>/dev/null || { echo "This script relies on virtualenv, you can install it with 'pip install virtualenv' (https://virtualenv.pypa.io)"; return ; }
34-
$PYTHON -m virtualenv venv
33+
echo "You need to upgrade to python3"
34+
exit 33
3535
fi
3636
fi
37-
if [ -e ./venv/bin/activate ]; then
38-
. ./venv/bin/activate
39-
$PYTHON setup.py develop
37+
if [ -e ./$VENV/bin/activate ]; then
38+
. ./$VENV/bin/activate
39+
pip install -e .
4040
echo "Ready to run emu-docker!"
4141
fi

emu/containers/docker_container.py

Lines changed: 40 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,11 @@
1717
import sys
1818
import shutil
1919
import abc
20+
from pathlib import Path
2021

2122
import docker
22-
from tqdm import tqdm
2323
from emu.utils import mkdir_p
24-
25-
26-
class ProgressTracker(object):
27-
"""Tracks progress using tqdm for a set of layers that are pushed."""
28-
29-
def __init__(self):
30-
# This tracks the information for a given layer id.
31-
self.progress = {}
32-
self.idx = -1
33-
34-
def __del__(self):
35-
for k in self.progress:
36-
self.progress[k]["tqdm"].close()
37-
38-
def update(self, entry):
39-
"""Update the progress bars given a an entry.."""
40-
if "id" not in entry:
41-
return
42-
43-
identity = entry["id"]
44-
if identity not in self.progress:
45-
self.idx += 1
46-
self.progress[identity] = {
47-
"tqdm": tqdm(total=0, position=self.idx, unit="B", unit_scale=True), # The progress bar
48-
"total": 0, # Total of bytes we are shipping
49-
"status": "", # Status message.
50-
"current": 0, # Current of total already send.
51-
}
52-
prog = self.progress[identity]
53-
54-
total = int(entry.get("progressDetail", {}).get("total", -1))
55-
current = int(entry.get("progressDetail", {}).get("current", 0))
56-
if prog["total"] != total and total != -1:
57-
prog["total"] = total
58-
prog["tqdm"].reset(total=total)
59-
if prog["status"] != entry["status"]:
60-
prog["tqdm"].set_description("{0} {1}".format(entry.get("status"), identity))
61-
if current != 0:
62-
diff = current - prog["current"]
63-
prog["current"] = current
64-
prog["tqdm"].update(diff)
24+
from emu.containers.progress_tracker import ProgressTracker
6525

6626

6727
class DockerContainer(object):
@@ -86,25 +46,30 @@ def get_api_client(self):
8646
api_client = docker.APIClient()
8747
logging.info(api_client.version())
8848
return api_client
89-
except:
90-
logging.exception("Failed to create default client, trying domain socket.", exc_info=True)
49+
except Exception as _err:
50+
logging.exception(
51+
"Failed to create default client, trying domain socket.", exc_info=True
52+
)
9153

9254
api_client = docker.APIClient(base_url="unix://var/run/docker.sock")
9355
logging.info(api_client.version())
9456
return api_client
9557

9658
def push(self):
9759
image = self.full_name()
98-
print("Pushing docker image: {}.. be patient this can take a while!".format(self.full_name()))
60+
print(
61+
f"Pushing docker image: {self.full_name()}.. be patient this can take a while!"
62+
)
63+
9964
tracker = ProgressTracker()
10065
try:
10166
client = docker.from_env()
10267
result = client.images.push(image, "latest", stream=True, decode=True)
10368
for entry in result:
10469
tracker.update(entry)
105-
self.docker_image().tag("{}{}:latest".format(self.repo, self.image_name()))
106-
except:
107-
logging.exception("Failed to push image.", exc_info=True)
70+
self.docker_image().tag(f"{self.repo}{self.image_name()}:latest")
71+
except Exception as err:
72+
logging.error("Failed to push image due to %s", err, exc_info=True)
10873
logging.warning("You can manually push the image as follows:")
10974
logging.warning("docker push %s", image)
11075

@@ -123,24 +88,26 @@ def launch(self, port_map):
12388
detach=True,
12489
ports=port_map,
12590
)
126-
print("Launched {} (id:{})".format(container.name, container.id))
127-
print("docker logs -f {}".format(container.name))
128-
print("docker stop {}".format(container.name))
91+
print(f"Launched {container.name} (id:{container.id})")
92+
print(f"docker logs -f {container.name}")
93+
print(f"docker stop {container.name}")
12994
return container
130-
except:
131-
logging.exception("Unable to run the %s", image_sha)
95+
except Exception as err:
96+
logging.exception("Unable to run the %s due to %s", image.id, err)
13297
print("Unable to start the container, try running it as:")
133-
print("./run.sh ", image_sha)
98+
print(f"./run.sh {image.id}")
13499

135-
def create_container(self, dest):
100+
def create_container(self, dest: Path):
136101
"""Creates the docker container, returning the sha of the container, or None in case of failure."""
137102
identity = None
138103
image_tag = self.full_name()
139-
print("docker build {} -t {}".format(dest, image_tag))
104+
print(f"docker build {dest} -t {image_tag}")
140105
try:
141106
api_client = self.get_api_client()
142-
logging.info("build(path=%s, tag=%s, rm=True, decode=True)", dest, image_tag)
143-
result = api_client.build(path=dest, tag=image_tag, rm=True, decode=True)
107+
logging.info(
108+
"build(path=%s, tag=%s, rm=True, decode=True)", dest, image_tag
109+
)
110+
result = api_client.build(path=str(dest.absolute()), tag=image_tag, rm=True, decode=True)
144111
for entry in result:
145112
if "stream" in entry:
146113
sys.stdout.write(entry["stream"])
@@ -149,17 +116,18 @@ def create_container(self, dest):
149116
client = docker.from_env()
150117
image = client.images.get(identity)
151118
image.tag(self.repo + self.image_name(), "latest")
152-
except:
153-
logging.exception("Failed to create container.", exc_info=True)
119+
except Exception as err:
120+
logging.error("Failed to create container due to %s.", err, exc_info=True)
154121
logging.warning("You can manually create the container as follows:")
155122
logging.warning("docker build -t %s %s", image_tag, dest)
156123

157124
return identity
158125

159-
def clean(self, dest):
160-
if os.path.exists(dest):
126+
def clean(self, dest: Path):
127+
if dest.exists():
161128
shutil.rmtree(dest)
162-
mkdir_p(dest)
129+
130+
dest.mkdir(parents=True)
163131

164132
def pull(self, image, tag):
165133
"""Tries to retrieve the given image and tag.
@@ -173,22 +141,24 @@ def pull(self, image, tag):
173141
for entry in result:
174142
tracker.update(entry)
175143
except:
176-
logging.info("Failed to retrieve image, this is not uncommon.", exc_info=True)
144+
logging.info(
145+
"Failed to retrieve image, this is not uncommon.", exc_info=True
146+
)
177147
return False
178148

179149
return True
180150

181151
def full_name(self):
182152
if self.repo:
183-
return "{}{}:{}".format(self.repo, self.image_name(), self.docker_tag())
153+
return f"{self.repo}{self.image_name()}:{self.docker_tag()}"
184154
return (self.image_name(), self.docker_tag())
185155

186156
def latest_name(self):
187157
if self.repo:
188-
return "{}{}:{}".format(self.repo, self.image_name(), "latest")
158+
return f"{self.repo}{self.image_name()}:{self.docker_tag()}"
189159
return (self.image_name(), "latest")
190160

191-
def create_cloud_build_step(self, dest):
161+
def create_cloud_build_step(self, dest: Path):
192162
return {
193163
"name": "gcr.io/cloud-builders/docker",
194164
"args": [
@@ -197,7 +167,7 @@ def create_cloud_build_step(self, dest):
197167
self.full_name(),
198168
"-t",
199169
self.latest_name(),
200-
os.path.basename(dest),
170+
dest.absolute().parent,
201171
],
202172
}
203173

@@ -218,7 +188,7 @@ def available(self):
218188
"""True if this container image is locally available."""
219189
return self.docker_image() != None
220190

221-
def build(self, dest):
191+
def build(self, dest: Path):
222192
self.write(dest)
223193
return self.create_container(dest)
224194

@@ -227,7 +197,7 @@ def can_pull(self):
227197
return self.pull(self.image_name(), self.docker_tag())
228198

229199
@abc.abstractmethod
230-
def write(self, destination):
200+
def write(self, destination: Path):
231201
"""Method responsible for writing the Dockerfile and all necessary files to build a container.
232202
233203
Args:

emu/containers/progress_tracker.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright 2019 The Android Open Source Project
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from tqdm import tqdm
15+
16+
17+
class ProgressTracker:
18+
"""
19+
A class that tracks progress using tqdm for a set of layers that are pushed.
20+
"""
21+
22+
def __init__(self):
23+
""" Initializes a new instance of ProgressTracker with an empty progress dictionary. """
24+
self.progress = {}
25+
26+
def __del__(self):
27+
""" Closes the tqdm progress bars for all entries in the progress dictionary. """
28+
for _key, value in self.progress.items():
29+
value["tqdm"].close()
30+
31+
def update(self, entry):
32+
"""Updates the progress bars given an entry dictionary."""
33+
if "id" not in entry:
34+
return
35+
36+
identity = entry["id"]
37+
if identity not in self.progress:
38+
self.progress[identity] = {
39+
"tqdm": tqdm(total=0, unit="B", unit_scale=True), # The progress bar
40+
"total": 0, # Total of bytes we are shipping
41+
"status": "", # Status message.
42+
"current": 0, # Current of total already send.
43+
}
44+
45+
prog = self.progress[identity]
46+
total = int(entry.get("progressDetail", {}).get("total", -1))
47+
current = int(entry.get("progressDetail", {}).get("current", 0))
48+
49+
if prog["total"] != total and total != -1:
50+
prog["total"] = total
51+
prog["tqdm"].reset(total=total)
52+
53+
if prog["status"] != entry["status"]:
54+
prog["status"] = entry["status"]
55+
prog["tqdm"].set_description(f"{entry.get('status')} {identity}")
56+
57+
if current != 0:
58+
diff = current - prog["current"]
59+
prog["current"] = current
60+
prog["tqdm"].update(diff)

emu/containers/system_image_container.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# limitations under the License.
1414
import logging
1515
import os
16-
import shutil
1716

1817
from emu.android_release_zip import SystemImageReleaseZip
1918
from emu.platform_tools import PlatformTools
@@ -40,17 +39,17 @@ def _copy_adb_to(self, dest):
4039
tools = PlatformTools()
4140
tools.extract_adb(dest)
4241

43-
def write(self, dest):
42+
def write(self, destination):
4443
# We do not really want to overwrite if the files already exist.
4544
# Make sure the destination directory is empty.
4645
if self.system_image_zip is None:
47-
self.system_image_zip = SystemImageReleaseZip(self.system_image_info.download(dest))
46+
self.system_image_zip = SystemImageReleaseZip(self.system_image_info.download(destination))
4847

49-
writer = TemplateWriter(dest)
50-
self._copy_adb_to(dest)
48+
writer = TemplateWriter(destination)
49+
self._copy_adb_to(destination)
5150

5251
props = self.system_image_zip.props
53-
dest_zip = os.path.basename(self.system_image_zip.copy(dest))
52+
dest_zip = os.path.basename(self.system_image_zip.copy(destination))
5453
props["system_image_zip"] = dest_zip
5554
writer.write_template(
5655
"Dockerfile.system_image",

0 commit comments

Comments
 (0)