Skip to content

feat(file_browser): add filter param for regex and callable filtering#9667

Merged
kirangadhave merged 4 commits into
marimo-team:mainfrom
kratos0718:feat/file-browser-filter-param
Jun 1, 2026
Merged

feat(file_browser): add filter param for regex and callable filtering#9667
kirangadhave merged 4 commits into
marimo-team:mainfrom
kratos0718:feat/file-browser-filter-param

Conversation

@kratos0718
Copy link
Copy Markdown
Contributor

Closes #8399

📝 Summary

Adds a filter keyword argument to mo.ui.file_browser() so users can filter files using regex patterns or custom functions — not just file extensions.

Three ways to use it:

  • Regex string: filter=r"^train_"
  • Compiled pattern: filter=re.compile(r"\.csv$", re.IGNORECASE)
  • Callable: filter=lambda p: p.stat().st_size > 1_000_000

Directories are always shown for navigation. Works together with filetypes and ignore_empty_dirs.

Merge Checklist

  • I have read the contributor guidelines
  • Documentation has been updated (docstring updated)
  • Tests have been added (8 new tests, 79 total pass)
  • AI generated code reviewed line-by-line by the PR author

@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 1, 2026 4:35pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@kratos0718
Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

Closes marimo-team#8399

Signed-off-by: Abhinav Tarigoppula <abhinav@Abhinavs-MacBook-Air.local>
@kratos0718 kratos0718 force-pushed the feat/file-browser-filter-param branch from 0c837e0 to 4888d06 Compare May 22, 2026 16:01
@kratos0718
Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@kratos0718 kratos0718 marked this pull request as ready for review May 26, 2026 15:38
@kratos0718
Copy link
Copy Markdown
Contributor Author

Hi, just following up on this PR.
All checks are passing and CLA is signed.
Happy to make any requested changes!

@kratos0718
Copy link
Copy Markdown
Contributor Author

Hi! Just checking in — all automated checks are passing and CLA is signed. Happy to make any requested changes or incorporate feedback. Thanks for your time!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the mo.ui.file_browser() UI element with a new filter keyword argument to support additional file filtering using either a regex (string or compiled re.Pattern) or a user-provided predicate callable, while keeping directories visible for navigation.

Changes:

  • Added a filter parameter to file_browser with support for regex-string, compiled-pattern, and callable filtering.
  • Applied the filter consistently in both direct directory listings and recursive directory scanning used by ignore_empty_dirs.
  • Added a focused test suite covering regex, compiled patterns, callables, directory visibility, AND-semantics with filetypes, and ignore_empty_dirs interactions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
marimo/_plugins/ui/_impl/file_browser.py Adds filter parameter normalization and applies it during listing and recursive empty-dir checks.
tests/_plugins/ui/_impl/test_file_browser.py Adds tests validating the new filter behavior across supported input types and interactions.

Comment on lines +172 to +176
additional filter applied to files (directories are always shown
for navigation). Accepts a regex string or compiled pattern
matched against the filename, or a callable that receives the
file's `Path` and returns `True` to include it. Applied together
with `filetypes` (both must match). Defaults to None.
@kirangadhave
Copy link
Copy Markdown
Member

@cubic-dev-ai

@kirangadhave kirangadhave added the enhancement New feature or request label Jun 1, 2026
@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Jun 1, 2026

@cubic-dev-ai

@kirangadhave I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 2 files

Architecture diagram
sequenceDiagram
    participant User as User
    participant UI as File Browser UI
    participant Filter as Filter Logic
    participant FileSystem as File System

    Note over User,FileSystem: Filter Applied in _list_directory and _has_files_recursive

    User->>UI: mo.ui.file_browser(filter=r"^train_" | compiled | callable)
    UI->>Filter: Store filter as self._filter
    alt filter is str
        Filter->>Filter: Compile to re.Pattern
    else filter is re.Pattern
        Filter->>Filter: Use as-is
    else filter is callable
        Filter->>Filter: Use as-is
    else filter is None
        Filter->>Filter: Set to None (no filtering)
    end

    Note over UI,FileSystem: Listing a directory
    UI->>UI: _list_directory(path, ...)
    loop For each file in directory
        alt File is a directory
            UI->>FileSystem: Check directory contents
            alt ignore_empty_dirs enabled
                UI->>UI: _has_files_recursive(directory)
                UI->>FileSystem: Check files recursively
                Note over UI,FileSystem: NEW: filter applied in recursive check
                alt Filter is re.Pattern
                    UI->>Filter: filter.search(filename)
                else Filter is callable
                    UI->>Filter: filter(file_path)
                end
                alt No matching files
                    UI->>UI: Skip directory
                else Matching files found
                    UI->>UI: Include directory
                end
            end
            UI-->>UI: Always include directory for navigation
        else File is not a directory
            alt filetypes is set
                UI->>FileSystem: Check file extension
                alt Extension not in filetypes
                    UI->>UI: Skip file
                end
            end
            alt filter is not None
                alt Filter is re.Pattern
                    UI->>Filter: filter.search(filename)
                else Filter is callable
                    UI->>Filter: filter(file_path)
                end
                alt No match
                    UI->>UI: Skip file
                end
            end
        end
    end
    UI-->>User: Return filtered file list
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread marimo/_plugins/ui/_impl/file_browser.py
Comment thread marimo/_plugins/ui/_impl/file_browser.py
Extract filter evaluation into a single _passes_filter helper used by both
_list_directory and _has_files_recursive, removing the duplicated regex/callable
branching that could drift between the two. The helper also wraps a callable
filter in try/except so one file that makes the callable raise no longer crashes
the entire directory listing — it is treated as a non-match and logged. Adds a
regression test for the exception-isolation behavior.
@kratos0718
Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed both findings:

  • P1 (exception isolation): filter evaluation now runs through a single _passes_filter helper that wraps a callable filter in try/except. If the callable raises on a file, that file is treated as a non-match and logged, instead of crashing the whole directory listing.
  • P2 (duplicated logic): the regex/callable branching previously duplicated in _list_directory and _has_files_recursive now lives only in _passes_filter, so the two paths can't drift.

Added a regression test (test_filter_callable_exception_is_isolated) covering the exception-isolation case.

Copy link
Copy Markdown
Member

@kirangadhave kirangadhave left a comment

Choose a reason for hiding this comment

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

Hi @kratos0718, the PR looks great. Please address the comments and we should be good to merge.

Overall note:
Please add logging with LOGGER.debug(...) to except blocks, e.g.

try:
	return bool(self._filter(file))
except OSError as e:
	LOGGER.debug(f"file_browser filter could not evaluate {file}: {e}")

we should add logging in other places as well, but for now please add it to the new code you're adding in this PR.

Comment thread marimo/_plugins/ui/_impl/file_browser.py Outdated
Comment thread marimo/_plugins/ui/_impl/file_browser.py Outdated
Comment thread marimo/_plugins/ui/_impl/file_browser.py Outdated
Comment thread marimo/_plugins/ui/_impl/file_browser.py Outdated
@kirangadhave kirangadhave self-requested a review June 1, 2026 16:33
…ng note

Per review feedback on _passes_filter:
- Catch OSError specifically (e.g. broken symlink) and treat as no-match so the
  rest of the directory still lists; let any other exception propagate instead
  of silently swallowing it.
- Log skipped files with LOGGER.debug including the file and error.
- Document that the regex filter uses search() (matches within the name) and
  that ^/$ should be used to anchor.
- Add tests for both the OSError-isolated and non-OSError-propagates paths.
@kratos0718
Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review @kirangadhave! Addressed everything:

  • Duplicated filter logic — extracted into a single _passes_filter helper, now used by both _list_directory and _has_files_recursive.
  • Exception handling — the callable filter is wrapped in try/except OSError. An OSError (e.g. a broken symlink) is treated as a non-match so the remaining files still list; any other exception propagates up as you suggested.
  • Logging — skipped files are logged with LOGGER.debug(f"file_browser filter could not evaluate {file}: {e}").
  • Docstring — noted that the regex filter uses search (matches within the filename) and that ^/$ should be used to anchor.
  • Tests — added test_filter_callable_oserror_is_isolated (OSError isolated) and test_filter_callable_non_oserror_propagates (other errors propagate).

Ready for another look — thanks!

Copy link
Copy Markdown
Member

@kirangadhave kirangadhave left a comment

Choose a reason for hiding this comment

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

🚀 Thanks for addressing the comments. Ready to merge.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Bundle Report

Changes will increase total bundle size by 20.15kB (0.08%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
marimo-esm 25.28MB 20.15kB (0.08%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: marimo-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/cells-*.js 69 bytes 710.0kB 0.01%
assets/JsonOutput-*.js -2.22kB 554.42kB -0.4%
assets/index-*.js -105 bytes 431.27kB -0.02%
assets/index-*.css 751 bytes 367.66kB 0.2%
assets/dist-*.js -62 bytes 102 bytes -37.8%
assets/dist-*.js -79 bytes 104 bytes -43.17%
assets/dist-*.js 60 bytes 164 bytes 57.69% ⚠️
assets/dist-*.js 35 bytes 137 bytes 34.31% ⚠️
assets/dist-*.js 73 bytes 177 bytes 70.19% ⚠️
assets/dist-*.js -227 bytes 160 bytes -58.66%
assets/dist-*.js 283 bytes 387 bytes 272.12% ⚠️
assets/dist-*.js 7 bytes 176 bytes 4.14%
assets/dist-*.js 99 bytes 259 bytes 61.88% ⚠️
assets/dist-*.js 32 bytes 169 bytes 23.36% ⚠️
assets/dist-*.js -72 bytes 104 bytes -40.91%
assets/dist-*.js -152 bytes 183 bytes -45.37%
assets/dist-*.js 46 bytes 183 bytes 33.58% ⚠️
assets/dist-*.js 76 bytes 335 bytes 29.34% ⚠️
assets/dist-*.js -266 bytes 137 bytes -66.0%
assets/dist-*.js -14 bytes 169 bytes -7.65%
assets/dist-*.js 234 bytes 403 bytes 138.46% ⚠️
assets/dist-*.js -73 bytes 104 bytes -41.24%
assets/edit-*.js -35 bytes 325.13kB -0.01%
assets/ai-*.js 17.56kB 274.83kB 6.82% ⚠️
assets/reveal-*.js -33 bytes 249.55kB -0.01%
assets/add-*.js 268 bytes 202.26kB 0.13%
assets/layout-*.js -35 bytes 198.7kB -0.02%
assets/cell-*.js 36 bytes 184.18kB 0.02%
assets/file-*.js -36 bytes 47.0kB -0.08%
assets/input-*.js 126 bytes 60.6kB 0.21%
assets/panels-*.js 5 bytes 47.9kB 0.01%
assets/dates-*.js 338 bytes 38.67kB 0.88%
assets/chat-*.js 595 bytes 15.26kB 4.06%
assets/useNotebookActions-*.js -35 bytes 29.95kB -0.12%
assets/utils-*.js 88 bytes 6.07kB 1.47%
assets/MarimoErrorOutput-*.js 2.9kB 26.64kB 12.22% ⚠️
assets/react-*.browser.esm-CV8-hvjx.js (New) 25.64kB 25.64kB 100.0% 🚀
assets/session-*.js 1 bytes 25.04kB 0.0%
assets/vega-*.browser-C8wT63Va.js (New) 24.8kB 24.8kB 100.0% 🚀
assets/command-*.js 3 bytes 18.34kB 0.02%
assets/download-*.js 374 bytes 9.85kB 3.95%
assets/useCellActionButton-*.js -35 bytes 9.45kB -0.37%
assets/react-*.esm-BNzu6e7h.js (New) 8.37kB 8.37kB 100.0% 🚀
assets/ttcn-*.js 7 bytes 64 bytes 12.28% ⚠️
assets/ttcn-*.js -7 bytes 57 bytes -10.94%
assets/readonly-*.js 1 bytes 4.68kB 0.02%
assets/emotion-*.esm-C59xfSYt.js (New) 4.37kB 4.37kB 100.0% 🚀
assets/mermaid-*.core-CMygPhv_.js (New) 2.38kB 2.38kB 100.0% 🚀
assets/__vite-*.js 5 bytes 98 bytes 5.38% ⚠️
assets/__vite-*.js -5 bytes 93 bytes -5.1%
assets/react-*.browser.esm-Ce2ksurd.js (Deleted) -25.64kB 0 bytes -100.0% 🗑️
assets/vega-*.browser-xq8miGHn.js (Deleted) -24.8kB 0 bytes -100.0% 🗑️
assets/react-*.esm--O4lBTlZ.js (Deleted) -8.37kB 0 bytes -100.0% 🗑️
assets/emotion-*.esm-Dangy3Bv.js (Deleted) -4.37kB 0 bytes -100.0% 🗑️
assets/mermaid-*.core-867JpRcd.js (Deleted) -2.38kB 0 bytes -100.0% 🗑️
assets/eye-*.js (Deleted) -430 bytes 0 bytes -100.0% 🗑️

@kirangadhave kirangadhave merged commit 981bba8 into marimo-team:main Jun 1, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

file browser should have more flexible filtering

3 participants