""".. _config_api:Configuration API=================The following are the basic initialization functions for :mod:`mkname`... autofunction:: mkname.get_config.. autofunction:: mkname.get_db"""fromconfigparserimportConfigParserfromimportlib.resourcesimportfilesfrompathlibimportPathfromsysimportversion_infofromtypingimportUnionifversion_info>=(3,11):importtomllibimportmkname.datafrommkname.exceptionsimport*frommkname.modelimportConfig,Section# Common data.DB_NAME='names.db'DEFAULTS_CONF_NAME='defaults.cfg'CONF_NAMES=(('setup.cfg',(3,10)),('pyproject.toml',(3,11)),('mkname.cfg',(3,10)),('mkname.toml',(3,11)),)# Configuration functions.defbuild_search_paths(path:Path|None)->list[Path]:"""Build the list of paths where config files might be. :param path: Any path given by the user for a config file. :returns: A :class:`list` object. :rtype: list """# The core configuration files.search_paths=[Path.cwd()/filenameforfilename,versioninCONF_NAMESifversion_info>=version]search_paths=[get_default_config(),*search_paths]# If the given path is a file, add that file to the search paths.ifpathandpath.is_file():search_paths.append(path)# If the given path is a directory, search for each of the# filenames for the core configuration files in that directory.elifpathandpath.is_dir():search_paths.extend(path/filenameforfilename,versioninCONF_NAMESifversion_info>=version)# Return the search paths.returnsearch_paths
[docs]defget_config(path:Path|str|None=None)->Config:"""Get the configuration. :param location: (Optional.) The path to the configuration file. If no path is passed or the passed path doesn't exist, it will fall back to a series of other files. See "Loading Configuration". :return: A :class:`dict` object. :rtype: dict :usage: .. doctest:: config >>> loc = 'tests/data/test_load_config.conf' >>> get_config(loc) {'mkname': {'consonants': 'bcd', 'db_path':... """# Ensure any passed config file exists.path=Path(path)ifpathelseNoneifpathandnotpath.exists():msg=f'File {path} does not exist.'raiseConfigFileDoesNotExistError(msg)# Start the config with the default values.config:Config=dict()# Search through possible config files and update the config.search_paths=build_search_paths(path)forsearch_pathinsearch_paths:ifsearch_pathandsearch_path.exists():ifsearch_path.suffix=='.toml':new=read_toml(search_path)else:new=read_config(search_path)forkeyinnew:config.setdefault(key,dict())config[key].update(new[key])# Return the loaded configuration.returnconfig
defget_default_config()->Path:"""Get the default configuration values. :return: The default configuration as a :class:`dict`. :rtype: dict """default_path=get_default_path()/DEFAULTS_CONF_NAMEreturndefault_pathdefread_config(path:Path)->Config:"""Read the configuration file at the given path. :param path: The path to the configuration file. :return: The configuration as a :class:`dict`. :rtype: dict """parser=ConfigParser()parser.read(path)sections=['mkname','mkname_files']return{k:dict(parser[k])forkinparserifkinsections}defread_toml(path:Path)->Config:"""Read the TOML file at the given path. :param path: The path to the TOML file. :return: The configuration as a :class:`dict`. :rtype: dict """ifversion_info<(3,11):msg=f'Python {version_info} does not support TOML.'raiseUnsupportedPythonVersionError(msg)withopen(path,'rb')asfh:data=tomllib.load(fh)sections=['mkname','mkname_files']return{k:dict(data[k])forkindataifkinsections}defwrite_config_file(path:Path,config:Config)->Config:"""Write an "INI" formatted configuration file. :param path: The path to the configuration file to write. :param config: The values to write into the configuration file. :return: The configuration values written into the files. :rtype: dict """parser=ConfigParser()parser.read_dict(config)withopen(path,'w')asfh:parser.write(fh)returnconfig# Database functions.
[docs]defget_db(path:Path|str|None=None,conf_path:Path|str|None=None)->Path:"""Get the path to the names database. :param path: The path of the names database. :return: The path to the names database as a :class:`pathlib.Path`. :rtype: pathlib.Path :usage: .. doctest:: config >>> loc = 'src/mkname/data/names.db' >>> get_db(loc) PosixPath('src/mkname/data/names.db') Database Structure ------------------ The names database is a sqlite3 database with a table named 'names'. The names table has the following columns: * `id`: A unique identifier for the name. * `name`: The name. * `source`: The URL where the name was found. * `culture`: The culture or nation the name is tied to. * `date`: The approximate year the name is tied to. * `kind`: A tag for how the name is used, such as a given name or a surname. """# Get the config.config=get_config(conf_path)cfg_path=config['mkname']['db_path']# The search paths.explicit_path=Path(path)ifpathelseNoneconfig_path=Path(cfg_path)ifcfg_pathelseNonelocal_path=Path.cwd()/DB_NAMEdefault_path=get_default_db()# If we are passed an explicit database path, use that database.ifexplicit_path:ifexplicit_path.is_dir():explicit_path=explicit_path/DB_NAMEdb_path=explicit_path# If there is no explicit database given, check if one was# configured.elifconfig_path:db_path=config_path# If we weren't given a database, check if there is one in# the current working directory.eliflocal_path.is_file():db_path=local_path# If all alse fails, use the default database.else:db_path=default_path# Double check to make sure the path could be a database.# Yelp if it isn't.ifnotdb_path.is_file():msg=f'{path} is not a file.'ifdb_path==default_path:msg=f'The default database is missing. Reinstall mkname.'raiseNotADatabaseError(msg)# Return the path to the database.returndb_path
defget_default_db()->Path:"""Get the path to the default names database. :return: The path to the default names database as a :class:`pathlib.Path`. :rtype: pathlib.Path """returnget_default_path()/DB_NAME# Text functions.defget_text(path:Path|str)->str:"""Get text from a text file in the package data. :param path: The relative path to the text file within the package data. :returns: A :class:`str` object. :rtype: str """pkg_data=get_default_path()path=pkg_data/pathreturnpath.read_text()# Utility functions.defget_default_path()->Path:"""Get the path to the default data files. :return: The path to the default data location as a :class:`pathlib.Path`. :rtype: pathlib.Path """data_pkg=files(mkname.data)returnPath(f'{data_pkg}')