Sfoglia il codice sorgente

app/pydrk: minor bug fixes, allow .. and ... type cd'ing

darkfi 2 settimane fa
parent
commit
957e07dfa2
3 ha cambiato i file con 74 aggiunte e 43 eliminazioni
  1. 12 2
      bin/app/pydrk/api.py
  2. 60 9
      bin/app/pydrk/cli.py
  3. 2 32
      bin/app/pydrk/print_tree.py

+ 12 - 2
bin/app/pydrk/api.py

@@ -63,7 +63,6 @@ class SceneNodeType:
     TEXTURE = 10
     TEXTURE = 10
     FONTS = 11
     FONTS = 11
     FONT = 12
     FONT = 12
-    CHAT_VIEW = 13
     EDIT = 14
     EDIT = 14
     IMAGE = 15
     IMAGE = 15
     BUTTON = 16
     BUTTON = 16
@@ -74,9 +73,21 @@ class SceneNodeType:
     MENU = 22
     MENU = 22
     TOKEN_TABLE = 23
     TOKEN_TABLE = 23
     TEXT_SCRAMBLE = 24
     TEXT_SCRAMBLE = 24
+    CHAT_VIEW = 25
+    PRIV_MSG_NODE = 26
+    DATE_MSG_NODE = 27
+    FILE_MSG_NODE = 28
+    SLIDER = 29
+    DROPDOWN = 30
     PLUGIN_ROOT = 100
     PLUGIN_ROOT = 100
     PLUGIN = 101
     PLUGIN = 101
 
 
+NODE_TYPE_NAMES = {
+    getattr(SceneNodeType, name): name.lower()
+    for name in dir(SceneNodeType)
+    if name.isupper()
+}
+
 class PropertyType:
 class PropertyType:
     NULL = 0
     NULL = 0
     BOOL = 1
     BOOL = 1
@@ -732,4 +743,3 @@ class Api:
         cur = self._make_request(Command.CALL_METHOD, req)
         cur = self._make_request(Command.CALL_METHOD, req)
         result = serial.decode_opt(cur, serial.decode_buf)
         result = serial.decode_opt(cur, serial.decode_buf)
         return result
         return result
-

+ 60 - 9
bin/app/pydrk/cli.py

@@ -15,6 +15,7 @@ from .api import (
     Api,
     Api,
     CallArgType,
     CallArgType,
     Expr,
     Expr,
+    NODE_TYPE_NAMES,
     PropertyStatus,
     PropertyStatus,
     PropertySubType,
     PropertySubType,
     PropertyType,
     PropertyType,
@@ -42,13 +43,6 @@ def error_name(err):
     return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
     return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
 
 
 
 
-NODE_TYPE_NAMES = {
-    getattr(SceneNodeType, name): name.lower()
-    for name in dir(SceneNodeType)
-    if name.isupper()
-}
-
-
 def resolve_path(cwd, arg):
 def resolve_path(cwd, arg):
     if arg.startswith("/"):
     if arg.startswith("/"):
         tokens = arg.split("/")
         tokens = arg.split("/")
@@ -230,11 +224,13 @@ COMMAND_PARSERS = {}
 MAIN_PARSER = argparse.ArgumentParser()
 MAIN_PARSER = argparse.ArgumentParser()
 
 
 
 
-SHELL_BUILTINS = ("cd", "pwd", "exit", "quit")
+SHELL_BUILTINS = ("cd", "..", "...", "pwd", "exit", "quit")
 
 
 
 
 BUILTIN_HELP = {
 BUILTIN_HELP = {
     "cd": "cd [path]           change the working node (no arg = /, .. pops one)",
     "cd": "cd [path]           change the working node (no arg = /, .. pops one)",
+    "..": ".., ..., ....       go up one, two, three, ... nodes",
+    "...": ".., ..., ....       go up one, two, three, ... nodes",
     "pwd": "pwd                 print the working node path",
     "pwd": "pwd                 print the working node path",
     "exit": "exit | quit         leave the shell (Ctrl-D also works)",
     "exit": "exit | quit         leave the shell (Ctrl-D also works)",
     "quit": "exit | quit         leave the shell (Ctrl-D also works)",
     "quit": "exit | quit         leave the shell (Ctrl-D also works)",
@@ -247,7 +243,7 @@ def print_help(command=None):
         MAIN_PARSER.print_help()
         MAIN_PARSER.print_help()
         print()
         print()
         print("shell builtins (interactive mode only):")
         print("shell builtins (interactive mode only):")
-        for name in ("cd", "pwd", "exit", "help"):
+        for name in ("cd", "..", "pwd", "exit", "help"):
             print(f"  {BUILTIN_HELP[name]}")
             print(f"  {BUILTIN_HELP[name]}")
     elif command in COMMAND_PARSERS:
     elif command in COMMAND_PARSERS:
         COMMAND_PARSERS[command].print_help()
         COMMAND_PARSERS[command].print_help()
@@ -300,6 +296,9 @@ class Shell:
             return
             return
 
 
         cmd = tokens[0]
         cmd = tokens[0]
+        if len(tokens) == 1 and len(cmd) >= 2 and not cmd.strip("."):
+            self.cd(["/".join([".."] * (len(cmd) - 1))])
+            return
         if cmd in ("exit", "quit"):
         if cmd in ("exit", "quit"):
             raise ShellExit
             raise ShellExit
         if cmd == "pwd":
         if cmd == "pwd":
@@ -905,6 +904,10 @@ def cmd_call(api, args, cwd):
 
 
 
 
 def run_selftests():
 def run_selftests():
+    from contextlib import redirect_stderr, redirect_stdout
+    from io import StringIO
+    from unittest.mock import Mock
+
     from .api import Expr, Property, PropertyStatus, PropertyType
     from .api import Expr, Property, PropertyStatus, PropertyType
 
 
     assert format_value(None) == "null"
     assert format_value(None) == "null"
@@ -923,6 +926,26 @@ def run_selftests():
     assert NODE_TYPE_NAMES[SceneNodeType.LAYER] == "layer"
     assert NODE_TYPE_NAMES[SceneNodeType.LAYER] == "layer"
     assert NODE_TYPE_NAMES[SceneNodeType.VECTOR_ART] == "vector_art"
     assert NODE_TYPE_NAMES[SceneNodeType.VECTOR_ART] == "vector_art"
     assert NODE_TYPE_NAMES[SceneNodeType.PLUGIN_ROOT] == "plugin_root"
     assert NODE_TYPE_NAMES[SceneNodeType.PLUGIN_ROOT] == "plugin_root"
+    for node_type, name in (
+        (25, "chat_view"),
+        (26, "priv_msg_node"),
+        (27, "date_msg_node"),
+        (28, "file_msg_node"),
+        (29, "slider"),
+        (30, "dropdown"),
+    ):
+        assert NODE_TYPE_NAMES[node_type] == name
+        api = Mock()
+        api.get_children.return_value = [("node", 42, node_type)]
+        api.get_properties.return_value = []
+        api.get_signals.return_value = []
+        api.get_methods.return_value = []
+        output = StringIO()
+        with redirect_stdout(output):
+            cmd_ls(api, argparse.Namespace(path="/"), [])
+            print_tree(api, depth=1)
+        assert f"node 42 {name}\n" in output.getvalue()
+        assert f"[{name}]\n" in output.getvalue()
 
 
     assert error_name(exc.SExprGlobalNotFound()) == "sexpr_global_not_found"
     assert error_name(exc.SExprGlobalNotFound()) == "sexpr_global_not_found"
     assert error_name(exc.PropertySExprNotAllowed()) == "property_sexpr_not_allowed"
     assert error_name(exc.PropertySExprNotAllowed()) == "property_sexpr_not_allowed"
@@ -942,6 +965,34 @@ def run_selftests():
     assert resolve_path(["window"], "../setting") == "/setting"
     assert resolve_path(["window"], "../setting") == "/setting"
     assert resolve_path(["window"], "./content/.") == "/window/content"
     assert resolve_path(["window"], "./content/.") == "/window/content"
     assert resolve_path([], "window//content/") == "/window/content"
     assert resolve_path([], "window//content/") == "/window/content"
+    assert resolve_path(["a", "b", "c"], "../../foo/") == "/a/foo"
+
+    api = Mock()
+    api.get_children.return_value = [
+        ("a", 1, SceneNodeType.LAYER),
+        ("b", 2, SceneNodeType.LAYER),
+        ("foo", 3, SceneNodeType.LAYER),
+    ]
+    shell = Shell(api)
+    for command, expected in (
+        ("..", ["a", "b"]),
+        ("...", ["a"]),
+        ("....", []),
+        ("........", []),
+        ("cd ../../foo/", ["a", "foo"]),
+    ):
+        shell.cwd = ["a", "b", "c"]
+        shell.execute(command)
+        assert shell.cwd == expected, command
+    api.get_children.assert_called_with("/a")
+    shell.cwd = []
+    shell.execute("..")
+    assert shell.cwd == []
+    shell.cwd = ["a", "b", "c"]
+    with redirect_stderr(StringIO()) as errors:
+        shell.execute("cd ../../missing/")
+    assert "node_not_found: /a/missing" in errors.getvalue()
+    assert shell.cwd == ["a", "b", "c"]
 
 
     assert parse_get_args(["alpha"], []) == ("/", "alpha", None)
     assert parse_get_args(["alpha"], []) == ("/", "alpha", None)
     assert parse_get_args(["/window/content", "alpha"], []) == ("/window/content", "alpha", None)
     assert parse_get_args(["/window/content", "alpha"], []) == ("/window/content", "alpha", None)

+ 2 - 32
bin/app/pydrk/print_tree.py

@@ -1,4 +1,4 @@
-from .api import Api, PropertyType, SceneNodeType, CallArgType, Expr
+from .api import Api, PropertyType, NODE_TYPE_NAMES, CallArgType, Expr
 
 
 def print_tree(api, node_path="/", depth=None):
 def print_tree(api, node_path="/", depth=None):
     print(node_path)
     print(node_path)
@@ -10,37 +10,7 @@ def print_node_info(api, parent_path, depth, indent):
 
 
     ws = " "*4*indent
     ws = " "*4*indent
     for (child_name, child_id, child_type) in api.get_children(parent_path):
     for (child_name, child_id, child_type) in api.get_children(parent_path):
-        match child_type:
-            case SceneNodeType.ROOT:
-                child_type = "root"
-            case SceneNodeType.WINDOW:
-                child_type = "window"
-            case SceneNodeType.WINDOW_INPUT:
-                child_type = "window_input"
-            case SceneNodeType.KEYBOARD:
-                child_type = "keyboard"
-            case SceneNodeType.MOUSE:
-                child_type = "mouse"
-            case SceneNodeType.LAYER:
-                child_type = "layer"
-            case SceneNodeType.OBJECT:
-                child_type = "object"
-            case SceneNodeType.VECTOR_ART:
-                child_type = "vector_art"
-            case SceneNodeType.TEXT:
-                child_type = "text"
-            case SceneNodeType.TEXTURE:
-                child_type = "texture"
-            case SceneNodeType.FONTS:
-                child_type = "fonts"
-            case SceneNodeType.FONT:
-                child_type = "font"
-            case SceneNodeType.CHAT_VIEW:
-                child_type = "chat_view"
-            case SceneNodeType.BUTTON:
-                child_type = "button"
-            case SceneNodeType.SETTING:
-                child_type = "setting"
+        child_type = NODE_TYPE_NAMES.get(child_type, child_type)
 
 
         desc = f"{ws}{child_name}:{child_id}/"
         desc = f"{ws}{child_name}:{child_id}/"
         desc += " "*(50 - len(desc))
         desc += " "*(50 - len(desc))