jingham marked an inline comment as done.
jingham added inline comments.
================
Comment at: lldb/include/lldb/Interpreter/ScriptInterpreter.h:469
+
+ virtual int GetNumArgumentsForCallable(const char *callable_name) {
+ return -1;
----------------
jingham wrote:
> labath wrote:
> > labath wrote:
> > > lawrence_danna wrote:
> > > > jingham wrote:
> > > > > lawrence_danna wrote:
> > > > > > labath wrote:
> > > > > > > jingham wrote:
> > > > > > > > lawrence_danna wrote:
> > > > > > > > > labath wrote:
> > > > > > > > > > In light of varargs functions (`*args, **kwargs`), which
> > > > > > > > > > are fairly popular in python, the concept of "number of
> > > > > > > > > > arguments of a callable" does not seem that well defined.
> > > > > > > > > > The current implementation seems to return the number of
> > > > > > > > > > fixed arguments, which might be fine, but I think this
> > > > > > > > > > behavior should be documented. Also, it would be good to
> > > > > > > > > > modernize this function signature -- have it take a
> > > > > > > > > > StringRef, and return a `Expected<unsigned (?)>` -- ongoing
> > > > > > > > > > work by @lawrence_danna will make it possible to return
> > > > > > > > > > errors from the python interpreter, and this will make it
> > > > > > > > > > possible to display those, instead of just guessing that
> > > > > > > > > > this is because the callable was not found (it could in
> > > > > > > > > > fact be because the named thing is not a callable, of
> > > > > > > > > > because resolving the name produced an exception, ...).
> > > > > > > > > I just took a look at PythonCallable::GetNumArguments() and
> > > > > > > > > it's horribly broken.
> > > > > > > > >
> > > > > > > > > It doesn't even work for the simplest test case I could think
> > > > > > > > > of.
> > > > > > > > >
> > > > > > > > > ```
> > > > > > > > > auto builtins = PythonModule::Import("builtins");
> > > > > > > > > ASSERT_THAT_EXPECTED(builtins, llvm::Succeeded());
> > > > > > > > > auto hex =
> > > > > > > > > As<PythonCallable>(builtins.get().GetAttribute("hex"));
> > > > > > > > > ASSERT_THAT_EXPECTED(hex, llvm::Succeeded());
> > > > > > > > > EXPECT_EQ(hex.get().GetNumArguments().count, 1u);
> > > > > > > > > ```
> > > > > > > > >
> > > > > > > > > we should really re-write it to use inspect.signature.
> > > > > > > > Interesting. We use it for free functions (what you pass to
> > > > > > > > the -F option of `breakpoint command add`) and for the __init__
> > > > > > > > and __call__ method in the little classes you can make up for
> > > > > > > > scripted thread plans and for the class version of Python
> > > > > > > > implemented command-line commands. We have tests for telling 3
> > > > > > > > vrs. 4 vrs. not enough or too many, and they all pass. So it
> > > > > > > > seems to work in the cases we currently need it to work for...
> > > > > > > >
> > > > > > > > "inspect.signature" is python 3 only, and the python 2
> > > > > > > > equivalent is deprecated. So it will take a little fancy
> > > > > > > > footwork to use it in the transition period.
> > > > > > > lol :)
> > > > > > >
> > > > > > > I would actually say that we should try not to use this
> > > > > > > function(ality) wherever possible. Making decisions based on the
> > > > > > > number of arguments the thing you're about to call takes sounds
> > > > > > > weird. I don't want to get too involved in this, but I was
> > > > > > > designing this, I'd just say that if one tries to pass arguments
> > > > > > > to the callback then the callback MUST take three arguments (or
> > > > > > > we'll abort processing the breakpoint command). If he wants his
> > > > > > > function to be called both with arguments and without, he can
> > > > > > > just add a default value to the third argument. (And if his
> > > > > > > function takes two arguments, but he still tries to pass
> > > > > > > something... well, it's his own fault).
> > > > > > >
> > > > > > > Anyway, feel free to ignore this comment, but I felt like I had
> > > > > > > to say something. :)
> > > > > > I completely agree with Pavel. Inspecting a function signature
> > > > > > before calling it is a big code smell in python. If there's a
> > > > > > way to avoid doing that introspection, that would be better.
> > > > > Unfortunately, we originally designed this interface to take three
> > > > > arguments, the frame pointer, the breakpoint location and the Python
> > > > > session dict. Then it became clear that it would be better to add
> > > > > this extra args argument (and in the case of Python based commands
> > > > > the ExecutionContext pointer). At that point we had three choices,
> > > > > abandon the improvement; switch to unconditionally passing the extra
> > > > > arguments, and break everybody's extant uses; or switch off of the
> > > > > number of arguments to decide whether the user had provided the old
> > > > > or new forms.
> > > > >
> > > > > My feeling about lldb Python scripts/commands etc. that people use in
> > > > > the debugger is that a lot of users don't know how they work at all,
> > > > > they just got them from somebody else; and many more figured out how
> > > > > to write them for some specific purpose, and then pretty much forgot
> > > > > how they worked. So suddenly breaking all these bits of
> > > > > functionality will result in folks just deciding that this affordance
> > > > > is not reliable and not worth their time, which would be a shame.
> > > > >
> > > > > So instead we accommodate both forms, which requires that we know
> > > > > which one the user provided. If you see a better way to do this,
> > > > > (and are willing to implement it because so far as I can see this
> > > > > method is going to work just fine) dig in, I'm not wedded to the
> > > > > particular approach. But I am not excited about penalizing our users
> > > > > because we didn't get the API design right the first time through.
> > > > makes sense.
> > > >
> > > > The only other way I can think of to solve it would be to have some
> > > > indication in the `break com add` command of what signature it expects
> > > > from the function. But that's really ugly too because now you're
> > > > asking users to understand yet another option.
> > > >
> > > > I put up https://reviews.llvm.org/D68995 this morning which adds
> > > > `inspect.signature` support for pythons that have it.
> > > >
> > > > Currently we have really gnarly ArgInfo tests like this:
> > > > ```
> > > > if (argc.count == 5 || argc.is_bound_method || argc.has_varargs)
> > > > pfunc(debugger_arg, PythonString(args), exe_ctx_arg,
> > > > cmd_retobj_arg, dict);
> > > > else
> > > > pfunc(debugger_arg, PythonString(args), cmd_retobj_arg, dict);
> > > > ```
> > > >
> > > > 😖
> > > >
> > > > I think if we replace `count` with `max_positional_args` we should be
> > > > able to replace that kindof test with just
> > > > ```
> > > > if (argc.max_positional_args < 5)
> > > > old_version
> > > > else
> > > > new_version
> > > > ```
> > > >
> > > >
> > > So, how about this: Put extra_args as the last argument, instead of
> > > inserting it in the middle (so the new signature becomes `frame, bp_loc,
> > > dict, extra_args` instead of `frame, bp_loc, extra_args, dict`. Then
> > > instead of
> > > ```
> > > if (arg_info.count == 3)
> > > result = pfunc(frame_arg, bp_loc_arg, dict);
> > > else if (arg_info.count == 4) {
> > > lldb::SBStructuredData *args_value = new
> > > lldb::SBStructuredData(args_impl);
> > > PythonObject args_arg(PyRefType::Owned,
> > > SBTypeToSWIGWrapper(args_value));
> > > result = pfunc(frame_arg, bp_loc_arg, args_arg, dict);
> > > ```
> > > we do:
> > > ```
> > > if (args_impl.was_specified())
> > > pfunc(frame_arg, bp_loc_arg, dict, args_arg)
> > > else
> > > pfunc(frame_arg, bp_loc_arg, dict);
> > > ```
> > > All existing scripts will not specify the extra arguments, so they will
> > > work as usual. New scripts which do pass extra arguments will have to use
> > > the new signature. New scripts can also put `args = None` in their python
> > > signature, so that they are callable both with and without arguments,
> > > should they so desire. (If we don't want to support the `=None` use case
> > > then we can even keep the arguments in the same order as in this patch.)
> > >
> > > Is there some reason why that would not work?
> > > The only other way I can think of to solve it would be to have some
> > > indication in the break com add command of what signature it expects from
> > > the function.
> >
> > That's kind of what I'm getting at. I am hoping that the presence of the
> > `--key`, `--value` options can be used as an indicator of that signature.
> > (Though maybe I am misunderstanding something and I should shut up.)
> The is_bound_method test is cheesy. We didn't offer a "class with __call__"
> for Python based commands until after we added the ExecutionContext argument,
> so this check knows that if you are providing a class method, then you are
> probably also providing the correct number of arguments. max_positional_args
> seems a more explicit approach.
>
> I think the varargs check is to allow you to write a command that takes the
> old three arguments and the ExecutionContext as a vararg, so the same Python
> function could work with an older lldb that didn't send the exe_ctx but take
> advantage of the better interface if it was present. After all, the fallback
> of using the currently selected "target/process/thread/frame" inside the
> function will mostly work.
>
> For the affordances taking the "extra_args", like breakpoint commands and
> scripted breakpoints and the like, it's hard to see how you could have a
> reasonable fallback to "you didn't tell me what function to look for..." So
> for these I want to count the fixed arguments, I don't think it is necessary
> to allow them to be passed as varargs.
>
> Thanks for fixing up the PythonObjects code, BTW!
I did consider switching off of whether the user provided key & value
arguments. But that would mean that you could not write Python breakpoint
command functions that have default behaviors, but will do special things if
the user provided `--key` and `--value` arguments. That seemed like a
reasonable thing to support, so I switched to doing everything based on the
actual signature we were provided.
Repository:
rLLDB LLDB
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D68671/new/
https://reviews.llvm.org/D68671
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits