GDAL Configuration System (gdal_config)
Incorporate a global, stateful GDAL configuration system into the existing options (gdal_opts) and internal package environment systems.
Motivation
The package's value layer (gdal_config_opts(), gdal_vsi_opts(), driver-typed builders) can describe configuration but nothing applies it: workflows still end in raw gdalraster::set_config_option() walls (see dev/work/pipelines/), with no memory of prior state, no restore, and no way to ask "what is in effect right now and who set it."
Design
A three-tier model, keeping GDAL itself as the single source of truth (nothing is ever mirrored):
| Tier |
Object |
Role |
| Value |
gdal_config |
An inert, composable bundle: one gdal_config_opts payload (global channel) + zero or more path-bound gdal_vsi_opts (VSI channel). Full S3 conventions: new_gdal_config(), gdal_config(...) constructor, as_gdal_config() coercions, c() merge (later wins - the composition primitive future presets build on), cli format()/print(), render methods. |
| State |
active gdal_config in .pkg_env |
What this session has applied, retrievable via gdal_config_active() for reuse/rendering anywhere (CLI args, optfile, child processes). Restore metadata (prior value, whether it came from an envvar, scrubbed envvars) is internal bookkeeping, not part of the value. |
| View |
gdal_config_sitrep() |
Live provenance report of effective configuration (see #12). |
Verbs
gdal_config_set(...) - applies to GDAL (set_config_option() / vsi_set_path_option()), records priors on first touch, merges into the active config. Accepts KEY = value pairs, gdal_config values, gdal_config_opts/gdal_vsi_opts objects, named lists, "KEY=VALUE" strings. Rejects open/creation opts (wrong channel) with a classed error.
gdal_config_get(keys) - effective values, read live from GDAL, never from package state.
gdal_config_unset(keys, mode) - explicit semantics for the envvar fallback (see below).
gdal_config_reset(keys) - restores touched keys to their true pre-package state (including re-setting scrubbed envvars).
local_gdal_config() / with_gdal_config() - scoped application with exact restoration (GDAL values and package state) even on error; the recommended pattern around pipeline executions.
The precedence model (why unset needs modes)
GDAL resolves a key: thread-local -> global in-memory (CPLSetConfigOption; includes config-file values loaded at init) -> environment variable (live getenv()) -> built-in default. Consequences:
- Set is authoritative - a set value beats an envvar while set.
- Unset is not - clearing the in-memory entry reveals the envvar underneath; the built-in default cannot be forced while an envvar exists.
Hence gdal_config_unset(mode = ):
"reveal" (default): clear; warn (classed) when an envvar re-surfaces.
"mask": pin the documented default from driver/runtime metadata - "GDAL default behavior" despite an envvar.
"scrub": also Sys.unsetenv() (recorded, so reset restores it) - the only true removal; explicit opt-in.
Known options (no unbacked hardcoded lists)
Advisory (never blocking) typo checking draws on, in decreasing authority:
- Runtime: VSI/network/credential options enumerated from the running GDAL build (
vsi_get_fs_prefixes() x vsi_get_fs_options()), lazily cached.
- Curated driver metadata: the existing per-driver config option table.
- Curated core: a small
GDAL_CORE_CONFIG_OPTS list (aaa.R) for core CPL/GDAL/OGR names only, which GDAL does not expose at runtime (gdalraster does not bind CPLGetKnownConfigOptions(), GDAL >= 3.11 - upstream request candidate).
- Envvars present at load (app-defined options are known to the user's setup by definition).
Keys GDAL already resolves to a value are never flagged.
VSI: one key namespace, two binding tiers
gdal_vsi_opts are config opts; the vsi_path attribute binds them at the path-specific tier (VSIGetPathSpecificOption: longest-prefix match, then fall through to the normal chain). Unbound VSI opts are plain config opts. Consequence for rendering: path-bound options cannot be expressed as CLI --config flags - as_gdal_args() omits them with a message by default, with an explicit flatten_vsi = TRUE escape hatch (warns when multiple paths carry conflicting values). The gdalrc [credentials] section is the only lossless out-of-process serialization (see #13).
Load-time behavior
gdal_config_init() (in rlang::on_load()): empty active config + restore store, snapshot of GDAL-relevant envvars, config-file discovery, an at_load baseline sitrep - then a tiny, curated, fill-only default set (currently just GDAL_HTTP_USERAGENT from pkg_user_agent()), applied through gdal_config_set() (ledgered, visible, reversible) only when the key resolves to nothing, opt-out via options(gdalvector.config_defaults = FALSE).
Related
GDAL Configuration System (
gdal_config)Incorporate a global, stateful GDAL configuration system into the existing options (
gdal_opts) and internal package environment systems.Motivation
The package's value layer (
gdal_config_opts(),gdal_vsi_opts(), driver-typed builders) can describe configuration but nothing applies it: workflows still end in rawgdalraster::set_config_option()walls (seedev/work/pipelines/), with no memory of prior state, no restore, and no way to ask "what is in effect right now and who set it."Design
A three-tier model, keeping GDAL itself as the single source of truth (nothing is ever mirrored):
gdal_configgdal_config_optspayload (global channel) + zero or more path-boundgdal_vsi_opts(VSI channel). Full S3 conventions:new_gdal_config(),gdal_config(...)constructor,as_gdal_config()coercions,c()merge (later wins - the composition primitive future presets build on), cliformat()/print(), render methods.gdal_configin.pkg_envgdal_config_active()for reuse/rendering anywhere (CLI args, optfile, child processes). Restore metadata (prior value, whether it came from an envvar, scrubbed envvars) is internal bookkeeping, not part of the value.gdal_config_sitrep()Verbs
gdal_config_set(...)- applies to GDAL (set_config_option()/vsi_set_path_option()), records priors on first touch, merges into the active config. AcceptsKEY = valuepairs,gdal_configvalues,gdal_config_opts/gdal_vsi_optsobjects, named lists,"KEY=VALUE"strings. Rejects open/creation opts (wrong channel) with a classed error.gdal_config_get(keys)- effective values, read live from GDAL, never from package state.gdal_config_unset(keys, mode)- explicit semantics for the envvar fallback (see below).gdal_config_reset(keys)- restores touched keys to their true pre-package state (including re-setting scrubbed envvars).local_gdal_config()/with_gdal_config()- scoped application with exact restoration (GDAL values and package state) even on error; the recommended pattern around pipeline executions.The precedence model (why unset needs modes)
GDAL resolves a key: thread-local -> global in-memory (
CPLSetConfigOption; includes config-file values loaded at init) -> environment variable (livegetenv()) -> built-in default. Consequences:Hence
gdal_config_unset(mode = ):"reveal"(default): clear; warn (classed) when an envvar re-surfaces."mask": pin the documented default from driver/runtime metadata - "GDAL default behavior" despite an envvar."scrub": alsoSys.unsetenv()(recorded, soresetrestores it) - the only true removal; explicit opt-in.Known options (no unbacked hardcoded lists)
Advisory (never blocking) typo checking draws on, in decreasing authority:
vsi_get_fs_prefixes()xvsi_get_fs_options()), lazily cached.GDAL_CORE_CONFIG_OPTSlist (aaa.R) for core CPL/GDAL/OGR names only, which GDAL does not expose at runtime (gdalraster does not bindCPLGetKnownConfigOptions(), GDAL >= 3.11 - upstream request candidate).Keys GDAL already resolves to a value are never flagged.
VSI: one key namespace, two binding tiers
gdal_vsi_optsare config opts; thevsi_pathattribute binds them at the path-specific tier (VSIGetPathSpecificOption: longest-prefix match, then fall through to the normal chain). Unbound VSI opts are plain config opts. Consequence for rendering: path-bound options cannot be expressed as CLI--configflags -as_gdal_args()omits them with a message by default, with an explicitflatten_vsi = TRUEescape hatch (warns when multiple paths carry conflicting values). The gdalrc[credentials]section is the only lossless out-of-process serialization (see #13).Load-time behavior
gdal_config_init()(inrlang::on_load()): empty active config + restore store, snapshot of GDAL-relevant envvars, config-file discovery, anat_loadbaseline sitrep - then a tiny, curated, fill-only default set (currently justGDAL_HTTP_USERAGENTfrompkg_user_agent()), applied throughgdal_config_set()(ledgered, visible, reversible) only when the key resolves to nothing, opt-out viaoptions(gdalvector.config_defaults = FALSE).Related
gdal_config_sitrep()), [Feature]: GDAL Configuration File (gdalrc) I/O #13 (gdalrc I/O), [Feature]: Configuration Resolution Layer - Defaults, Presets, Serialization (Phase 2) #14 (Phase 2 resolution layer), [Feature]: Polishgdal_sitrep()#10 (gdal_sitrep()polish)