everything-sdk 1.0.0__py3-none-any.whl

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.
everything/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ """
2
+ everything — Python wrapper for the Everything SDK v3.
3
+
4
+ Provides :class:`EverythingClient`, :class:`SearchResult`,
5
+ :class:`PropertyID`, :class:`FileAttribute`, :class:`EverythingError`,
6
+ and the :func:`format_size` utility.
7
+
8
+ Quick start::
9
+
10
+ from everything import EverythingClient, format_size
11
+
12
+ with EverythingClient() as client:
13
+ client.connect()
14
+ results, total = client.search("*.txt")
15
+ for r in results:
16
+ print(f"{r.full_path}: {format_size(r.size)}")
17
+ """
18
+
19
+ from .client import EverythingClient, EverythingError, PropertyID, FileAttribute, SearchResult, format_size
20
+
21
+ __version__ = "1.0.0"
22
+ __all__ = [
23
+ "EverythingClient",
24
+ "EverythingError",
25
+ "PropertyID",
26
+ "FileAttribute",
27
+ "SearchResult",
28
+ "format_size",
29
+ ]
everything/client.py ADDED
@@ -0,0 +1,757 @@
1
+ """
2
+ Everything SDK v3 Python Wrapper
3
+
4
+ This module provides a Pythonic interface to the Everything desktop search
5
+ engine (https://www.voidtools.com/) via its native v3 DLL API. It uses
6
+ ``ctypes`` to call the C exports directly, so no compilation or additional
7
+ C-extension build step is required.
8
+
9
+ Typical usage::
10
+
11
+ from everything import EverythingClient, format_size
12
+
13
+ with EverythingClient() as client:
14
+ client.connect()
15
+ results, total = client.search("*.txt", match_path=True)
16
+ for r in results:
17
+ print(f"{r.full_path}: {format_size(r.size)}")
18
+
19
+ Note:
20
+ Everything must be running on the host machine before a connection
21
+ can be established. Download it from https://www.voidtools.com/.
22
+ """
23
+
24
+ import ctypes
25
+ import ctypes.wintypes as wintypes
26
+ import os
27
+ import sys
28
+ from datetime import datetime
29
+
30
+
31
+ class PropertyID:
32
+ """Integer identifiers for the result properties exposed by the
33
+ Everything SDK. Used with ``Everything3_AddSearchPropertyRequest`` and
34
+ ``Everything3_GetResultProperty*`` calls.
35
+
36
+ See the SDK header ``Everything3.h`` for the canonical definitions.
37
+ """
38
+
39
+ NAME = 0
40
+ PATH = 1
41
+ SIZE = 2
42
+ EXTENSION = 3
43
+ TYPE = 4
44
+ DATE_MODIFIED = 5
45
+ DATE_CREATED = 6
46
+ DATE_ACCESSED = 7
47
+ ATTRIBUTES = 8
48
+ DATE_RECENTLY_CHANGED = 9
49
+ RUN_COUNT = 10
50
+ DATE_RUN = 11
51
+ FILE_LIST_NAME = 12
52
+ WIDTH = 13
53
+ HEIGHT = 14
54
+ DIMENSIONS = 15
55
+ ASPECT_RATIO = 16
56
+ BIT_DEPTH = 17
57
+ LENGTH = 18
58
+ AUDIO_SAMPLE_RATE = 19
59
+ AUDIO_CHANNELS = 20
60
+ AUDIO_BITS_PER_SAMPLE = 21
61
+ AUDIO_BIT_RATE = 22
62
+ AUDIO_FORMAT = 23
63
+ FILE_SIGNATURE = 24
64
+ TITLE = 25
65
+ ARTIST = 26
66
+ ALBUM = 27
67
+ YEAR = 28
68
+ COMMENT = 29
69
+ TRACK = 30
70
+ GENRE = 31
71
+ FRAME_RATE = 32
72
+ VIDEO_BIT_RATE = 33
73
+ VIDEO_FORMAT = 34
74
+ RATING = 35
75
+ TAGS = 36
76
+ MD5 = 37
77
+ SHA1 = 38
78
+ SHA256 = 39
79
+ CRC32 = 40
80
+ SIZE_ON_DISK = 41
81
+
82
+
83
+ class FileAttribute:
84
+ """Windows file-attribute bit flags (mirrors ``FILE_ATTRIBUTE_*``)."""
85
+
86
+ READONLY = 0x01
87
+ HIDDEN = 0x02
88
+ SYSTEM = 0x04
89
+ DIRECTORY = 0x10
90
+ ARCHIVE = 0x20
91
+ COMPRESSED = 0x40
92
+ NORMAL = 0x80
93
+ TEMPORARY = 0x100
94
+ OFFLINE = 0x200
95
+ INTEGRITY_STREAM = 0x1000
96
+
97
+
98
+ class EverythingError(Exception):
99
+ """Raised when an Everything SDK call reports a failure.
100
+
101
+ Attributes:
102
+ error_code: The 32-bit error code returned by
103
+ ``Everything3_GetLastError``.
104
+ """
105
+
106
+ ERROR_IPC_PIPE_NOT_FOUND = 0xE0000002
107
+
108
+ def __init__(self, error_code):
109
+ self.error_code = error_code
110
+ if error_code == self.ERROR_IPC_PIPE_NOT_FOUND:
111
+ message = "Everything is not running or IPC pipe not found"
112
+ else:
113
+ message = f"Everything SDK error, code: 0x{error_code:X}"
114
+ super().__init__(message)
115
+
116
+
117
+ class SearchResult:
118
+ """Immutable container holding the properties of a single Everything
119
+ search result.
120
+
121
+ All FILETIME timestamps are stored as raw 64-bit integers. Convenience
122
+ properties (``modified_time``, ``created_time``, ``accessed_time``)
123
+ convert them to :class:`datetime.datetime` objects on the fly.
124
+ """
125
+
126
+ def __init__(self, name, parent_path, full_path, size, is_folder,
127
+ date_modified, date_created, date_accessed, attributes,
128
+ extension=""):
129
+ self.name = name
130
+ self.parent_path = parent_path
131
+ self.full_path = full_path
132
+ self.size = size
133
+ self.is_folder = is_folder
134
+ self.date_modified = date_modified
135
+ self.date_created = date_created
136
+ self.date_accessed = date_accessed
137
+ self.attributes = attributes
138
+ self.extension = extension
139
+
140
+ @property
141
+ def type_str(self):
142
+ """Return ``"Folder"`` or ``"File"``."""
143
+ return "Folder" if self.is_folder else "File"
144
+
145
+ @property
146
+ def modified_time(self):
147
+ """Convert the FILETIME timestamp to a :class:`datetime`, or
148
+ ``None`` if the value is zero (not available)."""
149
+ if self.date_modified == 0:
150
+ return None
151
+ return datetime.fromtimestamp(
152
+ self.date_modified / 10_000_000 - 11644473600
153
+ )
154
+
155
+ @property
156
+ def created_time(self):
157
+ """Convert the FILETIME timestamp to a :class:`datetime`, or
158
+ ``None`` if the value is zero (not available)."""
159
+ if self.date_created == 0:
160
+ return None
161
+ return datetime.fromtimestamp(
162
+ self.date_created / 10_000_000 - 11644473600
163
+ )
164
+
165
+ @property
166
+ def accessed_time(self):
167
+ """Convert the FILETIME timestamp to a :class:`datetime`, or
168
+ ``None`` if the value is zero (not available)."""
169
+ if self.date_accessed == 0:
170
+ return None
171
+ return datetime.fromtimestamp(
172
+ self.date_accessed / 10_000_000 - 11644473600
173
+ )
174
+
175
+ @property
176
+ def attr_str(self):
177
+ """Human-readable attribute string, e.g. ``"DA"`` for a normal
178
+ directory with the archive bit set."""
179
+ attr_map = {
180
+ 0x01: "R", 0x02: "H", 0x04: "S", 0x10: "D",
181
+ 0x20: "A", 0x40: "C", 0x80: "N", 0x100: "T",
182
+ 0x200: "O", 0x1000: "I",
183
+ }
184
+ result = []
185
+ for bit, char in attr_map.items():
186
+ if self.attributes & bit:
187
+ result.append(char)
188
+ return "".join(result) if result else "-"
189
+
190
+
191
+ class EverythingClient:
192
+ """High-level wrapper around the Everything v3 DLL.
193
+
194
+ Args:
195
+ dll_path: Optional path to ``Everything3_x64.dll``. When *None*
196
+ (the default), the constructor searches for the DLL in several
197
+ well-known locations — see :meth:`_find_dll` for details.
198
+
199
+ Example::
200
+
201
+ with EverythingClient() as client:
202
+ client.connect()
203
+ results, total = client.search("*.pdf")
204
+ """
205
+
206
+ def __init__(self, dll_path=None):
207
+ if dll_path is None:
208
+ dll_path = self._find_dll()
209
+
210
+ if not os.path.exists(dll_path):
211
+ raise FileNotFoundError(
212
+ f"Everything3 DLL not found: {dll_path}"
213
+ )
214
+
215
+ self._dll = ctypes.WinDLL(dll_path)
216
+ self._client = None
217
+ self._setup_function_signatures()
218
+
219
+ # ------------------------------------------------------------------
220
+ # DLL discovery
221
+ # ------------------------------------------------------------------
222
+
223
+ @staticmethod
224
+ def _find_dll():
225
+ """Locate ``Everything3_x64.dll`` using a multi-strategy search.
226
+
227
+ Strategies (checked in order):
228
+
229
+ 1. ``EVERYTHING_SDK_DIR`` environment variable — looks for
230
+ ``<dir>/dll/Everything3_x64.dll`` or ``<dir>/Everything3_x64.dll``.
231
+ 2. ``PATH`` environment variable — scans every directory for
232
+ ``Everything3_x64.dll``.
233
+ 3. Relative to this module — looks in sibling ``Everything-SDK``
234
+ directories that are commonly found in developer setups.
235
+ 4. Common installation directories — checks ``Program Files`` and
236
+ ``Program Files (x86)`` under an ``Everything`` sub-folder.
237
+
238
+ Returns:
239
+ Absolute path to the DLL.
240
+
241
+ Raises:
242
+ FileNotFoundError: If none of the strategies found the DLL.
243
+ """
244
+ # Strategy 1: Check EVERYTHING_SDK_DIR environment variable
245
+ sdk_dir = os.environ.get("EVERYTHING_SDK_DIR")
246
+ if sdk_dir:
247
+ dll_path = os.path.join(sdk_dir, "dll", "Everything3_x64.dll")
248
+ if os.path.exists(dll_path):
249
+ return dll_path
250
+ dll_path = os.path.join(sdk_dir, "Everything3_x64.dll")
251
+ if os.path.exists(dll_path):
252
+ return dll_path
253
+
254
+ # Strategy 2: Check PATH environment variable
255
+ path_env = os.environ.get("PATH", "")
256
+ for path_dir in path_env.split(os.pathsep):
257
+ dll_path = os.path.join(path_dir, "Everything3_x64.dll")
258
+ if os.path.exists(dll_path):
259
+ return dll_path
260
+
261
+ # Strategy 3: Search relative to the SDK module location
262
+ module_dir = os.path.dirname(os.path.abspath(__file__))
263
+ for search_dir in [
264
+ os.path.join(module_dir, "..", "..", "Everything-SDK"),
265
+ os.path.join(module_dir, "..", "..", "..", "Everything-SDK"),
266
+ ]:
267
+ search_dir = os.path.normpath(search_dir)
268
+ if os.path.exists(search_dir):
269
+ for dll_subdir in ["dll", "SDK/DLL/x64", ""]:
270
+ dll_path = os.path.join(
271
+ search_dir, dll_subdir, "Everything3_x64.dll"
272
+ )
273
+ if os.path.exists(dll_path):
274
+ return dll_path
275
+
276
+ # Strategy 4: Common installation locations
277
+ common_paths = [
278
+ os.path.join(
279
+ os.environ.get("ProgramFiles", "C:\\Program Files"),
280
+ "Everything", "Everything3_x64.dll",
281
+ ),
282
+ os.path.join(
283
+ os.environ.get("ProgramFiles(x86)",
284
+ "C:\\Program Files (x86)"),
285
+ "Everything", "Everything3_x64.dll",
286
+ ),
287
+ ]
288
+ for dll_path in common_paths:
289
+ if os.path.exists(dll_path):
290
+ return dll_path
291
+
292
+ raise FileNotFoundError(
293
+ "Cannot find Everything3_x64.dll. Tried:\n"
294
+ " 1. EVERYTHING_SDK_DIR environment variable, or\n"
295
+ " 2. PATH environment variable, or\n"
296
+ " 3. Passing dll_path directly to EverythingClient()"
297
+ )
298
+
299
+ # ------------------------------------------------------------------
300
+ # DLL function signature setup
301
+ # ------------------------------------------------------------------
302
+
303
+ def _setup_function_signatures(self):
304
+ """Declare ``restype`` / ``argtypes`` for every DLL export we use.
305
+
306
+ This avoids ctypes calling-convention mis-detection and improves
307
+ both safety and error messages when argument types are wrong.
308
+ """
309
+ dll = self._dll
310
+
311
+ # Client lifecycle
312
+ dll.Everything3_ConnectW.restype = ctypes.c_void_p
313
+ dll.Everything3_ConnectW.argtypes = [ctypes.c_wchar_p]
314
+
315
+ dll.Everything3_DestroyClient.restype = wintypes.BOOL
316
+ dll.Everything3_DestroyClient.argtypes = [ctypes.c_void_p]
317
+
318
+ dll.Everything3_GetLastError.restype = wintypes.DWORD
319
+ dll.Everything3_GetLastError.argtypes = []
320
+
321
+ # Version information
322
+ dll.Everything3_GetMajorVersion.restype = wintypes.DWORD
323
+ dll.Everything3_GetMajorVersion.argtypes = [ctypes.c_void_p]
324
+
325
+ dll.Everything3_GetMinorVersion.restype = wintypes.DWORD
326
+ dll.Everything3_GetMinorVersion.argtypes = [ctypes.c_void_p]
327
+
328
+ dll.Everything3_GetRevision.restype = wintypes.DWORD
329
+ dll.Everything3_GetRevision.argtypes = [ctypes.c_void_p]
330
+
331
+ dll.Everything3_GetBuildNumber.restype = wintypes.DWORD
332
+ dll.Everything3_GetBuildNumber.argtypes = [ctypes.c_void_p]
333
+
334
+ # Database status
335
+ dll.Everything3_IsDBLoaded.restype = wintypes.BOOL
336
+ dll.Everything3_IsDBLoaded.argtypes = [ctypes.c_void_p]
337
+
338
+ dll.Everything3_GetTargetMachine.restype = wintypes.DWORD
339
+ dll.Everything3_GetTargetMachine.argtypes = [ctypes.c_void_p]
340
+
341
+ # Search state management
342
+ dll.Everything3_CreateSearchState.restype = ctypes.c_void_p
343
+ dll.Everything3_CreateSearchState.argtypes = []
344
+
345
+ dll.Everything3_DestroySearchState.restype = wintypes.BOOL
346
+ dll.Everything3_DestroySearchState.argtypes = [ctypes.c_void_p]
347
+
348
+ # Search options
349
+ dll.Everything3_SetSearchTextW.restype = wintypes.BOOL
350
+ dll.Everything3_SetSearchTextW.argtypes = [
351
+ ctypes.c_void_p, ctypes.c_wchar_p,
352
+ ]
353
+
354
+ dll.Everything3_SetSearchMatchPath.restype = wintypes.BOOL
355
+ dll.Everything3_SetSearchMatchPath.argtypes = [
356
+ ctypes.c_void_p, wintypes.BOOL,
357
+ ]
358
+
359
+ dll.Everything3_SetSearchMatchCase.restype = wintypes.BOOL
360
+ dll.Everything3_SetSearchMatchCase.argtypes = [
361
+ ctypes.c_void_p, wintypes.BOOL,
362
+ ]
363
+
364
+ dll.Everything3_SetSearchMatchWholeWords.restype = wintypes.BOOL
365
+ dll.Everything3_SetSearchMatchWholeWords.argtypes = [
366
+ ctypes.c_void_p, wintypes.BOOL,
367
+ ]
368
+
369
+ dll.Everything3_SetSearchRegex.restype = wintypes.BOOL
370
+ dll.Everything3_SetSearchRegex.argtypes = [
371
+ ctypes.c_void_p, wintypes.BOOL,
372
+ ]
373
+
374
+ # Viewport (pagination) control
375
+ dll.Everything3_SetSearchViewportCount.restype = wintypes.BOOL
376
+ dll.Everything3_SetSearchViewportCount.argtypes = [
377
+ ctypes.c_void_p, ctypes.c_size_t,
378
+ ]
379
+
380
+ dll.Everything3_SetSearchViewportOffset.restype = wintypes.BOOL
381
+ dll.Everything3_SetSearchViewportOffset.argtypes = [
382
+ ctypes.c_void_p, ctypes.c_size_t,
383
+ ]
384
+
385
+ # Property request / sort
386
+ dll.Everything3_AddSearchPropertyRequest.restype = wintypes.BOOL
387
+ dll.Everything3_AddSearchPropertyRequest.argtypes = [
388
+ ctypes.c_void_p, wintypes.DWORD,
389
+ ]
390
+
391
+ dll.Everything3_AddSearchSort.restype = wintypes.BOOL
392
+ dll.Everything3_AddSearchSort.argtypes = [
393
+ ctypes.c_void_p, wintypes.DWORD, wintypes.BOOL,
394
+ ]
395
+
396
+ dll.Everything3_ClearSearchSorts.restype = wintypes.BOOL
397
+ dll.Everything3_ClearSearchSorts.argtypes = [ctypes.c_void_p]
398
+
399
+ # Execution & result list
400
+ dll.Everything3_Search.restype = ctypes.c_void_p
401
+ dll.Everything3_Search.argtypes = [
402
+ ctypes.c_void_p, ctypes.c_void_p,
403
+ ]
404
+
405
+ dll.Everything3_DestroyResultList.restype = wintypes.BOOL
406
+ dll.Everything3_DestroyResultList.argtypes = [ctypes.c_void_p]
407
+
408
+ dll.Everything3_GetResultListCount.restype = ctypes.c_size_t
409
+ dll.Everything3_GetResultListCount.argtypes = [ctypes.c_void_p]
410
+
411
+ dll.Everything3_GetResultListViewportCount.restype = ctypes.c_size_t
412
+ dll.Everything3_GetResultListViewportCount.argtypes = [
413
+ ctypes.c_void_p,
414
+ ]
415
+
416
+ # Result property accessors
417
+ dll.Everything3_GetResultPropertyUINT64.restype = ctypes.c_uint64
418
+ dll.Everything3_GetResultPropertyUINT64.argtypes = [
419
+ ctypes.c_void_p, ctypes.c_size_t, wintypes.DWORD,
420
+ ]
421
+
422
+ dll.Everything3_GetResultPropertyDWORD.restype = wintypes.DWORD
423
+ dll.Everything3_GetResultPropertyDWORD.argtypes = [
424
+ ctypes.c_void_p, ctypes.c_size_t, wintypes.DWORD,
425
+ ]
426
+
427
+ dll.Everything3_GetResultPropertyTextW.restype = ctypes.c_size_t
428
+ dll.Everything3_GetResultPropertyTextW.argtypes = [
429
+ ctypes.c_void_p, ctypes.c_size_t, wintypes.DWORD,
430
+ ctypes.c_wchar_p, ctypes.c_size_t,
431
+ ]
432
+
433
+ dll.Everything3_IsFolderResult.restype = wintypes.BOOL
434
+ dll.Everything3_IsFolderResult.argtypes = [
435
+ ctypes.c_void_p, ctypes.c_size_t,
436
+ ]
437
+
438
+ dll.Everything3_GetResultFullPathNameW.restype = ctypes.c_size_t
439
+ dll.Everything3_GetResultFullPathNameW.argtypes = [
440
+ ctypes.c_void_p, ctypes.c_size_t, ctypes.c_wchar_p,
441
+ ctypes.c_size_t,
442
+ ]
443
+
444
+ # Folder size query
445
+ dll.Everything3_GetFolderSizeFromFilenameW.restype = ctypes.c_uint64
446
+ dll.Everything3_GetFolderSizeFromFilenameW.argtypes = [
447
+ ctypes.c_void_p, ctypes.c_wchar_p,
448
+ ]
449
+
450
+ # ------------------------------------------------------------------
451
+ # Public connection API
452
+ # ------------------------------------------------------------------
453
+
454
+ def connect(self, version="1.5.0.1409a"):
455
+ """Connect to a running Everything instance.
456
+
457
+ Args:
458
+ version: API version string to request (e.g. ``"1.5.0.1409a"``).
459
+
460
+ Returns:
461
+ *self*, to allow chaining with the constructor.
462
+
463
+ Raises:
464
+ EverythingError: If the connection attempt fails.
465
+ """
466
+ self._client = self._dll.Everything3_ConnectW(version)
467
+ if not self._client:
468
+ error = self._dll.Everything3_GetLastError()
469
+ raise EverythingError(error)
470
+ return self
471
+
472
+ def disconnect(self):
473
+ """Gracefully disconnect and release the client handle."""
474
+ if self._client:
475
+ self._dll.Everything3_DestroyClient(self._client)
476
+ self._client = None
477
+
478
+ # Context-manager support
479
+
480
+ def __enter__(self):
481
+ return self
482
+
483
+ def __exit__(self, exc_type, exc_val, exc_tb):
484
+ self.disconnect()
485
+
486
+ # ------------------------------------------------------------------
487
+ # Informational properties
488
+ # ------------------------------------------------------------------
489
+
490
+ @property
491
+ def version(self):
492
+ """Short version string, e.g. ``"1.5"``."""
493
+ if not self._client:
494
+ raise RuntimeError("Not connected to Everything")
495
+ major = self._dll.Everything3_GetMajorVersion(self._client)
496
+ minor = self._dll.Everything3_GetMinorVersion(self._client)
497
+ return f"{major}.{minor}"
498
+
499
+ @property
500
+ def full_version(self):
501
+ """Full version string including revision and build, e.g.
502
+ ``"1.5.0.1234"``."""
503
+ if not self._client:
504
+ raise RuntimeError("Not connected to Everything")
505
+ major = self._dll.Everything3_GetMajorVersion(self._client)
506
+ minor = self._dll.Everything3_GetMinorVersion(self._client)
507
+ revision = self._dll.Everything3_GetRevision(self._client)
508
+ build = self._dll.Everything3_GetBuildNumber(self._client)
509
+ return f"{major}.{minor}.{revision}.{build}"
510
+
511
+ def is_db_loaded(self):
512
+ """Return ``True`` if the Everything database has finished
513
+ loading."""
514
+ if not self._client:
515
+ raise RuntimeError("Not connected to Everything")
516
+ return bool(self._dll.Everything3_IsDBLoaded(self._client))
517
+
518
+ def get_target_machine(self):
519
+ """Return the target machine architecture code (0 = x86, 1 =
520
+ x64)."""
521
+ if not self._client:
522
+ raise RuntimeError("Not connected to Everything")
523
+ return self._dll.Everything3_GetTargetMachine(self._client)
524
+
525
+ # ------------------------------------------------------------------
526
+ # Search
527
+ # ------------------------------------------------------------------
528
+
529
+ def search(self, search_text, match_path=False, match_case=False,
530
+ match_whole_word=False, regex=False, properties=None,
531
+ sort=None, max_results=None, offset=None):
532
+ """Execute an Everything search query.
533
+
534
+ Args:
535
+ search_text: Everything query string (e.g. ``"*.txt"``,
536
+ ``parent:"C:\\\\Users"``).
537
+ match_path: Match against full file paths as well as names.
538
+ match_case: Perform a case-sensitive match.
539
+ match_whole_word: Only match whole words.
540
+ regex: Treat *search_text* as a regular expression.
541
+ properties: List of :class:`PropertyID` values to request.
542
+ Defaults to NAME, PATH, SIZE, DATE_CREATED, DATE_MODIFIED,
543
+ ATTRIBUTES, and EXTENSION.
544
+ sort: List of ``(PropertyID, ascending)`` tuples for
545
+ server-side result ordering.
546
+ max_results: Maximum number of results to return (viewport
547
+ count).
548
+ offset: Result offset for pagination.
549
+
550
+ Returns:
551
+ A tuple ``(results, total_count)`` where *results* is a list
552
+ of :class:`SearchResult` instances.
553
+
554
+ Raises:
555
+ RuntimeError: If the client is not connected.
556
+ EverythingError: If the DLL reports an error during the search.
557
+ """
558
+ if not self._client:
559
+ raise RuntimeError("Not connected to Everything")
560
+
561
+ if properties is None:
562
+ properties = [
563
+ PropertyID.NAME, PropertyID.PATH, PropertyID.SIZE,
564
+ PropertyID.DATE_CREATED, PropertyID.DATE_MODIFIED,
565
+ PropertyID.ATTRIBUTES, PropertyID.EXTENSION,
566
+ ]
567
+
568
+ search_state = self._dll.Everything3_CreateSearchState()
569
+ if not search_state:
570
+ raise RuntimeError("Failed to create search state")
571
+
572
+ try:
573
+ # Configure search options
574
+ self._dll.Everything3_SetSearchTextW(search_state, search_text)
575
+ self._dll.Everything3_SetSearchMatchPath(
576
+ search_state, match_path
577
+ )
578
+ self._dll.Everything3_SetSearchMatchCase(
579
+ search_state, match_case
580
+ )
581
+ self._dll.Everything3_SetSearchMatchWholeWords(
582
+ search_state, match_whole_word
583
+ )
584
+ self._dll.Everything3_SetSearchRegex(search_state, regex)
585
+
586
+ # Pagination
587
+ if max_results is not None:
588
+ self._dll.Everything3_SetSearchViewportCount(
589
+ search_state, max_results
590
+ )
591
+ if offset is not None:
592
+ self._dll.Everything3_SetSearchViewportOffset(
593
+ search_state, offset
594
+ )
595
+
596
+ # Request specific properties
597
+ for prop_id in properties:
598
+ self._dll.Everything3_AddSearchPropertyRequest(
599
+ search_state, prop_id
600
+ )
601
+
602
+ # Server-side sorting
603
+ if sort:
604
+ self._dll.Everything3_ClearSearchSorts(search_state)
605
+ for prop_id, ascending in sort:
606
+ self._dll.Everything3_AddSearchSort(
607
+ search_state, prop_id, ascending
608
+ )
609
+
610
+ # Execute
611
+ result_list = self._dll.Everything3_Search(
612
+ self._client, search_state
613
+ )
614
+ if not result_list:
615
+ error = self._dll.Everything3_GetLastError()
616
+ raise EverythingError(error)
617
+
618
+ try:
619
+ return self._parse_results(result_list, max_results)
620
+ finally:
621
+ self._dll.Everything3_DestroyResultList(result_list)
622
+ finally:
623
+ self._dll.Everything3_DestroySearchState(search_state)
624
+
625
+ # ------------------------------------------------------------------
626
+ # Internal result parsing
627
+ # ------------------------------------------------------------------
628
+
629
+ def _parse_results(self, result_list, max_results=None):
630
+ """Convert a raw DLL result list into Python :class:`SearchResult`
631
+ objects.
632
+
633
+ Args:
634
+ result_list: Opaque pointer returned by ``Everything3_Search``.
635
+ max_results: Optional cap on how many results to extract.
636
+
637
+ Returns:
638
+ A tuple ``(results, total_count)``.
639
+ """
640
+ num_results = self._dll.Everything3_GetResultListViewportCount(
641
+ result_list
642
+ )
643
+ total_results = self._dll.Everything3_GetResultListCount(
644
+ result_list
645
+ )
646
+
647
+ if max_results is not None:
648
+ num_results = min(num_results, max_results)
649
+
650
+ # Pre-allocate reusable buffers to avoid per-iteration allocations
651
+ name_buf = ctypes.create_unicode_buffer(260)
652
+ path_buf = ctypes.create_unicode_buffer(1024)
653
+ ext_buf = ctypes.create_unicode_buffer(64)
654
+
655
+ results = []
656
+ for i in range(num_results):
657
+ is_folder = bool(
658
+ self._dll.Everything3_IsFolderResult(result_list, i)
659
+ )
660
+
661
+ # File name
662
+ self._dll.Everything3_GetResultPropertyTextW(
663
+ result_list, i, PropertyID.NAME, name_buf, 260
664
+ )
665
+ file_name = name_buf.value
666
+
667
+ # Parent directory path
668
+ self._dll.Everything3_GetResultPropertyTextW(
669
+ result_list, i, PropertyID.PATH, path_buf, 1024
670
+ )
671
+ parent_path = path_buf.value
672
+
673
+ if parent_path and not parent_path.endswith("\\"):
674
+ parent_path += "\\"
675
+ full_path = os.path.join(parent_path, file_name)
676
+
677
+ # Numeric properties
678
+ size_val = self._dll.Everything3_GetResultPropertyUINT64(
679
+ result_list, i, PropertyID.SIZE
680
+ )
681
+ date_modified = self._dll.Everything3_GetResultPropertyUINT64(
682
+ result_list, i, PropertyID.DATE_MODIFIED
683
+ )
684
+ date_created = self._dll.Everything3_GetResultPropertyUINT64(
685
+ result_list, i, PropertyID.DATE_CREATED
686
+ )
687
+ date_accessed = self._dll.Everything3_GetResultPropertyUINT64(
688
+ result_list, i, PropertyID.DATE_ACCESSED
689
+ )
690
+ attrs_val = self._dll.Everything3_GetResultPropertyDWORD(
691
+ result_list, i, PropertyID.ATTRIBUTES
692
+ )
693
+
694
+ # Extension (files only — folders return an empty string)
695
+ extension = ""
696
+ if not is_folder:
697
+ self._dll.Everything3_GetResultPropertyTextW(
698
+ result_list, i, PropertyID.EXTENSION, ext_buf, 64
699
+ )
700
+ extension = ext_buf.value
701
+
702
+ results.append(SearchResult(
703
+ name=file_name,
704
+ parent_path=parent_path,
705
+ full_path=full_path,
706
+ size=size_val,
707
+ is_folder=is_folder,
708
+ date_modified=date_modified,
709
+ date_created=date_created,
710
+ date_accessed=date_accessed,
711
+ attributes=attrs_val,
712
+ extension=extension,
713
+ ))
714
+
715
+ return results, total_results
716
+
717
+ # ------------------------------------------------------------------
718
+ # Folder size helper
719
+ # ------------------------------------------------------------------
720
+
721
+ def get_folder_size(self, folder_path):
722
+ """Retrieve the on-disk size of a folder by querying Everything.
723
+
724
+ Args:
725
+ folder_path: Absolute path to the folder.
726
+
727
+ Returns:
728
+ Size in bytes.
729
+ """
730
+ if not self._client:
731
+ raise RuntimeError("Not connected to Everything")
732
+ return self._dll.Everything3_GetFolderSizeFromFilenameW(
733
+ self._client, folder_path
734
+ )
735
+
736
+
737
+ # ------------------------------------------------------------------
738
+ # Standalone utility
739
+ # ------------------------------------------------------------------
740
+
741
+ def format_size(size_bytes):
742
+ """Format a byte count into a human-readable string.
743
+
744
+ Examples::
745
+
746
+ >>> format_size(1024)
747
+ '1.00 KB'
748
+ >>> format_size(1073741824)
749
+ '1.00 GB'
750
+ """
751
+ if size_bytes == 0:
752
+ return "0 B"
753
+ for unit in ("B", "KB", "MB", "GB", "TB"):
754
+ if size_bytes < 1024.0:
755
+ return f"{size_bytes:.2f} {unit}"
756
+ size_bytes /= 1024.0
757
+ return f"{size_bytes:.2f} PB"
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: everything-sdk
3
+ Version: 1.0.0
4
+ Summary: Python wrapper for the Windows Everything SDK (v3)
5
+ Home-page:
6
+ Author: kinsurfong
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/firesurfing/everything-sdk-python
9
+ Project-URL: Documentation, https://github.com/firesurfing/everything-sdk-python#readme
10
+ Project-URL: Issues, https://github.com/firesurfing/everything-sdk-python/issues
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.7
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Dynamic: license-file
21
+ Dynamic: requires-python
22
+
23
+ # Everything SDK Python
24
+
25
+ Python wrapper for the Windows Everything SDK (v3). Provides a clean, Pythonic interface to search files and folders using the [Everything](https://www.voidtools.com/) search engine.
26
+
27
+ [中文文档](README_CN.md)
28
+
29
+ ## Requirements
30
+
31
+ - Windows OS
32
+ - Python 3.7+
33
+ - [Everything](https://www.voidtools.com/) application running (v1.5.0.1409a recommended)
34
+ - Everything SDK v3 DLL (`Everything3_x64.dll`)
35
+
36
+ ### Getting the SDK
37
+
38
+ Download SDK v3 from the Everything official forum: [SDK Download](https://www.voidtools.com/forum/viewtopic.php?t=15853&sid=b9bf71c12ae48b33567ab9f8dafdaccc)
39
+
40
+ Recommended version: **Everything SDK v3** (bundled with Everything 1.5.0.1409a)
41
+
42
+ ### Getting Everything 1.5
43
+
44
+ Download Everything 1.5 from the official site: [Everything 1.5 Download](https://www.voidtools.com/everything-1.5/)
45
+
46
+ > Note: This library requires the Everything application to be running.
47
+
48
+ ## Installation
49
+
50
+ ### From source
51
+
52
+ Download or clone the project repository, then:
53
+
54
+ ```bash
55
+ cd everything-sdk-python
56
+ pip install -e .
57
+ ```
58
+
59
+ ### Direct usage
60
+
61
+ Copy the `src/everything` folder to your project and import directly.
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ from everything import EverythingClient, PropertyID, format_size
67
+
68
+ with EverythingClient() as client:
69
+ client.connect("1.5")
70
+ print(f"Everything version: {client.version}")
71
+
72
+ # Search files in a directory
73
+ results, total = client.search('parent:"D:\\\\test"', match_path=True)
74
+ print(f"Found {len(results)} results (total: {total})")
75
+
76
+ for r in results:
77
+ if r.is_folder:
78
+ size = client.get_folder_size(r.full_path)
79
+ else:
80
+ size = r.size
81
+ print(f"{r.name}: {format_size(size)}")
82
+ ```
83
+
84
+ ## API Reference
85
+
86
+ ### EverythingClient
87
+
88
+ #### `__init__(dll_path=None)`
89
+
90
+ Initialize the client. DLL discovery order:
91
+
92
+ 1. If `dll_path` is specified, use it directly
93
+ 2. Check `EVERYTHING_SDK_DIR` environment variable, use `<SDK_DIR>/dll/Everything3_x64.dll`
94
+ 3. Search all directories in `PATH` environment variable for `Everything3_x64.dll`
95
+
96
+ #### `connect(version="1.5a")`
97
+
98
+ Connect to the Everything application. Raises `EverythingError` if connection fails.
99
+
100
+ #### `disconnect()`
101
+
102
+ Disconnect from Everything.
103
+
104
+ #### Context Manager
105
+
106
+ Supports `with` statement for automatic connection management:
107
+
108
+ ```python
109
+ with EverythingClient() as client:
110
+ client.connect("1.5a")
111
+ # ... use client
112
+ # automatically disconnected
113
+ ```
114
+
115
+ #### `version` (property)
116
+
117
+ Returns the Everything version string, e.g., `"1.5"`.
118
+
119
+ #### `full_version` (property)
120
+
121
+ Returns the full version string including major, minor, revision and build number, e.g., `"1.5.0.1234"`.
122
+
123
+ #### `is_db_loaded()`
124
+
125
+ Check if the Everything database has finished loading.
126
+
127
+ #### `get_target_machine()`
128
+
129
+ Get the target machine architecture (x86 or x64).
130
+
131
+ #### `search(search_text, match_path=False, match_case=False, match_whole_word=False, regex=False, properties=None, sort=None, max_results=None, offset=None)`
132
+
133
+ Execute a search.
134
+
135
+ - `search_text`: Everything search query (e.g., `parent:"D:\\test"`, `*.txt`, etc.)
136
+ - `match_path`: Whether to match against full paths
137
+ - `match_case`: Whether to match case
138
+ - `match_whole_word`: Whether to match whole words
139
+ - `regex`: Whether to use regular expressions
140
+ - `properties`: List of `PropertyID` values to retrieve. Defaults to NAME, PATH, SIZE, DATE_CREATED, DATE_MODIFIED, ATTRIBUTES, EXTENSION.
141
+ - `sort`: List of `(PropertyID, ascending)` tuples for server-side sorting.
142
+ - `max_results`: Maximum number of results to return.
143
+ - `offset`: Result offset for pagination.
144
+
145
+ Returns: `(results_list, total_count)` tuple.
146
+
147
+ #### `get_folder_size(folder_path)`
148
+
149
+ Get the actual size of a folder by querying Everything. Returns size in bytes.
150
+
151
+ ### SearchResult
152
+
153
+ Represents a single search result with the following attributes:
154
+
155
+ - `name`: File/folder name
156
+ - `parent_path`: Parent directory path
157
+ - `full_path`: Full file/folder path
158
+ - `size`: Size in bytes (for files; folders may show 0 - use `get_folder_size` instead)
159
+ - `is_folder`: Boolean indicating if it's a folder
160
+ - `date_modified`: FILETIME timestamp
161
+ - `date_created`: FILETIME timestamp
162
+ - `date_accessed`: FILETIME timestamp
163
+ - `attributes`: File attribute bitmask
164
+ - `extension`: File extension (without dot)
165
+
166
+ Properties:
167
+ - `type_str`: `"File"` or `"Folder"`
168
+ - `modified_time`: Python `datetime` object
169
+ - `created_time`: Python `datetime` object
170
+ - `accessed_time`: Python `datetime` object
171
+ - `attr_str`: Human-readable attribute string (e.g., `"DA"`)
172
+
173
+ ### PropertyID
174
+
175
+ Constants for all available property IDs:
176
+
177
+ ```python
178
+ PropertyID.NAME # 0
179
+ PropertyID.PATH # 1
180
+ PropertyID.SIZE # 2
181
+ PropertyID.EXTENSION # 3
182
+ PropertyID.TYPE # 4
183
+ PropertyID.DATE_MODIFIED # 5
184
+ PropertyID.DATE_CREATED # 6
185
+ PropertyID.DATE_ACCESSED # 7
186
+ PropertyID.ATTRIBUTES # 8
187
+ # ... and more
188
+ ```
189
+
190
+ ### format_size(size_bytes)
191
+
192
+ Format bytes into human-readable string:
193
+
194
+ ```python
195
+ format_size(1024) # "1.00 KB"
196
+ format_size(1048576) # "1.00 MB"
197
+ format_size(1073741824) # "1.00 GB"
198
+ ```
199
+
200
+ ### EverythingError
201
+
202
+ Exception raised on SDK errors. Includes `error_code` attribute.
203
+
204
+ ## Advanced Usage
205
+
206
+ ### Custom Properties
207
+
208
+ ```python
209
+ results, total = client.search(
210
+ search_text='*.txt',
211
+ properties=[
212
+ PropertyID.NAME,
213
+ PropertyID.PATH,
214
+ PropertyID.SIZE,
215
+ PropertyID.DATE_MODIFIED,
216
+ ]
217
+ )
218
+ ```
219
+
220
+ ### Sorting
221
+
222
+ ```python
223
+ # Sort by size ascending, then by name descending
224
+ sort = [
225
+ (PropertyID.SIZE, True),
226
+ (PropertyID.NAME, False),
227
+ ]
228
+ results, total = client.search(
229
+ search_text='parent:"D:\\\\test"',
230
+ match_path=True,
231
+ sort=sort,
232
+ )
233
+ ```
234
+
235
+ ### Limit Results
236
+
237
+ ```python
238
+ results, total = client.search(
239
+ search_text='*.pdf',
240
+ max_results=10,
241
+ )
242
+ ```
243
+
244
+ ## License
245
+
246
+ MIT
@@ -0,0 +1,7 @@
1
+ everything/__init__.py,sha256=mU7OsdiV6n2myLUeh18y6MfjweUZ3wGU9E5CvdbrZQk,775
2
+ everything/client.py,sha256=9MloqOEngGt4SDzY2mtr4xOZ0h8aaB1bZv6uMicHtzw,26725
3
+ everything_sdk-1.0.0.dist-info/licenses/LICENSE,sha256=cgElLKZ4JL3Oa5WJVzhOxzQhSBbUIpxIf12LSo2DuEc,1068
4
+ everything_sdk-1.0.0.dist-info/METADATA,sha256=2qZwxnEbFgOhbWZn-imIHzIZMpBKw7CfEPpC5It_vyU,6857
5
+ everything_sdk-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ everything_sdk-1.0.0.dist-info/top_level.txt,sha256=64FxbamBy6gWEhZE4npTYLGiS_tFEc_7PBr6gaxnzaA,11
7
+ everything_sdk-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 firesurfing
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.
@@ -0,0 +1 @@
1
+ everything