-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: Allow named and positional arguments to be used together #19762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Concept ACK. As a frequent cli user, this seems quite handy. |
Concept ACK: neat! |
src/rpc/server.cpp
Outdated
for (size_t i = 0; i < named_args.size(); ++i) { | ||
if (i >= out.params.size()) { | ||
out.params.push_back(named_args[i]); | ||
} else if (!named_args[i].isNull()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we overwrite the null with the named arg value??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
Where do we overwrite the null with the named arg value??
This code isn't overwriting the previous list of positional parameters, but building a new list from args
and parts of the old list.
Concept ACK on this functionality in the client. I wish it could be client-only, though and would not introduce a non-standard server side convention (and further complicate the RPC server's argument parsing logic). |
Seems useful for non-CLI callers too? |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
This changes interpretation of bitcoin-cli -named arguments that begin with '='. Previously these were sent as JSON-RPC named parameters with empty string "" as parameter names. Now they are sent as unnamed arguments. For example, "bitcoin-cli -named =VALUE" previously sent named "" parameter containing "VALUE". Now it sends unnamed positional argument containing "VALUE". Motivation for this change is to provide a way to pass unnamed positional arguments containing '=' with bitcoin-cli -named. Luke-jr suggested this might be useful bitcoin#19762 (comment) This change is backwards compatible because bitcoind does not have any RPC methods accepting "" as a parameter name, so this is just making previously invalid bitcoin-cli calls valid, not changing interpretation of any already valid bitcoin-cli call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated f8cd575 -> f0bb56e (pr/named.1
-> pr/named.2
, compare) with a few changes:
- Added JSON-RPC documentation and release notes
- Added two test cases suggested by Luke
- Added stricter server error checking in the two test cases. Instead of allowing the same parameter to be specified twice (in positional and named forms if the positional value is null), the server always throws an error if a parameter is specified twice. This could be changed later, but for now being more strict seems better for preventing mistakes.
src/rpc/server.cpp
Outdated
for (size_t i = 0; i < named_args.size(); ++i) { | ||
if (i >= out.params.size()) { | ||
out.params.push_back(named_args[i]); | ||
} else if (!named_args[i].isNull()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
Where do we overwrite the null with the named arg value??
This code isn't overwriting the previous list of positional parameters, but building a new list from args
and parts of the old list.
null is supposed to be equivalent to omitted, so I think it should work. |
Concept ACK. |
On the cli we could also support passing JSON arguments like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested almost-ACK. The example in the PR description works, but I wasn't able to make it work for me in some other cases. Perhaps I'm misunderstanding how it works, or the documentation can be improved.
These work:
$ ./src/bitcoin-cli -named getrawtransaction txid=abc verbose=true
$ ./src/bitcoin-cli -named getrawtransaction abc verbose=true
but
$ ./src/bitcoin-cli -named getrawtransaction txid=abc true
error code: -8
error message:
Parameter txid specified twice both as positional and named argument
$ ./src/bitcoin-cli -named getrawtransaction txid=abc 1
error code: -8
error message:
Parameter txid specified twice both as positional and named argument
src/rpc/server.cpp
Outdated
} | ||
// If leftover "args" param was found, use it as a source of positional | ||
// arguments and add named arguments after. | ||
auto positional_args = argsIn.find("args"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit 1: call it _args
to have some internal "feeling"?
nit 2: forbid registering arguments with this name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
nit 1: call it
_args
to have some internal "feeling"?
I don't think it would be good have RPC methods accepting both "_args" and "args" parameters. Naming was chosen to discourage this (while not disallowing it). Also it isn't really meant to be internal. It's supported by two clients in this PR, explained in documentation, and meant to be something that can be used by other clients.
nit 2: forbid registering arguments with this name.
I think it's fine if RPC methods want to handle positional arguments with their own logic, and don't think it would be good to add extra code to detect and forbid it a priori.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine if RPC methods want to handle positional arguments with their own logic, and don't think it would be good to add extra code to detect and forbid it a priori.
It makes this code needlessly complex. If in the future someone wants to add an "args" arg, they can modify the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
It makes this code needlessly complex. If in the future someone wants to add an "args" arg, they can modify the code.
It is true that this decision makes the implementation of the transformNamedArguments
function slightly more complex. But it keeps the design as a whole simpler because:
- It handles
args
parameter entirely withintransformNamedArguments
, and doesn't require adding special cases for parameters namedargs
in other parts of the code. - It avoids adding a footgun where parameters are treated differently based on their names and choosing a different parameter name could cause errors or munged data.
I also think the transformNamedArguments
function would be easier to simplify in the future with some changes to the UniValue class. The current UniValue API basically makes JSON objects append-only instead of read-write, and transformNamedArguments
could be simpler and more efficient if this were changed,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated f0bb56e -> 894c414 (pr/named.2
-> pr/named.3
, compare) with suggested doc improvments
re: #19762 (review)
Tested almost-ACK. The example in the PR description works, but I wasn't able to make it work for me in some other cases. Perhaps I'm misunderstanding how it works, or the documentation can be improved.
I changed documentation to say "initial positional values" instead of "positional values" (and feel free to suggest other documentation updates that would help) but the examples cited wouldn't be accepted before or after this PR, and don't work in python either. The first positional argument is always the first positional argument. The second positional argument is always the second positional argument. You can't bump a positional argument into a different positions by adding named arguments. If you try, you see an explicit error about conflicting values for the positional argument.
re: #19762 (comment)
On the cli we could also support passing JSON arguments like
query_options.minimumAmount=0.001 query_options.maximumCount=1
.
Presumably this would be for another PR, but it seems like a nice idea
src/rpc/server.cpp
Outdated
} | ||
// If leftover "args" param was found, use it as a source of positional | ||
// arguments and add named arguments after. | ||
auto positional_args = argsIn.find("args"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
nit 1: call it
_args
to have some internal "feeling"?
I don't think it would be good have RPC methods accepting both "_args" and "args" parameters. Naming was chosen to discourage this (while not disallowing it). Also it isn't really meant to be internal. It's supported by two clients in this PR, explained in documentation, and meant to be something that can be used by other clients.
nit 2: forbid registering arguments with this name.
I think it's fine if RPC methods want to handle positional arguments with their own logic, and don't think it would be good to add extra code to detect and forbid it a priori.
Looking over recent irc conversation, if you don't like the createwallet API, you probably won't like this PR. Even still, this PR should make the createwallet RPC easier to call in a backward compatible way, without getting in the way of future improvements. This PR can also nicely complement client side evaluation features like #20273 |
Thanks for the comment; reminds me to re-test this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested ACK 894c414
Thanks for the updates. Not sure why I was confused the first time, but this seems much clearer.
src/rpc/server.cpp
Outdated
if (out.params.size() == 0) skipped += 1; | ||
} | ||
// If leftover "args" param was found, use it as a source of positional | ||
// arguments and add named arguments after. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outside of this PR changeset, it may not be clear where "args" comes from. It may be good to mention in the comment here that "args" is set in rpc/client.cpp::RPCConvertNamedValues()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
Outside of this PR changeset, it may not be clear where "args" comes from. It may be good to mention in the comment here that "args" is set in
rpc/client.cpp::RPCConvertNamedValues()
.
Thanks, if I follow up I'll reference JSON-RPC-interface.md#parameter-passing here. Better to reference the documentation than the implementation details of one specific client, though. Referencing the RPCConvertNamedValues
function where "args" is set in the CLI client would be equivalent to referencing AuthServiceProxy.get_request
method where "args" is set in the python client. These would be potentially helpful comments, but also likely to become out of date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #19762 (comment)
Thanks, if I follow up I'll reference JSON-RPC-interface.md#parameter-passing here.
Thanks again, added this comment now
This PR would make it much nicer to use the |
Current behavior isn't ideal and will be changed in upcoming commits, but it's useful to have test coverage regardless. MarcoFalke reported the case of positional arguments overwriting the named "args" parameter in bitcoin-cli bitcoin#19762 (comment)
…ferent ways MarcoFalke reported the case of positional arguments silently overwriting the named "args" parameter in bitcoin-cli bitcoin#19762 (comment) and this behavior is confusing and was not intended when support for support for "args" was added to bitcoin-cli. Instead of letting one "args" value overwrite the other in the client, just pass the values to the server verbatim, and let the error be handled server side.
Current behavior isn't ideal and will be changed in upcoming commits, but it's useful to have test coverage regardless. MarcoFalke reported the case of bitcoin-cli positional arguments overwriting the named "args" parameter in bitcoin#19762 (comment)
…ferent ways MarcoFalke reported the case of positional arguments silently overwriting the named "args" parameter in bitcoin-cli bitcoin#19762 (comment) and this behavior is confusing and was not intended when support for "args" parameters was added to bitcoin-cli in bitcoin#19762. Instead of letting one "args" value overwrite the other in the client, just pass the values to the server verbatim, and let the error be handled server side.
No changes in behavior, just implements review suggestions from bitcoin#19762 (comment) bitcoin#19762 (comment) bitcoin#26628 (comment)
Current behavior isn't ideal and will be changed in upcoming commits, but it's useful to have test coverage regardless. MarcoFalke reported the case of bitcoin-cli positional arguments overwriting the named "args" parameter in bitcoin/bitcoin#19762 (comment)
…ferent ways MarcoFalke reported the case of positional arguments silently overwriting the named "args" parameter in bitcoin-cli bitcoin/bitcoin#19762 (comment) and this behavior is confusing and was not intended when support for "args" parameters was added to bitcoin-cli in #19762. Instead of letting one "args" value overwrite the other in the client, just pass the values to the server verbatim, and let the error be handled server side.
No changes in behavior, just implements review suggestions from bitcoin/bitcoin#19762 (comment) bitcoin/bitcoin#19762 (comment) bitcoin/bitcoin#26628 (comment)
backport: merge bitcoin#21391, bitcoin#19550, bitcoin#15946, bitcoin#21230, bitcoin#21252, bitcoin#21297, bitcoin#20605, bitcoin#21575, bitcoin#22309, bitcoin#19762 (assumeutxo backports: part 4)
It's nice to be able to use named options and positional arguments together.
Most shell tools accept both, and python functions combine options and arguments allowing them to be passed with even more flexibility. This change adds support for python's approach so as a motivating example:
Can be shortened to:
JSON-RPC standard doesn't have a convention for passing named and positional parameters together, so this implementation makes one up and interprets any unused
"args"
named parameter as a positional parameter array.This change is backwards compatible. It doesn't change the interpretation of any previously valid calls, just treats some previously invalid calls as valid.
Another use case even if you only occasionally use named arguments is that you can define an alias:
And now use both named named and unnamed arguments from the same alias without having to manually add
-named
option for named arguments or see annoying error "No '=' in named argument... this needs to be present for every argument (even if it is empty)`" for unnamed arguments