def import_from_filename(filename, silent=False): # pragma: no cover """If settings_module is a filename path import it.""" if not filename.endswith('.py'): filename = '{0}.py'.format(filename) if filename in default_settings.SETTINGS_MODULE_FOR_DYNACONF: silent = True mod = types.ModuleType(filename.rstrip('.py')) mod.__file__ = filename mod._is_error = False try: with io.open( find_file(filename), encoding=default_settings.ENCODING_FOR_DYNACONF ) as config_file: exec( compile(config_file.read(), filename, 'exec'), mod.__dict__ ) except IOError as e: e.strerror = ( 'py_loader: error loading file (%s %s)\n' ) % (e.strerror, filename) if silent and e.errno in (errno.ENOENT, errno.EISDIR): return raw_logger().debug(e.strerror) mod._is_error = True return mod
def import_from_filename(filename, silent=False): # pragma: no cover """If settings_module is a path use this.""" if not filename.endswith('.py'): filename = '{0}.py'.format(filename) if filename in default_settings.SETTINGS_MODULE_FOR_DYNACONF: silent = True mod = types.ModuleType('config') mod.__file__ = filename mod._is_error = False try: with open(find_file(filename)) as config_file: exec( compile(config_file.read(), filename, 'exec'), mod.__dict__ ) except IOError as e: e.strerror = ( 'Unable to load configuration file (%s %s)\n' ) % (e.strerror, filename) if silent and e.errno in (errno.ENOENT, errno.EISDIR): return raw_logger().debug(e.strerror) mod._is_error = True return mod
def test_find_file(tmpdir): """ Create a temporary folder structure like the following: tmpXiWxa5/ └── child1 └── child2 └── child3 └── child4 └── .env └── app.py 1) Then try to automatically `find_dotenv` starting in `child4` """ curr_dir = tmpdir dirs = [] for f in ["child1", "child2", "child3", "child4"]: curr_dir = os.path.join(str(curr_dir), f) dirs.append(curr_dir) os.mkdir(curr_dir) child4 = dirs[-1] assert find_file("file-does-not-exist") == "" assert find_file("/abs-file-does-not-exist") == "" for _dir in dirs: # search for abspath return the same path assert os.path.isabs(_dir) assert find_file(_dir) == _dir # now place a .env file a few levels up and make sure it's found filename = os.path.join(str(child4), ".env") with io.open( filename, "w", encoding=default_settings.ENCODING_FOR_DYNACONF ) as f: f.write("TEST=test\n") assert find_file(project_root=str(child4)) == filename # skip the inner child4/.env and force the find of /tmp.../.env assert find_file( project_root=str(child4), skip_files=[filename] ) == os.path.join(str(tmpdir), ".env")
def test_find_file(): """ Create a temporary folder structure like the following: tmpXiWxa5/ └── child1 ├── child2 │ └── child3 │ └── child4 └── .env Then try to automatically `find_dotenv` starting in `child4` """ tmpdir = os.path.realpath(tempfile.mkdtemp()) curr_dir = tmpdir dirs = [] for f in ['child1', 'child2', 'child3', 'child4']: curr_dir = os.path.join(curr_dir, f) dirs.append(curr_dir) os.mkdir(curr_dir) child1, child4 = dirs[0], dirs[-1] # change the working directory for testing os.chdir(child4) # try without a .env file and force error with pytest.raises(IOError): find_file(raise_error_if_not_found=True, usecwd=True) # try without a .env file and fail silently assert find_file(usecwd=True) == '' # now place a .env file a few levels up and make sure it's found filename = os.path.join(child1, '.env') with io.open(filename, 'w', encoding=default_settings.ENCODING_FOR_DYNACONF) as f: f.write("TEST=test\n") assert find_file(usecwd=True) == filename
def find_file(self, *args, **kwargs): kwargs.setdefault("project_root", self._root_path) kwargs.setdefault("skip_files", self.get("SKIP_FILES_FOR_DYNACONF", [])) return find_file(*args, **kwargs)
def load(obj, namespace=None, silent=True, key=None, filename=None): """ Reads and loads in to "settings" a single key or all keys from yaml file :param obj: the settings instance :param namespace: settings namespace default='DYNACONF' :param silent: if errors should raise :param key: if defined load a single key, else load all in namespace :return: None """ if yaml is None: # pragma: no cover obj.logger.warning( "PyYAML package is not installed in your environment.\n" "To use this loader you have to install it with\n" "pip install PyYAML\n" "or\n" "pip install dynaconf[yaml]") return filename = filename or obj.get('YAML') if not filename: return namespace = namespace or obj.get('NAMESPACE_FOR_DYNACONF') # clean(obj, namespace, identifier=filename) # can be a filename settings.yml # can be a multiple fileset settings1.yml, settings2.yaml etc # and also a list of strings ['aaa:a', 'bbb:c'] # and can also be a single string 'aa:a' if not isinstance(filename, (list, tuple)): split_files = filename.split(',') if all([f.endswith(YAML_EXTENSIONS) for f in split_files]): files = split_files # it is a ['file.yml', ...] else: # it is a single YAML string files = [filename] else: # it is already a list/tuple files = filename for yaml_file in files: if yaml_file.endswith(YAML_EXTENSIONS): # pragma: no cover try: yaml_data = yaml.load(open(find_file(yaml_file, usecwd=True))) except IOError as e: obj.logger.debug("Unable to load YAML file {}".format(str(e))) yaml_data = None else: obj.logger.debug('YAML {} has been loaded'.format(yaml_file)) else: # for tests it is possible to pass YAML string yaml_data = yaml.load(yaml_file) if not yaml_data: continue yaml_data = {key.lower(): value for key, value in yaml_data.items()} # ----> # Load from namespace_filename.yaml data = {} try: data = yaml_data[namespace.lower()] except KeyError: if silent: obj.logger.debug('%s namespace not defined in yaml source' % namespace) else: raise KeyError('%s namespace not defined in %s' % (namespace, filename)) if namespace and namespace != obj.get('NAMESPACE_FOR_DYNACONF'): identifier = "{0}_{1}".format(IDENTIFIER, namespace.lower()) else: identifier = IDENTIFIER if not key: obj.update(data, loader_identifier=identifier) else: obj.set(key, data.get(key), loader_identifier=identifier)
def load(obj, namespace=None, silent=True, key=None, filename=None): """ Reads and loads in to "settings" a single key or all keys from json file :param obj: the settings instance :param namespace: settings namespace default='DYNACONF' :param silent: if errors should raise :param key: if defined load a single key, else load all in namespace :return: None """ filename = filename or obj.get('JSON') if not filename: return namespace = namespace or obj.get('NAMESPACE_FOR_DYNACONF') # clean(obj, namespace, identifier=filename) # can be a filename settings.yml # can be a multiple fileset settings1.json, settings2.json etc # and also a list of strings ['aaa:a', 'bbb:c'] # and can also be a single string 'aa:a' if not isinstance(filename, (list, tuple)): split_files = filename.split(',') if all([f.endswith(JSON_EXTENSIONS) for f in split_files]): # noqa files = split_files # it is a ['file.json', ...] else: # it is a single json string files = [filename] else: # it is already a list/tuple files = filename for json_file in files: if json_file.endswith(JSON_EXTENSIONS): # pragma: no cover obj.logger.debug('Trying to load json {}'.format(json_file)) try: json_data = json.load(open(find_file(json_file, usecwd=True))) except (IOError, json.decoder.JSONDecodeError) as e: obj.logger.debug("Unable to load json {} file {}".format( json_file, str(e))) json_data = None else: # for tests it is possible to pass json string json_data = json.loads(json_file) if not json_data: continue json_data = {key.lower(): value for key, value in json_data.items()} # ----> # Load from namespace_filename.json data = {} try: data = json_data[namespace.lower()] except KeyError: if silent: obj.logger.debug('%s namespace not defined in json source' % namespace) else: raise KeyError('%s namespace not defined in %s' % (namespace, filename)) if namespace and namespace != obj.get('NAMESPACE_FOR_DYNACONF'): identifier = "{0}_{1}".format(IDENTIFIER, namespace.lower()) else: identifier = IDENTIFIER if not key: obj.update(data, loader_identifier=identifier) else: obj.set(key, data.get(key), loader_identifier=identifier)
def _read(self, files, envs, silent=True, key=None): for source_file in files: if source_file.endswith(self.extensions): # pragma: no cover try: source_data = self.file_reader( io.open( find_file( source_file, project_root=self.obj.get( 'PROJECT_ROOT_FOR_DYNACONF' ) ), encoding=self.obj.get( 'ENCODING_FOR_DYNACONF', 'utf-8' ) ) ) self.obj.logger.debug('{}_loader: {}'.format( self.identifier, source_file)) except IOError: self.obj.logger.debug( '{}_loader: {} (Ignored, file not Found)'.format( self.identifier, source_file) ) source_data = None else: # for tests it is possible to pass string source_data = self.string_reader(source_file) if not source_data: continue source_data = { k.lower(): value for k, value in source_data.items() } for env in envs: data = {} try: data = source_data[env.lower()] except KeyError: if env not in (self.obj.get('GLOBAL_ENV_FOR_DYNACONF'), 'GLOBAL'): message = '%s_loader: %s env not defined in %s' % ( self.identifier, env, source_file) if silent: self.obj.logger.warning(message) else: raise KeyError(message) continue if env != self.obj.get('DEFAULT_ENV_FOR_DYNACONF'): identifier = "{0}_{1}".format(self.identifier, env.lower()) else: identifier = self.identifier if not key: self.obj.update(data, loader_identifier=identifier) elif key in data: self.obj.set(key, data.get(key), loader_identifier=identifier) self.obj.logger.debug( '{}_loader: {}: {}'.format( self.identifier, env.lower(), list(data.keys()) if 'secret' in source_file else data ) )
def settings_loader(obj, settings_module=None, env=None, silent=True, key=None, filename=None): """Loads from defined settings module :param obj: A dynaconf instance :param settings_module: A path or a list of paths e.g settings.toml :param env: Env to look for data defaults: development :param silent: Boolean to raise loading errors :param key: Load a single key if provided :param filename: optional filename to override the settings_module """ settings_module = settings_module or obj.settings_module if not settings_module: # pragma: no cover return if not isinstance(settings_module, (list, tuple)): files = settings_module.split(',') else: files = [settings_module] obj.logger.debug("Looking for %s", files) if filename is not None: files.append(filename) found_files = [] modules_names = [] for item in files: if item.endswith(ct.ALL_EXTENSIONS + ('.py', )): found = find_file(item) if found: found_files.append(found) else: # a bare python module name w/o extension modules_names.append(item) obj.logger.debug("Found files %s", found_files) for mod_file in (modules_names + found_files): # can be set to multiple files settings.py,settings.yaml,... # Cascade all loaders loaders = [ { 'ext': ct.YAML_EXTENSIONS, 'name': 'YAML', 'loader': yaml_loader }, { 'ext': ct.TOML_EXTENSIONS, 'name': 'TOML', 'loader': toml_loader }, { 'ext': ct.INI_EXTENSIONS, 'name': 'INI', 'loader': ini_loader }, { 'ext': ct.JSON_EXTENSIONS, 'name': 'JSON', 'loader': json_loader }, ] for loader in loaders: if mod_file.endswith(loader['ext']): loader['loader'].load(obj, filename=mod_file, env=env, silent=silent, key=key) continue if mod_file.endswith(ct.ALL_EXTENSIONS): continue # must be Python file or module # load from default defined module settings.py or .secrets.py if exists py_loader.load(obj, mod_file, key=key) # load from the current env e.g: development_settings.py env = env or obj.current_env if mod_file.endswith('.py'): if '.secrets.py' == mod_file: tmpl = ".{0}_{1}{2}" mod_file = 'secrets.py' else: tmpl = "{0}_{1}{2}" dirname = os.path.dirname(mod_file) filename, extension = os.path.splitext(os.path.basename(mod_file)) new_filename = tmpl.format(env.lower(), filename, extension) env_mod_file = os.path.join(dirname, new_filename) global_filename = tmpl.format('global', filename, extension) global_mod_file = os.path.join(dirname, global_filename) else: env_mod_file = "{0}_{1}".format(env.lower(), mod_file) global_mod_file = "{0}_{1}".format('global', mod_file) py_loader.load(obj, env_mod_file, identifier='py_{0}'.format(env.upper()), silent=True, key=key) # load from global_settings.py py_loader.load(obj, global_mod_file, identifier='py_global', silent=True, key=key)