Skip to content
This repository was archived by the owner on Nov 15, 2021. It is now read-only.

Add the bases for making prompt commands plugin based #720

Merged
merged 5 commits into from
Nov 26, 2018

Conversation

LysanderGG
Copy link
Contributor

@LysanderGG LysanderGG commented Nov 18, 2018

What current issue(s) does this address, or what feature is it adding?
#623

How did you solve this problem?
This PR adds base classes for prompt commands and subcommands.
It also adds a few implementations to be sure the system is working:

  • create wallet
  • wallet
  • wallet migrate
  • wallet create_addr

This PR/branch will be quite big, so it is intended to be kept the simplest possible so the behaviour of the prompt and commands should not be changed in this PR/branch.

How did you make sure your solution works?
The prompt commands should all work as before.

Are there any special changes in the code that we should be aware of?
Not particularly.

Please check the following, if applicable:

  • Did you add any tests?
  • Did you run make lint?
  • Did you run make test?
  • Are you making a PR to a feature branch or development rather than master?
  • Did you add an entry to CHANGELOG.rst? (if not, please do)

What is left to do?
This PR is intended to put the bases for the refactoring.
Once this PR is merged, the following things are left to do.

  • Migrate all the commands to the command classes
  • Fix existing tests
  • Test the newly added classes

What can be done after?

  • This PR manually registers the commands, we should use an auto-discovery mechanism.
  • Help messages could be improved. It would be possible to have several levels, such as command listing with help, subcommands listing with command help and detailed help with command subcommand help (help could be the first word if we want).
  • Auto completion could be improved to suggest only subcommands after a command has been entered. For instance when the user has entered import, we should not suggest create_addr.

What I would like to be reviewed:

  • If the system I thought of is good enough
  • If the base classes are good enough

If possible, once the base is validated, I'd prefer to make several small PRs with a few classes at a time so it's easier and quicker to review and merge.

I am open to comments and suggestions!

Signed-off-by: Guillaume George <lysandergc@gmail.com>
@coveralls
Copy link

coveralls commented Nov 18, 2018

Coverage Status

Coverage decreased (-0.6%) to 83.26% when pulling 56e48a0 on LysanderGG:refactor-prompt into ec96ae7 on CityOfZion:refactor-prompt.

@localhuman
Copy link
Collaborator

Looks great!

@jseagrave21
Copy link
Contributor

@LysanderGG If this is implemented, I would like to help migrate the commands and tests. Perhaps we can cooperate to accomplish the goal?

@LysanderGG
Copy link
Contributor Author

@jseagrave21 Yes we should be able to split the work once this PR is merged

@ixje
Copy link
Member

ixje commented Nov 20, 2018

I've played around a bit with the code seeing if I could implement the help we discussed on discord. I've got a WIP here that results in the following (don't mind the colours)
image

So the initial idea of registering a subcommand help within the base command doesn't work because then we cannot get access to all available __sub_commands. That prevents us from displaying <base command> help.

I thought perhaps inheriting from a base command (i.e. CommandWallet) would work i.e.
def CommandWalletHelp(CommandWallet):
Unfortunately that gave me issues where most SubCommands have class methods, but one of them would require the self argument when called (i.e. CommandWalletHelp).

I ended up with the preliminary approach in the link but it feels messy as that would put all markup/alignment responsibility with the class inheriting from CommandBase and/or SubCommandBase. Perhaps I'm just too tired at this point to see a better approach, but we at least need to expose CommandBase.__sub_commands somehow to be able to provide proper help, and then I hope we can find a more solid solution to achieve the above than what I've created.

@LysanderGG
Copy link
Contributor Author

LysanderGG commented Nov 21, 2018

Thank you for having tried this!

Here are a few ideas:

  1. we could define a handle_help() method in CommandBase that returns True or False based on if it processed or not. We could then call super().handle_help(arguments) in the Commands implementations' execute().
    If I am not misreading there is nothing related to the wallet in your implementation so a default implementation in CommandBase would avoid duplicated code in all Command implementations and we wouldn't need to expose __sub_commands.
    Anyway, exposing __sub_commands is not a problem imo

  2. If we remove the @classmethod from SubCommandBase it could be implemented as a SubCommand but we couldn't handle the case of SubCommand's help (e.g. wallet migrate help).
    We would still need something like 1. or your implementation to handle this.
    Note that it would also need access to the CommandBase and __sub_commands but it's fine I think.
    It is sensible to have access to the BaseCommand from the SubCommands.

I thought perhaps inheriting from a base command (i.e. CommandWallet) would work i.e.
def CommandWalletHelp(CommandWallet):
Unfortunately that gave me issues where most SubCommands have class methods, but one of them would require the self argument when called (i.e. CommandWalletHelp).

I think I didn't understand this part.

  1. We could check if the last argument is help in prompt.py and then call a helper function in the BaseCommand similar to 1.

@ixje
Copy link
Member

ixje commented Nov 21, 2018

I followed up on 1 and 2 (result) if I understood you correctly. Works well so far as I've tested it.

In summary I did the following things

  • move help logic to CommandBase
  • let SubCommandBase inherit from CommandBase in case they need something from it
  • Make CommandDesc arguments mandatory so that we can rely on values being present when formatting instead of filtering. I think all text should be there to have a proper user experience.

Now having done this I think we might want to refactor CommandDesc.help to a list of (parameter,parameter description)'s such that we can dynamically build the help text instead of expecting the writer to \nits way to a correct help message format. See the docstrings I added for CommandDesc to see how messy that gets. Ideas?

@LysanderGG
Copy link
Contributor Author

LysanderGG commented Nov 21, 2018

Thank you for this!

The points 1 and 2 were different ideas to solve the same problem, maybe it wasn't clear in my previous message, sorry.

let SubCommandBase inherit from CommandBase in case they need something from it

This is not what I meant, I was suggesting to have SubCommandBase's __init__ method take a CommandBase to be able to access it through self.__parent_command or something like this.
The goal was to be able to call the parent's command handle_help or other method.

But with the handle_help you implemented it should not be necessary because help is not a SubCommand.
I would suggest not to inherit CommandBase for now.

Now having done this I think we might want to refactor CommandDesc.help to a list of (parameter,parameter description)

Absolutely!

However it should also have a free text variable imo, I would suggest __init__(self, command, short_help, arguments = None, help = None) because:

  • It is possible to have commands without arguments
  • the help could be more detailed than short_help and have examples of calls or warnings or whatever.

Last thing, about the point 3:

Now that I re-read my previous message, I can see it was confusing.
I think we should move the detection of help as argument[-1] in prompt.py to avoid the following duplicated code in every execute implementation.

is_handled = super().handle_help(arguments)
if is_handled:
    return

I think it could be handled as follows in prompt.py:

We wouldn't need to return a boolean any more because we would be sure that the last argument was help.

I hope this message is less confusing 😅
What do you think?

@ixje
Copy link
Member

ixje commented Nov 21, 2018

Pushed it a bit more forward again. After I took a short walk and thought why not add a ParamDesc class and then we can loop over that if there are params. See the result: here

Yeah I wasn't too happy yet with the if is_handled: construct. Perhaps you can take it a step forward and move that logic prompt.py, you seem to have a good idea for it :)

Finally, I'm not sure yet where and how to call upon the long help. I don't think we'd want a long help message every time. Meaning; wallet migrate help tells you:

neo> wallet migrate help                                                                                                                                                                        

Migrate a wallet from y to z  # <- short_help

Usage: migrate {option1} {option2} (option3) # <- command + params 

option1         - description of params 1 # <- param +  param_description
option2         - description of params 2
option3         - (Optional) description of params 3

If that isn't clear enough, then perhaps we might want to call upon help which shows examples? Or we add examples to the above after the param descriptions, but I'm not sure if that's going to be too elaborate? I'm open to opinions here.

@LysanderGG
Copy link
Contributor Author

ParamDesc is probably the cleanest way to handle it.
This looks quite good like this, maybe we don't need the help.
I personally like to have examples for complex commands in help messages but maybe it's too much indeed.

I would suggest to do without help for now and add it after if needed. Anyway it would be None by default so it won't be hard do add afterwards.

I think we've reached a good enough state, thank you for your help!

How shall we proceed now?
Should I merge your code to my branch in order to update this PR and then move the if is_handled logic to prompt.py?

@ixje
Copy link
Member

ixje commented Nov 22, 2018

Should I merge your code to my branch in order to update this PR and then move the if is_handled logic to prompt.py?

Sounds good!

Signed-off-by: Guillaume George <lysandergc@gmail.com>
Signed-off-by: Guillaume George <lysandergc@gmail.com>
@LysanderGG
Copy link
Contributor Author

LysanderGG commented Nov 26, 2018

e592858 moves the detection of help to prompt.py.
Do we have something else to handle in this PR?

Copy link
Member

@ixje ixje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of last touches such that we have a clean start where others can help migrate. Thanks for picking this issue up, this is going to be a good first step in cleaning up and improving usability/user experience 👍

@ixje
Copy link
Member

ixje commented Nov 26, 2018

I just took a moment to think about how to group all available commands and have a suggestion posted here: #623 (comment)

One thing I noticed is that we might want a 3rd level subcommand 🤔 If you look at the above proposal we might have wanted the following

neo> wallet token help
- send
- send_from
- approve
- history

neo> wallet token send help // <- this level is not possible atm

Usage: send {token symbol} {address_from} {address to} {amount}

token_symbol - description
address_from - description
address_to - description
amount - description

instead we're now forced to have

neo> wallet help
tkn_send - send tokens
tkn_send_from - send tokens from address
etc.

neo> wallet tkn_send help
Usage: tkn_send {token symbol} {address_from} {address to} {amount}

token_symbol - description
address_from - description
address_to - description
amount - description

similarly for wallet import and wallet export

Signed-off-by: Guillaume George <lysandergc@gmail.com>
Signed-off-by: Guillaume George <lysandergc@gmail.com>
@LysanderGG
Copy link
Contributor Author

I fixed the points you raised and moved create into wallet.

About the 3rd level of subcommands, I went ahead and remove SubCommandBase.
subcommands are now inheriting CommandBase and we now support any levels of subcommands.

I made the necessssary functions recursive and added a __parent_command member to CommandBase.

Here is for the sake of and example the console output when I put create wallet as a subcommand of wallet. (It is just for the example, I changed it afterwards).

example

Please let me know what you think :)

@ixje
Copy link
Member

ixje commented Nov 26, 2018

💯
Thanks allot, this is exciting!

@ixje ixje merged commit 9b44392 into CityOfZion:refactor-prompt Nov 26, 2018
@LysanderGG
Copy link
Contributor Author

Thank you for your help and reviews!
Let's move them all now 👍

ixje added a commit that referenced this pull request Jan 10, 2019
* Add the bases for making prompt commands plugin based (#720)

* WIP make prompt commands plugin based

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Merge ixje:refactor-prompt

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Handle help detection in prompt.py

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Use CommandBase for subcommands to be able to have N levels of commands.

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Move "create wallet" command into "wallet"

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Improve CommandBase.register_sub_command to use the subcommand's CommandDesc.command as an id. (#725)

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix 1 word commands (#727)

* Improve CommandBase.register_sub_command to use the subcommand's CommandDesc.command as an id.

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix exception when a command was used without arguments (for example "wallet")

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Add support for `wallet` `send`, `sendmany`, `sign`, `open`, and `close` (#726)

* Prompt cleanup (#730)

* Update description and clarify debug logs

* Make return's explicit, clarify multi-sig help message

* cleanup prompt

* remove colour from initial help, sort commands alphabetically, cleanup descriptions

* fix linting

* process feedback

* [refactor-prompt] Prompt fixes (#732)

* Improve CommandBase.register_sub_command to use the subcommand's CommandDesc.command as an id.

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix CommandWalletCreateAddress missing parameter desc

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix missing words for autocompletion

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* [refactor-prompt] Add missing tests for wallet create, verbose and create_addr (#733)

* Add missing tests for wallet create, verbose and create_addr

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* fix reviews

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* [refactor prompt] Add support for search method group (#739)

* [refactor prompt] Add support for show method group (pt 1) (#738)

* Fix NodeLeader was not reset between test cases. (#742)

This would lead to some problems when the blockchain is reset but the NodeLeader instance members are not.
Transactions were still considered as known.

Some members were not reset in NodeLeader.Setup()
Also removed unsued member NodeLeader.MissionsGlobal

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Migrate command: wallet rebuild (#747)

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* remove deprecated migrate command

* remove deprecated migrate command (#748)

* [refactor prompt] Add support for show method group (pt 2) (#741)

* [refactor-prompt] Implement CommandWalletClaimGas (#740)

* [refactor-prompt] Add wallet token base + delete (#757)

* [refactor-prompt] Migrate command: wallet delete_addr (#752)

* Migrate command: wallet delete_addr

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* UserWallet.DeleteAddress now returns False on error + proper handling of invalid addresses in CommandWalletDeleteAddress

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Migrate command: wallet alias (#753)

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* [refactor-prompt] Add wallet token send (#758)

* remove deprecated migrate command

* Add wallet token send

* Update requirements and use isValidPublicAddress from new neo-python-core version

* - fix send token description
- process feedback

* fix doc string typo

* [refactor prompt] Add support for config group pt1 (#759)

* add wallet import nep2/wif (#765)

* [refactor-prompt] add wallet token history (#763)

* Add wallet token history

* process feedback

* fix broken test after base branch merge

* [refactor-prompt] add wallet export commands (#764)

* add wallet export commands

* Fix export nep2 to ask for passwords to prevent pw leaking to logs

* process feedback

* [refactor-prompt] add wallet import watchaddr (#766)

* [refactor-prompt] add token sendfrom (#761)

* [refactor-prompt] add import multisig_addr (#767)

* add import multsig address

* remove obsolete function

* [refactor-prompt] Migrate command: wallet unspent (#760)

* migrate command: wallet unspent

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* wallet unspent - add tests

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* fix arguments names and missing doc

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Handle review: add feedback when there is no asset matching the arguments + use neocore.Utils.isValidPublicAddress

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* [refactor prompt] Add support for config maxpeers (#762)

* add token approve and token allowance (#769)

* add config nep8 (#771)

* [refactor-prompt] Migrate command: wallet split (#770)

* Migrate command wallet split

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix some comments

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Fix command desc + remove print() calls committed by mistake

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Add tests for CommandWalletSplit

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Review: test_wallet_split use string arguments instead of ints

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Handle Reviews - handle negative fees, improve error messages

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* add token mint (#773)

* [refactor prompt] Fix `search asset` and `search contract` (#774)

* Update prompt.py

- add CommandShow and CommandSearch to prompt

* Update prompt.py

revert changes

* Update Search.py

- update `search contract` and `search asset` per #623 (comment)

* Update test_search_commands.py

- add tests in case no search parameter is provided

* add token register (#775)

* [refactor-prompt] Migrate command: wallet import token (#772)

* test_wallet_commands.py: Move tests of non commands at the end

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Add wallet import token

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Review: return None implicitly where possible

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Add a few tests for SplitUnspentCoin()

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* CommandWalletImportToken - Handle review: better validation of contract_hash

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* Add wallet import contract_addr (#777)

Signed-off-by: Guillaume George <lysandergc@gmail.com>

* split prompt wallet into multiple files (#782)

* [refactor-prompt] add support for the sc group including build, build_run, and load_run (#779)

* [refactor-prompt] cleanup 2 (#783)

* make base command response consistent

* fix plurality

* check input parameters before closing wallet

* streamline missing arguments response
streamline accepted boolean options for config

* process feedback

* [refactor-prompt] add debugstorage command (#784)

* add debugstorage command (and fix auto save linting error)

* correct comments

* [refactor-prompt] add sc deploy (#785)

* add sc deploy (previously known as; import contract)

* process feedback

* [refactor-prompt] add sc invoke (#786)

* add sc invoke (previously known as testinvoke)

* process feedback

* streamline parameter descriptions (#787)

* add wallet password checking before exporting (#790)

* fix exception in help if command has no params (#792)

* [refactor-prompt] enforce pw prompt send (#791)

* remove password bypass

* remove unused import

* [refactor-prompt] update docs (#789)

* update global readme

* update docs

* process feedback

* update show contract output

* [refactor-prompt] restore theming support (#788)

* re-store theming support

* fix comment

* improve wallet usage help (#794)

* clarify insufficient funds error message due to not enough unspent outputs (#793)

* fix prompt printer for lists and other possible objects (#795)

* Fix send/sendmany default wallet source address for tx (#796)

* [refactor-prompt] Improve send & sendmany (third try) (#799)

* Update Send.py

* Update test_send_command.py

* rename test_send_command.py as test_send_commands.py

* [refactor-prompt] Fix #800 (#802)

* Update prompt.py

- add CommandShow and CommandSearch to prompt

* Update prompt.py

revert changes

* Fix #800

- adds support for contracts not requiring params

* fix calculation of change value when using alternating asset inputs (#803)

* test print fix

* oops

* test again

* update makefile

* update changelog to trigger stuck build
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants