Python 3.3+ ship the "venv" module in the standard library. Among other things, one can create a virtualenv with it. Unfortunately, diskimage-builder doesn't work in these virtualenvs. Here's a simple reproducer:
$ disk-image-create
Traceback (most recent call last):
File "/home/ichimonji10/.venvs/diskimage-builder/bin/disk-image-create", line 11, in <module> sys.exit(main())
File "/home/ichimonji10/.venvs/diskimage-builder/lib/python3.6/site-packages/diskimage_builder/disk_image_create.py", line 56, in main activate_venv()
File "/home/ichimonji10/.venvs/diskimage-builder/lib/python3.6/site-packages/diskimage_builder/disk_image_create.py", line 36, in activate_venv
globs = runpy.run_path(activate_this, globals())
File "/usr/lib/python3.6/runpy.py", line 261, in run_path
code, fname = _get_code_from_file(run_name, path_name)
File "/usr/lib/python3.6/runpy.py", line 231, in _get_code_from_file
with open(fname, "rb") as f:
FileNotFoundError: [Errno 2] No such file or directory: '/home/ichimonji10/.venvs/diskimage-builder/bin/activate_this.py'
The issue is easy enough to track down. Here's a snippet from `disk_image_create.py`:
def main():
# If we are called directly from a venv install
# (/path/venv/bin/disk-image-create) then nothing has added the
# virtualenv bin/ dir to $PATH. the exec'd script below will be
# unable to find call other dib tools like dib-run-parts.
#
# One solution is to say that you should only ever run
# disk-image-create in a shell that has already sourced
# bin/activate.sh (all this really does is add /path/venv/bin to
# $PATH). That's not a great interface as resulting errors will
# be very non-obvious.
#
# We can detect if we are running in a virtualenv and use
# virtualenv's "activate_this.py" script to activate it ourselves
# before we call the script. This ensures we have the path setting activate_venv()
In other words, according to this comment, both of these two workflows are supported:
# workflow supported by activate_venv()
~/.venvs/diskimage-builder/bin/activate
Unfortunately, activate_venv() makes assumptions about the internal structure about virtualenvs, and those assumptions aren't valid. Specifically, activate_venv() assumes that an executable called `activate_this.py` exists. `activate_this.py` does exist in virtualenvs created by `virtualenv`. It doesn't exist in virtualenvs created by the standard library's venv module.
Let's look a bit closer. Here's the full source of activate_venv():
def activate_venv():
if running_under_virtualenv(): activate_this = os.path.join(sys.prefix, "bin", "activate_this.py")
globs = runpy.run_path(activate_this, globals()) globals().update(globs)
del globs
Importantly, running_under_virtualenv() returns true when disk-image-create is called from a virtualenv, even when the virtualenv has already been activated. That means `activate_this.py` is unnecessarily executed in both of the following scenarios:
1. Drop the logic that automagically adds path-to-venv/bin/ to $PATH.
2. Make activate_venv() execute only when disk-image-create is in a venv *and* the venv hasn't been activated.
I like the first solution, because I dislike automagic things that break user expectations. My expectation is that virtualenvs are only active when I make them active, and this is the only Python application I'm aware of that tries to be clever about this. But that's a regression in functionality, and is therefore off the table (at least until a major version change). So, the second option is the more feasible one.
Python 3.3+ ship the "venv" module in the standard library. Among other things, one can create a virtualenv with it. Unfortunately, diskimage-builder doesn't work in these virtualenvs. Here's a simple reproducer:
python3 -m venv ~/.venvs/ diskimage- builder diskimage- builder/ bin/activate image-create --help # boom!
source ~/.venvs/
pip install diskimage-builder
disk-
Here's a sample traceback:
$ disk-image-create ichimonji10/ .venvs/ diskimage- builder/ bin/disk- image-create" , line 11, in <module>
sys.exit( main()) ichimonji10/ .venvs/ diskimage- builder/ lib/python3. 6/site- packages/ diskimage_ builder/ disk_image_ create. py", line 56, in main
activate_ venv() ichimonji10/ .venvs/ diskimage- builder/ lib/python3. 6/site- packages/ diskimage_ builder/ disk_image_ create. py", line 36, in activate_venv path(activate_ this, globals()) python3. 6/runpy. py", line 261, in run_path from_file( run_name, path_name) python3. 6/runpy. py", line 231, in _get_code_from_file dError: [Errno 2] No such file or directory: '/home/ ichimonji10/ .venvs/ diskimage- builder/ bin/activate_ this.py'
Traceback (most recent call last):
File "/home/
File "/home/
File "/home/
globs = runpy.run_
File "/usr/lib/
code, fname = _get_code_
File "/usr/lib/
with open(fname, "rb") as f:
FileNotFoun
The issue is easy enough to track down. Here's a snippet from `disk_image_ create. py`:
def main(): venv/bin/ disk-image- create) then nothing has added the
activate_ venv()
# If we are called directly from a venv install
# (/path/
# virtualenv bin/ dir to $PATH. the exec'd script below will be
# unable to find call other dib tools like dib-run-parts.
#
# One solution is to say that you should only ever run
# disk-image-create in a shell that has already sourced
# bin/activate.sh (all this really does is add /path/venv/bin to
# $PATH). That's not a great interface as resulting errors will
# be very non-obvious.
#
# We can detect if we are running in a virtualenv and use
# virtualenv's "activate_this.py" script to activate it ourselves
# before we call the script. This ensures we have the path setting
In other words, according to this comment, both of these two workflows are supported:
# typical workflow diskimage- builder/ bin/activate image-create --help
source ~/.venvs/
disk-
# workflow supported by activate_venv() venvs/diskimage -builder/ bin/activate
~/.
Unfortunately, activate_venv() makes assumptions about the internal structure about virtualenvs, and those assumptions aren't valid. Specifically, activate_venv() assumes that an executable called `activate_this.py` exists. `activate_this.py` does exist in virtualenvs created by `virtualenv`. It doesn't exist in virtualenvs created by the standard library's venv module.
Let's look a bit closer. Here's the full source of activate_venv():
def activate_venv(): under_virtualen v():
activate_ this = os.path. join(sys. prefix, "bin", "activate_this.py") path(activate_ this, globals())
globals( ).update( globs)
if running_
globs = runpy.run_
del globs
Importantly, running_ under_virtualen v() returns true when disk-image-create is called from a virtualenv, even when the virtualenv has already been activated. That means `activate_this.py` is unnecessarily executed in both of the following scenarios:
python3 -m venv ~/.venvs/ diskimage- builder/ diskimage- builder/ bin/activate image-create --help
source ~/.venvs/
pip install diskimage-builder
disk-
virtualenv ~/.venvs/ diskimage- builder/ diskimage- builder/ bin/activate image-create --help
source ~/.venvs/
pip install diskimage-builder
disk-
There's a couple possible fixes.
1. Drop the logic that automagically adds path-to-venv/bin/ to $PATH.
2. Make activate_venv() execute only when disk-image-create is in a venv *and* the venv hasn't been activated.
I like the first solution, because I dislike automagic things that break user expectations. My expectation is that virtualenvs are only active when I make them active, and this is the only Python application I'm aware of that tries to be clever about this. But that's a regression in functionality, and is therefore off the table (at least until a major version change). So, the second option is the more feasible one.