secsie-conf
pip3 install secsie-conf
Secsie is a configuration language parser for Python, made for speed and beauty. Instead of writing config files in JSON (don't get me wrong, JSON is FAR better than a lot of other things you could use (cough cough XML)), you can save time writing your config files in secsie.
The secsie language format is very similar to ini, except just a little better. You can use secsie-conf to read .ini files into Python dicts. secsie-conf can write .ini files too, see Write an .ini file.
Advantages over JSON:
- easier to read
- faster and more natural to write
- no special syntax required for strings vs ints, floats, bools, etc.
- file size usually smaller than readable JSON
Secsie Language Constructs
These are the rules of the secsie config language:
- Comment lines begin with
#or;, but inline comments can only begin with the octothorpe (#) and must have a space preceding them. - Whitespace is ignored everywhere except in key names and section tag names.
- A config file consists of sections and attributes(keys and values).
- A section ends where the next section begins. Attributes declared before sections begin are valid.
- To begin a section use the following syntax:
[section1]
# attribute lines in this section follow
- The syntax for an attribute line is:
key = value
- Spaces are not allowed in key names or section tags. Only
a-z(case insensitive),0-9,_, and-are allowed in section tag names, while other special characters are allowed in key names. - Values can contain any character, including
#. A#only starts an inline comment when it is preceded by a space, sopassword = p#ssis fine butpassword = p #ssis cut off at the space. Leading and trailing whitespace is removed. String values separated by commas are interpreted as lists.
INI
secsie-conf can be used to read .ini files, as long as the mode ini is specifed to the parser. The rules of interpretation for .ini files vary slightly.
Differences:
- Section names are allowed to contain spaces
- quoted strings are valid, but the quotes are removed (there is no need to quote string in
secsie;). A quoted value is always kept as a string, so"42"stays a string and"a,b"is not split into a list - Lists in
.inifiles can have a trailing comma with no effect, but a trailing comma insecsiewill create a blank string
secsie-confcan also write theinidialect described here, see Write an.inifile.
Valid values
Secsie supports strings, ints, floats, null types, booleans, and lists. Most of these types can be written out by themselves and will automatically be converted to the appropriate native type. Lists require comma separation.
Strings:
# Spaces are allowed in string value
string_key = whatever value you want
Ints:
numb = 42 # Automatically converted to int when parsed
Floats:
pi = 3.14159265 # Automatically converted to float when parsed
Null type (None):
# 'null' is used as the NoneType (case insensitive)
nothing = null
Booleans:
# True and yes are truthy values (case insensitive)
truth = true
truth2 = True
truth3 = yes
# False and no are falsy values (case insensitive)
untruth = falsE
untruth2 = False
untruth3 = no
Lists:
# To interpret a value as a list, you must separate the list values with a comma.
list = this, that, the other
Limitations
secsie has no quoting. A value is whatever you write, and its type is inferred from it, so quotes are just ordinary characters (name = "Bob" gives '"Bob"', quotes included). This keeps the language simple, but it means some values can't be written as strings:
- Digits that must stay text, like an account number or zip code with a leading zero.
090192837418is read as the int90192837418. - Words that are special values, such as
true,no,yes, andnull. - Strings containing a comma, which are read as lists.
- Strings containing a space followed by
#, which start an inline comment.
If your program needs one of these as a string, convert it after reading, for example str(config['zip']).zfill(5). Only ini mode understands quotes (see above), since it exists to read existing .ini files. If you need to write values like these, ini mode quotes them for you.
Examples
examples/valid.secsie.conf:
; This is an example of a valid secsie file
before_section = totally okay
# Whitespace don't matter
; Here are examples of how types are interpreted(no keywords are off limits!)
[special_values]
int = 42
float = 269.887
truth = yes
falsehood = no
true = true
; I don't encourage this but it's valid ;)
false = FaLSe
# this following will be interpreted as a list
coolKids = jim, bill, bob, alice, 42
# Null value
nah = Null
[anotherSection]
# The indent here is optional, included for readability
sections = are amazing
Parse result:
{'before_section': 'totally okay', 'special_values': {'int': 42, 'float': 269.887, 'truth': True, 'falsehood': False, 'true': True, 'false': False, 'coolKids': ['jim', 'bill', 'bob', 'alice', 42], 'nah': None}, 'anotherSection': {'sections': 'are amazing'}}
Module Usage
Read a secsie config file as a dict:
import json
import secsie
# To get a dict from a config file
config = secsie.parse_config_file('examples/valid.secsie.conf')
print(json.dumps(config, indent=2)) # For prettyness
Result:
{
"before_section": "totally okay",
"special_values": {
"int": 42,
"float": 269.887,
"truth": true,
"falsehood": false,
"true": true,
"false": false,
"coolKids": [
"jim",
"bill",
"bob",
"alice",
42
],
"nah": null
},
"anotherSection": {
"sections": "are amazing"
}
}
Read an .ini file:
import json
import secsie
# This might not work...
config = secsie.parse_config_file('examples/php.ini')
Result:
Traceback (most recent call last):
...
File "<string>", line 955
secsie.InvalidSyntax: Invalid syntax on line 955: "[CLI Server]" - bad section descriptor or value assignment (line 955)
We can see that this up and broke. WTF?! Actually, it's okay. Spaces aren't allowed in secsie section names, remember? When reading an .ini file, we need to pass the argument mode='ini' to the parse_config_file function, like this:
config = secsie.parse_config_file('examples/php.ini', mode='ini')
print(json.dumps(config, indent=2))
{
"PHP": {
"engine": "On",
"short_open_tag": "Off",
"precision": 14,
"output_buffering": 4096,
"zlib.output_compression": "Off",
"implicit_flush": "Off",
"unserialize_callback_func": "",
"serialize_precision": -1,
"disable_functions": [
"pcntl_alarm",
"pcntl_fork",
"pcntl_waitpid",
"pcntl_wait",
"pcntl_wifexited",
"pcntl_wifstopped",
"pcntl_wifsignaled",
"pcntl_wifcontinued",
"pcntl_wexitstatus",
"pcntl_wtermsig",
"pcntl_wstopsig",
"pcntl_signal",
"pcntl_signal_get_handler",
"pcntl_signal_dispatch",
"pcntl_get_last_error",
"pcntl_strerror",
"pcntl_sigprocmask",
"pcntl_sigwaitinfo",
"pcntl_sigtimedwait",
"pcntl_exec",
"pcntl_getpriority",
"pcntl_setpriority",
"pcntl_async_signals",
"pcntl_unshare"
],
"disable_classes": "",
"zend.enable_gc": "On",
"zend.exception_ignore_args": "On",
"expose_php": "Off",
"max_execution_time": 30,
"max_input_time": 60,
"memory_limit": "128M",
"error_reporting": "E_ALL & ~E_DEPRECATED & ~E_STRICT",
"display_errors": "Off",
"display_startup_errors": "Off",
"log_errors": "On",
"log_errors_max_len": 1024,
"ignore_repeated_errors": "Off",
"ignore_repeated_source": "Off",
"report_memleaks": "On",
"variables_order": "GPCS",
"request_order": "GP",
"register_argc_argv": "Off",
"auto_globals_jit": "On",
"post_max_size": "8M",
"auto_prepend_file": "",
"auto_append_file": "",
"default_mimetype": "text/html",
"default_charset": "UTF-8",
"doc_root": "",
"user_dir": "",
"enable_dl": "Off",
"file_uploads": "On",
"upload_max_filesize": "2M",
"max_file_uploads": 20,
"allow_url_fopen": "On",
"allow_url_include": "Off",
"default_socket_timeout": 60
},
"CLI Server": {
"cli_server.color": "On"
},
"Pdo_mysql": {
"pdo_mysql.default_socket": ""
},
"mail function": {
"SMTP": "localhost",
"smtp_port": 25,
"mail.add_x_header": "Off"
},
"ODBC": {
"odbc.allow_persistent": "On",
"odbc.check_persistent": "On",
"odbc.max_persistent": -1,
"odbc.max_links": -1,
"odbc.defaultlrl": 4096,
"odbc.defaultbinmode": 1
},
"MySQLi": {
"mysqli.max_persistent": -1,
"mysqli.allow_persistent": "On",
"mysqli.max_links": -1,
"mysqli.default_port": 3306,
"mysqli.default_socket": "",
"mysqli.default_host": "",
"mysqli.default_user": "",
"mysqli.default_pw": "",
"mysqli.reconnect": "Off"
},
"mysqlnd": {
"mysqlnd.collect_statistics": "On",
"mysqlnd.collect_memory_statistics": "Off"
},
"PostgreSQL": {
"pgsql.allow_persistent": "On",
"pgsql.auto_reset_persistent": "Off",
"pgsql.max_persistent": -1,
"pgsql.max_links": -1,
"pgsql.ignore_notice": 0,
"pgsql.log_notice": 0
},
"bcmath": {
"bcmath.scale": 0
},
"Session": {
"session.save_handler": "files",
"session.use_strict_mode": 0,
"session.use_cookies": 1,
"session.use_only_cookies": 1,
"session.name": "PHPSESSID",
"session.auto_start": 0,
"session.cookie_lifetime": 0,
"session.cookie_path": "/",
"session.cookie_domain": "",
"session.cookie_httponly": "",
"session.cookie_samesite": "",
"session.serialize_handler": "php",
"session.gc_probability": 0,
"session.gc_divisor": 1000,
"session.gc_maxlifetime": 1440,
"session.referer_check": "",
"session.cache_limiter": "nocache",
"session.cache_expire": 180,
"session.use_trans_sid": 0,
"session.sid_length": 26,
"session.trans_sid_tags": "a=href,area=href,frame=src,form=",
"session.sid_bits_per_character": 5
},
"Assertion": {
"zend.assertions": -1
},
"Tidy": {
"tidy.clean_output": "Off"
},
"soap": {
"soap.wsdl_cache_enabled": 1,
"soap.wsdl_cache_dir": "/tmp",
"soap.wsdl_cache_ttl": 86400,
"soap.wsdl_cache_limit": 5
},
"ldap": {
"ldap.max_links": -1
}
}
That ^'s a whole PHP configuration file converted to some sexy secsie JSON!
Write a secsie file:
Now that we have the php.ini contents stored in config, let's output it in secsie!
secsie.generate_config_file(config, output_file='examples/php_ini.secsie.conf')
Output (examples/php_ini.secsie.conf):
# php_ini.secsie.conf auto-generated by secsie
[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
; unserialize_callback_func =
serialize_precision = -1
disable_functions = pcntl_alarm, pcntl_fork, pcntl_waitpid, pcntl_wait, pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wifcontinued, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig, pcntl_signal, pcntl_signal_get_handler, pcntl_signal_dispatch, pcntl_get_last_error, pcntl_strerror, pcntl_sigprocmask, pcntl_sigwaitinfo, pcntl_sigtimedwait, pcntl_exec, pcntl_getpriority, pcntl_setpriority, pcntl_async_signals, pcntl_unshare
; disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
expose_php = Off
max_execution_time = 30
max_input_time = 60
memory_limit = 128M
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
variables_order = GPCS
request_order = GP
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 8M
; auto_prepend_file =
; auto_append_file =
default_mimetype = text/html
default_charset = UTF-8
; doc_root =
; user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 2M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
[CLIServer]
cli_server.color = On
[Pdo_mysql]
; pdo_mysql.default_socket =
[mailfunction]
SMTP = localhost
smtp_port = 25
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.default_port = 3306
; mysqli.default_socket =
; mysqli.default_host =
; mysqli.default_user =
; mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[PostgreSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
; session.cookie_domain =
; session.cookie_httponly =
; session.cookie_samesite =
session.serialize_handler = php
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
; session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = a=href,area=href,frame=src,form=
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled = 1
soap.wsdl_cache_dir = /tmp
soap.wsdl_cache_ttl = 86400
soap.wsdl_cache_limit = 5
[ldap]
ldap.max_links = -1
You should notice 2 things:
- Keys and value assignments are separated by an equals sign with a space ON BOTH SIDES!
key = value, NOTkey=value. That is ugly and lazy. This ain't minified JS, son. They make things for that... - Blank values were commented out. If you disagree with that, MAKE 'EM NULL!
key =doesn't say anything.
Also note that trans_sid_tags went in as a quoted ini string, and came out as a plain a=href,area=href,frame=src,form=. Since a comma means "list" in secsie, this will read back as a list. See the limits below.
Change the header
generate_config_file starts every file with a comment saying it was auto-generated. Use header to say something else, with one comment written for every line of your text, or pass an empty string to leave it out:
secsie.generate_config_file(config, 'app.secsie', header="Managed by the deploy script.\n\nDo not edit by hand.")
# Managed by the deploy script.
#
# Do not edit by hand.
generate_config only returns the config text, with no header at all. A header is always written as comments, so it can never change what the file is read back as.
What the generator can and can't write (secsie mode)
Generating a config keeps your data, not the original file. Comments, blank lines, spacing, and spellings like yes/no (they come back as true/false) are not preserved, and None is written as null. A few things can't be represented in secsie at all, and the generator tells you instead of writing a config that reads back differently:
- Nested dicts raise a
TypeError.secsieonly has one level of sections. - A string with a space followed by
#raises aValueError, since that would start an inline comment. A#with no space before it is fine.
Empty strings are commented out (see 2 above), so they are not present when the config is parsed again. Strings containing a comma are written as is, so they are read back as lists.
Keys that aren't in a section are always written before the first section, whatever order they have in your dict. A section only ends where the next one begins, so a key written after a section would be read back as part of it.
Write an .ini file
Pass mode='ini' to write the same ini dialect that mode='ini' reads:
config = {
"app_name": "my app",
"debug": False,
"Database": {"host": "localhost", "port": 5432, "password": "p#ss", "zip": "02134", "tags": ["a", "b"], "motto": "work, play"},
}
print(secsie.generate_config(config, mode='ini'))
# or: secsie.generate_config_file(config, 'my_settings.ini', mode='ini')
app_name = my app
debug = false
[Database]
host = localhost
port = 5432
password = p#ss
zip = "02134"
tags = a, b
motto = "work, play"
There is no one .ini standard (PHP, Python's configparser, and Windows all disagree on the details), so the promise is a narrow one: reading the result with mode='ini' gives you back exactly the data you wrote. Nothing more is promised about other programs, though the output is plain key = value lines that most of them will read.
How it differs from secsie mode:
- Strings that would otherwise change are quoted: ones that look like a number, a boolean, or
null("02134"), ones with a comma ("work, play"), empty ones (""), and ones with leading or trailing whitespace. Everything else is written plain. Quoted values are kept as strings when read (see theinidifferences above). - Empty strings are written as
""and read back, instead of being commented out. - Section names can have spaces (
[CLI Server]) and are never changed. - Keys in a section are not indented unless you pass
indent. As insecsiemode, top level keys are written first. generate_config_filestarts the file with a;comment (including a customheader) instead of a#one, because;is the comment characterinireaders agree on.- Lists are written as
a, b. A list with one item gets a trailing comma (a,) and an empty list is a lone comma (,), so they are still read as lists.
Whatever can't be written faithfully raises an error that names the key and says why, so the file is never written with a different value than you gave. Besides nested dicts, these can not be written in ini mode:
- strings with a space followed by
#, a line break, or whitespace touching an=(the reader would change or cut them) - strings that start or end with a quote character (there is no way to escape a quote)
- list items that are empty, have surrounding whitespace, contain a comma, or look like a number, boolean, or
null(list items can't be quoted), or a list that starts with a quote infandnan, keys with whitespace or=or that start with#or;, and section names with anything other than letters, numbers, spaces,_, and-
An empty section is written, but there is nothing in it for the reader to return, so it is not read back.
Metadata
Release files for secsie-conf 4.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| secsie_conf-4.0.0.tar.gz | 31.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| secsie_conf-4.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.7 kB
Release files / secsie_conf-4.0.0.tar.gz
| Download URL | secsie_conf-4.0.0.tar.gz |
|---|---|
| Size | 31.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4e45132e2fbc723160db2de2b4ab907d1793f68928665f0757bb704fcc28931b
|
|
BLAKE2b-256 checksum How to use checksums |
5aa7e2e18c4589e6f9bd518a1e4b1721fbb7fba918a11cce1ff3a14d627d8b2d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / secsie_conf-4.0.0-py3-none-any.whl
| Download URL | secsie_conf-4.0.0-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a528c13ea1bb2af7638a0393a4adb5a720b9dd0cf501908cab87cdb265025e5d
|
|
BLAKE2b-256 checksum How to use checksums |
c3326599c38bbff65b56186aaac3b1f6d91fac889d54da59aba4a922f7df1965
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|