2018-03-16 16:22:59 +00:00
|
|
|
"""Generates Dockerfiles based on an input matrix based on Python."""
|
2017-11-30 07:20:24 +00:00
|
|
|
import os
|
2018-03-16 16:22:59 +00:00
|
|
|
|
2018-02-01 11:19:06 +00:00
|
|
|
from ..base import BaseImage
|
2017-11-30 07:20:24 +00:00
|
|
|
|
|
|
|
|
2018-02-01 11:19:06 +00:00
|
|
|
class PythonBuildPack(BaseImage):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Setup Python 3 for use with a repository."""
|
2018-02-01 09:11:40 +00:00
|
|
|
def get_packages(self):
|
2018-03-16 22:02:12 +00:00
|
|
|
"""Return a list of the Python 3 core language packages to be installed
|
|
|
|
via apt-get for this BuildPack.
|
|
|
|
|
|
|
|
Note: The packages specified here are for the core Python3 language.
|
|
|
|
Third party libraries are specified in other configuration files.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_packages().union({
|
2018-02-01 09:11:40 +00:00
|
|
|
'python3',
|
|
|
|
'python3-venv',
|
|
|
|
'python3-dev',
|
2018-02-01 11:19:06 +00:00
|
|
|
})
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:21:17 +00:00
|
|
|
def get_env(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Return environment variables to be set.
|
|
|
|
|
|
|
|
We set `VENV_PATH` to the virtual environment location and
|
|
|
|
the `NB_PYTHON_PREFIX` to the location of the jupyter binary.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_env() + [
|
2018-02-01 09:21:17 +00:00
|
|
|
("VENV_PATH", "${APP_BASE}/venv"),
|
|
|
|
# Prefix to use for installing kernels and finding jupyter binary
|
|
|
|
("NB_PYTHON_PREFIX", "${VENV_PATH}"),
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:21:17 +00:00
|
|
|
def get_path(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Return paths (including virtual environment path) to be added to
|
|
|
|
the PATH environment variable.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_path() + [
|
2018-02-01 09:21:17 +00:00
|
|
|
"${VENV_PATH}/bin"
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:39:36 +00:00
|
|
|
def get_build_script_files(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Dict of files to be copied to the container image for use in building.
|
|
|
|
|
|
|
|
This is copied before the `build_scripts` & `assemble_scripts` are
|
|
|
|
run, so can be executed from either of them.
|
|
|
|
|
|
|
|
It's a dictionary where the key is the source file path in the host
|
|
|
|
system, and the value is the destination file path inside the
|
|
|
|
container image.
|
|
|
|
|
|
|
|
This currently adds a frozen set of Python 3 requirements to the dict
|
|
|
|
of files.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
files = {
|
2018-02-01 09:39:36 +00:00
|
|
|
'python/requirements.frozen.txt': '/tmp/requirements.frozen.txt',
|
|
|
|
}
|
2018-02-01 11:19:06 +00:00
|
|
|
files.update(super().get_build_script_files())
|
|
|
|
return files
|
2017-11-30 08:27:14 +00:00
|
|
|
|
2018-02-01 09:39:36 +00:00
|
|
|
def get_build_scripts(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Return series of build-steps common to all Python 3 repositories.
|
|
|
|
|
|
|
|
All scripts here should be independent of contents of the repository.
|
|
|
|
|
|
|
|
This sets up:
|
|
|
|
|
|
|
|
- a directory for the virtual environment and its ownership by the
|
|
|
|
notebook user
|
|
|
|
- a Python 3 interpreter for the virtual environement
|
|
|
|
- a Python 3 jupyter kernel including a base set of requirements
|
|
|
|
- support for Jupyter widgets
|
|
|
|
- support for JupyterLab
|
|
|
|
- support for nteract
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_build_scripts() + [
|
2018-02-01 09:39:36 +00:00
|
|
|
(
|
|
|
|
"root",
|
|
|
|
r"""
|
|
|
|
mkdir -p ${VENV_PATH} && \
|
|
|
|
chown -R ${NB_USER}:${NB_USER} ${VENV_PATH}
|
|
|
|
"""
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"${NB_USER}",
|
|
|
|
r"""
|
|
|
|
python3 -m venv ${VENV_PATH}
|
|
|
|
"""
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"${NB_USER}",
|
|
|
|
r"""
|
|
|
|
pip install --no-cache-dir -r /tmp/requirements.frozen.txt && \
|
|
|
|
jupyter nbextension enable --py widgetsnbextension --sys-prefix && \
|
2018-01-31 22:29:17 +00:00
|
|
|
jupyter serverextension enable --py jupyterlab --sys-prefix && \
|
|
|
|
jupyter serverextension enable nteract_on_jupyter --sys-prefix
|
2018-02-01 09:39:36 +00:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:43:14 +00:00
|
|
|
def get_assemble_scripts(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Return series of build-steps specific to this repository.
|
|
|
|
"""
|
2017-11-30 07:20:24 +00:00
|
|
|
# If we have a runtime.txt & that's set to python-2.7,
|
|
|
|
# we will *not* install requirements.txt but will find &
|
|
|
|
# install a requirements3.txt file if it exists.
|
|
|
|
# This way, when using python2 venv, requirements.txt will
|
|
|
|
# be installed in the python2 venv, and requirements3.txt
|
|
|
|
# will be installed in python3 venv. This is less of a
|
|
|
|
# surprise than requiring python2 to be requirements2.txt tho.
|
2018-02-01 11:19:06 +00:00
|
|
|
assemble_scripts = super().get_assemble_scripts()
|
2017-11-30 07:20:24 +00:00
|
|
|
try:
|
|
|
|
with open(self.binder_path('runtime.txt')) as f:
|
|
|
|
runtime = f.read().strip()
|
|
|
|
except FileNotFoundError:
|
|
|
|
runtime = 'python-3.5'
|
|
|
|
if runtime == 'python-2.7':
|
|
|
|
requirements_file = self.binder_path('requirements3.txt')
|
|
|
|
else:
|
|
|
|
requirements_file = self.binder_path('requirements.txt')
|
|
|
|
if os.path.exists(requirements_file):
|
2018-02-01 11:19:06 +00:00
|
|
|
assemble_scripts.append((
|
2017-11-30 07:20:24 +00:00
|
|
|
'${NB_USER}',
|
|
|
|
'pip3 install --no-cache-dir -r "{}"'.format(requirements_file)
|
2018-02-01 11:19:06 +00:00
|
|
|
))
|
|
|
|
return assemble_scripts
|
2017-11-30 07:20:24 +00:00
|
|
|
|
|
|
|
def detect(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Check if current repo should be built with the Python 3 Build pack.
|
|
|
|
"""
|
2018-03-15 15:22:01 +00:00
|
|
|
return (os.path.exists(self.binder_path('requirements.txt')) and
|
|
|
|
super().detect())
|
2017-11-30 07:20:24 +00:00
|
|
|
|
|
|
|
|
2018-02-01 11:19:06 +00:00
|
|
|
class Python2BuildPack(PythonBuildPack):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Setup Python 2 for use with a repository."""
|
2018-02-01 09:11:40 +00:00
|
|
|
def get_packages(self):
|
2018-03-16 22:02:12 +00:00
|
|
|
"""Return a list of the Python 2 core language packages to be installed
|
|
|
|
via apt-get for this BuildPack.
|
|
|
|
|
|
|
|
Note: The packages specified here are for the core Python2 language.
|
|
|
|
Third party libraries are specified in other configuration files.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_packages().union({
|
|
|
|
'python',
|
|
|
|
'python-dev',
|
|
|
|
'virtualenv'
|
|
|
|
})
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:21:17 +00:00
|
|
|
def get_env(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Return environment variables to be set.
|
|
|
|
|
|
|
|
We set `VENV_PATH` to the virtual environment location containing
|
|
|
|
Python 2.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_env() + [
|
2018-02-01 09:21:17 +00:00
|
|
|
('VENV2_PATH', '${APP_BASE}/venv2')
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:21:17 +00:00
|
|
|
def get_path(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Return paths (including virtual environment path) to be added to
|
|
|
|
the PATH environment variable.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_path() + [
|
2018-02-01 09:21:17 +00:00
|
|
|
"${VENV2_PATH}/bin"
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:39:36 +00:00
|
|
|
def get_build_script_files(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Dict of files to be copied to the container image for use in building.
|
|
|
|
|
|
|
|
This is copied before the `build_scripts` & `assemble_scripts` are
|
|
|
|
run, so can be executed from either of them.
|
|
|
|
|
|
|
|
It's a dictionary where the key is the source file path in the host
|
|
|
|
system, and the value is the destination file path inside the
|
|
|
|
container image.
|
|
|
|
|
|
|
|
This currently adds a frozen set of Python 2 requirements to the dict
|
|
|
|
of files.
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
files = {
|
2018-02-01 09:39:36 +00:00
|
|
|
'python/requirements2.frozen.txt': '/tmp/requirements2.frozen.txt',
|
|
|
|
}
|
2018-02-01 11:19:06 +00:00
|
|
|
files.update(super().get_build_script_files())
|
|
|
|
return files
|
2018-02-01 09:39:36 +00:00
|
|
|
|
|
|
|
def get_build_scripts(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""
|
|
|
|
Return series of build-steps common to all Python 2 repositories.
|
|
|
|
|
|
|
|
All scripts here should be independent of contents of the repository.
|
|
|
|
|
|
|
|
This sets up:
|
|
|
|
|
|
|
|
- a directory for the virtual environment and its ownership by the
|
|
|
|
notebook user
|
|
|
|
- a Python 2 interpreter for the virtual environement
|
|
|
|
- a Python 2 jupyter kernel
|
|
|
|
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_build_scripts() + [
|
2018-02-01 09:39:36 +00:00
|
|
|
(
|
|
|
|
"root",
|
|
|
|
r"""
|
|
|
|
mkdir -p ${VENV2_PATH} && \
|
|
|
|
chown -R ${NB_USER}:${NB_USER} ${VENV2_PATH}
|
|
|
|
"""
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"${NB_USER}",
|
|
|
|
r"""
|
|
|
|
virtualenv -p python2 ${VENV2_PATH}
|
|
|
|
"""
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"${NB_USER}",
|
|
|
|
r"""
|
|
|
|
pip2 install --no-cache-dir -r /tmp/requirements2.frozen.txt && \
|
|
|
|
python2 -m ipykernel install --prefix=${NB_PYTHON_PREFIX}
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
]
|
2017-11-30 07:20:24 +00:00
|
|
|
|
2018-02-01 09:43:14 +00:00
|
|
|
def get_assemble_scripts(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Return series of build-steps specific to this repository.
|
|
|
|
"""
|
2018-02-01 11:19:06 +00:00
|
|
|
return super().get_assemble_scripts() + [
|
2017-11-30 07:20:24 +00:00
|
|
|
(
|
|
|
|
'${NB_USER}',
|
|
|
|
'pip2 install --no-cache-dir -r requirements.txt'
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
|
|
|
def detect(self):
|
2018-03-16 16:22:59 +00:00
|
|
|
"""Check if current repo should be built with the Python 2 Build pack.
|
|
|
|
"""
|
2017-11-30 07:20:24 +00:00
|
|
|
requirements_txt = self.binder_path('requirements.txt')
|
|
|
|
runtime_txt = self.binder_path('runtime.txt')
|
|
|
|
if os.path.exists(requirements_txt) and os.path.exists(runtime_txt):
|
|
|
|
with open(runtime_txt) as f:
|
|
|
|
runtime = f.read().strip()
|
|
|
|
if runtime == 'python-2.7':
|
|
|
|
return True
|
|
|
|
return False
|