liminal/build/python.py (62 lines of code) (raw):

# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, 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. import os from liminal.build.image_builder import ImageBuilder class PythonImageVersions: """ Handles the python versions for python images. """ @property def default_version(self): return '3.7' @property def supported_versions(self): return '3.6', '3.7', '3.8', '3.9' def get_image_name(self, python_version): """ :param python_version: The python version that would be installed in the docker image. For example '3.8', '3.8.1' etc. :type python_version: str :return: The name of the base (slim) python image :rtype: str """ if not python_version: python_version = self.default_version else: python_version = str(python_version) if python_version[:3] not in self.supported_versions: raise ValueError( f'liminal supports the following python versions: ' f'{self.supported_versions} but {python_version} ' f'were passed' ) return f'python:{python_version}-slim' class BasePythonImageBuilder(ImageBuilder): """ Base class for building python images. """ __PIP_CONF = 'pip_conf' __PYTHON_VERSION = 'python_version' def __init__(self, config, base_path, relative_source_path, tag, base_image=PythonImageVersions()): super().__init__(config, base_path, relative_source_path, tag) self._base_image = base_image @staticmethod def _dockerfile_path(): raise NotImplementedError() def _write_additional_files(self, temp_dir): requirements_file_path = os.path.join(temp_dir, 'requirements.txt') if not os.path.exists(requirements_file_path): with open(requirements_file_path, 'w'): pass super()._write_additional_files(temp_dir) def _additional_files_from_filename_content_pairs(self): with open(self._dockerfile_path()) as original: data = original.read() data = self.__mount_pip_conf(data) data = self.__add_python_base_version(data) return [('Dockerfile', data)] def __mount_pip_conf(self, data): new_data = data if self.__PIP_CONF in self.config: new_data = '# syntax = docker/dockerfile:1.0-experimental\n' + data new_data = new_data.replace('{{mount}}', '--mount=type=secret,id=pip_config,dst=/etc/pip.conf \\\n') else: new_data = new_data.replace('{{mount}} ', '') return new_data def __add_python_base_version(self, data): python_version = self.config.get(self.__PYTHON_VERSION) base_image = self._base_image.get_image_name(python_version) return data.replace('{{python}}', base_image) def _build_flags(self): if self.__PIP_CONF in self.config: return f'--secret id=pip_config,src={self.config[self.__PIP_CONF]}' else: return '' def _use_buildkit(self): if self.__PIP_CONF in self.config: return True