janito 2.1.1__py3-none-any.whl → 2.3.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.
- janito/__init__.py +6 -6
- janito/agent/setup_agent.py +14 -5
- janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +3 -1
- janito/cli/chat_mode/bindings.py +6 -0
- janito/cli/chat_mode/session.py +16 -0
- janito/cli/chat_mode/shell/autocomplete.py +21 -21
- janito/cli/chat_mode/shell/commands/__init__.py +3 -2
- janito/cli/chat_mode/shell/commands/clear.py +12 -12
- janito/cli/chat_mode/shell/commands/exec.py +27 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -51
- janito/cli/chat_mode/shell/commands/tools.py +17 -6
- janito/cli/chat_mode/shell/input_history.py +62 -62
- janito/cli/chat_mode/shell/session/manager.py +1 -0
- janito/cli/chat_mode/toolbar.py +3 -1
- janito/cli/cli_commands/list_models.py +35 -35
- janito/cli/cli_commands/list_providers.py +9 -9
- janito/cli/cli_commands/list_tools.py +53 -53
- janito/cli/cli_commands/model_selection.py +50 -50
- janito/cli/cli_commands/model_utils.py +13 -2
- janito/cli/cli_commands/set_api_key.py +19 -19
- janito/cli/cli_commands/show_config.py +51 -51
- janito/cli/cli_commands/show_system_prompt.py +62 -62
- janito/cli/config.py +2 -1
- janito/cli/core/__init__.py +4 -4
- janito/cli/core/event_logger.py +59 -59
- janito/cli/core/getters.py +3 -1
- janito/cli/core/runner.py +27 -6
- janito/cli/core/setters.py +5 -1
- janito/cli/core/unsetters.py +54 -54
- janito/cli/main_cli.py +12 -1
- janito/cli/prompt_core.py +5 -2
- janito/cli/rich_terminal_reporter.py +22 -3
- janito/cli/single_shot_mode/__init__.py +6 -6
- janito/cli/single_shot_mode/handler.py +11 -1
- janito/cli/verbose_output.py +1 -1
- janito/config.py +5 -5
- janito/config_manager.py +2 -0
- janito/driver_events.py +14 -0
- janito/drivers/anthropic/driver.py +113 -113
- janito/drivers/azure_openai/driver.py +38 -3
- janito/drivers/driver_registry.py +0 -2
- janito/drivers/openai/driver.py +196 -36
- janito/formatting_token.py +54 -54
- janito/i18n/__init__.py +35 -35
- janito/i18n/messages.py +23 -23
- janito/i18n/pt.py +47 -47
- janito/llm/__init__.py +5 -5
- janito/llm/agent.py +443 -443
- janito/llm/auth.py +1 -0
- janito/llm/driver.py +7 -1
- janito/llm/driver_config.py +1 -0
- janito/llm/driver_config_builder.py +34 -34
- janito/llm/driver_input.py +12 -12
- janito/llm/message_parts.py +60 -60
- janito/llm/model.py +38 -38
- janito/llm/provider.py +196 -196
- janito/provider_config.py +7 -3
- janito/provider_registry.py +29 -5
- janito/providers/__init__.py +1 -0
- janito/providers/anthropic/model_info.py +22 -22
- janito/providers/anthropic/provider.py +2 -2
- janito/providers/azure_openai/model_info.py +7 -6
- janito/providers/azure_openai/provider.py +44 -2
- janito/providers/deepseek/__init__.py +1 -1
- janito/providers/deepseek/model_info.py +16 -16
- janito/providers/deepseek/provider.py +91 -91
- janito/providers/google/model_info.py +21 -29
- janito/providers/google/provider.py +49 -38
- janito/providers/mistralai/provider.py +2 -2
- janito/providers/openai/model_info.py +0 -11
- janito/providers/openai/provider.py +1 -1
- janito/providers/provider_static_info.py +2 -3
- janito/providers/registry.py +26 -26
- janito/tools/adapters/__init__.py +1 -1
- janito/tools/adapters/local/__init__.py +62 -62
- janito/tools/adapters/local/adapter.py +33 -11
- janito/tools/adapters/local/ask_user.py +102 -102
- janito/tools/adapters/local/copy_file.py +84 -84
- janito/tools/adapters/local/create_directory.py +69 -69
- janito/tools/adapters/local/create_file.py +82 -82
- janito/tools/adapters/local/delete_text_in_file.py +4 -7
- janito/tools/adapters/local/fetch_url.py +97 -97
- janito/tools/adapters/local/find_files.py +138 -140
- janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
- janito/tools/adapters/local/get_file_outline/core.py +117 -151
- janito/tools/adapters/local/get_file_outline/java_outline.py +40 -0
- janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
- janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
- janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
- janito/tools/adapters/local/move_file.py +3 -13
- janito/tools/adapters/local/open_html_in_browser.py +24 -29
- janito/tools/adapters/local/open_url.py +3 -2
- janito/tools/adapters/local/python_code_run.py +166 -166
- janito/tools/adapters/local/python_command_run.py +164 -164
- janito/tools/adapters/local/python_file_run.py +163 -163
- janito/tools/adapters/local/remove_directory.py +6 -17
- janito/tools/adapters/local/remove_file.py +9 -15
- janito/tools/adapters/local/replace_text_in_file.py +6 -9
- janito/tools/adapters/local/run_bash_command.py +176 -176
- janito/tools/adapters/local/run_powershell_command.py +219 -219
- janito/tools/adapters/local/search_text/__init__.py +1 -1
- janito/tools/adapters/local/search_text/core.py +201 -201
- janito/tools/adapters/local/search_text/match_lines.py +1 -1
- janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
- janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
- janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
- janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
- janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
- janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
- janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
- janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
- janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
- janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
- janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
- janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
- janito/tools/adapters/local/view_file.py +167 -167
- janito/tools/inspect_registry.py +17 -17
- janito/tools/tool_base.py +105 -105
- janito/tools/tool_events.py +58 -58
- janito/tools/tool_run_exception.py +12 -12
- janito/tools/tool_use_tracker.py +81 -81
- janito/tools/tool_utils.py +45 -45
- janito/tools/tools_adapter.py +78 -6
- janito/tools/tools_schema.py +104 -104
- janito/version.py +4 -4
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/METADATA +388 -232
- janito-2.3.0.dist-info/RECORD +181 -0
- janito-2.3.0.dist-info/licenses/LICENSE +21 -0
- janito/cli/chat_mode/shell/commands/last.py +0 -137
- janito/drivers/google_genai/driver.py +0 -54
- janito/drivers/google_genai/schema_generator.py +0 -67
- janito-2.1.1.dist-info/RECORD +0 -181
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/WHEEL +0 -0
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/entry_points.txt +0 -0
- {janito-2.1.1.dist-info → janito-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,82 +1,82 @@
|
|
1
|
-
import os
|
2
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
-
|
4
|
-
from janito.tools.tool_utils import display_path
|
5
|
-
from janito.tools.tool_base import ToolBase
|
6
|
-
from janito.report_events import ReportAction
|
7
|
-
from janito.i18n import tr
|
8
|
-
|
9
|
-
|
10
|
-
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
11
|
-
|
12
|
-
|
13
|
-
@register_local_tool
|
14
|
-
class CreateFileTool(ToolBase):
|
15
|
-
"""
|
16
|
-
Create a new file with the given content.
|
17
|
-
|
18
|
-
Args:
|
19
|
-
file_path (str): Path to the file to create.
|
20
|
-
content (str): Content to write to the file.
|
21
|
-
overwrite (bool, optional): Overwrite existing file if True. Default: False. Recommended only after reading the file to be overwritten.
|
22
|
-
Returns:
|
23
|
-
str: Status message indicating the result. Example:
|
24
|
-
- "✅ Successfully created the file at ..."
|
25
|
-
|
26
|
-
Note: Syntax validation is automatically performed after this operation.
|
27
|
-
"""
|
28
|
-
|
29
|
-
tool_name = "create_file"
|
30
|
-
|
31
|
-
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
32
|
-
expanded_file_path = file_path # Using file_path as is
|
33
|
-
disp_path = display_path(expanded_file_path)
|
34
|
-
file_path = expanded_file_path
|
35
|
-
if os.path.exists(file_path) and not overwrite:
|
36
|
-
try:
|
37
|
-
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
38
|
-
existing_content = f.read()
|
39
|
-
except Exception as e:
|
40
|
-
existing_content = f"[Error reading file: {e}]"
|
41
|
-
return tr(
|
42
|
-
"❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
|
43
|
-
disp_path=disp_path,
|
44
|
-
existing_content=existing_content,
|
45
|
-
)
|
46
|
-
# Determine if we are overwriting an existing file
|
47
|
-
is_overwrite = os.path.exists(file_path) and overwrite
|
48
|
-
if is_overwrite:
|
49
|
-
# Overwrite branch: log only overwrite warning (no create message)
|
50
|
-
self.report_action(
|
51
|
-
tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
|
52
|
-
ReportAction.CREATE,
|
53
|
-
)
|
54
|
-
dir_name = os.path.dirname(file_path)
|
55
|
-
if dir_name:
|
56
|
-
os.makedirs(dir_name, exist_ok=True)
|
57
|
-
if not is_overwrite:
|
58
|
-
# Create branch: log file creation message
|
59
|
-
self.report_action(
|
60
|
-
tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
|
61
|
-
ReportAction.CREATE,
|
62
|
-
)
|
63
|
-
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
64
|
-
f.write(content)
|
65
|
-
new_lines = content.count("\n") + 1 if content else 0
|
66
|
-
self.report_success(
|
67
|
-
tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
|
68
|
-
)
|
69
|
-
# Perform syntax validation and append result
|
70
|
-
validation_result = validate_file_syntax(file_path)
|
71
|
-
if is_overwrite:
|
72
|
-
# Overwrite branch: return minimal overwrite info to user
|
73
|
-
return (
|
74
|
-
tr("✅ {new_lines} lines", new_lines=new_lines)
|
75
|
-
+ f"\n{validation_result}"
|
76
|
-
)
|
77
|
-
else:
|
78
|
-
# Create branch: return detailed create success to user
|
79
|
-
return (
|
80
|
-
tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
|
81
|
-
+ f"\n{validation_result}"
|
82
|
-
)
|
1
|
+
import os
|
2
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
+
|
4
|
+
from janito.tools.tool_utils import display_path
|
5
|
+
from janito.tools.tool_base import ToolBase
|
6
|
+
from janito.report_events import ReportAction
|
7
|
+
from janito.i18n import tr
|
8
|
+
|
9
|
+
|
10
|
+
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
11
|
+
|
12
|
+
|
13
|
+
@register_local_tool
|
14
|
+
class CreateFileTool(ToolBase):
|
15
|
+
"""
|
16
|
+
Create a new file with the given content.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
file_path (str): Path to the file to create.
|
20
|
+
content (str): Content to write to the file.
|
21
|
+
overwrite (bool, optional): Overwrite existing file if True. Default: False. Recommended only after reading the file to be overwritten.
|
22
|
+
Returns:
|
23
|
+
str: Status message indicating the result. Example:
|
24
|
+
- "✅ Successfully created the file at ..."
|
25
|
+
|
26
|
+
Note: Syntax validation is automatically performed after this operation.
|
27
|
+
"""
|
28
|
+
|
29
|
+
tool_name = "create_file"
|
30
|
+
|
31
|
+
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
32
|
+
expanded_file_path = file_path # Using file_path as is
|
33
|
+
disp_path = display_path(expanded_file_path)
|
34
|
+
file_path = expanded_file_path
|
35
|
+
if os.path.exists(file_path) and not overwrite:
|
36
|
+
try:
|
37
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
38
|
+
existing_content = f.read()
|
39
|
+
except Exception as e:
|
40
|
+
existing_content = f"[Error reading file: {e}]"
|
41
|
+
return tr(
|
42
|
+
"❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
|
43
|
+
disp_path=disp_path,
|
44
|
+
existing_content=existing_content,
|
45
|
+
)
|
46
|
+
# Determine if we are overwriting an existing file
|
47
|
+
is_overwrite = os.path.exists(file_path) and overwrite
|
48
|
+
if is_overwrite:
|
49
|
+
# Overwrite branch: log only overwrite warning (no create message)
|
50
|
+
self.report_action(
|
51
|
+
tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
|
52
|
+
ReportAction.CREATE,
|
53
|
+
)
|
54
|
+
dir_name = os.path.dirname(file_path)
|
55
|
+
if dir_name:
|
56
|
+
os.makedirs(dir_name, exist_ok=True)
|
57
|
+
if not is_overwrite:
|
58
|
+
# Create branch: log file creation message
|
59
|
+
self.report_action(
|
60
|
+
tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
|
61
|
+
ReportAction.CREATE,
|
62
|
+
)
|
63
|
+
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
64
|
+
f.write(content)
|
65
|
+
new_lines = content.count("\n") + 1 if content else 0
|
66
|
+
self.report_success(
|
67
|
+
tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
|
68
|
+
)
|
69
|
+
# Perform syntax validation and append result
|
70
|
+
validation_result = validate_file_syntax(file_path)
|
71
|
+
if is_overwrite:
|
72
|
+
# Overwrite branch: return minimal overwrite info to user
|
73
|
+
return (
|
74
|
+
tr("✅ {new_lines} lines", new_lines=new_lines)
|
75
|
+
+ f"\n{validation_result}"
|
76
|
+
)
|
77
|
+
else:
|
78
|
+
# Create branch: return detailed create success to user
|
79
|
+
return (
|
80
|
+
tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
|
81
|
+
+ f"\n{validation_result}"
|
82
|
+
)
|
@@ -15,7 +15,7 @@ class DeleteTextInFileTool(ToolBase):
|
|
15
15
|
file_path (str): Path to the file to modify.
|
16
16
|
start_marker (str): The starting delimiter string.
|
17
17
|
end_marker (str): The ending delimiter string.
|
18
|
-
|
18
|
+
|
19
19
|
Returns:
|
20
20
|
str: Status message indicating the result.
|
21
21
|
"""
|
@@ -52,9 +52,7 @@ class DeleteTextInFileTool(ToolBase):
|
|
52
52
|
"No blocks found between markers in {file_path}.",
|
53
53
|
file_path=file_path,
|
54
54
|
)
|
55
|
-
|
56
|
-
if backup:
|
57
|
-
self._backup_file(file_path, backup_path)
|
55
|
+
|
58
56
|
new_content, deleted_blocks = self._delete_blocks(
|
59
57
|
content, start_marker, end_marker
|
60
58
|
)
|
@@ -62,10 +60,9 @@ class DeleteTextInFileTool(ToolBase):
|
|
62
60
|
validation_result = validate_file_syntax(file_path)
|
63
61
|
self._report_success(match_lines)
|
64
62
|
return tr(
|
65
|
-
"Deleted {count} block(s) between markers in {file_path}.
|
63
|
+
"Deleted {count} block(s) between markers in {file_path}. ",
|
66
64
|
count=deleted_blocks,
|
67
|
-
file_path=file_path
|
68
|
-
backup_path=backup_path if backup else "N/A",
|
65
|
+
file_path=file_path
|
69
66
|
) + (f"\n{validation_result}" if validation_result else "")
|
70
67
|
except Exception as e:
|
71
68
|
self.report_error(tr(" ❌ Error: {error}", error=e), ReportAction.REPLACE)
|
@@ -1,97 +1,97 @@
|
|
1
|
-
import requests
|
2
|
-
from bs4 import BeautifulSoup
|
3
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
4
|
-
from janito.tools.tool_base import ToolBase
|
5
|
-
from janito.report_events import ReportAction
|
6
|
-
from janito.i18n import tr
|
7
|
-
from janito.tools.tool_utils import pluralize
|
8
|
-
|
9
|
-
|
10
|
-
@register_local_tool
|
11
|
-
class FetchUrlTool(ToolBase):
|
12
|
-
"""
|
13
|
-
Fetch the content of a web page and extract its text.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
url (str): The URL of the web page to fetch.
|
17
|
-
search_strings (list[str], optional): Strings to search for in the page content.
|
18
|
-
Returns:
|
19
|
-
str: Extracted text content from the web page, or a warning message. Example:
|
20
|
-
- "<main text content...>"
|
21
|
-
- "No lines found for the provided search strings."
|
22
|
-
- "Warning: Empty URL provided. Operation skipped."
|
23
|
-
"""
|
24
|
-
|
25
|
-
tool_name = "fetch_url"
|
26
|
-
|
27
|
-
def run(self, url: str, search_strings: list[str] = None) -> str:
|
28
|
-
if not url.strip():
|
29
|
-
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
30
|
-
return tr("Warning: Empty URL provided. Operation skipped.")
|
31
|
-
self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
|
32
|
-
try:
|
33
|
-
response = requests.get(url, timeout=10)
|
34
|
-
response.raise_for_status()
|
35
|
-
except requests.exceptions.HTTPError as http_err:
|
36
|
-
status_code = http_err.response.status_code if http_err.response else None
|
37
|
-
if status_code and 400 <= status_code < 500:
|
38
|
-
self.report_error(
|
39
|
-
tr(
|
40
|
-
"❗ HTTP {status_code} error for URL: {url}",
|
41
|
-
status_code=status_code,
|
42
|
-
url=url,
|
43
|
-
),
|
44
|
-
ReportAction.READ,
|
45
|
-
)
|
46
|
-
return tr(
|
47
|
-
"Warning: HTTP {status_code} error for URL: {url}",
|
48
|
-
status_code=status_code,
|
49
|
-
url=url,
|
50
|
-
)
|
51
|
-
else:
|
52
|
-
self.report_error(
|
53
|
-
tr(
|
54
|
-
"❗ HTTP error for URL: {url}: {err}",
|
55
|
-
url=url,
|
56
|
-
err=str(http_err),
|
57
|
-
),
|
58
|
-
ReportAction.READ,
|
59
|
-
)
|
60
|
-
return tr(
|
61
|
-
"Warning: HTTP error for URL: {url}: {err}",
|
62
|
-
url=url,
|
63
|
-
err=str(http_err),
|
64
|
-
)
|
65
|
-
except Exception as err:
|
66
|
-
self.report_error(
|
67
|
-
tr("❗ Error fetching URL: {url}: {err}", url=url, err=str(err)),
|
68
|
-
ReportAction.READ,
|
69
|
-
)
|
70
|
-
return tr(
|
71
|
-
"Warning: Error fetching URL: {url}: {err}", url=url, err=str(err)
|
72
|
-
)
|
73
|
-
soup = BeautifulSoup(response.text, "html.parser")
|
74
|
-
text = soup.get_text(separator="\n")
|
75
|
-
if search_strings:
|
76
|
-
filtered = []
|
77
|
-
for s in search_strings:
|
78
|
-
idx = text.find(s)
|
79
|
-
if idx != -1:
|
80
|
-
start = max(0, idx - 200)
|
81
|
-
end = min(len(text), idx + len(s) + 200)
|
82
|
-
snippet = text[start:end]
|
83
|
-
filtered.append(snippet)
|
84
|
-
if filtered:
|
85
|
-
text = "\n...\n".join(filtered)
|
86
|
-
else:
|
87
|
-
text = tr("No lines found for the provided search strings.")
|
88
|
-
num_lines = len(text.splitlines())
|
89
|
-
self.report_success(
|
90
|
-
tr(
|
91
|
-
"✅ {num_lines} {line_word}",
|
92
|
-
num_lines=num_lines,
|
93
|
-
line_word=pluralize("line", num_lines),
|
94
|
-
),
|
95
|
-
ReportAction.READ,
|
96
|
-
)
|
97
|
-
return text
|
1
|
+
import requests
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
4
|
+
from janito.tools.tool_base import ToolBase
|
5
|
+
from janito.report_events import ReportAction
|
6
|
+
from janito.i18n import tr
|
7
|
+
from janito.tools.tool_utils import pluralize
|
8
|
+
|
9
|
+
|
10
|
+
@register_local_tool
|
11
|
+
class FetchUrlTool(ToolBase):
|
12
|
+
"""
|
13
|
+
Fetch the content of a web page and extract its text.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
url (str): The URL of the web page to fetch.
|
17
|
+
search_strings (list[str], optional): Strings to search for in the page content.
|
18
|
+
Returns:
|
19
|
+
str: Extracted text content from the web page, or a warning message. Example:
|
20
|
+
- "<main text content...>"
|
21
|
+
- "No lines found for the provided search strings."
|
22
|
+
- "Warning: Empty URL provided. Operation skipped."
|
23
|
+
"""
|
24
|
+
|
25
|
+
tool_name = "fetch_url"
|
26
|
+
|
27
|
+
def run(self, url: str, search_strings: list[str] = None) -> str:
|
28
|
+
if not url.strip():
|
29
|
+
self.report_warning(tr("ℹ️ Empty URL provided."), ReportAction.READ)
|
30
|
+
return tr("Warning: Empty URL provided. Operation skipped.")
|
31
|
+
self.report_action(tr("🌐 Fetch URL '{url}' ...", url=url), ReportAction.READ)
|
32
|
+
try:
|
33
|
+
response = requests.get(url, timeout=10)
|
34
|
+
response.raise_for_status()
|
35
|
+
except requests.exceptions.HTTPError as http_err:
|
36
|
+
status_code = http_err.response.status_code if http_err.response else None
|
37
|
+
if status_code and 400 <= status_code < 500:
|
38
|
+
self.report_error(
|
39
|
+
tr(
|
40
|
+
"❗ HTTP {status_code} error for URL: {url}",
|
41
|
+
status_code=status_code,
|
42
|
+
url=url,
|
43
|
+
),
|
44
|
+
ReportAction.READ,
|
45
|
+
)
|
46
|
+
return tr(
|
47
|
+
"Warning: HTTP {status_code} error for URL: {url}",
|
48
|
+
status_code=status_code,
|
49
|
+
url=url,
|
50
|
+
)
|
51
|
+
else:
|
52
|
+
self.report_error(
|
53
|
+
tr(
|
54
|
+
"❗ HTTP error for URL: {url}: {err}",
|
55
|
+
url=url,
|
56
|
+
err=str(http_err),
|
57
|
+
),
|
58
|
+
ReportAction.READ,
|
59
|
+
)
|
60
|
+
return tr(
|
61
|
+
"Warning: HTTP error for URL: {url}: {err}",
|
62
|
+
url=url,
|
63
|
+
err=str(http_err),
|
64
|
+
)
|
65
|
+
except Exception as err:
|
66
|
+
self.report_error(
|
67
|
+
tr("❗ Error fetching URL: {url}: {err}", url=url, err=str(err)),
|
68
|
+
ReportAction.READ,
|
69
|
+
)
|
70
|
+
return tr(
|
71
|
+
"Warning: Error fetching URL: {url}: {err}", url=url, err=str(err)
|
72
|
+
)
|
73
|
+
soup = BeautifulSoup(response.text, "html.parser")
|
74
|
+
text = soup.get_text(separator="\n")
|
75
|
+
if search_strings:
|
76
|
+
filtered = []
|
77
|
+
for s in search_strings:
|
78
|
+
idx = text.find(s)
|
79
|
+
if idx != -1:
|
80
|
+
start = max(0, idx - 200)
|
81
|
+
end = min(len(text), idx + len(s) + 200)
|
82
|
+
snippet = text[start:end]
|
83
|
+
filtered.append(snippet)
|
84
|
+
if filtered:
|
85
|
+
text = "\n...\n".join(filtered)
|
86
|
+
else:
|
87
|
+
text = tr("No lines found for the provided search strings.")
|
88
|
+
num_lines = len(text.splitlines())
|
89
|
+
self.report_success(
|
90
|
+
tr(
|
91
|
+
"✅ {num_lines} {line_word}",
|
92
|
+
num_lines=num_lines,
|
93
|
+
line_word=pluralize("line", num_lines),
|
94
|
+
),
|
95
|
+
ReportAction.READ,
|
96
|
+
)
|
97
|
+
return text
|