Pyjo.INI - INI loader and dumper

from Pyjo.INI import dump, load

config = load('[default]\nregion = eu-central-1\noutput = json\n')

string = dump({'www': {'listen': '127.0.0.1:9000', 'pm': 'static', 'pm.max_children': 2}})

Pyjo.INI loads and dumps the INI files that well-known programs read and write, as ~/.aws/config and ~/.aws/credentials, php.ini and php-fpm.conf, .editorconfig and my.cnf, the format of Pyjoyment.Plugin.INIConfig. Its syntax is the one of configparser of the standard library, which botocore reads ~/.aws/config with, and what PHP and the others need besides.

A line [section] starts a section, whose name is kept as it is, as [profile dev], and a section that comes again adds its keys. A line key = value or key: value sets a key, at the first delimiter of the line, with the case of the key kept, and a line without a delimiter is a key of the value None, as skip-name-resolve of my.cnf. The keys before the first section are at the top of the dict, as root = true of .editorconfig. A line that starts with ; or # is a comment, and there are no comments after a value, as in configparser.

# {'root': 'true', '*.py': {'indent_size': '4'}}
load('root = true\n\n[*.py]\nindent_size = 4\n')

The comments are left out, or kept with comments, each line a key of the value None, in the order of the file, which dump() writes as it is, so that a file that is changed keeps them. The key is a Comment, a string that counts the same comments before it in its dict, so that a comment that comes again is a key of its own.

# {Comment('; the region', 0): None, 'region': 'eu-central-1'}
load('; the region\nregion = eu-central-1\n', comments=True)

A value is a string. A line indented more than its key continues its value, whose lines are joined with \n, and a blank line ends it. An empty value whose lines are all key = value is a dict, as the nested settings of ~/.aws/config, and a key that comes again in a section is a list of its values, in their order, as extension of php.ini.

# {'profile dev': {'s3': {'max_concurrent_requests': '20'}}}
load('[profile dev]\ns3 =\n    max_concurrent_requests = 20\n')

# {'PHP': {'extension': ['curl', 'mbstring']}}
load('[PHP]\nextension = curl\nextension = mbstring\n')

dump() writes the same: the keys at the top first, then the sections, a dict of a dict, a list as its key again for each of its values, a dict in a section as an indented block, None as a key alone, a key that starts with ; or # of the value None as a comment, and True and False as true and false. A key is never quoted, as php-fpm takes a key with a dot, as pm.max_children, only as it is.

# '[default]\nregion = eu-central-1\n'
dump({'default': {'region': 'eu-central-1'}})

# '[www]\n; the workers\npm.max_children = 2\n'
dump({'www': {'; the workers': None, 'pm.max_children': 2}})

What INI cannot hold raises an error: a dict deeper than a dict in a section, a list in a list, an empty list, a value with spaces at the ends of a line, an empty line in a value, a line of a value that would be a comment, and a key or the name of a section that the loader would read otherwise. Another object is what Pyjo.JSON encodes, the value of its method __json__ or its string.

See also

Pyjoyment.Plugin.INIConfig, Pyjo.TOML, https://mojolicious.org.

class Pyjo.INI.Comment(line: str, occurrence: int = 0)[source]
comment = Comment('; the region')
comment = Comment('#', 1)

A line of a comment, the key of the value None that load() gives with comments, with occurrence, the number of the same comments before it in its dict, so that two of them are two keys. It equals only a Comment of the same line and occurrence.

# {Comment('#', 0): None, Comment('#', 1): None}
load('#\n#\n', comments=True)
Pyjo.INI.dump(value: Any, *, sort_keys: bool = False) → str[source]
string = dump(value)
string = dump(value, sort_keys=True)

Dump a dict to an INI string, with the keys of a dict in the order of the dict, or sorted with sort_keys.

# 'listen = 127.0.0.1:9000\npm.max_children = 2\n'
dump({'listen': '127.0.0.1:9000', 'pm.max_children': 2})
Pyjo.INI.load(string: str | bytes | bytearray, *, comments: bool = False, unquote: bool = False) → dict[str, Any][source]
value = load(string)
value = load(string, comments=True)
value = load(string, unquote=True)

Load an INI string, whose error is a Pyjo.Exception, without its comments, or with them with comments, and with the quotes around a value kept, as configparser keeps them, or taken off with unquote, as PHP does.

# {'PHP': {'error_log': '"/var/log/php.log"'}}
load('[PHP]\nerror_log = "/var/log/php.log"\n')

# {'PHP': {'error_log': '/var/log/php.log'}}
load('[PHP]\nerror_log = "/var/log/php.log"\n', unquote=True)

A byte order mark at the start of the string, which an editor on Windows writes, is not a part of its first line. Bytes are UTF-8, or UTF-16 or UTF-32 with a byte order mark, as Windows PowerShell writes UTF-16LE.

# {'default': {'region': 'eu-central-1'}}
load('[default]\nregion = eu-central-1\n'.encode('UTF-16'))