[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-13 Thread via lldb-commits

https://github.com/jimingham closed 
https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread Med Ismail Bennani via lldb-commits


@@ -0,0 +1,360 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOptionValueParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+And if you need to know whether a given option was set by the user or not, you 
can
+use the was_set API.  
+
+So for instance, if you have an option whose "dest" is "my_option", then:
+
+self.get_parser().my_option
+
+will fetch the value, and:
+
+self.get_parser().was_set("my_option")
+
+will return True if the user set this option, and False if it was left at its 
default
+value.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOptionValueParser:
+"""
+This class is holds the option definitions for the command, and when

medismailben wrote:

nit: typo

```suggestion
This class holds the option definitions for the command, and when
```

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread Med Ismail Bennani via lldb-commits

https://github.com/medismailben edited 
https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread Med Ismail Bennani via lldb-commits


@@ -805,19 +805,25 @@ let Command = "script add" in {
   def script_add_function : Option<"function", "f">, Group<1>,
 Arg<"PythonFunction">,
 Desc<"Name of the Python function to bind to this command name.">;
-  def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
-  Desc<"Name of the Python class to bind to this command name.">;
+  def script_add_class : Option<"class", "c">, Groups<[2,3]>, 

medismailben wrote:

That's unfortunate :/ 

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread Med Ismail Bennani via lldb-commits

https://github.com/medismailben approved this pull request.

Awesome! Thanks for addressing my comments @jimingham :) LGTM!

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread via lldb-commits


@@ -0,0 +1,321 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOptionValueParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+And if you need to know whether a given option was set by the user or not, you 
can
+use the was_set API.  
+
+So for instance, if you have an option whose "dest" is "my_option", then:
+
+self.get_parser().my_option
+
+will fetch the value, and:
+
+self.get_parser().was_set("my_option")
+
+will return True if the user set this option, and False if it was left at its 
default
+value.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""

jimingham wrote:

I added some docs, see what you think.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread via lldb-commits


@@ -0,0 +1,321 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOptionValueParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+And if you need to know whether a given option was set by the user or not, you 
can
+use the was_set API.  
+
+So for instance, if you have an option whose "dest" is "my_option", then:
+
+self.get_parser().my_option
+
+will fetch the value, and:
+
+self.get_parser().was_set("my_option")
+
+will return True if the user set this option, and False if it was left at its 
default
+value.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOptionValueParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in in ["y", "yes", "t", "true", "1"]:
+value = True
+error = False
+
+if not value and low_in in ["n", "no", "f", "false", "0"]:
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpress

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread via lldb-commits


@@ -805,19 +805,25 @@ let Command = "script add" in {
   def script_add_function : Option<"function", "f">, Group<1>,
 Arg<"PythonFunction">,
 Desc<"Name of the Python function to bind to this command name.">;
-  def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
-  Desc<"Name of the Python class to bind to this command name.">;
+  def script_add_class : Option<"class", "c">, Groups<[2,3]>, 
+Arg<"PythonClass">,
+Desc<"Name of the Python class to bind to this command name.">;
   def script_add_help : Option<"help", "h">, Group<1>, Arg<"HelpText">,
-  Desc<"The help text to display for this command.">;
-  def script_add_overwrite : Option<"overwrite", "o">, Groups<[1,2]>,
-  Desc<"Overwrite an existing command at this node.">;
+Desc<"The help text to display for this command.">;
+def script_add_overwrite : Option<"overwrite", "o">,
+Desc<"Overwrite an existing command at this node.">;
   def script_add_synchronicity : Option<"synchronicity", "s">,
 EnumArg<"ScriptedCommandSynchronicity">,
 Desc<"Set the synchronicity of this command's executions with regard to "
 "LLDB event system.">;
-  def completion_type : Option<"completion-type", "C">,
-  EnumArg<"CompletionType">,
-  Desc<"Specify which completion type the command should use - if none is 
specified, the command won't use auto-completion.">;
+  def script_add_completion_type : Option<"completion-type", "C">, 

jimingham wrote:

See above.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread via lldb-commits


@@ -805,19 +805,25 @@ let Command = "script add" in {
   def script_add_function : Option<"function", "f">, Group<1>,
 Arg<"PythonFunction">,
 Desc<"Name of the Python function to bind to this command name.">;
-  def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
-  Desc<"Name of the Python class to bind to this command name.">;
+  def script_add_class : Option<"class", "c">, Groups<[2,3]>, 

jimingham wrote:

I didn't add either of these options, they predate this patch.  This would 
break anybody that's adding scripted commands.  I don't remember why it's this 
way, but I don't think we can change it.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-12 Thread via lldb-commits


@@ -96,7 +96,9 @@ function(finish_swig_python swig_target 
lldb_python_bindings_dir lldb_python_tar
 ${lldb_python_target_dir}
 "utils"
 FILES "${LLDB_SOURCE_DIR}/examples/python/in_call_stack.py"
-  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py")
+  "${LLDB_SOURCE_DIR}/examples/python/parsed_cmd.py"

jimingham wrote:

Sure.  I moved the `parsed_cmd.py` file into lldb/examples/python/templates as 
well, since the other files were there.  , that seems like a strange 
location for these files.  At this point they really aren't examples, they are 
base classes we're vending.  That's also true of things like crashlog, however, 
so I don't feel the need to address this right now.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits

https://github.com/medismailben edited 
https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits


@@ -96,7 +96,9 @@ function(finish_swig_python swig_target 
lldb_python_bindings_dir lldb_python_tar
 ${lldb_python_target_dir}
 "utils"
 FILES "${LLDB_SOURCE_DIR}/examples/python/in_call_stack.py"
-  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py")
+  "${LLDB_SOURCE_DIR}/examples/python/parsed_cmd.py"

medismailben wrote:

I'd put this in the `plugins` submodule with the other scripting affordances 
base implementations, not in `utils`.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits


@@ -805,19 +805,25 @@ let Command = "script add" in {
   def script_add_function : Option<"function", "f">, Group<1>,
 Arg<"PythonFunction">,
 Desc<"Name of the Python function to bind to this command name.">;
-  def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
-  Desc<"Name of the Python class to bind to this command name.">;
+  def script_add_class : Option<"class", "c">, Groups<[2,3]>, 
+Arg<"PythonClass">,
+Desc<"Name of the Python class to bind to this command name.">;
   def script_add_help : Option<"help", "h">, Group<1>, Arg<"HelpText">,
-  Desc<"The help text to display for this command.">;
-  def script_add_overwrite : Option<"overwrite", "o">, Groups<[1,2]>,
-  Desc<"Overwrite an existing command at this node.">;
+Desc<"The help text to display for this command.">;
+def script_add_overwrite : Option<"overwrite", "o">,
+Desc<"Overwrite an existing command at this node.">;
   def script_add_synchronicity : Option<"synchronicity", "s">,
 EnumArg<"ScriptedCommandSynchronicity">,
 Desc<"Set the synchronicity of this command's executions with regard to "
 "LLDB event system.">;
-  def completion_type : Option<"completion-type", "C">,
-  EnumArg<"CompletionType">,
-  Desc<"Specify which completion type the command should use - if none is 
specified, the command won't use auto-completion.">;
+  def script_add_completion_type : Option<"completion-type", "C">, 

medismailben wrote:

This one also needs to be swapped.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits


@@ -0,0 +1,321 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOptionValueParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+And if you need to know whether a given option was set by the user or not, you 
can
+use the was_set API.  
+
+So for instance, if you have an option whose "dest" is "my_option", then:
+
+self.get_parser().my_option
+
+will fetch the value, and:
+
+self.get_parser().was_set("my_option")
+
+will return True if the user set this option, and False if it was left at its 
default
+value.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOptionValueParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in in ["y", "yes", "t", "true", "1"]:
+value = True
+error = False
+
+if not value and low_in in ["n", "no", "f", "false", "0"]:
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpress

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits


@@ -805,19 +805,25 @@ let Command = "script add" in {
   def script_add_function : Option<"function", "f">, Group<1>,
 Arg<"PythonFunction">,
 Desc<"Name of the Python function to bind to this command name.">;
-  def script_add_class : Option<"class", "c">, Group<2>, Arg<"PythonClass">,
-  Desc<"Name of the Python class to bind to this command name.">;
+  def script_add_class : Option<"class", "c">, Groups<[2,3]>, 

medismailben wrote:

Let's swap the short-option between `script_add_class` & 
`script_add_completion_type`. Scripted Process uses `-C` (capital C) in 
`process launch` & `process attach` to specify the python class. Same for 
scripted thread plans with `thread step-scripted`.

Let's try to stay consistent for our users.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits

https://github.com/medismailben requested changes to this pull request.

The implementation LGTM but as you know, I'm trying to consolidate scripting 
affordances & improve their documentation, so I left some comments that would 
greatly help me in this endeavor. Thanks!

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Med Ismail Bennani via lldb-commits


@@ -0,0 +1,321 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOptionValueParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+And if you need to know whether a given option was set by the user or not, you 
can
+use the was_set API.  
+
+So for instance, if you have an option whose "dest" is "my_option", then:
+
+self.get_parser().my_option
+
+will fetch the value, and:
+
+self.get_parser().was_set("my_option")
+
+will return True if the user set this option, and False if it was left at its 
default
+value.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""

medismailben wrote:

Although I really like this kind of narrated documentation style, I don't think 
its concise enough for users and might discourage some from using this feature 
because the documentation was too long to read.

I think we should keep this, but still provide docstrings comments with 
arguments and return types for every methods of this class, like we do in the 
scripted process base class implementation:

https://github.com/llvm/llvm-project/blob/2c3ba9f6225612caf7d2d5ba6613ba1454d52dc3/lldb/examples/python/templates/scripted_process.py#L64-L75

Or at least the methods that the user might use.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Alex Langford via lldb-commits

https://github.com/bulbazord approved this pull request.

Ok, I think this is good to go in. There are things that can be improved but it 
would be better to land this and change things in follow up PRs instead of 
continually adjusting this one.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-10 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation

bulbazord wrote:

I don't remember why I suggested this, it looks pretty clear to me after 
another pass.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.

jimingham wrote:

I converted the lldb/examples/python/cmdtemplate.py to the new form in the 
latest patch, if you want to get a sense of what you have to do to convert from 
one form to the other.  I think this is pretty straightforward.  But it also 
can't be a straight translation because you need to be able to specify the 
lldb.eArgType... .  Anyway, have a look at the diff between the two, and if you 
can think of anything to make this easier, I'm happy for suggestions (maybe as 
a follow-on patch, if this looks roughly okay, however...)

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,

jimingham wrote:

Note, whoever spelt this originally was sure implementor was what they wanted 
to use, the C++ files use the same spelling.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":

jimingham wrote:

string in array of strings is definitely easier to read, thanks!

I don't think the second comment is right, however.  Not all input values will 
make it into either of those if statements, for instance "low_in = 
definitely_not_a_boolean" will be rejected by both if's and so "error" will be 
left at True.

All of this conversion code should actually go away, because it's silly to have 
two versions of OptionArgParser::ToBool, one in C++ and one here in Python.  
This is just a shortcut for now because this patch was already getting somewhat 
unwieldily.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:

jimingham wrote:

Yes, I agree, nobody will ever type this one because that's not even the class 
you override.  So there's no reason for it to have a mysterious name.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.

jimingham wrote:

Yes, it's a part of the ov_parser API.  I added examples.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread David Spickett via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:

DavidSpickett wrote:

`self.ov_parser` is fine as a variable name since any IDE helper will show this 
type name for it.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread David Spickett via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread David Spickett via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread David Spickett via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-06 Thread David Spickett via lldb-commits


@@ -0,0 +1,313 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition() in your new command class, and call:
+
+  self.get_parser().add_option()
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser().add_argument()
+
+At present, lldb doesn't do as much work as it should verifying arguments, it
+only checks that commands that take no arguments don't get passed arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_list, exe_ctx, result):
+
+The arguments will be a list of strings.  
+
+You can access the option values using the 'dest' string you passed in when 
defining the option.
+
+If you need to know whether a given option was set by the user or not, you can
+use the was_set API.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+"""
+import inspect
+import lldb
+import sys
+from abc import abstractmethod
+
+class LLDBOVParser:
+def __init__(self):
+# This is a dictionary of dictionaries.  The key is the long option
+# name, and the value is the rest of the definition.
+self.options_dict = {}
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+if type(in_value) != str or len(in_value) == 0:
+return (value, error)
+
+low_in = in_value.lower()
+if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == 
"true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "n" or low_in == "no" or low_in == "f" or 
low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+@staticmethod
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndex

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-05 Thread via lldb-commits

jimingham wrote:

At this point, I'd like to get this in so I can iterate on it in future PR's 
rather than churning this one.  If folks have a bit to look over this, that 
would be great.  Thanks!

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-02-05 Thread via lldb-commits

jimingham wrote:

Okay, I made one more trivial change, I switched the `varname` field to `dest` 
so it matched what argparse had, and I renamed the `usage` entry to `help` so 
it matches the argparse usage as well.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-29 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-29 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-29 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-29 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {

jimingham wrote:

The C++ side of the option specification needs work.  When you actually define 
an option on the C++ side, you give it an eArgType, but that doesn't actually 
wire up completions directly.  Instead, you have to also do that by hand.  So 
writing the C++ based commands in inconvenient, and would make writing ones in 
Python tedious as well.  I don't want people to have to get in the habit of 
manually doing that, so I am just providing this shim.  The change to get the 
ArgType -> completer working correctly is much more work, and mostly orthogonal 
to the main purpose of this patch.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.

jimingham wrote:

I still think exceptions would be an inconvenient way to do this.  These aren't 
API's that users are going to call directly, so we aren't matching expectations 
for Python function behavior.  Rather, in the middle of parsing a command from 
lldb (happening in C++) the command parser will arrange to have this called.  
So if it raises a Python exception, the LLDB SWIG wrappers will have to catch 
that exception and turn it into a Status with the error we want to report.  I 
think it would be much less fragile to just pipe an SBError through directly 
and use that for error reporting.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation

jimingham wrote:

I don't think your version is grammatical.  You use it (a) for your... and (b) 
to do X.  "for" doesn't work with the fetch verb form.  You could say "for your 
option definition and fetching"...
But I actually don't see what's unclear about this form.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);

jimingham wrote:

That must have been problematic in development.  This sort of compressed style 
is less good when you are debugging...

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;

jimingham wrote:

I added this here, but we really need a more general mechanism for informing 
the user when the python code we've been handed is lacking some affordance.  
Most of these wrappers don't have a CommandReturnObject to complain through.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");

jimingham wrote:

We don't do any checking for arguments in any of the `pfunc` lookups in the 
python wrapper, except in one or two cases where we support overloaded 
implementation functions.  I don't think doing this piecemeal is a great idea.  
It would be better to have some kind of `check_pfunc` feature that does the 
lookup and takes some specification about what to expect of the found function. 
 Then we can insert this in all the current lookups.
But this patch is long enough already, it would be better to do that as a 
separate patch.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-26 Thread via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,

jimingham wrote:

It's spelled incorrectly in the whole rest of this file except for the thread 
plan instances.  We should either get rid of all of these by using 
ScriptedPythonInterface, or fix all the spellings to be consistent, but that 
seems better as a separate commit.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2024-01-21 Thread via lldb-commits

https://github.com/jimingham updated 
https://github.com/llvm/llvm-project/pull/70734

>From a0c72c07861218fa27420045aecdd5cd072cc60a Mon Sep 17 00:00:00 2001
From: Jim Ingham 
Date: Mon, 30 Oct 2023 14:48:37 -0700
Subject: [PATCH 1/2] Add the ability to define a Python based command that
 uses the CommandObjectParsed way to specify options and arguments so that
 these commands can be "first class citizens" with build-in commands.

---
 lldb/bindings/python/CMakeLists.txt   |   3 +-
 lldb/bindings/python/python-wrapper.swig  |  31 +
 lldb/examples/python/parsed_cmd.py| 315 
 lldb/include/lldb/Interpreter/CommandObject.h |   5 +-
 .../lldb/Interpreter/ScriptInterpreter.h  |  29 +
 .../source/Commands/CommandObjectCommands.cpp | 697 +-
 lldb/source/Commands/Options.td   |  22 +-
 lldb/source/Interpreter/CommandObject.cpp |  16 +
 .../Python/PythonDataObjects.h|   2 +
 .../Python/SWIGPythonBridge.h |   7 +
 .../Python/ScriptInterpreterPython.cpp| 252 ++-
 .../Python/ScriptInterpreterPythonImpl.h  |  21 +
 .../script/add/TestAddParsedCommand.py| 146 
 .../command/script/add/test_commands.py   | 175 +
 .../Python/PythonTestSuite.cpp|   8 +
 15 files changed, 1714 insertions(+), 15 deletions(-)
 create mode 100644 lldb/examples/python/parsed_cmd.py
 create mode 100644 
lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
 create mode 100644 lldb/test/API/commands/command/script/add/test_commands.py

diff --git a/lldb/bindings/python/CMakeLists.txt 
b/lldb/bindings/python/CMakeLists.txt
index c941f764dfc92a..657fdd2c959006 100644
--- a/lldb/bindings/python/CMakeLists.txt
+++ b/lldb/bindings/python/CMakeLists.txt
@@ -96,7 +96,8 @@ function(finish_swig_python swig_target 
lldb_python_bindings_dir lldb_python_tar
 ${lldb_python_target_dir}
 "utils"
 FILES "${LLDB_SOURCE_DIR}/examples/python/in_call_stack.py"
-  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py")
+  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py"
+  "${LLDB_SOURCE_DIR}/examples/python/parsed_cmd.py")
 
   create_python_package(
 ${swig_target}
diff --git a/lldb/bindings/python/python-wrapper.swig 
b/lldb/bindings/python/python-wrapper.swig
index 17bc7b1f219870..47887491d0c552 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);
+
+  // FIXME:
+  // I wanted to do something like:
+  // size_t num_elem = args.size();
+  // PythonList my_list(num_elem);
+  // for (const char *elem : args)
+  //   my_list.append(PythonString(elem);
+  //
+  // and then pass my_list to the pfunc, but that crashes somewhere
+  // deep in Python for reasons that aren't clear to me.
+  
+  pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger)), 
SWIGBridge::ToSWIGWrapper(args_impl),
+SWIGBridge::ToSWIGWrapper(exe_ctx_ref_sp), cmd_retobj_arg.obj());
+
+  return true;
+}
+
 PythonObject lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin(
 const char *python_class_name, const char *session_dictionary_name,
 const lldb::ProcessSP &process_sp) {
diff --git a/lldb/examples/python/parsed_cmd.py 
b/lldb/examples/python/parsed_cmd.py
new file mode 100644
index 00..7ee9e077d49ab5
--- /dev/null
+++ b/lldb/examples/python/parsed_cmd.py
@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -2755,6 +2755,58 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
   return ret_val;
 }
 
+bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand(
+StructuredData::GenericSP impl_obj_sp, Args &args,
+ScriptedCommandSynchronicity synchronicity,
+lldb_private::CommandReturnObject &cmd_retobj, Status &error,

bulbazord wrote:

Makes sense to me.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -476,6 +476,14 @@ class ScriptInterpreter : public PluginInterface {
 return false;
   }
 
+  virtual bool RunScriptBasedParsedCommand(
+  StructuredData::GenericSP impl_obj_sp, Args& args,
+  ScriptedCommandSynchronicity synchronicity,
+  lldb_private::CommandReturnObject &cmd_retobj, Status &error,

bulbazord wrote:

Agreed.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {

bulbazord wrote:

Is doing it in C++ independent of this change? I think it would be worth 
writing more about this (either in the FIXME or in a GitHub issue). I've been 
slowly learning and understanding how CommandObjects work but I have very 
little knowledge on the completer machinery.


https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)

bulbazord wrote:

I think I missed that the first time I read this. Thanks for clarifying.

I wonder if it might be worth introducing a `NamedTuple` type representing this 
if it's going to be the interface we go with?

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-14 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.

bulbazord wrote:

We don't do it often but there is prior art. Specifically in a few of our SBAPI 
extensions, such as SBAddress and SBData. My $0.02 is that it's worth making 
these interfaces more pythonic since people will want to write their own 
commands with python.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits

github-actions[bot] wrote:




:warning: Python code formatter, darker found issues in your code. :warning:



You can test this locally with the following command:


``bash
darker --check --diff -r 
a41b149f481e2bcba24e81f208a1938247f040e0..aaeb653198f32386f8f7d38e4607fdd274f285d5
 lldb/examples/python/parsed_cmd.py 
lldb/test/API/commands/command/script/add/TestAddParsedCommand.py 
lldb/test/API/commands/command/script/add/test_commands.py
``





View the diff from darker here.


``diff
--- test/API/commands/command/script/add/TestAddParsedCommand.py
2023-10-30 21:48:37.00 +
+++ test/API/commands/command/script/add/TestAddParsedCommand.py
2023-11-14 01:40:47.807666 +
@@ -14,11 +14,11 @@
 NO_DEBUG_INFO_TESTCASE = True
 
 def test(self):
 self.pycmd_tests()
 
-def check_help_options(self, cmd_name, opt_list, substrs = []):
+def check_help_options(self, cmd_name, opt_list, substrs=[]):
 """
 Pass the command name in cmd_name and a vector of the short option, 
type & long option.
 This will append the checks for all the options and test "help 
command".
 Any strings already in substrs will also be checked.
 Any element in opt list that begin with "+" will be added to the 
checked strings as is.
@@ -28,119 +28,172 @@
 substrs.append(elem[1:])
 else:
 (short_opt, type, long_opt) = elem
 substrs.append(f"-{short_opt} <{type}> ( --{long_opt} <{type}> 
)")
 print(f"Opt Vec\n{substrs}")
-self.expect("help " + cmd_name, substrs = substrs)
+self.expect("help " + cmd_name, substrs=substrs)
 
 def pycmd_tests(self):
 source_dir = self.getSourceDir()
 test_file_path = os.path.join(source_dir, "test_commands.py")
 self.runCmd("command script import " + test_file_path)
-self.expect("help", substrs = ["no-args", "one-arg-no-opt", 
"two-args"])
+self.expect("help", substrs=["no-args", "one-arg-no-opt", "two-args"])
 
 # Test that we did indeed add these commands as user commands:
 
 # This is the function to remove the custom commands in order to have a
 # clean slate for the next test case.
 def cleanup():
-self.runCmd("command script delete no-args one-arg-no-opt 
two-args", check=False)
+self.runCmd(
+"command script delete no-args one-arg-no-opt two-args", 
check=False
+)
 
 # Execute the cleanup function during test case tear down.
 self.addTearDownHook(cleanup)
 
 # First test the no arguments command.  Make sure the help is right:
-no_arg_opts = [["b", "boolean", "bool-arg"],
-   "+a boolean arg, defaults to True",
-   ["d", "filename", "disk-file-name"],
-   "+An on disk filename",
-   ["e", "none", "enum-option"],
-   "+An enum, doesn't actually do anything",
-   "+Values: foo | bar | baz",
-   ["l", "linenum", "line-num"],
-   "+A line number",
-   ["s", "shlib-name", "shlib-name"],
-   "+A shared library name"]
-substrs = ["Example command for use in debugging",
-   "Syntax: no-args "]
-
+no_arg_opts = [
+["b", "boolean", "bool-arg"],
+"+a boolean arg, defaults to True",
+["d", "filename", "disk-file-name"],
+"+An on disk filename",
+["e", "none", "enum-option"],
+"+An enum, doesn't actually do anything",
+"+Values: foo | bar | baz",
+["l", "linenum", "line-num"],
+"+A line number",
+["s", "shlib-name", "shlib-name"],
+"+A shared library name",
+]
+substrs = [
+"Example command for use in debugging",
+"Syntax: no-args ",
+]
+
 self.check_help_options("no-args", no_arg_opts, substrs)
 
 # Make sure the command doesn't accept arguments:
-self.expect("no-args an-arg", substrs=["'no-args' doesn't take any 
arguments."],
-error=True)
+self.expect(
+"no-args an-arg",
+substrs=["'no-args' doesn't take any arguments."],
+error=True,
+)
 
 # Try setting the bool with the wrong value:
-self.expect("no-args -b Something",
-substrs=["Error setting option: bool-arg to Something"],
-error=True)
+self.expect(
+"no-args -b Something",
+substrs=["Error setting option: bool-arg to Something"],
+error=True,
+)
 # Try setting the enum to an illegal value as well:
-self.expect("no-args --enum-option Something",
-substrs=["error: E

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits

jimingham wrote:

I addressed most of the review comments - a few I had questions about - and 
also did the clang-format & python-deformatting.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {

jimingham wrote:

Old habits

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -476,6 +476,14 @@ class ScriptInterpreter : public PluginInterface {
 return false;
   }
 
+  virtual bool RunScriptBasedParsedCommand(
+  StructuredData::GenericSP impl_obj_sp, Args& args,
+  ScriptedCommandSynchronicity synchronicity,
+  lldb_private::CommandReturnObject &cmd_retobj, Status &error,

jimingham wrote:

This is another place where I'm just following what the Raw command form did.  
We can go clean up both, and probably should, but that seems like a separate 
pass over the code.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {

jimingham wrote:

I agree as well, but I don't want to do that sort of change in this patch.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)

jimingham wrote:

The translator methods return `(Value, Error)` if there's no translator then 
there's no error, so I have to return False here as well.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 

jimingham wrote:

Comment got displaced, it goes with the "was_set" discussion.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.

jimingham wrote:

Do we raise Python exceptions anywhere else in our API's.  It is a pythonic way 
to do things, but we don't tend to do it that way.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -2873,6 +2925,204 @@ uint32_t 
ScriptInterpreterPythonImpl::GetFlagsForCommandObject(
   return result;
 }
 
+StructuredData::ObjectSP 
+ScriptInterpreterPythonImpl::GetOptionsForCommandObject(
+StructuredData::GenericSP cmd_obj_sp) {
+  StructuredData::ObjectSP result = {};
+
+  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
+
+  static char callee_name[] = "get_options_definition";
+
+  if (!cmd_obj_sp)
+return result;
+
+  PythonObject implementor(PyRefType::Borrowed,
+   (PyObject *)cmd_obj_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+return result;
+
+  PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+return result;
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+if (PyErr_Occurred())
+  PyErr_Clear();
+return result;
+  }
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  PythonList py_return = unwrapOrSetPythonException(
+  As(implementor.CallMethod(callee_name)));
+
+  // if it fails, print the error but otherwise go on
+  if (PyErr_Occurred()) {
+PyErr_Print();
+PyErr_Clear();
+return {};
+  }
+return py_return.CreateStructuredObject();
+}
+
+StructuredData::ObjectSP 
+ScriptInterpreterPythonImpl::GetArgumentsForCommandObject(
+StructuredData::GenericSP cmd_obj_sp) {
+  StructuredData::ObjectSP result = {};
+
+  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
+
+  static char callee_name[] = "get_args_definition";
+
+  if (!cmd_obj_sp)
+return result;
+
+  PythonObject implementor(PyRefType::Borrowed,
+   (PyObject *)cmd_obj_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+return result;
+
+  PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+return result;
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+if (PyErr_Occurred())
+  PyErr_Clear();
+return result;
+  }
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  PythonList py_return = unwrapOrSetPythonException(
+  As(implementor.CallMethod(callee_name)));
+
+  // if it fails, print the error but otherwise go on
+  if (PyErr_Occurred()) {
+PyErr_Print();
+PyErr_Clear();
+return {};
+  }
+return py_return.CreateStructuredObject();
+}
+
+void 
+ScriptInterpreterPythonImpl::OptionParsingStartedForCommandObject(
+StructuredData::GenericSP cmd_obj_sp) {
+
+  Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, 
Locker::FreeLock);
+
+  static char callee_name[] = "option_parsing_started";
+
+  if (!cmd_obj_sp)
+return ;
+
+  PythonObject implementor(PyRefType::Borrowed,
+   (PyObject *)cmd_obj_sp->GetValue());
+
+  if (!implementor.IsAllocated())
+return;
+
+  PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  if (!pmeth.IsAllocated())
+return;
+
+  if (PyCallable_Check(pmeth.get()) == 0) {
+if (PyErr_Occurred())
+  PyErr_Clear();
+return;
+  }
+
+  if (PyErr_Occurred())
+PyErr_Clear();
+
+  // FIXME: this should really be a void function

jimingham wrote:

The function being called is a void return, but I didn't see a way to call 
that.  I was explaining why I ignored the return, but I made the comment clearer

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -447,6 +447,22 @@ bool CommandObject::IsPairType(ArgumentRepetitionType 
arg_repeat_type) {
  (arg_repeat_type == eArgRepeatPairRangeOptional);
 }
 
+std::optional 
+CommandObject::ArgRepetitionFromString(llvm::StringRef string) {
+  if (string == "plain") return eArgRepeatPlain ;   
+  if (string ==  "optional") return eArgRepeatOptional;
+  if (string ==  "plus") return eArgRepeatPlus;
+  if (string ==  "star") return eArgRepeatStar; 
+  if (string ==  "range") return eArgRepeatRange;
+  if (string ==  "pair-plain") return eArgRepeatPairPlain;
+  if (string ==  "pair-optional") return eArgRepeatPairOptional;
+  if (string ==  "pair-plus") return eArgRepeatPairPlus;
+  if (string ==  "pair-star") return eArgRepeatPairStar;
+  if (string ==  "pair-range") return eArgRepeatPairRange;
+  if (string ==  "pair-range-optional") return eArgRepeatPairRangeOptional;
+  return {};

jimingham wrote:

Sure

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-13 Thread via lldb-commits


@@ -2755,6 +2755,58 @@ bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
   return ret_val;
 }
 
+bool ScriptInterpreterPythonImpl::RunScriptBasedParsedCommand(
+StructuredData::GenericSP impl_obj_sp, Args &args,
+ScriptedCommandSynchronicity synchronicity,
+lldb_private::CommandReturnObject &cmd_retobj, Status &error,

jimingham wrote:

In this patch, I was just adding the Parsed command to the Raw one that was 
already there, without changing more than I needed to.  If we want to go back 
and clean up little things like this in both interfaces, I'd rather do that as 
a separate piece of work.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-03 Thread via lldb-commits


@@ -1255,6 +1258,676 @@ class CommandObjectScriptingObject : public 
CommandObjectRaw {
   CompletionType m_completion_type = eNoCompletion;
 };
 
+
+/// This command implements a lldb parsed scripted command.  The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+/// 
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private: 
+  class CommandOptions : public Options {
+  public:
+CommandOptions(CommandInterpreter &interpreter, 
+StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter), 
+m_cmd_obj_sp(cmd_obj_sp) {}
+
+~CommandOptions() override = default;
+
+Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+  ExecutionContext *execution_context) override {
+  Status error;
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+error.SetErrorString("No script interpreter for SetOptionValue.");
+return error;
+  }
+  if (!m_cmd_obj_sp) {
+error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+return error;
+  }
+  if (!m_options_definition_up) {
+error.SetErrorString("SetOptionValue called before options definitions 
"
+ "were created.");
+return error;
+  }
+  // Pass the long option, since you aren't actually required to have a
+  // short_option, and for those options the index or short option 
character
+  // aren't meaningful on the python side.
+  const char * long_option = 
+m_options_definition_up.get()[option_idx].long_option;
+  bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp, 
+execution_context, long_option, option_arg);
+  if (!success)
+error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+long_option, option_arg);
+  return error;
+}
+
+void OptionParsingStarting(ExecutionContext *execution_context) override {
+  ScriptInterpreter *scripter = 
+m_interpreter.GetDebugger().GetScriptInterpreter();
+  if (!scripter) {
+return;
+  }
+  if (!m_cmd_obj_sp) {
+return;
+  }
+  scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+};
+
+llvm::ArrayRef GetDefinitions() override {
+  if (!m_options_definition_up)
+return {};
+  return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+}
+
+static bool ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp, 
+size_t counter, uint32_t &usage_mask, Status &error) {
+  // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+  // If the usage mask is a UINT, the option belongs to that group.
+  // If the usage mask is a vector of UINT's, the option belongs to all the
+  // groups listed.
+  // If a subelement of the vector is a vector of two ints, then the option
+  // belongs to the inclusive range from the first to the second element.
+  if (!obj_sp) {
+usage_mask = LLDB_OPT_SET_ALL;
+return true;
+  }
+  
+  usage_mask = 0;
+  
+  StructuredData::UnsignedInteger *uint_val = 
+  obj_sp->GetAsUnsignedInteger();
+  if (uint_val) {
+// If this is an integer, then this specifies a single group:
+uint32_t value = uint_val->GetValue();
+if (value == 0) {
+  error.SetErrorStringWithFormatv(
+  "0 is not a valid group for option {0}", counter);
+  return false;
+}
+usage_mask = (1 << (value - 1));
+return true;
+  }
+  // Otherwise it has to be an array:
+  StructuredData::Array *array_val = obj_sp->GetAsArray();
+  if (!array_val) {
+error.SetErrorStringWithFormatv(
+"required field is not a array for option {0}", counter);
+return false;
+  }
+  // This is the array ForEach for accumulating a group usage mask from
+  // an array of string descriptions of groups.
+  auto groups_accumulator 
+  = [counter, &usage_mask, &error] 
+(StructuredData::Object *obj) -> bool {
+StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+i

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-02 Thread Med Ismail Bennani via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()

medismailben wrote:

Good point but type annotations don't enforce the type of the argument so we 
should still do some error handling in case `type(in_value) is not "str"`

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-02 Thread via lldb-commits

jimingham wrote:

I actually run against a hand-build Python with debug symbols, so it was in 
fact a backtrace that contains only python internal functions, and even though 
they had symbols I still couldn't tell what was going on...  There must be a 
bunch of handy tricks for debugging the Python interpreter, but I don't know 
them.

Jim


> On Nov 1, 2023, at 11:06 PM, Med Ismail Bennani ***@***.***> wrote:
> 
> 
> @medismailben commented on this pull request.
> 
> In lldb/bindings/python/python-wrapper.swig 
> :
> 
> > +  // FIXME:
> +  // I wanted to do something like:
> +  // size_t num_elem = args.size();
> +  // PythonList my_list(num_elem);
> +  // for (const char *elem : args)
> +  //   my_list.append(PythonString(elem);
> +  //
> +  // and then pass my_list to the pfunc, but that crashes somewhere
> +  // deep in Python for reasons that aren't clear to me.
> My guess is that the backtrace only contains for python internal non 
> symbolicated frames that are basically useless for debugging. I've 
> experienced that myself multiple times.
> 
> —
> Reply to this email directly, view it on GitHub 
> , or 
> unsubscribe 
> .
> You are receiving this because you were mentioned.
> 



https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-02 Thread via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.

jimingham wrote:

The problem with that approach is that completion happens in C++ in the 
CommandInterpreter, and if argparse were doing the parsing, to support 
completion the CommandInterpreter would have to call back to argparse to figure 
out which option was being completed.  
What I tried to do is make the interface for defining your options look the 
same as what you would have written for argparse so that if you have an extant 
command it will be easy to convert it and if you know argparse, you know how to 
define lldb commands already, the only difference being you don't use argparse, 
you add it to our object instead.

Look at the test_commands.py in the tests to get a sense of what that looks 
like.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);
+
+  // FIXME:
+  // I wanted to do something like:
+  // size_t num_elem = args.size();
+  // PythonList my_list(num_elem);
+  // for (const char *elem : args)
+  //   my_list.append(PythonString(elem);
+  //
+  // and then pass my_list to the pfunc, but that crashes somewhere
+  // deep in Python for reasons that aren't clear to me.

medismailben wrote:

My guess is that the backtrace only contains for python internal non 
symbolicated frames that are basically useless for debugging. I've experienced 
that myself multiple times.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);

medismailben wrote:

ditto.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;

medismailben wrote:

ditto.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");

medismailben wrote:

ditto.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits

https://github.com/medismailben edited 
https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Med Ismail Bennani via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,

medismailben wrote:

I'll get rid of function altogether later this week.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Greg Clayton via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.

clayborg wrote:

s/class for you command/class for your command/

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Greg Clayton via lldb-commits

https://github.com/clayborg edited 
https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Greg Clayton via lldb-commits

https://github.com/clayborg commented:

I like this idea. Since python is so dynamic, I wonder if we can just add extra 
metadata to the standard "argparse" objects so that users can just write their 
stuff in the most pythonic way possible using `import argparse` and then adding 
some extra metadata that lldb can access. For example something like:

```
$ python3
Python 3.8.9 (default, Jul 27 2021, 02:53:04) 
[Clang 7.1.0 (tags/RELEASE_710/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import argparse
>>> ap = argparse.ArgumentParser()
>>> v = ap.add_argument('-v')
>>> print(v)
_StoreAction(option_strings=['-v'], dest='v', nargs=None, const=None, 
default=None, type=None, choices=None, help=None, metavar=None)
>>> print(type(v))

>>> v.lldb_type = lldb.eArgTypeBoolean
>>> v.lldb_type
lldb.eArgTypeBoolean
```
Then users can use the standard `argparse` module and just add a bit of extra 
metadata to it that LLDB can then access when needed. This would remove the 
need for a custom LLDBOVParser class.


https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Will Hawkins via lldb-commits

hawkinsw wrote:

@jimingham Looks like great work -- I hope my few comments were helpful!

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Will Hawkins via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Will Hawkins via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)
+
+# FIXME: would this be better done on the C++ side?
+# The common completers are missing some useful ones.
+# For instance there really should be a common Type completer
+# And an "lldb command name" completer.
+completion_table = {
+lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,
+lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,
+lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,
+lldb.eArgTypeClassName : lldb.eSymbolCompletion,
+lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,
+lldb.eArgTypeExpression : lldb.eVariablePathCompletion,
+lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,
+lldb.eArgTypeFilename : lldb.eDiskFileCompletion,
+lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,
+lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,
+lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,

[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Will Hawkins via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  

hawkinsw wrote:

```suggestion
You can access the option values using the varname you passed in when defining 
the option.  
```

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)
+
+translators = {
+lldb.eArgTypeBoolean : to_bool,
+lldb.eArgTypeBreakpointID : to_unsigned,
+lldb.eArgTypeByteSize : to_unsigned,
+lldb.eArgTypeCount : to_unsigned,
+lldb.eArgTypeFrameIndex : to_unsigned,
+lldb.eArgTypeIndex : to_unsigned,
+lldb.eArgTypeLineNum : to_unsigned,
+lldb.eArgTypeNumLines : to_unsigned,
+lldb.eArgTypeNumberPerLine : to_unsigned,
+lldb.eArgTypeOffset : to_int,
+lldb.eArgTypeThreadIndex : to_unsigned,
+lldb.eArgTypeUnsignedInteger : to_unsigned,
+lldb.eArgTypeWatchpointID : to_unsigned,
+lldb.eArgTypeColumnNum : to_unsigned,
+lldb.eArgTypeRecognizerID : to_unsigned,
+lldb.eArgTypeTargetID : to_unsigned,
+lldb.eArgTypeStopHookID : to_unsigned
+}
+
+@classmethod
+def translate_value(cls, value_type, value):
+error = False
+try:
+return cls.translators[value_type](value)
+except KeyError:
+# If we don't have a translator, return the string value.
+return (value, False)

bulbazord wrote:

The 'False' is unused here, just return `value`

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":
+value = False
+error = False
+
+return (value, error)
+
+@staticmethod
+def to_int(in_value):
+#FIXME: Not doing errors yet...
+return (int(in_value), False)
+
+def to_unsigned(in_value):
+# FIXME: find an unsigned converter...
+# And handle errors.
+return (int(in_value), False)

bulbazord wrote:

What's the difference between these two? Might be worth changing `to_int` to 
`to_signed`.

Also, it looks like `to_unsigned` should be decorated with `@staticmethod`

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.
+
+There are example commands in the lldb testsuite at:
+
+llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
+
+FIXME: I should make a convenient wrapper for that. 
+"""
+import inspect
+import lldb
+import sys
+
+class LLDBOVParser:
+def __init__(self):
+self.options_array = []
+self.args_array = []
+
+# Some methods to translate common value types.  Should return a
+# tuple of the value and an error value (True => error) if the
+# type can't be converted.
+# FIXME: Need a way to push the conversion error string back to lldb.
+@staticmethod
+def to_bool(in_value):
+error = True
+value = False
+low_in = in_value.lower()
+if low_in == "yes" or low_in == "true" or low_in == "1":
+value = True
+error = False
+
+if not value and low_in == "no" or low_in == "false" or low_in == "0":

bulbazord wrote:

Empty string is false-y, this might not do what you want in that case.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -96,7 +96,8 @@ function(finish_swig_python swig_target 
lldb_python_bindings_dir lldb_python_tar
 ${lldb_python_target_dir}
 "utils"
 FILES "${LLDB_SOURCE_DIR}/examples/python/in_call_stack.py"
-  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py")
+  "${LLDB_SOURCE_DIR}/examples/python/symbolication.py"
+  "${LLDB_SOURCE_DIR}/examples/python/parsed_cmd.py")

bulbazord wrote:

Please sort these

Also, I wonder if we should put the `)` on a separate line for code archaeology 
sake?

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  

bulbazord wrote:

```suggestion
def __call__(self, debugger, args_list, exe_ctx, result):

The arguments will be in a list of strings.  
```

Python doesn't really have arrays

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument
+
+at present, lldb doesn't do as much work as it should verifying arguments, it 
pretty
+much only checks that commands that take no arguments don't get passed 
arguments.
+
+Then implement the execute function for your command as:
+
+def __call__(self, debugger, args_array, exe_ctx, result):
+
+The arguments will be in a python array as strings.  
+
+You can access the option values using varname you passed in when defining the 
option.  
+If you need to know whether a given option was set by the user or not, you can 
retrieve 
+the option definition array with:
+
+  self.get_options_definition()
+
+look up your element by varname and check the "_value_set" element.

bulbazord wrote:

```suggestion
Look up your element by varname and check the "_value_set" element.
```
What is 'varname'? And check the `_value_set` element how? Is it a bool?

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");

bulbazord wrote:

Can we add some other safety checks to `pfunc` other than if it's allocated? 
Something like "It takes the number of arguments expected"?

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.

bulbazord wrote:

```suggestion
The way to use it is to make a class for your command that inherits from 
ParsedCommandBase.
```

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation
+of your command.  Access to the OV parser is through:
+
+ParsedCommandBase.get_parser()
+
+Next, implement setup_command_definition in your new command class, and call:
+
+  self.get_parser().add_option
+
+to add all your options.  The order doesn't matter for options, lldb will sort 
them
+alphabetically for you when it prints help.
+
+Similarly you can define the arguments with:
+
+  self.get_parser.add_argument

bulbazord wrote:

```suggestion
  self.get_parser().add_argument()
```

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -0,0 +1,315 @@
+"""
+This module implements a couple of utility classes to make writing
+lldb parsed commands more Pythonic.
+The way to use it is to make a class for you command that inherits from 
ParsedCommandBase.
+That will make an LLDBOVParser which you will use for your
+option definition, and to fetch option values for the current invocation

bulbazord wrote:

```suggestion
option definition and fetch option values for the current invocation
```

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);

bulbazord wrote:

Why are you doing this here instead of in the invocation of `pfunc` below like 
you do for all the other arguments?

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


[Lldb-commits] [lldb] Add the ability to define a Python based command that uses CommandObjectParsed (PR #70734)

2023-11-01 Thread Alex Langford via lldb-commits


@@ -831,6 +831,37 @@ bool 
lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommandObject(
   return true;
 }
 
+bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
+PyObject *implementor, lldb::DebuggerSP debugger, 
lldb_private::StructuredDataImpl &args_impl,
+lldb_private::CommandReturnObject &cmd_retobj,
+lldb::ExecutionContextRefSP exe_ctx_ref_sp) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName("__call__");
+
+  if (!pfunc.IsAllocated())
+return false;
+
+  auto cmd_retobj_arg = SWIGBridge::ToSWIGWrapper(cmd_retobj);
+
+  // FIXME:
+  // I wanted to do something like:
+  // size_t num_elem = args.size();
+  // PythonList my_list(num_elem);
+  // for (const char *elem : args)
+  //   my_list.append(PythonString(elem);
+  //
+  // and then pass my_list to the pfunc, but that crashes somewhere
+  // deep in Python for reasons that aren't clear to me.

bulbazord wrote:

Do you have a backtrace or other useful info here? It would be useful to 
actually write out what you want the code to be instead of this so future 
contributors can reproduce the crash.

https://github.com/llvm/llvm-project/pull/70734
___
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits


  1   2   >