Skip to content

fix: avoid mutable default arguments in custom filter plugins - #70

Merged
LukoJy3D merged 4 commits into
mainfrom
hex/daily-improvement-2026-09-08
Sep 10, 2026
Merged

LukoJy3D merged 4 commits into
mainfrom
hex/daily-improvement-2026-09-08

Conversation

@hex-botas

@hex-botas hex-botas Bot commented Sep 8, 2026

Copy link
Copy Markdown

Summary

Removes the mutable default arguments from filter_plugins/custom.py — a classic Python anti-pattern (flake8-bugbear / ruff B006). Six filter functions declared values=[], users={}, exclude_users=[] or user_roles={}, meaning a single list/dict object is shared by every call for the lifetime of the process. Nothing mutates them today, so this is a latent bug rather than an active one, but it is exactly the kind of footgun that bites the first time someone adds an in-place append/update.

Category: Simplification / consistency (low-risk, self-contained).

Changes

filter_plugins/custom.py:

  • modify_list, append_to_list, array_to_str, extract_role_users, remove_reserved, filter_reserved now default their collection parameters to None and normalise with x = x or [] / x or {} inside the function. Behaviour is unchanged for every existing call.
  • filter_reserved(users_role=...)filter_reserved(user_roles=...), so it matches its sibling remove_reserved(user_roles=...). Safe: all call sites pipe the value positionally ({{ user_list_response.json | filter_reserved }} in tasks/xpack/security/elasticsearch-security-native.yml, {{ es_users.file | extract_role_users() }} in elasticsearch-security-file.yml, {{ es_data_dirs | array_to_str }} in templates/elasticsearch.yml.j2) — no keyword usage anywhere in the role.
  • Dropped a redundant list() wrapper around .items() in extract_role_users (a Python 2 leftover; the dict is only read, never mutated during iteration) and tidied the modify_list docstring.

Tests

tests/test_custom_filters.py grows from 5 to 13 tests. The new None normalisation adds branches, so coverage is maintained and extended rather than diluted:

  • New tests for filters that had no coverage at all: modify_list (incl. ignorecase), append_to_list (list and comma-separated-string input), array_to_str (default and custom separator), extract_role_users (role/user pairing and exclude_users), filename.
  • test_filters_tolerate_omitted_arguments calls every filter with no arguments, pinning the new default-handling paths.
$ python3 -m pytest tests -q
13 passed

Risk

Low. No behavioural change for any existing call path, no signature change visible to Jinja (all usages are positional pipes), no Ansible task or template touched.


Generated by DEX CODE
Triggered by agent:weekly-code-improvements

Replace the shared `[]` / `{}` default arguments in filter_plugins/custom.py
with `None` and normalise inside each function, and rename filter_reserved's
parameter to `user_roles` to match remove_reserved. Adds unit tests for the
previously untested filters and for the omitted-argument defaults.
@hex-botas
hex-botas Bot requested a review from a team as a code owner September 8, 2026 05:03
Co-authored-by: Cursor <cursoragent@cursor.com>
@LukoJy3D LukoJy3D changed the title Daily code improvement (2026-09-08) fix: avoid mutable default arguments in custom filter plugins Sep 10, 2026

@LukoJy3D LukoJy3D left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

The filter change is the right fix and the new tests are the valuable part of this PR. Existing Jinja call sites stay positional (filter_reserved, extract_role_users, array_to_str, filename), so the users_roleuser_roles rename is safe inside this role.

CI is green (Molecule + CodeQL). Pytest is still local-only — same as #69 — so the new tests are not enforced on PRs.

I renamed this PR off the generic Daily code improvement (2026-09-08) title and added AGENTS.md so Cursor / Claude / DEX keep titles tied to the actual diff.

What looks good

  • Replacing shared [] / {} defaults is a real (latent) B006 footgun.
  • Tests now cover filters that previously had none (modify_list, append_to_list, array_to_str, extract_role_users, filename) plus omitted-argument defaults.
  • No task/template changes; blast radius is the Python filters only.

Nits (non-blocking)

  1. x = x or [] is not the same as a missing argument. Empty string, empty list, and None are all falsy. append_to_list("") used to split to [""] and return [suffix]; it now becomes []. Prefer if values is None: values = [] so only the default is rewritten. Same for the other filters.
  2. filter_reserved keyword rename would break an external caller using users_role=. None in this repo; worth a one-liner in the description that Jinja keyword use is unsupported.
  3. Pytest is not in .github/workflows. python3 -m pytest tests -q should be a CI job if we want these 13 tests to stay green.

Low risk to merge as-is for current call paths. The is None normalisation is the only behaviour I’d still tighten.

Comment thread filter_plugins/custom.py Outdated
Comment thread tests/test_custom_filters.py
LukoJy3D and others added 2 commits September 10, 2026 09:45
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@LukoJy3D
LukoJy3D merged commit d791caa into main Sep 10, 2026
6 checks passed
@LukoJy3D
LukoJy3D deleted the hex/daily-improvement-2026-09-08 branch September 10, 2026 07:07
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.

1 participant