# python3 # Copyright 2016 Google Inc. All Rights Reserved. # # Licensed 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. """Functions for interacting with yaml configuration files.""" import re from typing import Any, Text from glazier.lib import download from glazier.lib import file_util import yaml class Error(Exception): pass def Remove(path: Text, backup: bool = True): """Remove a config file. Args: path: The filesystem path to the file. backup: Whether to make a backup of the file being removed. Raises: Error: Failure performing the filesystem operation. """ if backup: try: file_util.Move(path, path + '.bak') except file_util.Error as e: raise Error('Failed to create backup file (%s)' % str(e)) else: try: file_util.Remove(path) except file_util.Error as e: raise Error('Failed to remove file (%s)' % str(e)) def Dump(path: Text, data: Any, mode: Text = 'w'): """Write a config file containing some data. Args: path: The filesystem path to the destination file. data: Data to be written to the file as yaml. mode: Mode to use for writing the file (default: w) """ file_util.CreateDirectories(path) tmp_f = path + '.tmp' # Write to a .tmp file to avoid corrupting the original if aborted mid-way. try: with open(tmp_f, mode) as handle: handle.write(yaml.dump(data)) except IOError as e: raise Error('Could not save data to yaml file %s: %s' % (path, str(e))) # Replace the original with the tmp. try: file_util.Move(tmp_f, path) except file_util.Error as e: raise Error('Could not replace config file. (%s)' % str(e)) def Read(path: Text): """Read a config file at path and return any data it contains. Will attempt to download files from remote repositories prior to reading. Args: path: The path (either local or remote) to read from. Returns: The parsed YAML content from the file. Raises: Error: Failure retrieving a remote file or parsing file content. """ if re.match('^http(s)?://', path): downloader = download.Download() try: path = downloader.DownloadFileTemp(path) except download.DownloadError as e: raise Error('Could not download yaml file %s: %s' % (path, str(e))) return _YamlReader(path) def _YamlReader(path: Text) -> Text: """Read a configuration file and return the contents. Can be overloaded to read configs from different sources. Args: path: The config file name (eg build.yaml). Returns: The parsed content of the yaml file. """ try: with open(path, 'r') as yaml_file: yaml_config = yaml.safe_load(yaml_file) except IOError as e: raise Error('Could not read yaml file %s: %s' % (path, str(e))) return yaml_config