Skip to content

feat: add --descendant and --ancestor flags to verdi node list - #7300

Open
Khushi281300 wants to merge 2 commits into
aiidateam:mainfrom
Khushi281300:feat/node-list-relationships
Open

feat: add --descendant and --ancestor flags to verdi node list#7300
Khushi281300 wants to merge 2 commits into
aiidateam:mainfrom
Khushi281300:feat/node-list-relationships

Conversation

@Khushi281300

Copy link
Copy Markdown

PR Description

Overview

This PR introduces the --descendant and --ancestor flags to verdi node list, addressing issue #6782. These options allow users to explore the provenance graph directly from the CLI by filtering nodes based on their relationships.

What’s Included

  • New CLI Options
    Added DESCENDANT and ANCESTOR options in src/aiida/cmdline/params/options/main.py using NodeParamType.

  • Enhanced Query Logic
    Refactored the node_list command in src/aiida/cmdline/commands/cmd_node.py to use a custom QueryBuilder.
    This enables:

    • with_ancestors
    • with_descendants
      with explicit tagging (tag='node') to ensure correct filtering.
  • Backward Compatibility
    All existing features (sorting, projections, limits, and entry-point filters) remain unchanged.

Testing & Verification

1. Unit Tests
Added tests/cmdline/commands/test_node_list_relationship.py with test cases for:

  • Ancestor filtering
  • Descendant filtering
  • Combined usage

Result:
3 passed

Screenshot 2026-03-23 185102

2. Manual Verification
Tested on a local SQLite profile using a simple provenance chain:

Data (PK 3) → CalcJobNode (PK 4)
  • verdi node list --ancestor 3 → returns node 4
  • verdi node list --descendant 4 → returns node 3
Screenshot 2026-03-23 190248

3. Regression Check
Executed tests/cmdline/commands/test_node.py to ensure no regressions.

  • Observed some pre-existing failures on Windows (related to graphviz and prompt formatting), which are unrelated to this PR.

@GeigerJ2 GeigerJ2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the contribution @Khushi281300, and sorry for the slow reply. I added a few comments. Please address them and then re-request review.

Please also make sure pre-commit passes locally. E.g., I just used the command below to run pre-commit on all your changes of this PR. You can find more info in our AGENTS.md and Claude skills. Thanks!

❯ uv run pre-commit run --from-ref $(git merge-base HEAD main) --to-ref HEAD
check for merge conflicts................................................Passed
check for added large files..............................................Passed
check yaml...........................................(no files to check)Skipped
fix double quoted strings................................................Passed
fix end of files.........................................................Passed
fix python encoding pragma (deprecated)..................................Passed
mixed line ending........................................................Passed
trim trailing whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing tests/cmdline/commands/test_node_list_relationship.py

Validate GitHub Workflows............................(no files to check)Skipped
ruff-format..............................................................Failed
- hook id: ruff-format
- files were modified by this hook

1 file reformatted, 2 files left unchanged

ruff.....................................................................Failed
- hook id: ruff
- exit code: 1
- files were modified by this hook

Fixed 2 errors:
- src/aiida/cmdline/params/options/main.py:
    1 × RUF022 (unsorted-dunder-all)
- tests/cmdline/commands/test_node_list_relationship.py:
    1 × I001 (unsorted-imports)

Found 2 errors (2 fixed, 0 remaining).

Pretty format TOML...................................(no files to check)Skipped
Pretty format YAML...................................(no files to check)Skipped
uv-lock..............................................(no files to check)Skipped
nbstripout...........................................(no files to check)Skipped
imports..................................................................Failed
- hook id: imports
- files were modified by this hook
mypy.....................................................................Passed
Update conda environment file........................(no files to check)Skipped
Validate environment.yml.............................(no files to check)Skipped
Automatically generating verdi docs......................................Passed

Comment on lines +927 to +929
ANCESTOR = OverridableOption(
'-a', '--ancestor', type=types.NodeParamType(), help='Filter for nodes that are an ancestor of this node.'
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The -a short flag is already used for --all, see here, and you're now reusing it for --ancestor. As these are both OverridableOptions that can be composed onto any command, the moment someone decorates a command with both @options.ALL() and @options.ANCESTOR(), click will blow up with a duplicate option error. Even if node_list doesn't currently use ALL, this is a landmine. I recommend dropping the short flag alltogether (see also my other comment).

Comment on lines +922 to +929
DESCENDANT = OverridableOption(
'-d', '--descendant', type=types.NodeParamType(), help='Filter for nodes that are a descendant of this node.'
)


ANCESTOR = OverridableOption(
'-a', '--ancestor', type=types.NodeParamType(), help='Filter for nodes that are an ancestor of this node.'
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

--descendant node_c actually returns node_c's ancestors (the query uses with_ancestors='node'). The current names read as "show me descendants of X", but the semantics are "show me nodes whose descendant is X". Renaming to --has-descendant / --has-ancestor makes it unambiguous: verdi node list --has-ancestor 42 clearly means "list nodes that have 42 as an ancestor". In addition, I'd fully drop the short flags.

For a demonstration, see this:
image

When using verdi node list --descendant 8, I actually get the ancestors of node 8, not the descendants (and vice versa). The current behavior is quite confusing.

Comment on lines +115 to +116
'DESCENDANT',
'ANCESTOR',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also here, would become HAS_DESCENDANT and HAS_ANCESTOR. Also, please ensure the list is alphabetically sorted.

@@ -0,0 +1,73 @@
###########################################################################

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • Please don't create a new test file or fixture. tests/cmdline/commands/test_node.py already has TestNodeList for verdi node list tests. The generate_calculation_node fixture in tests/conftest.py already creates a linked provenance graph (Data → Calc → Data) via inputs/outputs kwargs. Use that instead of adding a new provenance_graph fixture. Delete tests/cmdline/commands/test_node_list_relationship.py and add a separate method on TestNodeList that uses generate_calculation_node. Don't try to merge into the existing parametrized test_node_list since that uses unlinked nodes with index-based expectations, which doesn't fit the relationship queries.

Comment on lines +21 to +23
node_a = orm.Data().store()
node_a.label = 'node_a'
node_a.store()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

.store() called twice on the node here

def node_list(entry_point, subclassing, project, past_days, order_by, order_dir, limit, raw):
@options.DESCENDANT()
@options.ANCESTOR()
def node_list(entry_point, subclassing, project, past_days, order_by, order_dir, limit, raw, descendant, ancestor):

@GeigerJ2 GeigerJ2 Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also rename here to has_descendant and has_ancestor in this file.

@GeigerJ2

Copy link
Copy Markdown
Collaborator

And, please also merge main into your branch, so we keep the code and PR up to date :) Thanks!

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Node relationship filtering

Layer / File(s) Summary
CLI options and relationship query
src/aiida/cmdline/params/options/main.py, src/aiida/cmdline/commands/cmd_node.py
Adds reusable --ancestor and --descendant options and applies them through a QueryBuilder-based verdi node list query.
Provenance relationship validation
tests/cmdline/commands/test_node_list_relationship.py
Creates a linked three-node graph and tests ancestor, descendant, and combined filters.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as verdi node list
  participant node_list
  participant QueryBuilder
  participant Node as Node provenance graph
  CLI->>node_list: Pass relationship options
  node_list->>QueryBuilder: Build filtered node query
  QueryBuilder->>Node: Apply provenance constraints
  Node-->>QueryBuilder: Return matching nodes
  QueryBuilder-->>CLI: Return projected raw output
Loading

Suggested reviewers: danielhollas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: adding ancestor/descendant flags to verdi node list.
Description check ✅ Passed The description matches the implemented CLI options, query refactor, and relationship tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/cmdline/commands/test_node_list_relationship.py (1)

16-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the required Sphinx-style docstrings.

Document fixture inputs and return values with :param: / :return: directives, keeping any types in annotations rather than the docstrings. As per coding guidelines, “Use Sphinx-style docstrings (:param:, :return:, :raises:), with types written in annotations rather than docstrings.”

Also applies to: 38-39, 51-52, 64-65

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cmdline/commands/test_node_list_relationship.py` around lines 16 - 20,
Update the docstrings for the fixtures/functions around provenance_graph and the
additionally affected sections to use Sphinx directives for every input and
return value, adding :param: and :return: descriptions as applicable. Keep
parameter and return types only in the Python annotations, not in the
docstrings, and preserve the existing behavioral descriptions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/cmdline/commands/test_node_list_relationship.py`:
- Line 40: Update the tuple unpacking of provenance_graph in each affected test
to assign unasserted node values to `_`, while retaining named variables for
nodes used in assertions. Apply this consistently at the unpacking statements
around lines 40, 53, and 66.

---

Nitpick comments:
In `@tests/cmdline/commands/test_node_list_relationship.py`:
- Around line 16-20: Update the docstrings for the fixtures/functions around
provenance_graph and the additionally affected sections to use Sphinx directives
for every input and return value, adding :param: and :return: descriptions as
applicable. Keep parameter and return types only in the Python annotations, not
in the docstrings, and preserve the existing behavioral descriptions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a6f0252-dbf0-416a-a66e-603b0596e3f6

📥 Commits

Reviewing files that changed from the base of the PR and between 85f5f86 and 5855a51.

📒 Files selected for processing (3)
  • src/aiida/cmdline/commands/cmd_node.py
  • src/aiida/cmdline/params/options/main.py
  • tests/cmdline/commands/test_node_list_relationship.py


def test_node_list_ancestor(run_cli_command, provenance_graph):
"""Test the `--ancestor` filter."""
node_a, node_b, node_c = provenance_graph

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the unused fixture values reported by Ruff.

Use _ for values not asserted by each test.

Proposed fix
-    node_a, node_b, node_c = provenance_graph
+    node_a, _, _ = provenance_graph
...
-    node_a, node_b, node_c = provenance_graph
+    _, _, node_c = provenance_graph
...
-    node_a, node_b, node_c = provenance_graph
+    node_a, _, node_c = provenance_graph

Also applies to: 53-53, 66-66

🧰 Tools
🪛 Ruff (0.15.21)

[warning] 40-40: Unpacked variable node_b is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


[warning] 40-40: Unpacked variable node_c is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cmdline/commands/test_node_list_relationship.py` at line 40, Update the
tuple unpacking of provenance_graph in each affected test to assign unasserted
node values to `_`, while retaining named variables for nodes used in
assertions. Apply this consistently at the unpacking statements around lines 40,
53, and 66.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants