italorossi / ishell Goto Github PK
View Code? Open in Web Editor NEWCreate shell environments with Python
License: MIT License
Create shell environments with Python
License: MIT License
There is a command with a dynamic argument, let's say "calls" is the command, "continuous" is the dynamic arg. There is also another command that starts with the same letter as the dynamic arg, say "call".
If I now type "calls c" and hit TAB for autocompletion, the console completes the "c" to "call", rather than "continuous".
This does not happen, if the "call" command does not exist.
The debug log looks like this:
2014-04-10 12:22:09,903 DEBUG(40) Line=>['show', 'calls', 'call']
2014-04-10 12:22:09,905 DEBUG(49) Found existing command=>show
2014-04-10 12:22:09,905 DEBUG(34) Walked to: show
2014-04-10 12:22:09,906 DEBUG(54) More than one candidate, not walking in call
2014-04-10 12:22:09,906 DEBUG(103) PossibleCompletions=>['call ', 'calls ', None]
2014-04-10 12:22:09,906 DEBUG(40) Line=>['show', 'calls', 'call']
2014-04-10 12:22:09,906 DEBUG(49) Found existing command=>show
2014-04-10 12:22:09,906 DEBUG(34) Walked to: show
2014-04-10 12:22:09,906 DEBUG(54) More than one candidate, not walking in call
2014-04-10 12:22:09,907 DEBUG(103) PossibleCompletions=>['call ', 'calls ', None]
2014-04-10 12:22:09,907 DEBUG(40) Line=>['show', 'calls', 'call']
2014-04-10 12:22:09,907 DEBUG(49) Found existing command=>show
2014-04-10 12:22:09,907 DEBUG(34) Walked to: show
2014-04-10 12:22:09,907 DEBUG(54) More than one candidate, not walking in call
2014-04-10 12:22:09,907 DEBUG(103) PossibleCompletions=>['call ', 'calls ', None]
The example program to demonstrate the bug is this:
#!/usr/local/pythonenv/evolux/bin/python
# -*- coding: utf-8 -*-
import logging
from ishell.console import Console
from ishell.command import Command
from ishell.utils import _print
from ishell.log import logger
class SomeClass(object):
def __init__(self):
self.var = 1
def some_function(self):
self.var += 1
_print(str(self.var))
class ShowCommand(Command):
""" show first level command. """
def run(self, line):
_print("Missing parameter. Hit tab for possible completions")
class ShowCalls(Command):
def args(self):
return ["raw", "continuous"]
def run(self, line):
_print("This is the show calls command")
class ShowCall(Command):
def args(self):
return ["100", "101", "103"]
def run(self, line):
_print("This is the show call command")
def main():
logger.setLevel(logging.DEBUG)
console = Console("")
show = ShowCommand("show")
calls = ShowCalls("calls", dynamic_args=True)
call = ShowCall("call", dynamic_args=True)
show.addChild(calls)
show.addChild(call)
console.addChild(show)
try:
console.loop()
except (KeyboardInterrupt, EOFError):
pass
if __name__ == "__main__":
main()
Currently the shell just returns with no message if the user type a unknown command.
It'll be useful if a message like 'Command not found: command name' is printed.
It would be useful to have visibility into the command line being completed in Command.args().
For instance, if you wanted to do bash-style path completion, you could simply:
from ishell.console import Console
from ishell.command import Command
import glob
class ExampleCommand(Command):
def args(self, line):
line_without_command_name = ''.join(line.split()[1:])
return glob.glob(line_without_command_name + '*')
def run(self, line):
print "This is an example command."
example = ExampleCommand('example', dynamic_args=True)
console = Console()
console.addChild(example)
The version of ishell
on PyPI fails to install on Windows since the latest pull request which fixes that issue has not been put up on PyPI.
(Yes, I know you only accepted that PR yesterday but I'm very excited for FeatherDuster to finally be installable via pip :)
Hitting ctrl+d
, a common exit command, exits uncleanly and throws a traceback. This should act in the same way as ctrl+d
does.
When using Ctrl+C, and causing a KeyboardInterrupt Exception, the reset code for the prompt and the completer are not executed. When using a console wrapped in another console, this leads to the outer console still completing to the inner console's commands after closing the latter one.
As we can see, the readline project at PyPI is deprecated because of it's name: https://pypi.org/project/readline/
We need to change it for https://pypi.org/project/gnureadline/.
When I have tried install I got the error below:
============ Building the readline extension module ============
running bdist_wheel
running build
running build_ext
building 'readline' extension
creating build
creating build/temp.linux-x86_64-2.7
creating build/temp.linux-x86_64-2.7/Modules
creating build/temp.linux-x86_64-2.7/Modules/2.x
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -DHAVE_RL_CALLBACK -DHAVE_RL_CATCH_SIGNAL -DHAVE_RL_COMPLETION_APPEND_CHARACTER -DHAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK -DHAVE_RL_COMPLETION_MATCHES -DHAVE_RL_COMPLETION_SUPPRESS_APPEND -DHAVE_RL_PRE_INPUT_HOOK -I. -I/usr/include/python2.7 -c Modules/2.x/readline.c -o build/temp.linux-x86_64-2.7/Modules/2.x/readline.o -Wno-strict-prototypes
creating build/lib.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security -Wl,-Bsymbolic-functions -Wl,-z,relro -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/Modules/2.x/readline.o readline/libreadline.a readline/libhistory.a -lncurses -o build/lib.linux-x86_64-2.7/readline.so
/usr/bin/x86_64-linux-gnu-ld: não foi possível localizar -lncurses
collect2: error: ld returned 1 exit status
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
----------------------------------------
Failed building wheel for readline
Running setup.py clean for readline
Failed to build readline
Installing collected packages: readline
Running setup.py install for readline ... error
Complete output from command /home/rafael/.virtualenvs/ishell_poc/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-xzLi3U/readline/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-sKWvZX/install-record.txt --single-version-externally-managed --compile --install-headers /home/rafael/.virtualenvs/ishell_poc/include/site/python2.7/readline:
running install
running build
running build_ext
building 'readline' extension
creating build
creating build/temp.linux-x86_64-2.7
creating build/temp.linux-x86_64-2.7/Modules
creating build/temp.linux-x86_64-2.7/Modules/2.x
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -DHAVE_RL_CALLBACK -DHAVE_RL_CATCH_SIGNAL -DHAVE_RL_COMPLETION_APPEND_CHARACTER -DHAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK -DHAVE_RL_COMPLETION_MATCHES -DHAVE_RL_COMPLETION_SUPPRESS_APPEND -DHAVE_RL_PRE_INPUT_HOOK -I. -I/usr/include/python2.7 -c Modules/2.x/readline.c -o build/temp.linux-x86_64-2.7/Modules/2.x/readline.o -Wno-strict-prototypes
creating build/lib.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security -Wl,-Bsymbolic-functions -Wl,-z,relro -Wdate-time -D_FORTIFY_SOURCE=2 -g -fdebug-prefix-map=/build/python2.7-nbjU53/python2.7-2.7.15~rc1=. -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/Modules/2.x/readline.o readline/libreadline.a readline/libhistory.a -lncurses -o build/lib.linux-x86_64-2.7/readline.so
/usr/bin/x86_64-linux-gnu-ld: não foi possível localizar -lncurses
collect2: error: ld returned 1 exit status
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
----------------------------------------
Command "/home/rafael/.virtualenvs/ishell_poc/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-xzLi3U/readline/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-sKWvZX/install-record.txt --single-version-externally-managed --compile --install-headers /home/rafael/.virtualenvs/ishell_poc/include/site/python2.7/readline" failed with error code 1 in /tmp/pip-install-xzLi3U/readline/
It would be nice to be able to allow arbitrary arguments in command sequences. For example for your example Cisco-like CLI...
interface encapsulation dot1ad dot1q
The arbitrary elements could be defined by regex. I'm not sure how iShell's classes would manage this.
If you have suggestions for how to do this, I could try to implement it.
When I run python test_shell.py
I got the error below:
$ python test_shell.py
Traceback (most recent call last):
File "test_shell.py", line 2, in <module>
from ishell.console import Console
File "/home/rafael/prjs/ishell/ishell/console.py", line 6, in <module>
from builtins import input
ImportError: No module named builtins
We need to install future to solve it, then it could be done at setup.py and/or requirements.txt
It would be useful to be able to display a welcome message when the user firsts enters the shell.
Given the following chain:
console.addChild(Command('show')).addChild(Command('disk')).addChild(DiskIO('io'))
Issuing the command:
show disk
Results in:
Exec all(line=show disk), overwrite this method!
If all
understands that it has children it should provide autocomplete results, otherwise it should show a clean/simple "unable to find command" message.
Given the following chain:
console.addChild(Command('show')).addChild(Command('disk')).addChild(DiskIO('io'))
Issuing the help command (return with a blank line) shows the following:
Help:
show - No help provided
If possible it should show something like:
Help:
show disk io - no help provided
The parents/prefixes should be repeated for each child command, for example:
Help:
show disk io - no help provided
show disk space - no help provided
show cpu info - no help provided
show cpu usage - no help provided
Apologies if this isn't an appropriate use of the issue tracker. I'm new at this...
I came across ishell recently and it looks very nice! Seems like a good fit for a CLI I'm trying to build. Something like the Cisco example.
But the behavior of child commands is not quite what I expect - I'm probably not using it correctly, so any help would be greatly appreciated.
I've started with a very simple example with a 'show' command and 3 sub-commands - 'modules', 'hosts' and 'ports'. The first 2 are just stubs, and I'm trying to get the correct nested command behavior with "ports". There are 4 ports defined, each just a key-value pair, (port number and description) for now.
This mostly works as I would expect. I'll just list what doesn't work.
I'm sure I'm just not using the library correctly. I've tried a bunch of variations and can't get the expected behavior. Any guidance you can give would be appreciated. My minimal code is below.
Thanks,
ws
from ishell.console import Console
from ishell.command import Command
from ishell.utils import _print
def get_ports():
# return a dicitonary of ports for testing
port_info = {'10': "blah", '20' : "blah blah", '30': "blahdy blah blah", '40': "blahdy blahdy blah"}
return port_info
class Hosts(Command):
def run(self, line):
_print("Here are the Hosts: Sunnyvale Cupertino Palo Alto")
class Modules(Command):
def run(self, line):
_print("Here are the Modules: Hub1, Leaf1, Leaf2, Leaf3")
class Ports(Command):
def args(self):
return(get_ports().keys())
def run(self, line):
self.prompt = '(show ports)'
port_info = get_ports()
arg = line.split()[-1]
if arg != 'ports': # print details for a single port for "show ports 10" e.g.
ss = "%s: %s" % (arg, port_info[arg])
_print(ss)
else: # print the list of port numbers for "show ports" e.g.
for port in port_info.keys():
ss = "%s" % port
_print(ss)
self.loop()
class Show(Command):
"""
Children: ports, modules, hosts
"""
def args(self):
return ['ports', 'modules', 'hosts']
def run(self, line):
pass
self.prompt = '(show)'
ports = Ports('ports', help='Ports', dynamic_args=True)
modules = Modules('modules', help='Modules', dynamic_args=True)
hosts = Hosts('hosts', help='Hosts', dynamic_args=True)
self.addChild(ports)
self.addChild(modules)
self.addChild(hosts)
self.loop()
def main():
console = Console(prompt="TEST_CLI", prompt_delim =">")
show = Show("show", help="Show resources", dynamic_args=True)
console.addChild(show)
console.loop()
if __name__ == '__main__':
main()
Every command should have a "help" dynamic arg, that displays a help text (Maybe the same text as in the help statement in the command definition).
from ishell.command import Command
class SetTimezoneCommand(Command):
command_list = ['Africa/Dakar', 'Africa/Conakry']
def args(self):
return self.command_list
def run(self, line):
pass
linux@louis$ python myshell.py
>>>>>>>>>>>>>Hello<<<<<<<<<<
> set timezone Africa
The string behind the tab slash cannot be displayed, Is this a bug? Or is there another way to write this problem?
The console should support dynamic arguments that contain spaces. Now every space is considered the end of a command or argument.
Therefore, arguments with spaces can show up in the autocompletion, but they can not be selected.
These arguments should be recognized by the parser and handled as one atomic argument.
Hi, it would be great if you press key "?" and trigger a help function, similar to Cisco IOS.
I see this is set as MIT, but I dont see a license file nor do I see any comments for a license in any py file. Can you add a license file for this tool please?
When using the autocomplete function in the console for dynamic arguments, self.args() is called 8 (!) times for each completion. That might not be a big issue when it just returns a list, but once the functions becomes more complex it adds a lot of overhead.
The function could be called once and the return value stored locally.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.