A Python wrapper for ripgrep
Project description
rpygrep: A Pythonic ripgrep Wrapper
rpygrep is a strongly-typed Python library for interacting with the ripgrep command-line utility. It allows you to programmatically construct ripgrep commands for finding files and searching content. It provides syncronous and asyncronous interfaces and generator for results.
Used in the Filesystem Operations MCP Server.
Features
- Fluent API: Build
ripgrepcommands using method chaining. - Type Safety: Uses Pydantic models to ensure type safety and validation of the
ripgrepoutput. - File Finding & Content Searching: Dedicated classes (
RipGrepFindandRipGrepSearch) for differentripgrepmodes. RipGrepFind returns PathlibPathobjects, RipGrepSearch returnsRipGrepSearchResultobjects. - Synchronous & Asynchronous Execution: Supports both
run()andarun()methods for flexible integration. - Structured Output: Automatically parses
ripgrep's JSON output into rich Python data classes, making results easy to access and manipulate. - Comprehensive Options: Control over a wide range of
ripgrepoptions, including case sensitivity, glob patterns, file types, context lines, maximum depth, and more.
Example
ripgrep = (
self._ripgrep_find.include_types(included_types or [])
.exclude_types(excluded_types or [])
.include_globs(included_globs or [])
.exclude_globs(excluded_globs or [])
.max_depth(max_depth)
)
for path in ripgrep.run():
print(path)
ripgrep = (
self._ripgrep_search.include_types(included_types or [])
.add_safe_defaults()
.exclude_types(excluded_types or [])
.include_globs(included_globs or [])
.exclude_globs(excluded_globs or [])
.before_context(before_context)
.after_context(after_context)
.add_patterns(patterns)
.max_depth(max_depth)
.max_count(matches_per_file)
.case_sensitive(case_sensitive)
)
async for result in ripgrep.arun():
print(result)
Prerequisites
rpygrep requires the ripgrep command-line tool to be installed on your system. You can find installation instructions for ripgrep here.
Installation
uv add rpygrep
Usage Examples
The following examples demonstrate common use cases for rpygrep. For these examples to work, assume you have a directory structure similar to the one used in the tests:
.
├── code_with_hello_world.py
├── data.json
├── should_be_ignored.env
├── test_with_Hello_World.txt
├── .hidden
├── .git/
└── subdir/
├── nested.txt
├── script_with_hello.sh
└── should_be_ignored.env
Basic File Finding (RipGrepFind)
Find all Python files in the current directory and its subdirectories:
from pathlib import Path
from rpygrep import RipGrepFind
# Assuming your script is run from the root of the example directory structure
current_dir = Path(".")
rg_find = RipGrepFind(working_directory=current_dir)
rg_find.include_types(["py"])
print("Python files found:")
for path in rg_find.run():
print(path)
# Expected output:
# code_with_hello_world.py
Basic Content Search (RipGrepSearch)
Search for "hello" (case-insensitive) in all files and print matching lines:
from pathlib import Path
from rpygrep import RipGrepSearch
current_dir = Path(".")
rg_search = RipGrepSearch(working_directory=current_dir)
rg_search.add_pattern("hello")
rg_search.case_sensitive(False) # Make it case-insensitive
print("\nSearch results for 'hello' (case-insensitive):")
for result in rg_search.run():
print(f"Found in {result.path}:")
for match in result.matches:
# .text might be None if lines.bytes is used, so check for it
line_content = match.data.lines.text.strip() if match.data.lines.text else "Binary content"
print(f" Line {match.data.line_number}: {line_content}")
# Expected output (similar to):
# Found in test_with_Hello_World.txt:
# Line 1: Hello, World!
# Found in code_with_hello_world.py:
# Line 2: print('hello, world!')
# Found in subdir/script_with_hello.sh:
# Line 2: echo 'Hello'
Asynchronous Usage
Both RipGrepFind and RipGrepSearch support asynchronous execution using their arun() methods.
import asyncio
from pathlib import Path
from rpygrep import RipGrepSearch
async def main():
current_dir = Path(".")
rg_search = RipGrepSearch(working_directory=current_dir)
rg_search.add_pattern("World")
print("\nAsynchronous search results for 'World':")
async for result in rg_search.arun():
print(f"Async found in {result.path}:")
for match in result.matches:
line_content = match.data.lines.text.strip() if match.data.lines.text else "Binary content"
print(f" Line {match.data.line_number}: {line_content}")
# To run this:
# asyncio.run(main())
Excluding File Types
Exclude common binary and data file types from your search:
from pathlib import Path
from rpygrep import RipGrepFind, DEFAULT_EXCLUDED_TYPES
current_dir = Path(".")
rg_find = RipGrepFind(working_directory=current_dir)
rg_find.exclude_types(DEFAULT_EXCLUDED_TYPES) # Exclude common binary/data types
print("\nFiles found excluding default binary/data types:")
for path in rg_find.run():
print(path)
Limiting Search Depth
Search only the current directory, without recursing into subdirectories:
from pathlib import Path
from rpygrep import RipGrepFind
current_dir = Path(".")
rg_find = RipGrepFind(working_directory=current_dir)
rg_find.max_depth(1) # Only search current directory, no subdirectories
print("\nFiles found with max depth 1:")
for path in rg_find.run():
print(path)
# Expected output (may vary based on actual files at depth 1):
# code_with_hello_world.py
# data.json
# should_be_ignored.env
# test_with_Hello_World.txt
# .hidden
Adding Context Lines
Include lines before and after a match:
from pathlib import Path
from rpygrep import RipGrepSearch
current_dir = Path(".")
rg_search = RipGrepSearch(working_directory=current_dir)
rg_search.add_pattern("print")
rg_search.before_context(1) # 1 line before
rg_search.after_context(1) # 1 line after
print("\nSearch results for 'print' with context:")
for result in rg_search.run():
print(f"Found in {result.path}:")
# Context lines are also in result.context
for context_line in result.context:
line_content = context_line.data.lines.text.strip() if context_line.data.lines.text else "Binary content"
print(f" Context Line {context_line.data.line_number}: {line_content}")
for match in result.matches:
line_content = match.data.lines.text.strip() if match.data.lines.text else "Binary content"
print(f" Match Line {match.data.line_number}: {line_content}")
API Reference (Key Classes)
rpygrep.RipGrepFind
Used for finding files based on various criteria.
include_glob(glob: str): Includes files matching the given glob pattern.exclude_glob(glob: str): Excludes files matching the given glob pattern.include_type(ripgrep_type: RIPGREP_TYPE_LIST): Includes files of a specificripgrepfile type (e.g., "py", "ts", "md").exclude_type(ripgrep_type: RIPGREP_TYPE_LIST): Excludes files of a specificripgrepfile type.max_depth(depth: int): Limits the search to a specified number of subdirectory levels.sort(by: Literal["none", "path", "name", "size", "accessed", "created", "modified"], ascending: bool = True): Sorts the results.run() -> Iterator[Path]: Executes the command synchronously and yieldsPathobjects.arun() -> AsyncIterator[Path]: Executes the command asynchronously and yieldsPathobjects.
rpygrep.RipGrepSearch
Used for searching content within files.
add_pattern(pattern: str): Adds a regular expression pattern to search for. Can be called multiple times for multiple patterns.case_sensitive(case_sensitive: bool): Sets whether the search should be case-sensitive (True) or case-insensitive (False).before_context(context: int): Specifies the number of lines of context to include before a match.after_context(context: int): Specifies the number of lines of context to include after a match.max_count(count: int): Sets the maximum number of matches to return per file.max_file_size(size: int): Sets the maximum file size (in bytes) to search.as_json(): Configuresripgrepto output results in JSON format (automatically called byrun/arun).run() -> Iterator[RipGrepSearchResult]: Executes the command synchronously and yieldsRipGrepSearchResultobjects.arun() -> AsyncIterator[RipGrepSearchResult]: Executes the command asynchronously and yieldsRipGrepSearchResultobjects.
rpygrep.RipGrepSearchResult
A dataclass representing a single search result for a file.
path: Path: The path to the file where matches were found.begin: RipGrepBegin: Information about the beginning of the file's search results.matches: list[RipGrepMatch]: A list ofRipGrepMatchobjects, each representing a found match.context: list[RipGrepContext]: A list ofRipGrepContextobjects, representing context lines around matches.end: RipGrepEnd: Information about the end of the file's search results, including statistics.
Other Important Types
RIPGREP_TYPE_LIST: ALiteraltype listing all supportedripgrepfile types.DEFAULT_EXCLUDED_TYPES: A list ofRIPGREP_TYPE_LISTvalues that are commonly excluded (e.g., binary files, large data files).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rpygrep-0.1.1.tar.gz.
File metadata
- Download URL: rpygrep-0.1.1.tar.gz
- Upload date:
- Size: 34.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb21a978df7736e2a5b1e0b76f5ea694f0923e637f55a386e0ad7e66f5ea8ec1
|
|
| MD5 |
e0669bfdee1de4c54aea99d9f7137212
|
|
| BLAKE2b-256 |
6007cc11ef47cbd42c0b6bed35ce9fb39fdd424cc83144cbc3ed304898b0199e
|
File details
Details for the file rpygrep-0.1.1-py3-none-any.whl.
File metadata
- Download URL: rpygrep-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c547b37f37a46e63c45804b047c9249d41118bc58e6f202872e2a03a5fa35ea9
|
|
| MD5 |
4e5d445ec52dd2463872d37c15a2c646
|
|
| BLAKE2b-256 |
d6555c3f3256d9db9ac43ab69b50925fbf280afad514cf42055c74031998be09
|