pseek 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pseek-1.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Arian
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pseek-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.2
2
+ Name: pseek
3
+ Version: 1.0.1
4
+ Summary: Pseek is a Python library to search files, folders, and text
5
+ Home-page: https://github.com/ArianN8610/pysearch
6
+ Author: Arian
7
+ Author-email: ariannasiri86@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: click==8.1.8
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: requires-dist
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # Pseek
26
+
27
+ ## Overview
28
+
29
+ A powerful command-line tool for searching files, directories, and content inside files efficiently. The tool supports searching by name, content, extensions, and more with advanced filtering options.
30
+
31
+ ## Features
32
+
33
+ * **Search in file & folder names**
34
+ * **Search inside file contents**
35
+ * **Highlight matches** in terminal output
36
+ * **Optimized for speed** with ThreadPoolExecutor
37
+ * **Cross-platform** (Linux, macOS, Windows)
38
+
39
+ ## Installation
40
+
41
+ ### **1️⃣ Install via `pip` (Recommended)**
42
+
43
+ ```sh
44
+ pip install pseek
45
+ ```
46
+
47
+ ### **2️⃣ Install from source**
48
+
49
+ ```sh
50
+ git clone https://github.com/ArianN8610/pysearch.git
51
+ cd pseek
52
+ pip install click==8.1.8
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ Run the command with a search query:
58
+ ```sh
59
+ pseek <query> [options]
60
+ ```
61
+
62
+ ## Examples
63
+
64
+ ### Search for a keyword in file & folder names
65
+
66
+ ```sh
67
+ pseek "my_keyword" --path /path/to/search --file --directory
68
+ ```
69
+
70
+ ### Search inside file contents
71
+
72
+ ```sh
73
+ pseek "error" --path /var/logs --content
74
+ ```
75
+
76
+ ### Search only in specific file types
77
+
78
+ ```sh
79
+ pseek "TODO" --path ./projects --ext py --ext txt
80
+ ```
81
+
82
+ ### Search by regex
83
+
84
+ ```sh
85
+ pseek "error\d+" --regex
86
+ ```
87
+
88
+ ## Command Options
89
+
90
+ | Option | Description |
91
+ |----------------------------|--------------------------------------------------------------|
92
+ | `--path` | Base directory to search in (default: current directory `.`) |
93
+ | `--file` | Search only in file names |
94
+ | `--directory` | Search only in directory names |
95
+ | `--content` | Search inside file contents |
96
+ | `--ext` | Filter by file extension (e.g., `.txt`, `.log`) |
97
+ | `--case-sensitive` | Make the search case-sensitive |
98
+ | `--regex` | Use regular expression for searching |
99
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
100
+ | `--word` | Match the whole word only |
101
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
102
+ | `--full-path` | Display full path of files and directories |
pseek-1.0.1/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Pseek
2
+
3
+ ## Overview
4
+
5
+ A powerful command-line tool for searching files, directories, and content inside files efficiently. The tool supports searching by name, content, extensions, and more with advanced filtering options.
6
+
7
+ ## Features
8
+
9
+ * **Search in file & folder names**
10
+ * **Search inside file contents**
11
+ * **Highlight matches** in terminal output
12
+ * **Optimized for speed** with ThreadPoolExecutor
13
+ * **Cross-platform** (Linux, macOS, Windows)
14
+
15
+ ## Installation
16
+
17
+ ### **1️⃣ Install via `pip` (Recommended)**
18
+
19
+ ```sh
20
+ pip install pseek
21
+ ```
22
+
23
+ ### **2️⃣ Install from source**
24
+
25
+ ```sh
26
+ git clone https://github.com/ArianN8610/pysearch.git
27
+ cd pseek
28
+ pip install click==8.1.8
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ Run the command with a search query:
34
+ ```sh
35
+ pseek <query> [options]
36
+ ```
37
+
38
+ ## Examples
39
+
40
+ ### Search for a keyword in file & folder names
41
+
42
+ ```sh
43
+ pseek "my_keyword" --path /path/to/search --file --directory
44
+ ```
45
+
46
+ ### Search inside file contents
47
+
48
+ ```sh
49
+ pseek "error" --path /var/logs --content
50
+ ```
51
+
52
+ ### Search only in specific file types
53
+
54
+ ```sh
55
+ pseek "TODO" --path ./projects --ext py --ext txt
56
+ ```
57
+
58
+ ### Search by regex
59
+
60
+ ```sh
61
+ pseek "error\d+" --regex
62
+ ```
63
+
64
+ ## Command Options
65
+
66
+ | Option | Description |
67
+ |----------------------------|--------------------------------------------------------------|
68
+ | `--path` | Base directory to search in (default: current directory `.`) |
69
+ | `--file` | Search only in file names |
70
+ | `--directory` | Search only in directory names |
71
+ | `--content` | Search inside file contents |
72
+ | `--ext` | Filter by file extension (e.g., `.txt`, `.log`) |
73
+ | `--case-sensitive` | Make the search case-sensitive |
74
+ | `--regex` | Use regular expression for searching |
75
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
76
+ | `--word` | Match the whole word only |
77
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
78
+ | `--full-path` | Display full path of files and directories |
File without changes
@@ -0,0 +1,65 @@
1
+ import re
2
+ import click
3
+
4
+ from .utils import display_results
5
+ from .searcher import search_in_names, search_in_file_contents
6
+
7
+
8
+ @click.command()
9
+ @click.argument('query')
10
+ @click.option('-p', '--path', type=click.Path(exists=True, file_okay=False, dir_okay=True), default='.',
11
+ show_default=True, help='Base directory to search in.')
12
+ @click.option('-f', '--file', is_flag=True, help='Search only in file names.')
13
+ @click.option('-d', '--directory', is_flag=True, help='Search only in directory names.')
14
+ @click.option('-c', '--content', is_flag=True, help='Search inside file contents.')
15
+ @click.option('-C', '--case-sensitive', is_flag=True, help='Make the search case-sensitive.')
16
+ @click.option('--ext', multiple=True, type=click.STRING,
17
+ help='Filter results by file extension. Example: --ext py --ext js')
18
+ @click.option('--regex', is_flag=True, help='Use regular expression for searching.')
19
+ @click.option('-i', '--include', type=click.Path(exists=True, file_okay=True, dir_okay=True),
20
+ multiple=True, help='Directories or files that contain search results.')
21
+ @click.option('-e', '--exclude', type=click.Path(exists=True, file_okay=True, dir_okay=True),
22
+ multiple=True, help='Directories or files not included in search results.')
23
+ @click.option('--word', is_flag=True, help='Search for results that match the whole word.')
24
+ @click.option('--max-size', type=click.FLOAT, help='Maximum size directory or file can have (MB)')
25
+ @click.option('--min-size', type=click.FLOAT, help='Minimum size directory or file can have (MB)')
26
+ @click.option('--full-path', is_flag=True, help='Display complete paths of files and directories.')
27
+ def search(query, path, file, directory, content, case_sensitive, ext, regex, include, exclude, word,
28
+ max_size, min_size, full_path):
29
+ """Search for files, directories, and content based on the query."""
30
+
31
+ if regex:
32
+ try:
33
+ re.compile(query) # Ensure valid regex
34
+ except re.error:
35
+ click.echo(click.style('Invalid regex pattern: ', fg='red') + query)
36
+ return
37
+
38
+ # Enable all search types if none are explicitly selected
39
+ if not any([file, directory, content]):
40
+ file = directory = content = True
41
+
42
+ results = []
43
+
44
+ if file:
45
+ filename_results = search_in_names(path, query, case_sensitive, regex, include, exclude, word, max_size,
46
+ min_size, full_path, ext)
47
+ display_results(filename_results, 'Files', 'file')
48
+ results.extend(filename_results)
49
+ if directory and not ext:
50
+ dir_results = search_in_names(path, query, case_sensitive, regex, include, exclude, word, max_size, min_size,
51
+ full_path, is_file=False)
52
+ display_results(dir_results, 'Directories', 'directory')
53
+ results.extend(dir_results)
54
+ if content:
55
+ content_results = search_in_file_contents(path, query, case_sensitive, ext, regex, include, exclude, word,
56
+ max_size, min_size, full_path)
57
+ display_results(content_results, 'Contents', 'content')
58
+ results.extend(content_results)
59
+
60
+ message = f'\nTotal results: {len(results)}' if results else 'No results found'
61
+ click.echo(click.style(message, fg='red')) # Display total of results
62
+
63
+
64
+ if __name__ == "__main__":
65
+ search()
@@ -0,0 +1,106 @@
1
+ import re
2
+ import click
3
+ from pathlib import Path
4
+ from concurrent.futures import ThreadPoolExecutor
5
+
6
+ from .utils import highlight_matches, safe_is_file
7
+
8
+
9
+ def search_in_names(base_path, query, case_sensitive, regex, include, exclude, whole_word, max_size, min_size,
10
+ full_path, ext=tuple(), is_file=True):
11
+ """Search for file and folder names"""
12
+ base_path = Path(base_path)
13
+ matches = []
14
+
15
+ if not regex:
16
+ query = re.escape(query) # If regex is disabled, convert query to plain text
17
+
18
+ if whole_word:
19
+ query = rf'\b{query}\b' # Ensure search for whole words
20
+
21
+ flags = 0 if case_sensitive else re.IGNORECASE # Adjust case sensitivity
22
+
23
+ # Convert include and exclude paths to `Path`
24
+ include_paths = {Path(p).resolve() for p in include}
25
+ exclude_paths = {Path(p).resolve() for p in exclude}
26
+
27
+ for p in base_path.rglob('*'):
28
+ p_resolved = p.resolve() # Actual file or folder path
29
+ p_size_mb = p_resolved.stat().st_size / 1_048_576 # Convert to MB
30
+
31
+ if (
32
+ # If include is given, at least one of them must be present in the name
33
+ (include and not any(p_resolved.is_relative_to(inc) for inc in include_paths))
34
+ # If exclude is given, none should be in the name
35
+ or (exclude and any(p_resolved.is_relative_to(exc) for exc in exclude_paths))
36
+ # Prevent unnecessary continuation in case of type mismatch
37
+ or (is_file and ext and p.suffix[1:] not in ext)
38
+ # Check file or directory size
39
+ or (max_size and p_size_mb > max_size)
40
+ or (min_size and p_size_mb < min_size)
41
+ ):
42
+ continue
43
+
44
+ if re.search(query, p.name, flags) and ((is_file and p.is_file()) or (not is_file and p.is_dir())):
45
+ # Specify the found part
46
+ highlighted_name = re.sub(query, lambda m: click.style(m.group(), fg='green'), p.name, flags=flags)
47
+ p_parent = p_resolved.parent if full_path else p.parent # Get full path if full_path is True
48
+ matches.append(f'{p_parent}\\{highlighted_name}')
49
+
50
+ return matches
51
+
52
+
53
+ def search_in_file_contents(base_path, query, case_sensitive, ext, regex, include, exclude, whole_word,
54
+ max_size, min_size, full_path):
55
+ """Search inside file contents"""
56
+ base_path = Path(base_path)
57
+ matches = []
58
+
59
+ if not regex:
60
+ query = re.escape(query)
61
+
62
+ if whole_word:
63
+ query = rf'\b{query}\b'
64
+
65
+ flags = 0 if case_sensitive else re.IGNORECASE
66
+
67
+ include_paths = {Path(p).resolve() for p in include}
68
+ exclude_paths = {Path(p).resolve() for p in exclude}
69
+
70
+ def process_file(file_path):
71
+ """Processes a single file and searches for the query inside its content."""
72
+ p_resolved = file_path.resolve()
73
+ p_size_mb = p_resolved.stat().st_size / 1_048_576
74
+
75
+ if (
76
+ # Filter by include/exclude directories
77
+ (include and not any(p_resolved.is_relative_to(inc) for inc in include_paths))
78
+ or (exclude and any(p_resolved.is_relative_to(exc) for exc in exclude_paths))
79
+ # Filter by extension
80
+ or (ext and p_resolved.suffix[1:] not in ext)
81
+ # Filter by file size
82
+ or (max_size and p_size_mb > max_size)
83
+ or (min_size and p_size_mb < min_size)
84
+ ):
85
+ return
86
+
87
+ try:
88
+ for num, line in enumerate(p_resolved.read_text(encoding='utf-8', errors='ignore').splitlines(), 1):
89
+ line = line.strip()
90
+
91
+ if re.search(query, line, flags):
92
+ highlighted_snippet, count_query = highlight_matches(line, query, case_sensitive, regex, whole_word)
93
+ file_path = p_resolved if full_path else file_path
94
+ matches.append(
95
+ click.style(file_path, fg='cyan')
96
+ + click.style(f' (Line {num}) (Repeated {count_query} time(s)): ', fg='magenta')
97
+ + highlighted_snippet
98
+ )
99
+ except Exception:
100
+ return # Ignore unreadable files
101
+
102
+ # Use multi-threading for faster processing
103
+ with ThreadPoolExecutor() as executor:
104
+ executor.map(process_file, (file for file in base_path.rglob('*') if safe_is_file(file)))
105
+
106
+ return matches
@@ -0,0 +1,66 @@
1
+ import re
2
+ import click
3
+
4
+
5
+ def highlight_matches(line, query, case_sensitive, regex, whole_word, context=20):
6
+ """Highlight matches and truncate long lines with proper handling"""
7
+
8
+ flags = 0 if case_sensitive else re.IGNORECASE
9
+
10
+ if not regex and not whole_word:
11
+ query = re.escape(query) # If regex is disabled, use escape to search for regular text
12
+
13
+ # Find all matches
14
+ matches = list(re.finditer(query, line, flags))
15
+
16
+ if not matches:
17
+ return None # If there is no match, return None
18
+
19
+ start_positions = [max(0, m.start() - context) for m in matches] # 20 characters before
20
+ end_positions = [min(len(line), m.end() + context) for m in matches] # 20 characters after
21
+
22
+ # Combine sections close together to avoid repetition
23
+ merged_snippets = []
24
+ current_start, current_end = start_positions[0], end_positions[0]
25
+
26
+ for i in range(1, len(matches)):
27
+ if start_positions[i] <= current_end: # If the next section is close, combine them
28
+ current_end = max(current_end, end_positions[i])
29
+ else:
30
+ merged_snippets.append((current_start, current_end))
31
+ current_start, current_end = start_positions[i], end_positions[i]
32
+
33
+ merged_snippets.append((current_start, current_end)) # Add the last section
34
+
35
+ result = []
36
+ for start, end in merged_snippets:
37
+ snippet = line[start:end]
38
+ snippet = re.sub(query, lambda m: click.style(m.group(), fg='green'), snippet, flags=flags)
39
+ result.append(snippet)
40
+
41
+ final_output = ' ... '.join(result) # If there are multiple results, put `...` between them
42
+
43
+ # Add `...` at the beginning and end if needed
44
+ if merged_snippets[0][0] > 0:
45
+ final_output = '...' + final_output
46
+ if merged_snippets[-1][1] < len(line):
47
+ final_output += '...'
48
+
49
+ return final_output, len(matches) # Count the number of repetitions
50
+
51
+
52
+ def safe_is_file(file):
53
+ """Check if a file is accessible and return True/False."""
54
+ try:
55
+ return file.is_file()
56
+ except OSError:
57
+ return False # Ignore files that cannot be accessed
58
+
59
+
60
+ def display_results(results, title, result_name):
61
+ """Display search results"""
62
+ if results:
63
+ click.echo(click.style(f'\n{title}:\n', fg='yellow'))
64
+ for result in results:
65
+ click.echo(result)
66
+ click.echo(click.style(f'\n{len(results)} result(s) found for {result_name}', fg='blue'))
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.2
2
+ Name: pseek
3
+ Version: 1.0.1
4
+ Summary: Pseek is a Python library to search files, folders, and text
5
+ Home-page: https://github.com/ArianN8610/pysearch
6
+ Author: Arian
7
+ Author-email: ariannasiri86@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: click==8.1.8
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: requires-dist
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # Pseek
26
+
27
+ ## Overview
28
+
29
+ A powerful command-line tool for searching files, directories, and content inside files efficiently. The tool supports searching by name, content, extensions, and more with advanced filtering options.
30
+
31
+ ## Features
32
+
33
+ * **Search in file & folder names**
34
+ * **Search inside file contents**
35
+ * **Highlight matches** in terminal output
36
+ * **Optimized for speed** with ThreadPoolExecutor
37
+ * **Cross-platform** (Linux, macOS, Windows)
38
+
39
+ ## Installation
40
+
41
+ ### **1️⃣ Install via `pip` (Recommended)**
42
+
43
+ ```sh
44
+ pip install pseek
45
+ ```
46
+
47
+ ### **2️⃣ Install from source**
48
+
49
+ ```sh
50
+ git clone https://github.com/ArianN8610/pysearch.git
51
+ cd pseek
52
+ pip install click==8.1.8
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ Run the command with a search query:
58
+ ```sh
59
+ pseek <query> [options]
60
+ ```
61
+
62
+ ## Examples
63
+
64
+ ### Search for a keyword in file & folder names
65
+
66
+ ```sh
67
+ pseek "my_keyword" --path /path/to/search --file --directory
68
+ ```
69
+
70
+ ### Search inside file contents
71
+
72
+ ```sh
73
+ pseek "error" --path /var/logs --content
74
+ ```
75
+
76
+ ### Search only in specific file types
77
+
78
+ ```sh
79
+ pseek "TODO" --path ./projects --ext py --ext txt
80
+ ```
81
+
82
+ ### Search by regex
83
+
84
+ ```sh
85
+ pseek "error\d+" --regex
86
+ ```
87
+
88
+ ## Command Options
89
+
90
+ | Option | Description |
91
+ |----------------------------|--------------------------------------------------------------|
92
+ | `--path` | Base directory to search in (default: current directory `.`) |
93
+ | `--file` | Search only in file names |
94
+ | `--directory` | Search only in directory names |
95
+ | `--content` | Search inside file contents |
96
+ | `--ext` | Filter by file extension (e.g., `.txt`, `.log`) |
97
+ | `--case-sensitive` | Make the search case-sensitive |
98
+ | `--regex` | Use regular expression for searching |
99
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
100
+ | `--word` | Match the whole word only |
101
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
102
+ | `--full-path` | Display full path of files and directories |
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ pseek/__init__.py
5
+ pseek/cli.py
6
+ pseek/searcher.py
7
+ pseek/utils.py
8
+ pseek.egg-info/PKG-INFO
9
+ pseek.egg-info/SOURCES.txt
10
+ pseek.egg-info/dependency_links.txt
11
+ pseek.egg-info/entry_points.txt
12
+ pseek.egg-info/requires.txt
13
+ pseek.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pseek = pseek.cli:search
@@ -0,0 +1 @@
1
+ click==8.1.8
@@ -0,0 +1 @@
1
+ pseek
pseek-1.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
pseek-1.0.1/setup.py ADDED
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="pseek",
8
+ version="1.0.1",
9
+ author="Arian",
10
+ author_email="ariannasiri86@gmail.com",
11
+ description="Pseek is a Python library to search files, folders, and text",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/ArianN8610/pysearch",
15
+ packages=find_packages(),
16
+ classifiers=[
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ ],
21
+ python_requires=">=3.6",
22
+ install_requires=["click==8.1.8"],
23
+ entry_points={"console_scripts": ["pseek=pseek.cli:search"]}
24
+ )