{"id":13725089,"url":"https://github.com/etingof/apacheconfig","last_synced_at":"2025-12-30T01:27:07.496Z","repository":{"id":50306061,"uuid":"124651632","full_name":"etingof/apacheconfig","owner":"etingof","description":"Apache / Config::General configuration file parser","archived":false,"fork":false,"pushed_at":"2020-08-12T18:25:18.000Z","size":276,"stargazers_count":48,"open_issues_count":24,"forks_count":14,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-06T13:17:48.841Z","etag":null,"topics":["apache","httpd-conf","perl-config-general"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/etingof.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.rst","contributing":null,"funding":null,"license":"LICENSE.rst","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-10T12:00:23.000Z","updated_at":"2023-07-21T00:25:15.000Z","dependencies_parsed_at":"2022-09-26T21:10:38.977Z","dependency_job_id":null,"html_url":"https://github.com/etingof/apacheconfig","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etingof%2Fapacheconfig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etingof%2Fapacheconfig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etingof%2Fapacheconfig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etingof%2Fapacheconfig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/etingof","download_url":"https://codeload.github.com/etingof/apacheconfig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224420151,"owners_count":17308063,"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":["apache","httpd-conf","perl-config-general"],"created_at":"2024-08-03T01:02:12.631Z","updated_at":"2025-12-30T01:27:07.467Z","avatar_url":"https://github.com/etingof.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"\n# Apache-style config parser\n\n[![PyPI](https://img.shields.io/pypi/v/apacheconfig.svg?maxAge=1800)](https://pypi.org/project/apacheconfig)\n[![Python Versions](https://img.shields.io/pypi/pyversions/apacheconfig.svg)](https://pypi.org/project/apacheconfig/)\n[![Build status](https://travis-ci.org/etingof/apacheconfig.svg?branch=master)](https://secure.travis-ci.org/etingof/apacheconfig)\n[![Coverage Status](https://img.shields.io/codecov/c/github/etingof/apacheconfig.svg)](https://codecov.io/github/etingof/apacheconfig)\n[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/apacheconfig/master/LICENSE.rst)\n\nThis is a pure-Python implementation of Apache-like configuration file parser into Python built-in\ntypes. Similar file format is utilized by Perl's [Config::General](http://search.cpan.org/dist/Config-General/General.pm)\nmodule.\n\n## How to use apacheconfig\n\nWith apacheconfig you can build a tree of Python objects from Apache configuration\nfile.\n\nFor example, the following Apache configuration:\n\n```bash\n\u003ccops\u003e\n  name stein\n  age  25\n  \u003ccolors\u003e\n    color \\#000000\n  \u003c/colors\u003e\n\u003c/cops\u003e\n```\n\nWould be transformed by `apacheconfig` into:\n\n```python\n{\n'cops': {\n  'name': 'stein',\n  'age': '25',\n  'colors': {\n    'color': '#000000'\n  }\n}\n```\n\nBy running the following code:\n\n```python\nfrom apacheconfig import *\n\nwith make_loader() as loader:\n    config = loader.load('httpd.conf')\n\nprint(config)\n```\n\nYou can also dump the config `dict` back into Apache configuration file form\nby calling `dump` or `dumps` method:\n\n```python\nfrom apacheconfig import *\n\nwith make_loader() as loader:\n    config = loader.load('httpd.conf')\n\n# ...potentially modify the `config` dict anyhow...\n\nprint(loader.dumps(config))\n```\n\nPlease, note that dumped config file might differ in its representation from the original\nbecause `dict` form does not contain all pieces of the original file (like comments),\nsome items may get modified on load (like variable expansion) and included files\nrendered into the single `dict`.\n\n## Parsing options\n\nParser behavior can be modified by passing it one or more options. The options are passed as a dictionary:\n\n```python\nfrom apacheconfig import *\n\noptions = {\n    'lowercasenames': True\n}\n\nwith make_loader(**options) as loader:\n    config = loader.load('httpd.conf')\n\nprint(config)\n```\n\nThe options are largely patterned after Perl's Config::General module, documentation for the options is also\nborrowed there.\n\nThe following options are currently supported:\n\n### allowmultioptions\n\nIf the value is `False`, then multiple identical options are disallowed. The default is `True` in which\ncase values of the identical options are collected into a list.\n\n### forcearray\n\nYou may force a single config line to get parsed into a list by turning on the option *forcearray* and by\nsurrounding the value of the config entry by *[]*.\n\nExample:\n\n```python\nhostlist = [foo.bar]\n```\n\nWill result in a single value array entry if the *forcearray* option is turned on.\n\n### lowercasenames\n\nIf set to `True`, then all options found in the config will be converted to lowercase. This allows you to\nprovide case-in-sensitive configs. The values of the options will not lowercased.\n\n### nostripvalues\n\nIf set to `False`, then each value found in the config will not be right-stripped. This allows you to gather the options as long as their trailing whitespaces.\n\n### useapacheinclude\n\nIf set to `True`, the parser will consider \"include ...\" as valid include statement (just like the well known\nApache include statement).\n\nIt also supports apache's \"IncludeOptional\" statement with the same behavior, that is, if the include file\ndoesn't exist no error will be thrown.\n\n### includeagain\n\nIf set to `True`, you will be able to include a sub-configfile multiple times. With the default, `False`, duplicate\nincludes are silently ignored and only the first include will succeed.\n\nReincluding a configfile can be useful if it contains data that you want to be present in multiple places in the\ndata tree.\n\nNote, however, that there is currently no check for include recursion.\n\n### includerelative\n\nIf set to `True`, included files with a relative path (i.e. \"cfg/blah.conf\") will be opened from within the location\nof the configfile instead from within the location of the script ($0). This works only if the configfile has a\nabsolute pathname (i.e. \"/etc/main.conf\").\n\nIf the *configpath* option has been set and if the file to be included could not be found in the location relative\nto the current config file, the module will search within *configpath* for the file.\n\n### includedirectories\n\nIf set to `True`, you may specify include a directory, in which case all files inside the directory will be loaded\nin ASCII order. Directory includes will not recurse into subdirectories. This is comparable to including a directory\nin Apache-style config files.\n\n### includeglob\n\nIf set to `True`, you may specify a glob pattern for an include to include all matching files\n(e.g. \u003c\u003cinclude conf.d/*.conf\u003e\u003e).\n\nAn include option will not cause a parser error if the glob didn't return anything.\n\n### configpath\n\nYou can use this variable to specify a search path for relative config files which have to be included.\nThe apacheconfig tool will search within this path for the file if it cannot find the file at the location\nrelative to the current config file.\n\nTo provide multiple search paths you can specify an array reference for the path. For example:\n\n```python\n\noptions = {\n    'configpath': ['dira', 'dirb']\n}\n```\n\n### mergeduplicateblocks\n\nIf set to `True`, then duplicate blocks, that means blocks and named blocks, will be merged into a single one\nThe default behavior is to create an array if some junk in a config appears more than once.\n\n### mergeduplicateoptions\n\nIf set to `True`, then duplicate options will be merged. That means, if the same option occurs more than once, the\nlast one will be used in the resulting config dictionary.\n\n### autotrue\n\nIf set to `True`, then options in your config file, whose values are set to *true* or *false* values, will be\nnormalised to *1* or *0* respectively.\n\nThe following values will be considered as *true*:\n\n- yes\n- on\n- 1\n- true\n\nThe following values will be considered as *false*:\n\n- no\n- off\n- 0\n- false\n\nThis effect is case-insensitive, i.e. both *Yes* or *No* will result in *1*.\n\n### namedblocks\n\nThis option enables tag splitting by the first whitespace and turning the trailing\npiece into a nested block. `True` by default.\n\n### flagbits\n\nThis option takes one required parameter, which must be a dictionary defining variables for which you want to preset\nvalues. Each variable you have defined in this dictionary and which occurs in your config file, will cause this\nvariable being set to the preset values to which the value in the config file refers to.\n\nMultiple flags can be used, separated by the pipe character |.\n\nFor example, this option:\n\n```python\noptions = {\n    'flagbits': {\n        'mode': {\n            'CLEAR': '1',\n            'STRONG': '1',\n            'UNSECURE': '32bit'\n        }\n    }\n}\n```\n\nIn this example we are defining a variable named *mode* which may contain one or more of *CLEAR*, *STRONG* and\n*UNSECURE* as value.\n\nThe appropriate config entry may look like this:\n\n```\nmode = CLEAR | UNSECURE\n```\n\nThe parser will create a dictionary which will be the value of the key *mode*. This dictionary will contain all\nflags which you have pre-defined, but only those which were set in the config will contain the pre-defined value,\nthe other ones will be undefined.\n\nThe resulting config structure would look like this after parsing:\n\n```python\nconfig = {\n    'mode': {\n        'CLEAR': '1',\n        'UNSECURE': '32bit',\n        'STRONG': None\n    }\n}\n```\n\nThis method allows the user to set multiple pre-defined values for one option.\n\nPlease beware, that all occurrences of those variables will be handled this way, there is no way to distinguish between\nvariables in different scopes. That means, if *mode* would also occur inside a named block, it would also parsed this\nway.\n\nValues which are not defined in the dictionary supplied to the parameter *flagbits* and used in the corresponding\nvariable in the config will be ignored.\n\n### defaultconfig\n\nThe value of this option should be a dictionary holding default options-values. This causes the module to populate\nthe resulting config dictionary with the given values, which allows you to set default values for particular config\noptions directly.\n\nNote that you probably want to use this with *mergeduplicateoptions*, otherwise a default value already in the\nconfiguration file will produce an array of two values.\n\n### interpolatevars\n\nIf set to `True`, variable interpolation will be done on your config input.\n\nVariables can be defined everywhere in the config and can be used afterwards as the value of an option. Variables\ncannot be used as keys or as part of keys.\n\nIf you define a variable inside a block or a named block then it is only visible within this block or within blocks\nwhich are defined inside this block.\n\nFor example:\n\n```python\n# sample config which uses variables\nbasedir    = /opt/ora\nuser       = t_space\nsys        = unix\n\u003ctable intern\u003e\n instance  = INTERN\n owner     = $user                 # \"t_space\"\n logdir    = $basedir/log          # \"/opt/ora/log\"\n sys       = macos\n \u003cprocs\u003e\n     misc1 = ${sys}_${instance}    # macos_INTERN\n     misc2 = $user                 # \"t_space\"\n \u003c/procs\u003e\n\u003c/table\u003e\n```\n\nThis will result in the following structure:\n\n```python\n{\n  'basedir': '/opt/ora',\n  'user': 't_space'\n  'sys': 'unix',\n  'table': {\n    'intern': {\n      'sys': 'macos',\n      'logdir': '/opt/ora/log',\n      'instance': 'INTERN',\n      'owner': 't_space',\n      'procs': {\n        'misc1': 'macos_INTERN',\n        'misc2': 't_space'\n      }\n    }\n  }\n}\n```\n\nAs you can see, the variable *sys* has been defined twice. Inside the *\u003cprocs\u003e* block a variable *${sys}* has been\nused, which then were interpolated into the value of *sys* defined inside the *\u003ctable\u003e* block, not the *sys* variable\none level above. If *sys* were not defined inside the *\u003ctable\u003e* block then the \"global\" variable *sys* would have\nbeen used instead with the value of *unix*.\n\nVariables inside double quotes will be interpolated, but variables inside single quotes will not interpolated unless\n*allowsinglequoteinterpolation* option is set.\n\nIn addition you can surround variable names with curly braces to avoid misinterpretation by the parser.\n\n### interpolateenv\n\nIf set to `True`, environment variables can be referenced in configs, their values will be substituted in place of\ntheir reference in the value.\n\nThis option enables *interpolatevars*.\n\n### allowsinglequoteinterpolation\n\nBy default variables inside single quotes will not be interpolated. If you turn on this option, they will be\ninterpolated as well.\n\n### strictvars\n\nBy default this is set to `True`, which causes parser to fail if an undefined variable with *interpolatevars* turned\non occurs in a config. Set to `False` to avoid such error messages.\n\n### ccomments\n\nThe parser will process C-style comments as well as hash-style comments. By default C-style comments are processed,\nyou can disable that by setting *ccomments* option to `False`.\n\n### noescape\n\nIf you want to preserve escaping special characters ($\\\"#) in the configuration data, turn this parameter on. It is\nset to `False` by default.\n\n### preservewhitespace\n\nWhen this option is enabled, the parser would collect insignificant whitespaces\ninto AST. This information could then be used for code generation. The default\nis `False`.\n\n### multilinehashcomments\n\nEscaped newlines at the end of hash comments add the following line to the comment. Apache's native config parser processes hash comments this way. Set to `False` by default.\n\n### disableemptyelementtags\n\nWhen this option is enabled, the parser does not recognize empty element tags\nsuch as `\u003cblock name/\u003e`. The former, for instance, would be re-interpreted\nas opening tag with tag name `block`, and argument `name/`.\nThe default is `False`.\n\n## Parser plugins\n\nYou can alter the behavior of the parser by supplying callables which will be invoked on certain hooks during\nconfig file processing and parsing.\n\nThe general approach works like this:\n\n```python\n\ndef pre_open_hook(file, base):\n    print('trying to open %s... ' % file)\n    if 'blah' in file:\n      print('ignored')\n      return False, file, base\n    else:\n      print('allowed')\n      return True, file, base\n\noptions = {\n    'plug': {\n        'pre_open': pre_open_hook\n    }\n}\n```\n\nOutput:\n\n```bash\ntrying to open cfg ... allowed\ntrying to open x/*.conf ... allowed\ntrying to open x/1.conf ... allowed\ntrying to open x/2.conf ... allowed\ntrying to open x/blah.conf ... ignored\n```\n\nAs you can see, we wrote a little function which takes a filename and a base directory as parameters. We tell\nthe parser via the *plug* option to call this sub every time before it attempts to open a file.\n\nGeneral processing continues as usual if the first value of the returned array is `True`. The second value of that\ntuple depends on the kind of hook being called.\n\nThe following hooks are available so far:\n\n### pre_open\n\nTakes two parameters: *filename* and *basedirectory*.\n\nHas to return a tuple consisting of 3 values:\n\n - `True` or `False` (continue processing or not)\n - filename\n - base directory\n\nThe returned *basedirectory* and *filename* will be used for opening the file.\n\n### pre_read\n\nTakes two parameters: the source of the contents read from and a string containing the raw contents.\nThis hook gets the unaltered, original contents as it's read from a file (or some other source).\n\nHas to return an array of 3 values:\n\n - `True` or `False` (continue processing or not)\n - the source of the contents or `None` if `loads()` is invoked rather than `load()`\n - a string that replaces the read contents\n\nYou can use this hook to apply your own normalizations or whatever.\n\n## Command-line tool\n\nThe library comes with a simple command-line tool `apacheconfigtool` which can convert Apache-style\nconfig files into JSON. The tool is also useful for playing with config file formats and parser\noptions.\n\n```bash\n$ apacheconfigtool  --help\nusage: apacheconfigtool [-h] [-v] [--json-input] [--allowmultioptions]\n                        [--forcearray] [--lowercasenames] [--nostripvalues]\n                        [--useapacheinclude] [--includeagain]\n                        [--includerelative] [--includedirectories]\n                        [--includeglob] [--mergeduplicateblocks]\n                        [--mergeduplicateoptions] [--namedblocks] [--autotrue]\n                        [--interpolatevars] [--interpolateenv]\n                        [--allowsinglequoteinterpolation] [--strictvars]\n                        [--noescape] [--ccomments]\n                        [--configpath CONFIGPATH] [--flagbits \u003cJSON\u003e]\n                        [--defaultconfig \u003cJSON\u003e]\n                        file [file ...]\n\nDump Apache config files into JSON\n\npositional arguments:\n  file                  Path to the configuration file to dump\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -v, --version         show program's version number and exit\n  --json-input          Expect JSON file(s) on input, produce Apache\n                        configuration\n\nparsing options:\n  --allowmultioptions   Collect multiple identical options into a list\n  --forcearray          Force a single config line to get parsed into a list\n                        by turning on this option and by surrounding the value\n                        of the config entry by []\n  --lowercasenames      All options found in the config will be converted to\n                        lowercase\n  --nostripvalues       All values found in the config will not be right-\n                        stripped\n  --useapacheinclude    Consider \"include ...\" as valid include statement\n  --includeagain        Allow including sub-configfiles multiple times\n  --includerelative     Open included config files from within the location of\n                        the configfile instead from within the location of the\n                        script\n  --includedirectories  Include statement may point to a directory, in which\n                        case all files inside the directory will be loaded in\n                        ASCII order\n  --includeglob         Include statement may point to a glob pattern, in\n                        which case all files matching the pattern will be\n                        loaded in ASCII order\n  --mergeduplicateblocks\n                        Duplicate blocks (blocks and named blocks), will be\n                        merged into a single one\n  --mergeduplicateoptions\n                        If the same option occurs more than once, the last one\n                        will be used in the resulting config dictionary\n  --namedblocks         Do not split tags by the first whitespace and turn the\n                        trailing part into a nested block\n  --autotrue            Turn various forms of binary values in config into \"1\"\n                        and \"0\"\n  --interpolatevars     Enable variable interpolation\n  --interpolateenv      Enable process environment variable interpolation\n  --allowsinglequoteinterpolation\n                        Perform variable interpolation even when being in\n                        single quotes\n  --strictvars          Do not fail on an undefined variable when performing\n                        interpolation\n  --noescape            Preserve special escape characters left outs in the\n                        configuration values\n  --ccomments           Do not parse C-style comments\n  --configpath CONFIGPATH\n                        Search path for the configuration files being\n                        included. Can repeat.\n  --flagbits \u003cJSON\u003e     Named bits for an option in form of a JSON object of\n                        the following structure {\"OPTION\": {\"NAME\": \"VALUE\"}}\n  --defaultconfig \u003cJSON\u003e\n                        Default values for parsed configuration in form of a\n                        JSON object\n\n$ apacheconfigtool --includedirectories include-directory-test.conf\n{\n  \"final_include\": \"true\",\n  \"seen_first_config\": \"true\",\n  \"seen_second_config\": \"true\",\n  \"inner\": {\n    \"final_include\": \"true\",\n    \"seen_third_config\": \"true\"\n  },\n  \"seen_third_config\": \"true\"\n}\n```\n\n## How to get apacheconfig\n\nThe apacheconfig package is distributed under terms and conditions of 2-clause\nBSD [license](https://github.com/etingof/apacheconfig/LICENSE.rst). Source code is freely\navailable as a GitHub [repo](https://github.com/etingof/apacheconfig).\n\nYou could `pip install apacheconfig` or download it from [PyPI](https://pypi.org/project/apacheconfig).\n\nIf something does not work as expected, \n[open an issue](https://github.com/etingof/apacheconfig/issues) at GitHub.\n\nCopyright (c) 2018-2020, [Ilya Etingof](mailto:etingof@gmail.com). All rights reserved.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetingof%2Fapacheconfig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fetingof%2Fapacheconfig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetingof%2Fapacheconfig/lists"}