Skip to content

Modernize IniFile.jl for 0.6.0: configparser-compatible parsing, ordered round-trips, strict typed getters - #38

Merged
ViralBShah merged 8 commits into
masterfrom
modernize
Nov 14, 2026
Merged

ViralBShah merged 8 commits into
masterfrom
modernize

Conversation

@ViralBShah

@ViralBShah ViralBShah commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

This modernizes IniFile.jl and prepares the 0.6.0 release. It started from evaluating IniFile for Circuitscape.jl, which reads INI files written for Python's ConfigParser. The parser now follows Python's configparser. On a file of edge cases (multi-line values with blank and comment lines, indented keys, delimiters, and inline comments on and off) it gives the same results as Python 3's configparser.

Fixes

  • Short entries such as a= were dropped, because every line shorter than 3 characters was skipped. Now only blank lines and comments are skipped.
  • Malformed lines were printed to stdout with println. They are now reported with @warn, including the line number and file name. With strict=true they throw instead.
  • The delimiter is now simply the first = or : on the line.
  • get_bool read any typo, such as Ture, as false. It now accepts 1/yes/true/on and 0/no/false/off in any case, and throws an ArgumentError naming the section, key and value for anything else.
  • get_bool, get_int and get_float threw a MethodError on a missing key. They now take an optional default; without one they throw KeyError((section, key)). Values that cannot be parsed throw an ArgumentError naming the section, key and value.
  • write scrambled sections and keys, because they were stored in Dicts. They are now stored in order, so files round-trip. write emits key = value and puts a blank line between sections.
  • Keys and values are stored as Strings (OrderedDict{String,String}) instead of Dict{AbstractString,Union{...}}, so lookups are type-stable.
  • get(ini, key, default) is removed. It was the same call as get(ini, section, key) whenever the default was a string (Ambiguity in get function #16).
  • A UTF-8 byte order mark and CRLF line endings are accepted.

New features

Breaking changes

  • set stores string(value), so get always returns a String. For example, get returns "1.5" after set(ini, "s", "k", 1.5).
  • get(ini, section, key) and get(ini, key) return nothing instead of :notfound for a missing key. === :notfound checks do not error, they stop matching.
  • get(ini, key, default) is removed. Use get(defaults(ini), key, default).
  • get_bool throws for values other than 1/yes/true/on and 0/no/false/off, and yes, on and 1 now read as true (they used to read as false). The typed getters throw a KeyError for missing keys.
  • sections, section and defaults return OrderedDicts with String keys and values.
  • show(io, ini) and repr(ini) give a one-line summary instead of the file contents. print, string and write still give the contents.
  • A line indented more than the previous entry continues that entry's value.
  • Inifile is an immutable struct with a keyword constructor.
  • Julia 1.6 or later is required (0.5 supported Julia 1.0).

Infrastructure

  • [compat] entries for julia (1.6) and every dependency. OrderedCollections (1.6, 2) is the only new dependency.
  • CI runs the latest Julia release on Linux, macOS and Windows, and the oldest supported version (1.6) on Linux, with julia-actions/cache and codecov.
  • Dependabot now updates Julia [compat] entries as well as GitHub Actions, monthly, instead of CompatHelper. TagBot is updated to the current template.
  • Tests are expanded to 243, including Aqua.jl.

Version

0.6.0. The changes above are breaking, and for 0.x versions Julia's resolver treats a minor bump as breaking, so packages with IniFile = "0.5" keep 0.5 until they opt in.

Closes #16
Closes #20
Supersedes #8 and #10, which have been open since 2014 and 2015; their authors are credited as co-authors of the commit that adds these features.

🤖 Generated with Claude Code

ViralBShah and others added 4 commits November 14, 2026 10:27
- Require Julia 1.10, the current LTS, and add a compat entry for Test.
- CI: test on lts, release and nightly (nightly may fail) on Linux, macOS
  and Windows, using setup-julia@v3, julia-actions/cache, buildpkg,
  runtest and codecov@v7. Also run on workflow_dispatch and cancel
  superseded pull request builds.
- Add CompatHelper, update TagBot to the current template, and group
  dependabot's GitHub Actions updates.
- Add a .gitignore for manifests and coverage output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parsing, following Python's configparser:
- Skip only blank lines and comments. Short entries such as `a=` were
  dropped before because every line shorter than 3 characters was skipped.
- Report malformed lines with @warn, giving the line number and file name,
  instead of println. With the new `strict=true` option they throw an
  ArgumentError instead.
- Use the first `=` or `:` on the line as the delimiter, replacing the
  min/max over two findfirst calls.
- Treat empty keys, `[]` and unterminated section headers as malformed.
  Accept a UTF-8 byte order mark and CRLF line endings.

Storage and output:
- Store sections and keys in OrderedDicts with String keys and String
  values, so insertion order is kept and lookups are type-stable. `set`
  stores `string(value)`.
- `write` keeps the order, writes `key = value`, puts a blank line
  between sections, and returns the number of bytes written.
- Two-argument `show` is now a one-line summary; the full contents are
  shown for MIME"text/plain" (the REPL).

API:
- `get(ini, section, key)` returns `nothing` instead of `:notfound` when
  the key is missing.
- `get_bool` accepts what configparser's getboolean accepts (1/yes/true/on,
  0/no/false/off, any case) and throws an ArgumentError naming the
  section, key and value otherwise, instead of reading typos as false.
- `get_bool`, `get_int` and `get_float` take an optional default for
  missing keys and otherwise throw a KeyError((section, key)) instead of a
  MethodError. Unparseable values throw an ArgumentError naming the
  section, key and value.
- Remove `get(ini, key, default)`: it was the same call as
  `get(ini, section, key)` whenever the default was a string (#16).
  Use `get(defaults(ini), key, default)`.
- Add the `lowercase_keys` option, like configparser's default
  optionxform. Keys stay case-sensitive by default.
- Add `read(Inifile, path_or_io; kwargs...)`.
- Add docstrings for every exported function and the Base methods.
- Drop `__precompile__()`, `== nothing` and the `import Base.x` lists.

Tests are rewritten and extended to cover each change, including
order-preserving round trips and the use case from #20, and run
Aqua.jl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Multi-line values work as in Python's configparser: a line indented more
than the line that started an entry continues its value. The lines are
joined with "\n", blank lines inside the value are kept, trailing blank
lines are dropped, and comment lines are skipped. `write` emits
multi-line values as tab-indented continuation lines, so they round-trip.

Inline comments are off by default, as in Python 3, and are enabled with
the new `inline_comment_prefixes` option, e.g. `(";", "#")`. A prefix
starts a comment only at the start of a line or after whitespace, so
`a = b;c` and URLs with `#` fragments keep their text.

This brings in the features proposed in #10 (multi-line values and
optional mid-line comments) and #8 (strip inline comments separated by
whitespace), adapted to configparser's syntax. #10 continued lines ending
in a backslash; configparser uses indentation instead, and a trailing
backslash would break Windows paths such as `C:\data\`. The test cases
from #10 are adapted in test/test.ini.

Uniformly indented entries are still separate entries, but an entry
indented more than the one before it is now part of that entry's value.

Co-authored-by: William Douglas <13699381+sleepywill@users.noreply.github.com>
Co-authored-by: Shoibal Chakravarty <1609634+shoibalc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document the API with examples, the options, the file format, the
differences from Python's configparser, and the breaking changes since
0.5.

0.6.0 rather than 0.5.2: the release changes return types and removes a
method, which is breaking, and for 0.x versions Julia's resolver treats a
minor bump as breaking, so dependents with `IniFile = "0.5"` compat will
not pick it up until they opt in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Nov 14, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

Removed LTS and nightly versions from CI matrix.
ViralBShah and others added 3 commits November 14, 2026 10:55
Dependabot now supports Julia, and CompatHelper's docs recommend
migrating to it. Add a julia entry to dependabot.yml so [compat] bumps
come from Dependabot, and remove the CompatHelper workflow. Dependabot's
pull requests trigger CI without a deploy key.

Run both the github-actions and julia updates monthly instead of weekly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 0.5, `print(io, ini)` and `string(ini)` wrote the INI text, so code
that saved a file with `print` would silently write a one-line summary
instead. Define `print` to write the contents, as before. Two-argument
`show` stays a compact summary, so an Inifile inside a container or in
`repr` does not dump the whole file.

Make the upgrade guide explicit about the two changes that break code
without an error: `=== :notfound` checks stop matching, and `get_bool`
now reads yes, on and 1 as true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lower the julia compat floor from 1.10 to 1.6. The test suite passes on
Julia 1.6.7 with OrderedCollections 1.7 and Aqua 0.8.

Add a `min` CI job, which runs the oldest version allowed by [compat],
on Linux only. macos-latest runs on Apple silicon, which has no Julia 1.6
build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ViralBShah
ViralBShah merged commit 592a9ea into master Nov 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Modified version which allows also setting specific variable inside a new or existing file Ambiguity in get function

1 participant