{"id":23511202,"url":"https://github.com/percolate/dripconfig","last_synced_at":"2025-05-13T17:20:59.237Z","repository":{"id":148675251,"uuid":"14153140","full_name":"percolate/dripconfig","owner":"percolate","description":"A tool for doing configuration nicely","archived":false,"fork":false,"pushed_at":"2023-03-16T17:38:01.000Z","size":34,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":76,"default_branch":"master","last_synced_at":"2025-02-16T20:33:24.774Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/percolate.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2013-11-05T20:26:28.000Z","updated_at":"2023-03-16T17:38:06.000Z","dependencies_parsed_at":"2023-05-28T15:30:23.169Z","dependency_job_id":null,"html_url":"https://github.com/percolate/dripconfig","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fdripconfig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fdripconfig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fdripconfig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fdripconfig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/percolate","download_url":"https://codeload.github.com/percolate/dripconfig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990506,"owners_count":21995776,"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":[],"created_at":"2024-12-25T12:13:43.980Z","updated_at":"2025-05-13T17:20:59.228Z","avatar_url":"https://github.com/percolate.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# dripconfig\n\n[![CircleCI](https://circleci.com/gh/percolate/dripconfig.svg?style=svg\u0026circle-token=80b53a2510ca246c448fd7e65c900b2102cc4e4a)](https://circleci.com/gh/percolate/dripconfig)\n\nA tool for doing configuration nicely -- sorta.\n\nThis package has helpers for loading configuration and performing common setup\ntasks, e.g. for logging, stats, and datastore machinery. It provides a\ndjango-like singleton to consolidate configured values. It also provides\nutilities for implementing lightweight [dependency\ninjection](http://en.wikipedia.org/wiki/Dependency_injection).\n\nConfiguration can be loaded easily from  from a variety of sources in a\nseamless, polymorphic way and is checked/cleaned by registered validation\nextensions (usually by section). You can load from many different sources\n(JSON, INI, dict, environment variable to name a few), as well as specifying\na prioritized preference for loading (load from sys.argv if available, then\nfrom environment variable, then finally fall back to a certain file).\n\n## Example\n\n```python\nfrom dripconfig import config, sources\n\nif __name__ == '__main__':\n\n    # wire up a RedisTrigger instance to configure redis based on info\n    config.register_trigger(RedisTrigger())\n\n    # load some config\n\n    config.merge_from(\n        # prefer a config loaded from filename passed as argument\n        sources.Argv(1),\n        # if we don't have an argv, load from a file\n        sources.EnvVar(\"CONF_FILENAME\"),\n        # if the env var isn't set, fall back to a particular file\n        sources.Filename(\"some_conf.json\"),\n    )\n\n    # specific values can be loaded very easily from environment variables\n    config['this']['that'] = os.environ.get('THIS_THAT_VAL', 'default_val')\n\n    # validate, run any global configuration steps, and have the triggers act\n    config.configure()\n\n    # on with the show...\n```\n\nThe `RedisTrigger` definition takes the loaded configuration info and renders\nit into objects that let us use redis:\n\n```python\n\nclass RedisTrigger(ConfigurationTrigger):\n    \"\"\"\n    Require that redis connection information is specified.\n\n    \"\"\"\n    # voluptuous Schema objects are a great way of validating config.\n    SCHEMA = Schema({\n        'redis': {\n            Required('hostname'): basestring,\n            Required('realtime_updates_ss_name',\n                     default='melrose:realtalk'): basestring,\n        },\n    }, extra=True)\n\n    def clean(self, configuration):\n        \"\"\"Make sure all the required values are here.\"\"\"\n        if 'redis' not in configuration:\n            raise RuntimeError(\n                \"Redis not properly configured; \"\n                \"see `melrose.config:RedisTrigger.schema`.\"\n            )\n\n        # make sure we match the schema we need\n        return self.SCHEMA(configuration)\n\n    def configure(self, configuration):\n        \"\"\"\n        Instantiate a redis client and any reliant objects.\n\n        \"\"\"\n        conf = configuration.redis\n        some_module.redis_client = redis.Redis(conf.hostname)\n\n```\n\nMeanwhile in `some_submodule`, the module-level attribute `redis_client` is\ndeclared to be injected by dripconfig. This allows the module to be\n(blissfully) unaware of the configuration needed to instantiate its\ndependencies.\n\n```python\n# some_module.py\n\nredis_client = dripconfig.ToBeInjected(redis.Redis)\n```\n\nBy the time `dripconfig.configure()` is called, `redis_client` will be an\nobject of type `redis.Redis`, and will be ready for use. Until that call is\nmade, though, any attempt to use that object will result in a RuntimeError.\n\n## Note on INI logging configurations\n\nNote: this does not support the python logging module 'ini' configuration\nformat although you can load ini files.  Use logging.fileConfig on that\ndirectly if desired.\n\n## Sources\n\nThe preferred format is JSON for nested data (logging configuration = yuck...),\nbut configuration can be merged together from a variety of sources including\nini files, yamls, dicts etc.  A consolidated attribute-accessible object is\npresented for triggering further configuration actions.\n\n```\nfrom dripconfig import config\n\n\nif __name__ == '__main__':\n    config.register_trigger(...)\n\n    # `merge` will automagically determine the type\n    config.merge('thisconf.yaml')\n    config.merge({'yo': 'bang!'})\n\n    # or you can do it explicitly\n    config.merge_dict({'some': 'default'})\n    config.merge_ini_file(\"/usr/local/etc/common.ini\")\n    config.merge_json('{\"foo\": \"bar\"}')\n\n    # done loading, normalize and apply\n    config.configure()\n\n    # great, now get to work!\n    serve(port=config.port)\n```\n\n## Validation and Global Configuration\n\nConfigurationTrigger objects are tasked with validating/cleaning relevent\nportions of the configuration in the clean() method.  After cleaning the values\nin the configuration are expected to be of the correct type, consumers should\nnot worry about type checking the configuration.  This is mainly a concern when\nloading .ini files.\n\nThe suggested method is to declare a Voluptuous Schema and use Corece() on\nnumerical values and anything that may require additional interpretation.\n\nConfigurationTriggers can also apply portions of the configuration in a global\nfashion if necesary via the configure() method.  Stats and logging are often\nconfigured by this method.\n\n## Note on INI Files\n\nThe best thing is that they're simple. The worst thing is they sort of stink\nfor the same reason and no magic to support nesting is done.  Anything in the\n[main] section is considered top-level, everything else is nested under a key\nwith the name of the section.\n\n## Helpers and other Tidbits\n\nFor logging configurations that use syslog, a slightly improved handler is\nprovided that logs the process name with outgoing messages when used\nwith an appropriate formatter.  The variable `ident` is provided, which\ncan be used as the prefix to the logging format to identify the process\nto syslog eg `%(ident)s %(message)`.\n\nAn example configuration might include a handler such as the following in the\nlogging.dictConfig format:\n\n```\n{\n    \"logging\": {\n        \"formatters\": {\n            ...\n            \"syslog\": {\n                \"format\": \"%(ident)s [%(levelname)s %(asctime)s %(module)s] %(message)s\"\n            }\n        },\n        ...\n        \"handlers\": {\n            \"syslog\":{\n                \"class\":\"dripconfig.SysLogHandler\",\n                \"level\":\"INFO\",\n                \"formatter\": \"syslog\",\n                \"address\": \"/dev/log\"\n            },\n        },\n        ...\n    }\n}\n```\n\n## TODO\n\n* arg parse example or helper for specifying config files to load?\n* some way to do email based logging without django?\n* json with comments is shady, works but can't give good line-number errors,\n  which stinks\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpercolate%2Fdripconfig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpercolate%2Fdripconfig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpercolate%2Fdripconfig/lists"}