ebk 0.4.4__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.
- ebk/__init__.py +35 -0
- ebk/ai/__init__.py +23 -0
- ebk/ai/knowledge_graph.py +450 -0
- ebk/ai/llm_providers/__init__.py +26 -0
- ebk/ai/llm_providers/anthropic.py +209 -0
- ebk/ai/llm_providers/base.py +295 -0
- ebk/ai/llm_providers/gemini.py +285 -0
- ebk/ai/llm_providers/ollama.py +294 -0
- ebk/ai/metadata_enrichment.py +394 -0
- ebk/ai/question_generator.py +328 -0
- ebk/ai/reading_companion.py +224 -0
- ebk/ai/semantic_search.py +433 -0
- ebk/ai/text_extractor.py +393 -0
- ebk/calibre_import.py +66 -0
- ebk/cli.py +6433 -0
- ebk/config.py +230 -0
- ebk/db/__init__.py +37 -0
- ebk/db/migrations.py +507 -0
- ebk/db/models.py +725 -0
- ebk/db/session.py +144 -0
- ebk/decorators.py +1 -0
- ebk/exports/__init__.py +0 -0
- ebk/exports/base_exporter.py +218 -0
- ebk/exports/echo_export.py +279 -0
- ebk/exports/html_library.py +1743 -0
- ebk/exports/html_utils.py +87 -0
- ebk/exports/hugo.py +59 -0
- ebk/exports/jinja_export.py +286 -0
- ebk/exports/multi_facet_export.py +159 -0
- ebk/exports/opds_export.py +232 -0
- ebk/exports/symlink_dag.py +479 -0
- ebk/exports/zip.py +25 -0
- ebk/extract_metadata.py +341 -0
- ebk/ident.py +89 -0
- ebk/library_db.py +1440 -0
- ebk/opds.py +748 -0
- ebk/plugins/__init__.py +42 -0
- ebk/plugins/base.py +502 -0
- ebk/plugins/hooks.py +442 -0
- ebk/plugins/registry.py +499 -0
- ebk/repl/__init__.py +9 -0
- ebk/repl/find.py +126 -0
- ebk/repl/grep.py +173 -0
- ebk/repl/shell.py +1677 -0
- ebk/repl/text_utils.py +320 -0
- ebk/search_parser.py +413 -0
- ebk/server.py +3608 -0
- ebk/services/__init__.py +28 -0
- ebk/services/annotation_extraction.py +351 -0
- ebk/services/annotation_service.py +380 -0
- ebk/services/export_service.py +577 -0
- ebk/services/import_service.py +447 -0
- ebk/services/personal_metadata_service.py +347 -0
- ebk/services/queue_service.py +253 -0
- ebk/services/tag_service.py +281 -0
- ebk/services/text_extraction.py +317 -0
- ebk/services/view_service.py +12 -0
- ebk/similarity/__init__.py +77 -0
- ebk/similarity/base.py +154 -0
- ebk/similarity/core.py +471 -0
- ebk/similarity/extractors.py +168 -0
- ebk/similarity/metrics.py +376 -0
- ebk/skills/SKILL.md +182 -0
- ebk/skills/__init__.py +1 -0
- ebk/vfs/__init__.py +101 -0
- ebk/vfs/base.py +298 -0
- ebk/vfs/library_vfs.py +122 -0
- ebk/vfs/nodes/__init__.py +54 -0
- ebk/vfs/nodes/authors.py +196 -0
- ebk/vfs/nodes/books.py +480 -0
- ebk/vfs/nodes/files.py +155 -0
- ebk/vfs/nodes/metadata.py +385 -0
- ebk/vfs/nodes/root.py +100 -0
- ebk/vfs/nodes/similar.py +165 -0
- ebk/vfs/nodes/subjects.py +184 -0
- ebk/vfs/nodes/tags.py +371 -0
- ebk/vfs/resolver.py +228 -0
- ebk/vfs_router.py +275 -0
- ebk/views/__init__.py +32 -0
- ebk/views/dsl.py +668 -0
- ebk/views/service.py +619 -0
- ebk-0.4.4.dist-info/METADATA +755 -0
- ebk-0.4.4.dist-info/RECORD +87 -0
- ebk-0.4.4.dist-info/WHEEL +5 -0
- ebk-0.4.4.dist-info/entry_points.txt +2 -0
- ebk-0.4.4.dist-info/licenses/LICENSE +21 -0
- ebk-0.4.4.dist-info/top_level.txt +1 -0
ebk/repl/grep.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""grep implementation for REPL shell."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import List, Tuple
|
|
5
|
+
|
|
6
|
+
from ebk.vfs import LibraryVFS, DirectoryNode, FileNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GrepMatcher:
|
|
10
|
+
"""Unix-like grep functionality for VFS."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, vfs: LibraryVFS):
|
|
13
|
+
"""Initialize grep matcher.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
vfs: VFS instance
|
|
17
|
+
"""
|
|
18
|
+
self.vfs = vfs
|
|
19
|
+
|
|
20
|
+
def grep(
|
|
21
|
+
self,
|
|
22
|
+
pattern: str,
|
|
23
|
+
paths: List[str],
|
|
24
|
+
recursive: bool = False,
|
|
25
|
+
ignore_case: bool = False,
|
|
26
|
+
line_numbers: bool = False,
|
|
27
|
+
) -> List[Tuple[str, int, str]]:
|
|
28
|
+
"""Search for pattern in files.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
pattern: Regex pattern to search for
|
|
32
|
+
paths: List of paths to search (files or directories)
|
|
33
|
+
recursive: Search directories recursively
|
|
34
|
+
ignore_case: Case-insensitive matching
|
|
35
|
+
line_numbers: Include line numbers in output
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
List of (file_path, line_number, line_content) tuples
|
|
39
|
+
"""
|
|
40
|
+
results = []
|
|
41
|
+
|
|
42
|
+
# Compile regex
|
|
43
|
+
flags = re.IGNORECASE if ignore_case else 0
|
|
44
|
+
try:
|
|
45
|
+
regex = re.compile(pattern, flags)
|
|
46
|
+
except re.error as e:
|
|
47
|
+
raise ValueError(f"Invalid regex pattern: {e}")
|
|
48
|
+
|
|
49
|
+
# Process each path
|
|
50
|
+
for path in paths:
|
|
51
|
+
results.extend(
|
|
52
|
+
self._search_path(path, regex, recursive, line_numbers)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
def _search_path(
|
|
58
|
+
self,
|
|
59
|
+
path: str,
|
|
60
|
+
regex: re.Pattern,
|
|
61
|
+
recursive: bool,
|
|
62
|
+
line_numbers: bool,
|
|
63
|
+
) -> List[Tuple[str, int, str]]:
|
|
64
|
+
"""Search a single path.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
path: Path to search
|
|
68
|
+
regex: Compiled regex pattern
|
|
69
|
+
recursive: Search recursively
|
|
70
|
+
line_numbers: Include line numbers
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
List of matches
|
|
74
|
+
"""
|
|
75
|
+
results = []
|
|
76
|
+
|
|
77
|
+
# Resolve path
|
|
78
|
+
node = self.vfs.get_node(path)
|
|
79
|
+
if node is None:
|
|
80
|
+
return results
|
|
81
|
+
|
|
82
|
+
# If it's a file, search it
|
|
83
|
+
if isinstance(node, FileNode):
|
|
84
|
+
results.extend(self._search_file(node, regex, line_numbers))
|
|
85
|
+
|
|
86
|
+
# If it's a directory, search children
|
|
87
|
+
elif isinstance(node, DirectoryNode):
|
|
88
|
+
if recursive:
|
|
89
|
+
results.extend(
|
|
90
|
+
self._search_directory_recursive(node, regex, line_numbers)
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
# Just search immediate children that are files
|
|
94
|
+
for child in node.list_children():
|
|
95
|
+
if isinstance(child, FileNode):
|
|
96
|
+
results.extend(
|
|
97
|
+
self._search_file(child, regex, line_numbers)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return results
|
|
101
|
+
|
|
102
|
+
def _search_file(
|
|
103
|
+
self,
|
|
104
|
+
file_node: FileNode,
|
|
105
|
+
regex: re.Pattern,
|
|
106
|
+
line_numbers: bool,
|
|
107
|
+
) -> List[Tuple[str, int, str]]:
|
|
108
|
+
"""Search a single file.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
file_node: File node to search
|
|
112
|
+
regex: Compiled regex pattern
|
|
113
|
+
line_numbers: Include line numbers
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of matches
|
|
117
|
+
"""
|
|
118
|
+
results = []
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
content = file_node.read_content()
|
|
122
|
+
lines = content.split("\n")
|
|
123
|
+
|
|
124
|
+
file_path = file_node.get_path()
|
|
125
|
+
|
|
126
|
+
for i, line in enumerate(lines, start=1):
|
|
127
|
+
if regex.search(line):
|
|
128
|
+
line_num = i if line_numbers else 0
|
|
129
|
+
results.append((file_path, line_num, line))
|
|
130
|
+
|
|
131
|
+
except Exception:
|
|
132
|
+
# Skip files that can't be read
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
return results
|
|
136
|
+
|
|
137
|
+
def _search_directory_recursive(
|
|
138
|
+
self,
|
|
139
|
+
dir_node: DirectoryNode,
|
|
140
|
+
regex: re.Pattern,
|
|
141
|
+
line_numbers: bool,
|
|
142
|
+
) -> List[Tuple[str, int, str]]:
|
|
143
|
+
"""Search directory recursively.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
dir_node: Directory node to search
|
|
147
|
+
regex: Compiled regex pattern
|
|
148
|
+
line_numbers: Include line numbers
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
List of matches
|
|
152
|
+
"""
|
|
153
|
+
results = []
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
children = dir_node.list_children()
|
|
157
|
+
|
|
158
|
+
for child in children:
|
|
159
|
+
if isinstance(child, FileNode):
|
|
160
|
+
results.extend(self._search_file(child, regex, line_numbers))
|
|
161
|
+
elif isinstance(child, DirectoryNode):
|
|
162
|
+
# Recurse into subdirectory
|
|
163
|
+
results.extend(
|
|
164
|
+
self._search_directory_recursive(
|
|
165
|
+
child, regex, line_numbers
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
except Exception:
|
|
170
|
+
# Skip directories that can't be read
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
return results
|