{"id":20818821,"url":"https://github.com/ricnord/configparser-override","last_synced_at":"2025-05-07T14:10:38.827Z","repository":{"id":252526500,"uuid":"834169365","full_name":"RicNord/configparser-override","owner":"RicNord","description":"ConfigParser with environment variable and direct assignment overrides","archived":false,"fork":false,"pushed_at":"2025-01-21T22:18:06.000Z","size":285,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-31T10:51:19.332Z","etag":null,"topics":["configparser","configuration-parser","override","python","viper"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RicNord.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-26T15:11:07.000Z","updated_at":"2025-01-21T22:18:10.000Z","dependencies_parsed_at":"2024-08-10T13:55:54.399Z","dependency_job_id":"d2344c00-2e8a-4a4e-8d6b-c23d3c2c9ba1","html_url":"https://github.com/RicNord/configparser-override","commit_stats":null,"previous_names":["ricnord/configparser-override"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RicNord%2Fconfigparser-override","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RicNord%2Fconfigparser-override/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RicNord%2Fconfigparser-override/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RicNord%2Fconfigparser-override/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RicNord","download_url":"https://codeload.github.com/RicNord/configparser-override/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252892503,"owners_count":21820648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configparser","configuration-parser","override","python","viper"],"created_at":"2024-11-17T21:50:15.208Z","updated_at":"2025-05-07T14:10:38.809Z","avatar_url":"https://github.com/RicNord.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ConfigParser Override\n\n[![Version](https://img.shields.io/pypi/v/configparser-override?color=blue)](https://pypi.org/project/configparser-override/)\n[![Build\nStatus](https://github.com/RicNord/configparser-override/actions/workflows/ci.yaml/badge.svg)](https://github.com/RicNord/configparser-override/actions)\n\n`ConfigParserOverride` enhances the python standard library built-in\n[ConfigParser](https://docs.python.org/3/library/configparser.html) by allowing\nyou to override or add new options using; environment variables and directly\nassigned key-value arguments.\n\n\u003e **NOTE:** This package only depends on the Python Standard Library!\n\n## Features\n\n- Override configuration options with environment variables.\n- Override configuration options with directly assigned arguments.\n- Convert configuration object to a dataclass and cast the values to predefined\n  datatypes.\n- Find and collect configuration files in conventional locations based on your\n  operating system.\n\n## Install\n\n```sh\npip install configparser-override\n```\n\n## Usage\n\nExample of how to use `ConfigParserOverride`:\n\n### Example `config.ini` File\n\n```ini\n[DEFAULT]\ndefault_key1 = default_value1\ndefault_key2 = default_value2\n\n[section1]\nkey1 = value1\nkey2 = value2\n\n[section2]\nkey3 = value3\nkey4 = value4\n```\n\n### Python Code\n\n```python\nimport os\n\nfrom configparser_override import ConfigParserOverride\n\n# Optionally set environment variables for overriding\nos.environ[\"MYAPP_DEFAULT_KEY1\"] = \"overridden_default_value1\"\nos.environ[\"MYAPP_SECTION1__KEY1\"] = \"overridden_value1\"\nos.environ[\"MYAPP_SECTION2__KEY3\"] = \"overridden_value3\"\n\n# Initialize the parser with an optional environment variable prefix and\n# overrides from direct assignments.\nparser = ConfigParserOverride(\n    env_prefix=\"MYAPP_\",\n    # Sections \u0026 options are case insensitive by default\n    SECTION2__KEY4=\"direct_override_value4\",\n    section2__key5=\"direct_override_value5\",\n)\n\n# Read configuration from a file\nparser.read(filenames=\"config.ini\")\n\n# Apply overrides\nparser.apply_overrides()\n\n# Access the configuration\nprint(config.defaults()[\"default_key1\"])  # Output: overridden_default_value1\nprint(config.defaults()[\"default_key2\"])  # Output: default_value2\nprint(config[\"section1\"][\"key1\"])  # Output: overridden_value1\nprint(config[\"section1\"][\"key2\"])  # Output: value2\nprint(config[\"section2\"][\"key3\"])  # Output: overridden_value3\nprint(config[\"section2\"][\"key4\"])  # Output: direct_override_value4\nprint(config[\"section2\"][\"key5\"])  # Output: direct_override_value5\n```\n\n#### Configuration source precedence\n\nConfiguration options can be overridden in three ways. This is the order of\nprecedence:\n\n1. **Directly assigned arguments** during initialization of the\n   `ConfigParserOverride` class.\n2. **Environment variables**.\n3. **Configuration files**.\n\n#### Environment variable configuration\n\nTo override configuration options, use environment variables with the following\nformat. Separate sections and options using double underscores (`__`):\n\n- **With Prefix** (`MYAPP_` as an example):\n  - For `DEFAULT` section: `[PREFIX][OPTION]`\n  - For other sections: `[PREFIX][SECTION]__[OPTION]`\n\n- **No Prefix**:\n  - For `DEFAULT` section: `[OPTION]`\n  - For other sections: `[SECTION]__[OPTION]`\n\n**Example**:\n\n- To override `key1` in `section1` with prefix `MYAPP_`, use\n  `MYAPP_SECTION1__KEY1`.\n\n## Find and collect configuration files\n\nThe library also contains a helper function `config_file_collector` that will\nsearch for configuration files in conventional locations based on your OS.\nThe collected files can then be used as input to `ConfigParserOverride.read()`\n\n### Searched paths\n\n#### Linux and MacOS\n\nUnix systems follows [XDG base directory\nspecification](https://specifications.freedesktop.org/basedir-spec/latest/) and\nused environment variables:\n\n- **XDG_CONFIG_HOME** (User config)\n  - Default to **$HOME/.config**\n- **XDG_CONFIG_DIRS** (System wide config)\n  - List of directories separated by semicolon `:`\n  - Default to **/etc/xdg**\n\n#### Windows\n\nWindows paths are specific in environment variables:\n\n- **APPDATA** (User config)\n  - Usually: **C:\\Users\\USERNAME\\AppData\\Roaming**\n- **PROGRAMDATA** (System wide config)\n  - Usually: **C:\\ProgramData**\n\n### Example\n\n```python\nfrom configparser_override import ConfigParserOverride, config_file_collector\n\ncollected_files = config_file_collector(file_name=\"config.ini\", app_name=\"myapp\")\n\nprint(collected_files)\n# For Linux and MacOS\n# Output: [\"/etc/xdg/myapp/config.ini\", \"/home/USERNAME/.config/myapp/config.ini\"]\n\n# For Windows\n# Output: [\"C:/ProgramData/myapp/config.ini\", \"C:/Users/USERNAME/AppData/Roaming/myapp/config.ini\"]\n\nparser = ConfigParserOverride()\nparser.read(filenames=collected_files)\nparser.apply_overrides()\nconfig = parser.config\n```\n\n## Convert to a Dataclass and Validate Data Types\n\nThe library features a `ConfigConverter` class, which enables the conversion of\nconfiguration data into a dataclass instance. This functionality is\nparticularly useful for ensuring that the configuration adheres to the expected\nformat, since it tries to cast the option in the config to the types in the\ndataclass. Hence, it also allows you to take advantage of various typing\nframeworks and tools, such as integrations with your text editor, providing\nenhanced validation and code assistance.\n\n### Example\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom configparser_override import ConfigParserOverride\n\n\n@dataclass\nclass Section1:\n    key1: int\n    key2: list[str]\n    key3: Optional[str] = None\n\n\n@dataclass\nclass ExampleConfig:\n    section1: Section1\n\n\n# Initialize the parser with overrides\nparser = ConfigParserOverride(\n    section1__key1=\"42\", section1__key2=\"['item1', 'item2']\"\n)\n\n# Read configuration from **optional** file\nparser.read(filenames=[])\n\n# Apply overrides\nparser.apply_overrides()\n\n# Convert to dataclass\nconfig_as_dataclass = parser.to_dataclass(ExampleConfig)\n\nprint(config_as_dataclass.section1.key1)  # Output: 42\nprint(type(config_as_dataclass.section1.key1))  # Output: \u003cclass 'int'\u003e\nprint(config_as_dataclass.section1.key2)  # Output: ['item1', 'item2']\nprint(type(config_as_dataclass.section1.key2))  # Output: \u003cclass 'list'\u003e\nprint(config_as_dataclass.section1.key3)  # Output: None\n```\n\n### Data Types\n\n**Supported data types are:**\n\n- String\n- Integer\n- Bool\n- Float\n- Complex\n- Bytes\n- pathlib.Path\n\n**Collections (nesting is supported):**\n\n- List\n- Dict\n- Set\n- Tuple\n\n**Others:**\n\n- None\n- Optional | Option does not need to exist in config\n- Union | Tries to cast until successful, in the order the types are specified\n- Any | no type cast\n\n**Built-in custom types:**\n\n- SecretType (abstract): Custom abstract type that masks the secret value when\n  converted to a string. Use SecretType.get_secret_value() to retrieve the\n  actual value.\n  - SecretStr | Implementation for strings\n  - SecretBytes | Implementation for bytes\n\n**Experimental Support for Arbitrary Types:**\n\nThe converter offers an experimental option to accept any object that can\nbe initialized with a single, unnamed string argument. To enable this feature,\nset `allow_custom_types = True` when using the converter (the default is\n`False`).\n\nFor example, a compatible object initialization might look like this:\n`MyCustomType(\"string value from config\")`.\n\n## Platform Dependency\n\nDifferent operating systems handle environment variables differently. Linux is\ncase sensitive while Windows is not. See [os.environ\ndocs](https://docs.python.org/3/library/os.html#os.environ). Hence, it is safest\nto always use capitalized environment variables to avoid any unexpected\nbehavior.\n\n### Recommendation\n\nIn order to avoid any unanticipated issues and make your code safe to run on\nany platform, follow these rules:\n\n| Element                       | Recommended Case |\n|-------------------------------|------------------|\n| Environment variables         | UPPERCASE        |\n| Environment variable prefix   | UPPERCASE        |\n| DEFAULT section in config.ini (as per convention in the standard library ConfigParser) | UPPERCASE |\n| Sections in config.ini files  | lowercase        |\n| Options in config.ini files   | lowercase        |\n| Directly assigned arguments   | lowercase        |\n\n### Case Sensitivity Handling\n\nBy default, `ConfigParserOverride` tries to stores everything as lowercase,\nwith the exception of `Section` headers that are read from configuration files,\nwhere the existing casing in the file is honored. However, if you want to\noverride such a section with an environment variable or direct assignment, it\nwill recognize the existing casing of the section and continue to use that even\nthough you use other casing in the override method.\n\nIt is highly discouraged, but you can make `ConfigParserOverride` case-sensitive\nby initializing it with the argument `case_sensitive_overrides=True`.\n\n```python\nfrom configparser_override import ConfigParserOverride\n\nparser = ConfigParserOverride(env_prefix=\"MYAPP_\", case_sensitive_overrides=True)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricnord%2Fconfigparser-override","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricnord%2Fconfigparser-override","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricnord%2Fconfigparser-override/lists"}