pseek 2.1.4__tar.gz → 2.2.0__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-2.2.0/PKG-INFO ADDED
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: pseek
3
+ Version: 2.2.0
4
+ Summary: Pseek is a Python library to search files, folders, and text
5
+ Author-email: Arian <ariannasiri86@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ArianN8610/pysearch
8
+ Requires-Python: >=3.6
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: click==8.1.8
12
+ Requires-Dist: lark==1.2.2
13
+ Dynamic: license-file
14
+
15
+ # Pseek
16
+
17
+ ## Overview
18
+
19
+ 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.
20
+
21
+ ## Features
22
+
23
+ * **Search in file & folder names**
24
+ * **Search inside file contents**
25
+ * **Highlight matches** in terminal output
26
+ * **Optimized for speed** with ThreadPoolExecutor
27
+ * **Cross-platform** (Linux, macOS, Windows)
28
+
29
+ ## Installation
30
+
31
+ ### **1️⃣ Install via `pip` (Recommended)**
32
+
33
+ ```sh
34
+ pip install pseek
35
+ ```
36
+
37
+ ### **2️⃣ Install from source**
38
+
39
+ ```sh
40
+ git clone https://github.com/ArianN8610/pysearch.git
41
+ cd pseek
42
+ pip install click==8.1.8
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ Run the command with a search query:
48
+ ```sh
49
+ pseek <query> [options]
50
+ ```
51
+
52
+ ## Examples
53
+
54
+ ### Search for a keyword in file & folder names
55
+
56
+ ```sh
57
+ pseek "my_keyword" --path /path/to/search --file --directory
58
+ ```
59
+
60
+ ### Search inside file contents
61
+
62
+ ```sh
63
+ pseek "error" --path /var/logs --content
64
+ ```
65
+
66
+ ### Search only in specific file types
67
+
68
+ ```sh
69
+ pseek "TODO" --path ./projects --ext py --ext txt
70
+ ```
71
+
72
+ ### Search by regex
73
+
74
+ ```sh
75
+ pseek "error\d+" --regex
76
+ ```
77
+
78
+ ## Command Options
79
+
80
+ | Option | Description |
81
+ |--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
82
+ | `--path` | Base directory to search in (default: current directory `.`) |
83
+ | `--file` | Search only in file names |
84
+ | `--directory` | Search only in directory names |
85
+ | `--content` | Search inside file contents |
86
+ | `--ext`, `--exclude-ext` | Filter by file extension (e.g., `.txt`, `.log`) |
87
+ | `--case-sensitive` | Make the search case-sensitive (except when --expr is enabled, in which case you can make it case sensitive by putting c before term: c"foo") |
88
+ | `--regex` | Use regular expressions to search (except when --expr is enabled, in which case you can make it regex by putting r before term: r"foo") |
89
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
90
+ | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
91
+ | `--word` | Match the whole word only (except when --expr is enabled, in which case you can make it match whole word by putting w before term: w"foo") |
92
+ | `--expr` | Enable to write conditions in the query. Example: r"foo.*bar" and ("bar" or "baz") and not "qux" (To use regex, word, and case-sensitive features, you can use the prefixes r, w, and c before terms. Allowed modes: r, w, c, wc, cw, rc, cr. Examples: r"foo.*bar", wc"Foo", cr".*Foo", ...) |
93
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
94
+ | `--full-path` | Display full path of files and directories |
95
+ | `--no-content` | Only display files path for content search |
pseek-2.2.0/README.md ADDED
@@ -0,0 +1,81 @@
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`, `--exclude-ext` | Filter by file extension (e.g., `.txt`, `.log`) |
73
+ | `--case-sensitive` | Make the search case-sensitive (except when --expr is enabled, in which case you can make it case sensitive by putting c before term: c"foo") |
74
+ | `--regex` | Use regular expressions to search (except when --expr is enabled, in which case you can make it regex by putting r before term: r"foo") |
75
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
76
+ | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
77
+ | `--word` | Match the whole word only (except when --expr is enabled, in which case you can make it match whole word by putting w before term: w"foo") |
78
+ | `--expr` | Enable to write conditions in the query. Example: r"foo.*bar" and ("bar" or "baz") and not "qux" (To use regex, word, and case-sensitive features, you can use the prefixes r, w, and c before terms. Allowed modes: r, w, c, wc, cw, rc, cr. Examples: r"foo.*bar", wc"Foo", cr".*Foo", ...) |
79
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
80
+ | `--full-path` | Display full path of files and directories |
81
+ | `--no-content` | Only display files path for content search |
@@ -11,9 +11,23 @@ from .searcher import Search
11
11
  @click.option('-d', '--directory', is_flag=True, help='Search only in directory names.')
12
12
  @click.option('-c', '--content', is_flag=True, help='Search inside file contents.')
13
13
  # Additional options
14
- @click.option('-C', '--case-sensitive', is_flag=True, help='Make the search case-sensitive.')
15
- @click.option('--regex', is_flag=True, help='Use regular expression for searching.')
16
- @click.option('-w', '--word', is_flag=True, help='Match whole words only.')
14
+ @click.option('-C', '--case-sensitive', is_flag=True,
15
+ help='Make the search case-sensitive '
16
+ '(except when --expr is enabled, '
17
+ 'in which case you can make it case sensitive by putting c before term: c"foo")')
18
+ @click.option('-r', '--regex', is_flag=True,
19
+ help='Use regular expressions to search '
20
+ '(except when --expr is enabled, '
21
+ 'in which case you can make it regex by putting r before term: r"foo")')
22
+ @click.option('-w', '--word', is_flag=True,
23
+ help='Match whole words only '
24
+ '(except when --expr is enabled, '
25
+ 'in which case you can make it match whole word by putting w before term: w"foo")')
26
+ @click.option('--expr', is_flag=True,
27
+ help='Enable to write conditions in the query. Example: r"foo.*bar" and ("bar" or "baz") and not "qux" '
28
+ '(To use regex, word, and case-sensitive features, '
29
+ 'you can use the prefixes r, w, and c before terms. Allowed modes: r, w, c, wc, cw, rc, cr. '
30
+ 'Examples: r"foo.*bar", wc"Foo", cr".*Foo", ...)')
17
31
  # Extension filters
18
32
  @click.option('--ext', multiple=True, type=click.STRING,
19
33
  help='Include files with these extensions. Example: --ext py --ext js')
@@ -35,7 +49,7 @@ from .searcher import Search
35
49
  @click.option('--full-path', is_flag=True, help='Display full paths for results.')
36
50
  @click.option('--no-content', is_flag=True, help='Only display files path for content search.')
37
51
  def search(query, path, file, directory, content, case_sensitive, ext, exclude_ext, regex, include, exclude,
38
- re_include, re_exclude, word, max_size, min_size, full_path, no_content):
52
+ re_include, re_exclude, word, expr, max_size, min_size, full_path, no_content):
39
53
  """Search for files, directories, and file content based on the query."""
40
54
  # If no search type is specified, search in all types.
41
55
  if not any((file, directory, content)):
@@ -54,6 +68,7 @@ def search(query, path, file, directory, content, case_sensitive, ext, exclude_e
54
68
  re_include=re_include,
55
69
  re_exclude=re_exclude,
56
70
  whole_word=word,
71
+ expr=expr,
57
72
  max_size=max_size,
58
73
  min_size=min_size,
59
74
  full_path=full_path,
@@ -0,0 +1,187 @@
1
+ import re
2
+ import sys
3
+ import click
4
+ from lark import Lark, Transformer
5
+ from .utils import compile_regex
6
+
7
+
8
+ class ExprNode:
9
+ """Base class for expression tree nodes"""
10
+ def evaluate(self, text: str) -> bool:
11
+ raise NotImplementedError
12
+
13
+
14
+ class TermNode(ExprNode):
15
+ """Node representing a single search term"""
16
+ def __init__(self, term: str, regex, whole_word, case_sensitive):
17
+ self.raw_term = term
18
+ self.whole_word = whole_word
19
+ self.case_sensitive = case_sensitive
20
+
21
+ flags = 0 if case_sensitive else re.IGNORECASE # Adjust case sensitivity
22
+
23
+ # Build the regex pattern
24
+ if not regex:
25
+ term = re.escape(term) # Escape if not regex
26
+ # Apply whole-word matching only if not using regex (or if desired behavior is defined)
27
+ if whole_word:
28
+ term = r'\b' + term + r'\b'
29
+
30
+ self.pattern = compile_regex(term, flags) # Precompile the regex pattern for performance
31
+
32
+ def evaluate(self, text: str) -> bool:
33
+ return bool(self.pattern.search(text))
34
+
35
+ def count_matches(self, text: str) -> int:
36
+ """Count how many times the pattern appears in the text"""
37
+ return len(list(self.pattern.finditer(text)))
38
+
39
+ def get_binary_pattern(self) -> re.Pattern:
40
+ """Return a compiled binary regex pattern"""
41
+ return re.compile(self.pattern.pattern.encode("utf-8"), self.pattern.flags)
42
+
43
+
44
+ class NotNode(ExprNode):
45
+ """Node representing logical NOT"""
46
+ def __init__(self, child: ExprNode):
47
+ self.child = child
48
+
49
+ def evaluate(self, text: str) -> bool:
50
+ return not self.child.evaluate(text)
51
+
52
+
53
+ class AndNode(ExprNode):
54
+ """Node representing logical AND"""
55
+ def __init__(self, left: ExprNode, right: ExprNode):
56
+ self.left = left
57
+ self.right = right
58
+
59
+ def evaluate(self, text: str) -> bool:
60
+ return self.left.evaluate(text) and self.right.evaluate(text)
61
+
62
+
63
+ class OrNode(ExprNode):
64
+ """Node representing logical OR"""
65
+ def __init__(self, left: ExprNode, right: ExprNode):
66
+ self.left = left
67
+ self.right = right
68
+
69
+ def evaluate(self, text: str) -> bool:
70
+ return self.left.evaluate(text) or self.right.evaluate(text)
71
+
72
+
73
+ # Lark grammar for parsing logical expressions
74
+ query_grammar = r"""
75
+ ?start: expr
76
+
77
+ ?expr: or_expr
78
+
79
+ ?or_expr: and_expr
80
+ | or_expr "or" and_expr -> or_expr
81
+
82
+ ?and_expr: not_expr
83
+ | and_expr "and" not_expr -> and_expr
84
+
85
+ ?not_expr: "not" not_expr -> not_expr
86
+ | term
87
+
88
+ ?term: PREFIXED_STRING -> prefixed_string
89
+ | ESCAPED_STRING -> string
90
+ | "(" expr ")"
91
+
92
+ PREFIXED_STRING: /(r|c|w|rc|cr|cw|wc)"([^"\\]|\\.)*"/
93
+
94
+ %import common.ESCAPED_STRING
95
+ %import common.WS
96
+ %ignore WS
97
+ """
98
+
99
+
100
+ class TreeToExpr(Transformer):
101
+ """Transform parsed tree into expression tree (ExprNode subclasses)"""
102
+ def __init__(self):
103
+ super().__init__()
104
+
105
+ def string(self, s):
106
+ """ Match normal quoted string: "foo" """
107
+ term = s[0][1:-1] # Remove surrounding quotes (e.g., "foo" -> foo)
108
+ return TermNode(
109
+ term,
110
+ False,
111
+ False,
112
+ False
113
+ )
114
+
115
+ def prefixed_string(self, s):
116
+ text = str(s[0]) # e.g., 'rc"pattern"'
117
+ prefix = text.split('"', 1)[0].lower()
118
+ content = text.split('"', 1)[1][:-1]
119
+
120
+ return TermNode(
121
+ content,
122
+ regex='r' in prefix,
123
+ whole_word='w' in prefix,
124
+ case_sensitive='c' in prefix,
125
+ )
126
+
127
+ def and_expr(self, args):
128
+ return AndNode(args[0], args[1])
129
+
130
+ def or_expr(self, args):
131
+ return OrNode(args[0], args[1])
132
+
133
+ def not_expr(self, args):
134
+ return NotNode(args[0])
135
+
136
+
137
+ def parse_query_expression(query: str, expr, regex, whole_word, case_sensitive) -> ExprNode:
138
+ """
139
+ Function to parse the query and return expression tree.
140
+ If expr is False, treat the whole query as a single term.
141
+ """
142
+
143
+ if not expr:
144
+ return TermNode(query, regex, whole_word, case_sensitive)
145
+
146
+ # Otherwise, parse using Lark
147
+ parser = Lark(query_grammar, parser="lalr")
148
+ try:
149
+ tree = parser.parse(query)
150
+ return TreeToExpr().transform(tree)
151
+ except Exception as e:
152
+ click.echo(click.style("Query parser error:\n\n", fg='red') + str(e))
153
+ sys.exit(1)
154
+
155
+
156
+ def highlight_text_safe(expr: ExprNode, text: str) -> str:
157
+ matches = []
158
+
159
+ def collect_matches(node):
160
+ if isinstance(node, TermNode):
161
+ for match in node.pattern.finditer(text):
162
+ matches.append((match.start(), match.end()))
163
+ elif isinstance(node, (AndNode, OrNode)):
164
+ collect_matches(node.left)
165
+ collect_matches(node.right)
166
+ elif isinstance(node, NotNode):
167
+ collect_matches(node.child)
168
+
169
+ collect_matches(expr)
170
+
171
+ # Remove overlaps (for example, if one match was inside another match)
172
+ matches.sort() # sort by start position
173
+ merged = []
174
+ for start, end in matches:
175
+ if not merged or start >= merged[-1][1]: # no overlap
176
+ merged.append((start, end))
177
+
178
+ # Create the final text with highlight
179
+ result = []
180
+ last = 0
181
+ for start, end in merged:
182
+ result.append(text[last:start])
183
+ result.append(click.style(text[start:end], fg='green'))
184
+ last = end
185
+ result.append(text[last:])
186
+
187
+ return ''.join(result)
@@ -1,8 +1,8 @@
1
- import re
2
- import sys
3
- import click
4
1
  import mmap
2
+ import click
5
3
  from pathlib import Path
4
+ from .utils import compile_regex
5
+ from .parser import parse_query_expression, TermNode, highlight_text_safe
6
6
  from concurrent.futures import ThreadPoolExecutor
7
7
 
8
8
  # Extensions that are not suitable for content search (binary, media, etc.)
@@ -19,18 +19,9 @@ EXCLUDED_EXTENSIONS = {
19
19
  }
20
20
 
21
21
 
22
- def compile_regex(txt, flags=0):
23
- if txt is not None:
24
- try:
25
- return re.compile(txt, flags)
26
- except re.error as e:
27
- click.echo(click.style(f"Regex compile error: {e}", fg='red'))
28
- sys.exit(1)
29
-
30
-
31
22
  class Search:
32
23
  def __init__(self, base_path, query, case_sensitive, ext, exclude_ext, regex, include, exclude, re_include,
33
- re_exclude, whole_word, max_size, min_size, full_path, no_content):
24
+ re_exclude, whole_word, expr, max_size, min_size, full_path, no_content):
34
25
  """Initialize search parameters"""
35
26
  self.base_path = Path(base_path)
36
27
  self.query = query
@@ -43,6 +34,7 @@ class Search:
43
34
  self.re_include = re_include
44
35
  self.re_exclude = re_exclude
45
36
  self.whole_word = whole_word
37
+ self.expr = expr
46
38
  self.max_size = max_size
47
39
  self.min_size = min_size
48
40
  self.full_path = full_path
@@ -83,18 +75,7 @@ class Search:
83
75
 
84
76
  def search(self, search_type: str):
85
77
  """Main search function. search_type can be 'file', 'directory' or 'content'"""
86
- query = self.query
87
-
88
- # Prepare query: escape if not regex
89
- if not self.regex:
90
- query = re.escape(query)
91
-
92
- # Apply whole-word matching only if not using regex (or if desired behavior is defined)
93
- if self.whole_word and not self.regex:
94
- query = rf'\b{query}\b'
95
-
96
- flags = 0 if self.case_sensitive else re.IGNORECASE # Adjust case sensitivity
97
- pattern = compile_regex(query, flags) # Precompile the regex pattern for performance
78
+ pattern = parse_query_expression(self.query, self.expr, self.regex, self.whole_word, self.case_sensitive)
98
79
 
99
80
  if search_type in ('file', 'directory'):
100
81
  matches = []
@@ -104,19 +85,25 @@ class Search:
104
85
  except Exception:
105
86
  continue
106
87
  # Skip if conditions fail or if name doesn't match the query
107
- if self.should_skip(p_resolved, search_type) or not pattern.search(p.name):
88
+ if self.should_skip(p_resolved, search_type) or not pattern.evaluate(p.name):
108
89
  continue
109
90
 
110
91
  # Highlight matched query in the name
111
- highlighted_name = pattern.sub(lambda m: click.style(m.group(), fg='green'), p.name)
92
+ highlighted_name = highlight_text_safe(pattern, p.name)
112
93
  # Choose parent path based on full_path flag
113
94
  p_parent = p_resolved.parent if self.full_path else p.parent
114
95
  matches.append(f'{p_parent}\\{highlighted_name}')
115
96
  else: # content search
116
97
  # Use dictionary: key: file path (colored), value: list of line matches
117
98
  matches = {} if not self.no_content else set()
118
- # Compile binary version of the pattern
119
- pattern_bytes = re.compile(pattern.pattern.encode('utf-8'))
99
+
100
+ # If expression is simple and is a single TermNode, we can use binary pattern
101
+ binary_pattern = None
102
+ if isinstance(pattern, TermNode):
103
+ try:
104
+ binary_pattern = pattern.get_binary_pattern()
105
+ except Exception:
106
+ binary_pattern = None
120
107
 
121
108
  def process_file(file_path: Path):
122
109
  """Process a single file for content search"""
@@ -129,48 +116,55 @@ class Search:
129
116
  with open(file_path, 'rb') as f:
130
117
  # Memory-map the file for efficient access
131
118
  with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
132
- # Search for the pattern in the entire file as bytes
133
- if pattern_bytes.search(mm):
134
- # Choose the file path format based on the full_path setting
135
- file_label = str(file_path.resolve()) if self.full_path else str(file_path)
136
-
137
- # Avoid searching through the entire file content if the fast-content flag is True
138
- if self.no_content:
139
- matches.add(click.style(file_label, fg='cyan'))
119
+ if binary_pattern is not None:
120
+ if not binary_pattern.search(mm):
121
+ return
122
+ else:
123
+ # fallback: decode whole file for complex expressions
124
+ try:
125
+ content = mm.read().decode('utf-8')
126
+ except UnicodeDecodeError:
127
+ return
128
+
129
+ if not pattern.evaluate(content):
140
130
  return
141
131
 
142
- lines = []
143
- mm.seek(0) # Move the cursor to the beginning of the file
144
-
145
- # Iterate over each line in the file
146
- for num, line in enumerate(iter(mm.readline, b''), 1):
147
- try:
148
- # Decode the binary line as UTF-8 and strip whitespace
149
- line_decoded = line.decode('utf-8').strip()
150
- except UnicodeDecodeError:
151
- # Skip lines that can't be decoded
152
- continue
153
-
154
- # If the pattern matches in the decoded line
155
- if pattern_list := list(pattern.finditer(line_decoded)):
156
- # Count how many times the pattern appears in the line
157
- count = len(pattern_list)
158
- # Highlight the matching parts in green
159
- highlighted = pattern.sub(
160
- lambda m: click.style(m.group(), fg='green'),
161
- line_decoded
162
- )
163
- # Show a note if the pattern repeats 3 or more times
164
- count_query = f' - Repeated {count} times' if count >= 3 else ''
165
- # Format the output line with line number and highlighted matches
166
- lines.append(
167
- click.style(f'Line {num}{count_query}: ', fg='magenta') + highlighted
168
- )
169
-
170
- # If any matching lines were found
171
- if lines:
172
- # Add the file and its matching lines to the results
173
- matches[click.style(file_label, fg='cyan')] = lines
132
+ # Choose the file path format based on the full_path setting
133
+ file_label = str(file_path.resolve()) if self.full_path else str(file_path)
134
+
135
+ # Avoid searching through the entire file content if the fast-content flag is True
136
+ if self.no_content:
137
+ matches.add(click.style(file_label, fg='cyan'))
138
+ return
139
+
140
+ lines = []
141
+ mm.seek(0) # Move the cursor to the beginning of the file
142
+
143
+ # Iterate over each line in the file
144
+ for num, line in enumerate(iter(mm.readline, b''), 1):
145
+ try:
146
+ # Decode the binary line as UTF-8 and strip whitespace
147
+ line_decoded = line.decode('utf-8').strip()
148
+ except UnicodeDecodeError:
149
+ # Skip lines that can't be decoded
150
+ continue
151
+
152
+ # If the pattern matches in the decoded line
153
+ if pattern.evaluate(line_decoded):
154
+ count = pattern.count_matches(line_decoded) if isinstance(pattern, TermNode) else 0
155
+ # Highlight the matching parts in green
156
+ highlighted = highlight_text_safe(pattern, line_decoded)
157
+ # Show a note if the pattern repeats 3 or more times
158
+ count_query = f' - Repeated {count} times' if count >= 3 else ''
159
+ # Format the output line with line number and highlighted matches
160
+ lines.append(
161
+ click.style(f'Line {num}{count_query}: ', fg='magenta') + highlighted
162
+ )
163
+
164
+ # If any matching lines were found
165
+ if lines:
166
+ # Add the file and its matching lines to the results
167
+ matches[click.style(file_label, fg='cyan')] = lines
174
168
  except Exception:
175
169
  return
176
170
 
@@ -0,0 +1,12 @@
1
+ import re
2
+ import sys
3
+ import click
4
+
5
+
6
+ def compile_regex(txt, flags=0):
7
+ if txt is not None:
8
+ try:
9
+ return re.compile(txt, flags)
10
+ except re.error as e:
11
+ click.echo(click.style(f"Regex compile error: {e}", fg='red'))
12
+ sys.exit(1)
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: pseek
3
+ Version: 2.2.0
4
+ Summary: Pseek is a Python library to search files, folders, and text
5
+ Author-email: Arian <ariannasiri86@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ArianN8610/pysearch
8
+ Requires-Python: >=3.6
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: click==8.1.8
12
+ Requires-Dist: lark==1.2.2
13
+ Dynamic: license-file
14
+
15
+ # Pseek
16
+
17
+ ## Overview
18
+
19
+ 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.
20
+
21
+ ## Features
22
+
23
+ * **Search in file & folder names**
24
+ * **Search inside file contents**
25
+ * **Highlight matches** in terminal output
26
+ * **Optimized for speed** with ThreadPoolExecutor
27
+ * **Cross-platform** (Linux, macOS, Windows)
28
+
29
+ ## Installation
30
+
31
+ ### **1️⃣ Install via `pip` (Recommended)**
32
+
33
+ ```sh
34
+ pip install pseek
35
+ ```
36
+
37
+ ### **2️⃣ Install from source**
38
+
39
+ ```sh
40
+ git clone https://github.com/ArianN8610/pysearch.git
41
+ cd pseek
42
+ pip install click==8.1.8
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ Run the command with a search query:
48
+ ```sh
49
+ pseek <query> [options]
50
+ ```
51
+
52
+ ## Examples
53
+
54
+ ### Search for a keyword in file & folder names
55
+
56
+ ```sh
57
+ pseek "my_keyword" --path /path/to/search --file --directory
58
+ ```
59
+
60
+ ### Search inside file contents
61
+
62
+ ```sh
63
+ pseek "error" --path /var/logs --content
64
+ ```
65
+
66
+ ### Search only in specific file types
67
+
68
+ ```sh
69
+ pseek "TODO" --path ./projects --ext py --ext txt
70
+ ```
71
+
72
+ ### Search by regex
73
+
74
+ ```sh
75
+ pseek "error\d+" --regex
76
+ ```
77
+
78
+ ## Command Options
79
+
80
+ | Option | Description |
81
+ |--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
82
+ | `--path` | Base directory to search in (default: current directory `.`) |
83
+ | `--file` | Search only in file names |
84
+ | `--directory` | Search only in directory names |
85
+ | `--content` | Search inside file contents |
86
+ | `--ext`, `--exclude-ext` | Filter by file extension (e.g., `.txt`, `.log`) |
87
+ | `--case-sensitive` | Make the search case-sensitive (except when --expr is enabled, in which case you can make it case sensitive by putting c before term: c"foo") |
88
+ | `--regex` | Use regular expressions to search (except when --expr is enabled, in which case you can make it regex by putting r before term: r"foo") |
89
+ | `--include`, `--exclude` | Limit search results to specific set of directories or files |
90
+ | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
91
+ | `--word` | Match the whole word only (except when --expr is enabled, in which case you can make it match whole word by putting w before term: w"foo") |
92
+ | `--expr` | Enable to write conditions in the query. Example: r"foo.*bar" and ("bar" or "baz") and not "qux" (To use regex, word, and case-sensitive features, you can use the prefixes r, w, and c before terms. Allowed modes: r, w, c, wc, cw, rc, cr. Examples: r"foo.*bar", wc"Foo", cr".*Foo", ...) |
93
+ | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
94
+ | `--full-path` | Display full path of files and directories |
95
+ | `--no-content` | Only display files path for content search |
@@ -1,9 +1,11 @@
1
1
  LICENSE
2
2
  README.md
3
- setup.py
3
+ pyproject.toml
4
4
  pseek/__init__.py
5
5
  pseek/cli.py
6
+ pseek/parser.py
6
7
  pseek/searcher.py
8
+ pseek/utils.py
7
9
  pseek.egg-info/PKG-INFO
8
10
  pseek.egg-info/SOURCES.txt
9
11
  pseek.egg-info/dependency_links.txt
@@ -1 +1,2 @@
1
1
  click==8.1.8
2
+ lark==1.2.2
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pseek"
7
+ version = "2.2.0"
8
+ description = "Pseek is a Python library to search files, folders, and text"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [
13
+ {name = "Arian", email = "ariannasiri86@gmail.com"}
14
+ ]
15
+ requires-python = ">=3.6"
16
+ dependencies = [
17
+ "click==8.1.8",
18
+ "lark==1.2.2"
19
+ ]
20
+ urls = {Homepage = "https://github.com/ArianN8610/pysearch"}
21
+
22
+ [project.scripts]
23
+ pseek = "pseek.cli:search"
pseek-2.1.4/PKG-INFO DELETED
@@ -1,104 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: pseek
3
- Version: 2.1.4
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`, `--exclude-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
- | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
101
- | `--word` | Match the whole word only |
102
- | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
103
- | `--full-path` | Display full path of files and directories |
104
- | `--no-content` | Only display files path for content search |
pseek-2.1.4/README.md DELETED
@@ -1,80 +0,0 @@
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`, `--exclude-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
- | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
77
- | `--word` | Match the whole word only |
78
- | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
79
- | `--full-path` | Display full path of files and directories |
80
- | `--no-content` | Only display files path for content search |
@@ -1,104 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: pseek
3
- Version: 2.1.4
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`, `--exclude-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
- | `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
101
- | `--word` | Match the whole word only |
102
- | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
103
- | `--full-path` | Display full path of files and directories |
104
- | `--no-content` | Only display files path for content search |
pseek-2.1.4/setup.py DELETED
@@ -1,24 +0,0 @@
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="2.1.4",
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
- )
File without changes
File without changes
File without changes