Skip to content

Config.get() returns a bare string instead of a single-item list for single-value comma-separated config, breaking extensions handling in FlightPath Data and elsewhere #210

Description

@dk107dk

Root cause of a bug David has observed in FlightPath Data's extension config panel (a single configured extension displays as its individual characters, e.g. csvpath -> c, s, v, p, a, t, h).

In csvpath/util/config.py, Config._get() only splits a config value into a list when it contains a comma:

s = self._config[section][name]
if no_list is False and s and isinstance(s, str) and s.find(",") > -1:
    ret = [s.strip() for s in s.split(",")]
elif isinstance(s, str):
    ret = s.strip()
else:
    ret = s

So [extensions] csvpaths = csvpath (a single value, no comma) makes config.get(section="extensions", name="csvpaths") return the bare string "csvpath", not ["csvpath"]. Any caller that assumes the result is always a list and iterates it directly (for ext in extensions: ...) silently walks the individual characters instead when there is only one value configured.

Config already has the correct defensive pattern elsewhere -- csvpath_errors_policy and csvpaths_errors_policy properties both do:

p = self._get(...)
if not isinstance(p, list):
    return [p]

but this normalization is not applied at the call sites that read the [extensions] section, or generally anywhere callers use config.get()/._get() directly for a comma-splittable value.

Confirmed 4 call sites inside csvpath itself with the same unguarded pattern, all reading [extensions] csvpath_files / csv_files via config.get() and assuming the result is list-like without checking:

  • csvpath/managers/paths/paths_manager.py:207-210 -- "if ext not in csvpathexts:" -- with a single-value config this becomes a substring check instead of list membership, silently wrong rather than crashing (e.g. ext="c" would incorrectly match "csvpath").
  • csvpath/managers/files/file_manager.py:550 -- same "in" substring-check issue.
  • csvpath/cli/drill_down.py:31 and :84 -- feed the raw value into _get_path(), which does extensions.append(...) -- would raise AttributeError outright on a single-value config since a str has no .append().
  • csvpath/cli/drill_down.py:217-227 (_filter_extensions) -- same "in" substring-check issue as the manager call sites.

FlightPath Data's extension panel almost certainly has its own equivalent code (not in this repo) doing the same unguarded iteration over the config.get() result, which is what produces the individual-character symptom David is seeing there.

Options for a fix, to be decided later (this issue is being filed to capture the root cause and confirmed call sites, not to fix immediately):

  1. Normalize at the source in Config._get()/get() so any comma-splittable value always returns a list, even with a single entry -- fixes all present and future callers, but is a broader behavior change to Config's core get() semantics that needs checking against every existing caller expecting a bare string today.
  2. Add a dedicated list-safe accessor (e.g. get_list()) and migrate the extensions call sites (and any other comma-list-style config reads) to use it, leaving get()'s existing string/list duck-typing behavior alone for backward compatibility.
  3. Patch just the 4 confirmed call sites the same way csvpath_errors_policy already does it (if not isinstance(p, list): p = [p]), narrower and lower risk but leaves the underlying footgun in Config.get() for the next caller who does not know to guard against it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions