<!-- markdownlint-disable MD033 -->
# <img src="https://gitlab.com/danieljrmay/pyinilint/-/raw/master/doc/images/logo.png" width="100" alt="logo"/> PyINILint

[![pipeline status](https://gitlab.com/danieljrmay/pyinilint/badges/master/pipeline.svg)](https://gitlab.com/danieljrmay/pyinilint/commits/master)
[![Copr build status](https://copr.fedorainfracloud.org/coprs/danieljrmay/pyinilint/package/python-pyinilint/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/danieljrmay/pyinilint/package/python-pyinilint)
[![pypi package](https://badge.fury.io/py/pyinilint.svg)](https://pypi.org/project/pyinilint/)

The `pyinilint` command line utility allows you to lint (check the
syntax) of [INI](https://en.wikipedia.org/wiki/INI_file)-like
configuration files. Here’s an example of the simplest possible
useage.

```console
pyinilint myfile.ini
```

Silent output, and a `0` exit status means that `myfile.ini` has been
parsed successfully.

The `pyinilint` utility is a front-end to the Python
[configparser](https://docs.python.org/3/library/configparser.html)
module, which supports interpolation. This means you can have
"variables" in your INI-files which can be optionally replaced with
values, which can be defined within the same, or different,
INI-file. Using different pyinilint command line switches you can
inspect what is happening with variable interpolation in your INI-files.

## Installation

### Copr

If you use Fedora then you can install `pyinilint` via a [Copr
respository](https://copr.fedorainfracloud.org/coprs/danieljrmay/pyinilint/).

```console
dnf copr enable danieljrmay/pyinilint
dnf install pyinilint
```

The installed RPMs include a man page and Bash completion script.

### PyPI

You can get a distribution of [`pyinilint` from
PyPI](https://pypi.org/project/pyinilint/).

```console
pip install pyinilint
```

However, this distribution currently does not include the man page or
Bash completion script.

### Source Code Release

You can download a source code release from the [`pyinilint` project’s
GitLab releases](https://gitlab.com/danieljrmay/pyinilint/releases)
page. You can then install with something like the following:

```console
tar -xzf pyinilint-0.10.tar.gz
cd pyinilint-0.10
make
make pybuild
sudo make install
sudo make pyinstall
```

The `make` and `sudo make install` builds and installs the man page
and Bash completion script. The `make pybuild` and `sudo make
pyinstall` builds and installs the Python module and console script.

## Usage

```console
pyinilint --help
usage: pyinilint [-h] [-b] [-d] [-e ENCODING] [-m] [-o] [-r] [-s] [-v]
                 paths [paths ...]

pyinilint (version 0.10) is a linter and inspector for INI format files.

positional arguments:
  paths                 paths of the file(s) to check

optional arguments:
  -h, --help            show this help message and exit
  -b, --basic           use basic interpolation, the default is extended
  -d, --debug           show debugging messages
  -e ENCODING, --encoding ENCODING
                        set the encoding to be used, omit to use the default
  -i, --interpolate     interpolate the parsed configuration without output
  -m, --merge           merge files into a single configuration
  -o, --output          output the parsed configuration to stdout
  -r, --raw             output raw, do not interpolate
  -s, --serialize       output the interpolated and serialized configuration
                        to stdout
  -v, --verbose         show verbose messages

See https://github.com/danieljrmay/pyinilint for more information.
```

### Options

#### `-b`, `--basic`

Use basic interpolation when parsing. See the Python configparser
interpolation documentation below for more information.

#### `-d`, `--debug`

Output debugging messages, probably only of interest to those
developing pyinilint.

#### `-e ENCODING`, `--encoding ENCODING`

Specifiy a non-default encoding to use when parsing the files to be
checked.

#### `h`, `--help`

Display help and version information.

#### `i`, `--interpolate`

Interpolate the parsed configuration but do not output anything. This
is useful for detecting warnings and errors in the interpolation
syntax or values.

#### `-m`, `--merge`

Read all the specified `file`s into a single Python `ConfigParser`
object; this allows interpolation between files. When this option is
not specified each file is read into its own seperate `ConfigParser`
object.

#### `-o`, `--output`

Output the parsed configuration to `STDOUT` without any
interpolation. Use the `--serialize` options to enable interpolation.

#### `-r`, `--raw`

Use raw mode, so there is no interpolation when parsing. See the
Python configparser interpolation documentation below for more
information.

#### `-s`, `--serialize`

Output the parsed, interpolated and serialized configuration to
`STDOUT`. Use this together with the `--basic`, `--merge` and `--raw`
options to inspect the interpolation of "variables" within you
INI-files.

#### `-v`, `--verbose`

Print verbose messages.

### Exit status

This is the list of exit status codes and their meanings returned to
the shell by `pyinilint`.

| Exit Status | Name | Meaning |
| :---------: | ---- | ------- |
| 0 | EXIT_OK | Everything went well, all files linted successfully.  |
| 1 | EXIT_NON_EXISTANT_FILE | At least one of the specified files does not exist. |
| 2 | EXIT_SYNTAX_ERROR | There was an error in the command line syntax. |
| 3 | EXIT_UNREADABLE_FILE | At least one of the specified files existed but was not readable. |
| 4 | EXIT_DUPLICATE_SECTION | There is a duplicate section in the INI file. |
| 5 | EXIT_DUPLICATE_OPTION | There is a duplicate option in the INI file. |
| 6 | EXIT_INTERPOLATION_MISSING_OPTION | There is no corresponding option to a given interpolation key in the configuration. |
| 7 | EXIT_INTERPOLATION_DEPTH | Maximum recursion depth has been exceeded in interpolation.  |
| 8 | EXIT_MISSING_SECTION_HEADER | There is a missing section header. |
| 9 | EXIT_PARSING_ERROR | Usually some kind of syntax error within an INI file. |
| 10 | EXIT_UNKNOWN_ERROR | A catch-all for some other kind of error, inspect the error message for more details. |

### Examples

#### Check a single file

```console
pyinilint myfile.ini
```

A silent response (with exit status of 0) means that `myfile.ini` has
passed the lint check.

#### Check multiple individual files

```console
pyinilint -v myfile1.ini myfile2.ini
```

Check multiple files treating each one individually and output verbose
messages.

#### Check multiple files in a collection, and output the serialized results

```console
pyinilint -m -s myfile1.ini myfile2.ini
```

Check multiple files as part of a single `ConfigParser` object,
and output the parsed and interpolated values.

#### Check a file with a custom encoding

```console
pyinilint -e iso8859_15 myfile.ini
```

Check `myfile.ini` using  iso8859_15 encoding.

### Caution

If your INI-files are ultimatly going to be parsed by an INI-parser
different from `ConfigParser` then you should be aware that there can
be subtle differences in INI-file format between parsers. However, it
should still spot most howling errors!

## Contact

Please get in contact via the [`pyinilint` project’s GitLab
website](https://gitlab.com/danieljrmay/pyinilint) to:

* [Report issues](https://gitlab.com/danieljrmay/pyinilint/issues) or
  make feature requests.
* Clone or fork the Git source code tree.
* Say “hello!” 😉

## References

* Python [configparser interpolation documentation](https://docs.python.org/3/library/configparser.html#interpolation-of-values)
* The [pyinilint project website](https://gitlab.com/danieljrmay/pyinilint)
