pseek 2.5.1__tar.gz → 2.5.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pseek
3
- Version: 2.5.1
3
+ Version: 2.5.2
4
4
  Summary: Pseek is a Python library to search files, folders, and text
5
5
  Author-email: Arian <ariannasiri86@gmail.com>
6
6
  License-Expression: MIT
@@ -102,6 +102,7 @@ pseek "error\d+" --regex
102
102
  | `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
103
103
  | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
104
104
  | `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
105
+ | `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
105
106
  | `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
106
107
  | `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
107
108
  | `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
@@ -112,4 +113,4 @@ pseek "error\d+" --regex
112
113
  ## Requirements
113
114
 
114
115
  * Python 3.6 or higher
115
- * `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
116
+ * `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
@@ -85,6 +85,7 @@ pseek "error\d+" --regex
85
85
  | `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
86
86
  | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
87
87
  | `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
88
+ | `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
88
89
  | `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
89
90
  | `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
90
91
  | `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
@@ -95,4 +96,4 @@ pseek "error\d+" --regex
95
96
  ## Requirements
96
97
 
97
98
  * Python 3.6 or higher
98
- * `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
99
+ * `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
@@ -77,6 +77,8 @@ def run_search_process(file, directory, content, search_instance):
77
77
  # Archive options
78
78
  @click.option('--archive', is_flag=True,
79
79
  help='Enable search within archive files (e.g. zip, rar, 7z, gz, bz2, xz, tar, tar.gz, tar.bz2, tar.xz)')
80
+ @click.option('--depth', type=click.IntRange(min=0), show_default=True,
81
+ help='Maximum archive depth to recurse into (e.g. 2 means only 2 levels).')
80
82
  @click.option('--arc-ext', multiple=True, type=click.STRING,
81
83
  help='Include files with these extensions inside archive files. Example: --arc-ext py --arc-ext js')
82
84
  @click.option('--arc-ee', multiple=True, type=click.STRING,
@@ -94,8 +96,8 @@ def run_search_process(file, directory, content, search_instance):
94
96
  @click.option('--full-path', is_flag=True, help='Display full paths for results.')
95
97
  @click.option('--no-content', is_flag=True, help='Only display files path for content search.')
96
98
  def search(query, path, file, directory, content, case_sensitive, ext, exclude_ext, regex, include, exclude,
97
- re_include, re_exclude, word, expr, timeout, fuzzy, fuzzy_level, max_size, min_size, archive, arc_ext,
98
- arc_ee, arc_inc, arc_exc, arc_max, arc_min, rarfb, full_path, no_content):
99
+ re_include, re_exclude, word, expr, timeout, fuzzy, fuzzy_level, max_size, min_size, archive, depth,
100
+ arc_ext, arc_ee, arc_inc, arc_exc, arc_max, arc_min, rarfb, full_path, no_content):
99
101
  """Search for files, directories, and file content based on the query."""
100
102
 
101
103
  check_rar_backend(archive, rarfb, query)
@@ -140,6 +142,7 @@ def search(query, path, file, directory, content, case_sensitive, ext, exclude_e
140
142
  max_size=max_size,
141
143
  min_size=min_size,
142
144
  archive=archive,
145
+ depth=depth,
143
146
  arc_ext=arc_ext,
144
147
  arc_ee=arc_ee,
145
148
  arc_inc=arc_inc,
@@ -1,7 +1,7 @@
1
1
  import mmap
2
2
  import click
3
3
  from pathlib import Path
4
- from .utils import compile_regex, get_archive_path_size, try_decode
4
+ from .utils import compile_regex, get_archive_path_size, try_decode, get_path_suffix
5
5
  from .parser import parse_query_expression, TermNode, highlight_text
6
6
  from concurrent.futures import ThreadPoolExecutor
7
7
  import zipfile
@@ -11,9 +11,10 @@ import gzip
11
11
  import bz2
12
12
  import lzma
13
13
  import rarfile
14
+ import io
14
15
 
15
16
  # Archive extensions that are allowed
16
- ARCHIVE_EXTS = ('zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'tar.gz', 'tar.bz2', 'tar.xz')
17
+ ARCHIVE_EXTS = ('zip', 'rar', '7z', 'tar', 'tar.gz', 'tar.bz2', 'tar.xz', 'gz', 'bz2', 'xz')
17
18
 
18
19
  # Extensions that are not suitable for content search (binary, media, etc.)
19
20
  EXCLUDED_EXTENSIONS = (
@@ -30,7 +31,7 @@ EXCLUDED_EXTENSIONS = (
30
31
 
31
32
  class Search:
32
33
  def __init__(self, base_path, query, case_sensitive, ext, exclude_ext, regex, include, exclude, re_include,
33
- re_exclude, whole_word, expr, fuzzy, fuzzy_level, max_size, min_size, archive, arc_ext, arc_ee,
34
+ re_exclude, whole_word, expr, fuzzy, fuzzy_level, max_size, min_size, archive, depth, arc_ext, arc_ee,
34
35
  arc_inc, arc_exc, arc_max, arc_min, full_path, no_content):
35
36
  """Initialize search parameters"""
36
37
  self.base_path = Path(base_path)
@@ -50,6 +51,7 @@ class Search:
50
51
  self.max_size = max_size
51
52
  self.min_size = min_size
52
53
  self.archive = archive
54
+ self.depth = depth
53
55
  self.arc_ext = set(arc_ext)
54
56
  self.arc_ee = set(arc_ee) | {''} if arc_ee else set()
55
57
  self.arc_inc = {Path(p) for p in arc_inc}
@@ -71,10 +73,10 @@ class Search:
71
73
  # If path is inaccessible, skip it.
72
74
  return True
73
75
 
74
- file_ext = ''.join(p_resolved.suffixes)[1:].lower()
76
+ file_ext = get_path_suffix(p_resolved)
75
77
 
76
78
  # Ignore some filters for archive files when archive is enabled
77
- if (not self.archive or not file_ext in ('zip', 'rar', '7z', 'tar', 'tar.gz', 'tar.bz2', 'tar.xz')) and \
79
+ if (not self.archive or not file_ext in ARCHIVE_EXTS[:-3]) and \
78
80
  ((search_type in ('file', 'content') and not p_resolved.is_file())
79
81
  or (search_type == 'directory' and not p_resolved.is_dir())):
80
82
  return True
@@ -96,11 +98,10 @@ class Search:
96
98
 
97
99
  return False
98
100
 
99
-
100
101
  def archive_should_skip(self, path_info: Path, search_type: str, is_file: bool, is_dir: bool, p_size: float):
101
102
  """Check whether the file/directory inside archive files should be skipped based on various filters"""
102
103
 
103
- file_ext = ''.join(path_info.suffixes)[1:].lower()
104
+ file_ext = get_path_suffix(path_info)
104
105
 
105
106
  if (search_type in ('file', 'content') and not is_file) \
106
107
  or (search_type == 'directory' and not is_dir):
@@ -122,38 +123,57 @@ class Search:
122
123
 
123
124
  return False
124
125
 
125
- def extract_names_from_archive(self, file_path: Path, search_type: str):
126
- """Extract files and directories name from archive files to search"""
126
+ def extract_names_from_archive(self, file_path: Path, search_type: str,
127
+ file_bytes: bytes = None, parent_label: str = ''):
128
+ """
129
+ Recursively extract files and directories name from archive files.
130
+ Supports nested archives like a.zip::b.7z::c.txt.
131
+
132
+ Parameters:
133
+ file_path (Path): the archive file path
134
+ search_type (str): search type ( file / directory )
135
+ file_bytes (bytes | None): optional byte data if already read (for recursion)
136
+ parent_label (str): string for nested archive tracking like a.zip::b.7z::file.txt
137
+
138
+ Yields:
139
+ (str, Path): tuple of parent label and file or directory name
140
+ """
127
141
 
128
- file_ext = ''.join(file_path.suffixes)[1:].lower()
142
+ file_ext = get_path_suffix(file_path)
143
+ label_prefix = (parent_label + str(file_path) + '::') if parent_label else '::'
129
144
 
130
145
  try:
131
- if file_ext == 'zip':
132
- with zipfile.ZipFile(file_path) as zf:
133
- for info in zf.infolist():
146
+ # Decide the stream source: from disk or memory
147
+ file_stream = io.BytesIO(file_bytes) if file_bytes is not None else open(file_path, 'rb')
148
+
149
+ # Handle ZIP and RAR archives
150
+ if file_ext in ('zip', 'rar'):
151
+ opener = {'zip': zipfile.ZipFile, 'rar': rarfile.RarFile}[file_ext]
152
+ with opener(file_stream) as f:
153
+ for info in f.infolist():
134
154
  name = Path(info.filename)
135
155
  if not self.archive_should_skip(
136
156
  name,
137
157
  search_type,
138
158
  not info.is_dir(),
139
159
  info.is_dir(),
140
- get_archive_path_size(info, 'zip')
160
+ get_archive_path_size(info, file_ext)
141
161
  ):
142
- yield name
143
- elif file_ext == 'rar':
144
- with rarfile.RarFile(file_path) as rf:
145
- for info in rf.infolist():
146
- name = Path(info.filename)
147
- if not self.archive_should_skip(
162
+ yield label_prefix, name
163
+
164
+ # At each recursion, subtract 1 from depth if it's set
165
+ new_depth = None if self.depth is None else self.depth - 1
166
+ # Check if this is a nested archive
167
+ if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
168
+ yield from self.extract_names_from_archive(
148
169
  name,
149
170
  search_type,
150
- not info.is_dir(),
151
- info.is_dir(),
152
- get_archive_path_size(info, 'rar')
153
- ):
154
- yield name
171
+ f.read(info),
172
+ label_prefix
173
+ )
174
+ # Handle 7Z archives
155
175
  elif file_ext == '7z':
156
- with py7zr.SevenZipFile(file_path, mode='r') as z:
176
+ with py7zr.SevenZipFile(file_stream, mode='r') as z:
157
177
  for info in z.list():
158
178
  name = Path(info.filename)
159
179
  if not self.archive_should_skip(
@@ -163,18 +183,31 @@ class Search:
163
183
  info.is_directory,
164
184
  get_archive_path_size(info, '7z')
165
185
  ):
166
- yield name
186
+ yield label_prefix, name
187
+
188
+ new_depth = None if self.depth is None else self.depth - 1
189
+ if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
190
+ file_data = z.read([info.filename]).get(info.filename)
191
+ if file_data is None:
192
+ continue
193
+
194
+ yield from self.extract_names_from_archive(
195
+ name,
196
+ search_type,
197
+ file_data.read(),
198
+ label_prefix
199
+ )
200
+ # Handle TAR and compressed TAR formats
167
201
  elif file_ext in ('tar', 'tar.gz', 'tar.bz2', 'tar.xz'):
168
202
  # Specify the mode based on the file ext to open it
169
- mode = 'r'
170
- if file_ext == 'tar.gz':
171
- mode = 'r:gz'
172
- elif file_ext == 'tar.bz2':
173
- mode = 'r:bz2'
174
- elif file_ext == 'tar.xz':
175
- mode = 'r:xz'
176
-
177
- with tarfile.open(file_path, mode) as tf:
203
+ mode = {
204
+ 'tar': 'r',
205
+ 'tar.gz': 'r:gz',
206
+ 'tar.bz2': 'r:bz2',
207
+ 'tar.xz': 'r:xz'
208
+ }[file_ext]
209
+
210
+ with tarfile.open(fileobj=file_stream, mode=mode) as tf:
178
211
  for member in tf.getmembers():
179
212
  name = Path(member.name)
180
213
  if not self.archive_should_skip(
@@ -184,120 +217,139 @@ class Search:
184
217
  member.isdir(),
185
218
  get_archive_path_size(member, file_ext)
186
219
  ):
187
- yield name
220
+ yield label_prefix, name
221
+
222
+ new_depth = None if self.depth is None else self.depth - 1
223
+ if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
224
+ f = tf.extractfile(member)
225
+ if f is None:
226
+ continue
227
+
228
+ yield from self.extract_names_from_archive(
229
+ name,
230
+ search_type,
231
+ f.read(),
232
+ label_prefix
233
+ )
188
234
  except Exception:
189
235
  return # silently skip invalid archives
190
236
 
191
- def extract_text_from_archive(self, file_path: Path):
237
+ def extract_text_from_archive(self, file_path: Path, file_bytes: bytes = None, parent_label: str = ''):
192
238
  """
193
- Generator yielding (filename, content_text) from archive files.
194
- Supports zip, rar, tar, gz, bz2, xz, 7z.
239
+ Recursively extract (path_label, text_content) from any archive file.
240
+ Supports nested archives like a.zip::b.7z::c.txt.
241
+
242
+ Parameters:
243
+ file_path (Path): the archive file path
244
+ file_bytes (bytes | None): optional byte data if already read (for recursion)
245
+ parent_label (str): string for nested archive tracking like a.zip::b.7z::file.txt
246
+
247
+ Yields:
248
+ (str, str): tuple of full virtual path and decoded content text
195
249
  """
196
250
 
197
- file_ext = ''.join(file_path.suffixes)[1:].lower()
251
+ file_ext = get_path_suffix(file_path)
252
+ label_prefix = (parent_label + str(file_path) + '::') if parent_label else '::'
198
253
 
199
254
  try:
200
- if file_ext == 'zip':
201
- with zipfile.ZipFile(file_path) as zf:
202
- for info in zf.infolist():
203
- file_name = Path(info.filename)
204
- if self.archive_should_skip(
205
- file_name,
206
- 'content',
207
- not info.is_dir(),
208
- info.is_dir(),
209
- get_archive_path_size(info, 'zip')
210
- ):
211
- continue
212
-
213
- with zf.open(info) as f:
214
- data = f.read()
215
- text = try_decode(data)
216
- if text is not None:
217
- yield info.filename, text
218
- elif file_ext == 'rar':
219
- with rarfile.RarFile(file_path) as rf:
220
- for info in rf.infolist():
255
+ # Decide the stream source: from disk or memory
256
+ file_stream = io.BytesIO(file_bytes) if file_bytes is not None else open(file_path, 'rb')
257
+
258
+ # Handle ZIP and RAR archives
259
+ if file_ext in ('zip', 'rar'):
260
+ opener = {'zip': zipfile.ZipFile, 'rar': rarfile.RarFile}[file_ext]
261
+ with opener(file_stream) as f:
262
+ for info in f.infolist():
221
263
  file_name = Path(info.filename)
222
- if self.archive_should_skip(
223
- file_name,
224
- 'content',
225
- not info.is_dir(),
226
- info.is_dir(),
227
- get_archive_path_size(info, 'rar')
228
- ):
229
- continue
264
+ data = f.read(info)
265
+ # At each recursion, subtract 1 from depth if it's set
266
+ new_depth = None if self.depth is None else self.depth - 1
267
+
268
+ # Check if this is a nested archive
269
+ if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
270
+ yield from self.extract_text_from_archive(file_name, data, label_prefix)
271
+ else:
272
+ if self.archive_should_skip(
273
+ file_name,
274
+ 'content',
275
+ not info.is_dir(),
276
+ info.is_dir(),
277
+ get_archive_path_size(info, file_ext)
278
+ ):
279
+ continue
230
280
 
231
- with rf.open(info) as f:
232
- data = f.read()
233
281
  text = try_decode(data)
234
282
  if text is not None:
235
- yield info.filename, text
283
+ yield label_prefix + str(file_name), text
284
+ # Handle 7Z archives
236
285
  elif file_ext == '7z':
237
- with py7zr.SevenZipFile(file_path, mode='r') as archive:
286
+ with py7zr.SevenZipFile(file_stream, mode='r') as archive:
238
287
  for info in archive.list():
239
- file_name = Path(info.filename)
240
- if self.archive_should_skip(
241
- file_name,
242
- 'content',
243
- not info.is_directory,
244
- info.is_directory,
245
- get_archive_path_size(info, '7z')
246
- ):
288
+ file_data = archive.read([info.filename]).get(info.filename)
289
+ if file_data is None:
247
290
  continue
248
291
 
249
- data = archive.read([info.filename])
250
- filedata = data.get(info.filename)
251
- if filedata is None:
252
- continue
253
- text = try_decode(filedata.read())
254
- if text is not None:
255
- yield info.filename, text
292
+ file_name = Path(info.filename)
293
+ data = file_data.read()
294
+ new_depth = None if self.depth is None else self.depth - 1
295
+
296
+ if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
297
+ yield from self.extract_text_from_archive(file_name, data, label_prefix)
298
+ else:
299
+ if self.archive_should_skip(
300
+ file_name,
301
+ 'content',
302
+ not info.is_directory,
303
+ info.is_directory,
304
+ get_archive_path_size(info, '7z')
305
+ ):
306
+ continue
307
+
308
+ text = try_decode(data)
309
+ if text is not None:
310
+ yield label_prefix + str(file_name), text
311
+ # Handle TAR and compressed TAR formats
256
312
  elif file_ext in ('tar', 'tar.gz', 'tar.bz2', 'tar.xz'):
257
- mode = 'r'
258
- if file_ext == 'tar.gz':
259
- mode = 'r:gz'
260
- elif file_ext == 'tar.bz2':
261
- mode = 'r:bz2'
262
- elif file_ext == 'tar.xz':
263
- mode = 'r:xz'
264
- with tarfile.open(file_path, mode) as tf:
313
+ mode = {
314
+ 'tar': 'r',
315
+ 'tar.gz': 'r:gz',
316
+ 'tar.bz2': 'r:bz2',
317
+ 'tar.xz': 'r:xz'
318
+ }[file_ext]
319
+
320
+ with tarfile.open(fileobj=file_stream, mode=mode) as tf:
265
321
  for member in tf.getmembers():
266
- file_name = Path(member.name)
267
- if self.archive_should_skip(
268
- file_name,
269
- 'content',
270
- member.isfile(),
271
- member.isdir(),
272
- get_archive_path_size(member, file_ext)
273
- ):
274
- continue
275
-
276
322
  f = tf.extractfile(member)
277
323
  if f is None:
278
324
  continue
325
+
326
+ file_name = Path(member.name)
279
327
  data = f.read()
280
- text = try_decode(data)
281
- if text is not None:
282
- yield member.name, text
283
- elif file_ext == 'gz':
284
- with gzip.open(file_path, 'rb') as f:
285
- data = f.read()
286
- text = try_decode(data)
287
- if text is not None:
288
- yield file_path.name, text
289
- elif file_ext == 'bz2':
290
- with bz2.open(file_path, 'rb') as f:
291
- data = f.read()
292
- text = try_decode(data)
293
- if text is not None:
294
- yield file_path.name, text
295
- elif file_ext == 'xz':
296
- with lzma.open(file_path, 'rb') as f:
328
+ new_depth = None if self.depth is None else self.depth - 1
329
+
330
+ if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
331
+ yield from self.extract_text_from_archive(file_name, data, label_prefix)
332
+ else:
333
+ if self.archive_should_skip(
334
+ file_name,
335
+ 'content',
336
+ member.isfile(),
337
+ member.isdir(),
338
+ get_archive_path_size(member, file_ext)
339
+ ):
340
+ continue
341
+
342
+ text = try_decode(data)
343
+ if text is not None:
344
+ yield label_prefix + str(file_name), text
345
+ # Handle single compressed files like .gz, .bz2, .xz
346
+ elif file_ext in ARCHIVE_EXTS[-3:]:
347
+ opener = {'gz': gzip.open, 'bz2': bz2.open, 'xz': lzma.open}[file_ext]
348
+ with opener(file_stream, 'rb') as f:
297
349
  data = f.read()
298
350
  text = try_decode(data)
299
351
  if text is not None:
300
- yield file_path.name, text
352
+ yield file_ext, text
301
353
  except Exception:
302
354
  return
303
355
 
@@ -319,7 +371,7 @@ class Search:
319
371
 
320
372
  # Choose parent path based on full_path flag
321
373
  p_parent = p_resolved.parent if self.full_path else p.parent
322
- p_ext = ''.join(p_resolved.suffixes)[1:].lower()
374
+ p_ext = get_path_suffix(p_resolved)
323
375
 
324
376
  if pattern.evaluate(p.name) and not (search_type == 'directory' and p_resolved.is_file()):
325
377
  # Highlight matched query in the name
@@ -327,11 +379,11 @@ class Search:
327
379
  matches.append(f'{p_parent}\\{highlighted_name}')
328
380
 
329
381
  # Search for files and directories name inside archive files if archive is active
330
- if self.archive and p_ext in ('zip', 'rar', '7z', 'tar', 'tar.gz', 'tar.bz2', 'tar.xz'):
331
- for name in self.extract_names_from_archive(p_resolved, search_type):
382
+ if self.archive and p_ext in ARCHIVE_EXTS[:-3]:
383
+ for label, name in self.extract_names_from_archive(p_resolved, search_type):
332
384
  if pattern.evaluate(name.name):
333
385
  highlighted_name = highlight_text(pattern, name.name, self.fuzzy)
334
- matches.append(f'{p_parent}\\{p.name}::{name.parent}\\{highlighted_name}')
386
+ matches.append(f'{p_parent}\\{p.name}{label}{name.parent}\\{highlighted_name}')
335
387
  else: # content search
336
388
  # Use dictionary: key: file path (colored), value: list of line matches
337
389
  matches = {} if not self.no_content else set()
@@ -355,13 +407,14 @@ class Search:
355
407
  file_label = str(file_path.resolve()) if self.full_path else str(file_path)
356
408
 
357
409
  # First, check if the file is an archive, extract it from the archive and perform a search
358
- if self.archive and any(str(file_path).endswith(ext) for ext in ARCHIVE_EXTS):
410
+ if self.archive and get_path_suffix(file_path) in ARCHIVE_EXTS:
359
411
  for fname, content in self.extract_text_from_archive(file_path):
360
412
  if not pattern.evaluate(content) and not self.expr:
361
413
  continue
362
414
 
363
- # Change file_label for archive files
364
- file_label += '::' + fname.replace('/', '\\')
415
+ # Change file_label for archive files (zip, rar, 7z, tar)
416
+ if fname not in ARCHIVE_EXTS[-3:]:
417
+ file_label += fname
365
418
 
366
419
  if self.no_content and not self.expr:
367
420
  matches.add(click.style(file_label, fg='cyan'))
@@ -85,3 +85,7 @@ def check_rar_backend(archive_enabled: bool, tool_path: str, backend: str):
85
85
  rarfile.UNAR_TOOL = tool
86
86
  elif b == '7z':
87
87
  rarfile.SEVENZIP_TOOL = tool
88
+
89
+
90
+ def get_path_suffix(path: Path) -> str:
91
+ return ''.join(path.suffixes)[1:].lower()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pseek
3
- Version: 2.5.1
3
+ Version: 2.5.2
4
4
  Summary: Pseek is a Python library to search files, folders, and text
5
5
  Author-email: Arian <ariannasiri86@gmail.com>
6
6
  License-Expression: MIT
@@ -102,6 +102,7 @@ pseek "error\d+" --regex
102
102
  | `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
103
103
  | `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
104
104
  | `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
105
+ | `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
105
106
  | `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
106
107
  | `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
107
108
  | `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
@@ -112,4 +113,4 @@ pseek "error\d+" --regex
112
113
  ## Requirements
113
114
 
114
115
  * Python 3.6 or higher
115
- * `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
116
+ * `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pseek"
7
- version = "2.5.1"
7
+ version = "2.5.2"
8
8
  description = "Pseek is a Python library to search files, folders, and text"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes