flyshell 1.1.2__tar.gz → 1.1.4__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.
- {flyshell-1.1.2/src/flyshell.egg-info → flyshell-1.1.4}/PKG-INFO +1 -1
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/abstract/parent_cmd.py +12 -12
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/builtin/utils.py +2 -2
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/constants.py +2 -2
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/loader.py +8 -9
- {flyshell-1.1.2 → flyshell-1.1.4/src/flyshell.egg-info}/PKG-INFO +1 -1
- {flyshell-1.1.2 → flyshell-1.1.4}/tests/test_loader.py +12 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/LICENSE +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/README.md +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/pyproject.toml +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/setup.cfg +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/__init__.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/abstract/__init__.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/abstract/base_cmd.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/abstract/base_plugin.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/builtin/__init__.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/builtin/diskops.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/builtin/fs.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/builtin/system.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/__init__.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/auth.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/console.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/data.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/core/directory.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell/main.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell.egg-info/SOURCES.txt +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell.egg-info/dependency_links.txt +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell.egg-info/entry_points.txt +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell.egg-info/requires.txt +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/src/flyshell.egg-info/top_level.txt +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/tests/test_auth.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/tests/test_data.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/tests/test_directory.py +0 -0
- {flyshell-1.1.2 → flyshell-1.1.4}/tests/test_security.py +0 -0
|
@@ -43,28 +43,28 @@ class ParentCmd(BaseCmd):
|
|
|
43
43
|
f"\nFor subcommand assistance use '{self.name} <subcmd> -h' or '--help'\n"
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
-
def execute(self, args: List[str]) ->
|
|
46
|
+
def execute(self, args: List[str]) -> Tuple[int, str]:
|
|
47
47
|
if not args or args[0] in ("-h", "--help"):
|
|
48
48
|
self.help()
|
|
49
|
-
return 0
|
|
49
|
+
return (0, "")
|
|
50
50
|
sub_name = args[0].lower()
|
|
51
51
|
sub_args = args[1:]
|
|
52
52
|
if sub_name not in self._sub_registry:
|
|
53
|
-
|
|
54
|
-
f"\nCommand Error: Unknown subcommand '{sub_name}' for '{self.name}'
|
|
53
|
+
err = (
|
|
54
|
+
f"\nCommand Error: Unknown subcommand '{sub_name}' for '{self.name}'.\n"
|
|
55
|
+
f"Use '{self.name} -h' or '--help' to see all valid subcommands.\n"
|
|
55
56
|
)
|
|
56
|
-
|
|
57
|
-
return 1
|
|
57
|
+
return (1, err)
|
|
58
58
|
target = self._sub_registry[sub_name]
|
|
59
59
|
if "-h" in sub_args or "--help" in sub_args:
|
|
60
60
|
target.help(prefix=self.name)
|
|
61
|
-
return 0
|
|
61
|
+
return (0, "")
|
|
62
62
|
if len(sub_args) < target.min_args:
|
|
63
|
-
|
|
63
|
+
err_lines = [
|
|
64
64
|
f"\nCommand Error: '{self.name} {target.name}' requires at least {target.min_args} argument(s)."
|
|
65
|
-
|
|
65
|
+
]
|
|
66
66
|
if target.options:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return 1
|
|
67
|
+
err_lines.append(f"Accepted arguments: {target.options}")
|
|
68
|
+
err_lines.append("")
|
|
69
|
+
return (1, "\n".join(err_lines))
|
|
70
70
|
return target.execute(sub_args)
|
|
@@ -83,8 +83,7 @@ class Kill(BaseCmd):
|
|
|
83
83
|
if choice == "y":
|
|
84
84
|
print("\nShutting down...")
|
|
85
85
|
sys.exit(0)
|
|
86
|
-
|
|
87
|
-
return (1, "\nAction cancelled.\n")
|
|
86
|
+
return (1, "\nAction cancelled.\n")
|
|
88
87
|
|
|
89
88
|
|
|
90
89
|
class Reboot(BaseCmd):
|
|
@@ -113,6 +112,7 @@ class Reboot(BaseCmd):
|
|
|
113
112
|
signal.signal(signal.SIGINT, old_handler)
|
|
114
113
|
else:
|
|
115
114
|
os.execv(cmd[0], cmd)
|
|
115
|
+
return (0, "")
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
class Sleep(BaseCmd):
|
|
@@ -33,10 +33,17 @@ def scan_plugins(plugin_folder=None):
|
|
|
33
33
|
if entry.exists():
|
|
34
34
|
file_path = entry
|
|
35
35
|
plugin_name = item.name
|
|
36
|
-
if not file_path:
|
|
36
|
+
if not file_path or not plugin_name:
|
|
37
|
+
continue
|
|
38
|
+
if plugin_name in RESERVED_NAMES:
|
|
39
|
+
print(
|
|
40
|
+
f"\n⚠️ Collision Error - Plugin '{plugin_name}' conflicts with a built-in Flyshell command and will not be loaded."
|
|
41
|
+
)
|
|
37
42
|
continue
|
|
38
43
|
try:
|
|
39
44
|
spec = importlib.util.spec_from_file_location(plugin_name, file_path)
|
|
45
|
+
if spec is None or spec.loader is None:
|
|
46
|
+
continue
|
|
40
47
|
module = importlib.util.module_from_spec(spec)
|
|
41
48
|
spec.loader.exec_module(module)
|
|
42
49
|
for _, obj in inspect.getmembers(module, inspect.isclass):
|
|
@@ -51,12 +58,6 @@ def scan_plugins(plugin_folder=None):
|
|
|
51
58
|
loaded_count = 0
|
|
52
59
|
first_print = True
|
|
53
60
|
for name, entries in candidates.items():
|
|
54
|
-
failed = False
|
|
55
|
-
if name in RESERVED_NAMES:
|
|
56
|
-
print(
|
|
57
|
-
f"\n⚠️ Collision Error - Plugin '{name}' conflicts with a built-in Flyshell command and will not be loaded."
|
|
58
|
-
)
|
|
59
|
-
failed = True
|
|
60
61
|
if len(entries) > 1:
|
|
61
62
|
paths = "\n - ".join(str(path) for _, path in entries)
|
|
62
63
|
print(
|
|
@@ -65,8 +66,6 @@ def scan_plugins(plugin_folder=None):
|
|
|
65
66
|
print(
|
|
66
67
|
"The above plugins will not be loaded. Please rename them to unique names."
|
|
67
68
|
)
|
|
68
|
-
failed = True
|
|
69
|
-
if failed:
|
|
70
69
|
continue
|
|
71
70
|
instance, _ = entries[0]
|
|
72
71
|
c.PLUGINS[name] = instance
|
|
@@ -41,3 +41,15 @@ def test_loader_handles_broken_plugin_gracefully(tmp_path, monkeypatch):
|
|
|
41
41
|
monkeypatch.setattr(c, "USER_PLUGIN_DIR", tmp_path)
|
|
42
42
|
loader.scan_plugins(plugin_folder=tmp_path)
|
|
43
43
|
assert "broken" not in c.PLUGINS
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_loader_does_not_execute_code_in_reserved_plugin(tmp_path, monkeypatch):
|
|
47
|
+
sentinel = tmp_path / "sentinel.txt"
|
|
48
|
+
bad_plugin = tmp_path / "cd.py"
|
|
49
|
+
bad_plugin.write_text(
|
|
50
|
+
f"from pathlib import Path\nPath('{sentinel.as_posix()}').write_text('executed')\n"
|
|
51
|
+
)
|
|
52
|
+
monkeypatch.setattr(c, "USER_PLUGIN_DIR", tmp_path)
|
|
53
|
+
loader.scan_plugins(plugin_folder=tmp_path)
|
|
54
|
+
assert "cd" not in c.PLUGINS
|
|
55
|
+
assert not sentinel.exists()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|