RBTools Shell command for preconfigured Python shell environment
Review Request #7936 — Created Jan. 31, 2016 and submitted
This new command
rbt shell
opens a Python shell. Works with Ipython and bpython. Imports configuration files and command options into that shell as dictionaries.Makes a client connection to a server if one is provided either as a command option or configuration option.
Can work with Ipython and bpython if installed.
Also added the command to rbtools' setup.py.
Also added documentation to rbtools/docs/rbtools/rbt/commands/shell.rst
Created test cases for shell rbtools/rbtools/commands/tests.py
Manual Testing:
Ran the command to see if it works.
Automatically made a client connection by typing
rbt shell
, and also with providing a server url.Automatically made a client connection by providing configuration keys.
Successfully made api calls from within the shell.
Installed Ipython and bpython with
pip
and made sure that both opened.Tested to see that shell would still open if an invalid shell was specified.
Automated Testing:
Test to see if Ipython module import is called
Test to see if bpython module import is called
Test messages being logged to the shell
Description | From | Last Updated |
---|---|---|
Col: 6 E113 unexpected indentation |
reviewbot | |
Col: 6 E111 indentation is not a multiple of four |
reviewbot | |
Col: 1 E302 expected 2 blank lines, found 1 |
reviewbot | |
Col: 9 E265 block comment should start with '# ' |
reviewbot | |
This looks good as a first draft You may want to have a look at https://github.com/django/django/blob/master/django/core/management/commands/shell.py Also, I'd personally love … |
brennie | |
Blank line between these. |
brennie | |
'logging' imported but unused |
reviewbot | |
'Option' imported but unused |
reviewbot | |
'LogLevelFilter' imported but unused |
reviewbot | |
'OptionGroup' imported but unused |
reviewbot | |
'get_config_paths' imported but unused |
reviewbot | |
'load_config' imported but unused |
reviewbot | |
'parse_config_file' imported but unused |
reviewbot | |
'execute' imported but unused |
reviewbot | |
'die' imported but unused |
reviewbot | |
New commands should not use die(). They should raise an exception. |
brennie | |
This should be written in the imperitive mood. i.e., 'Start a ...' |
brennie | |
How about: 'This command will inject variables into a new interactive shell from your .reviewboardrc.` |
brennie | |
You probably don't want to copy wholesale from django's shell for the final version. |
brennie | |
Col: 32 E231 missing whitespace after ':' |
reviewbot | |
Col: 34 E231 missing whitespace after ',' |
reviewbot | |
Col: 38 E231 missing whitespace after ':' |
reviewbot | |
Single quotes for strings. |
brennie | |
Why not just have readline and rlcompleter be dependencies (in setup.py) so that we're guaranteed they exist? |
brennie | |
Col: 80 E501 line too long (84 > 79 characters) |
reviewbot | |
Col: 30 E225 missing whitespace around operator |
reviewbot | |
Col: 80 E501 line too long (80 > 79 characters) |
reviewbot | |
Col: 34 E711 comparison to None should be 'if cond is not None:' |
reviewbot | |
Col: 36 E711 comparison to None should be 'if cond is not None:' |
reviewbot | |
Col: 80 E501 line too long (83 > 79 characters) |
reviewbot | |
What, exactly, are these config files going to be? |
brennie | |
Should use sentence casing. Needs a period. |
brennie | |
Parens not necessary here. |
brennie | |
Parens not necessary here. |
brennie | |
'RBClient' imported but unused |
reviewbot | |
'CONFIG_FILE' imported but unused |
reviewbot | |
Col: 1 E302 expected 2 blank lines, found 1 |
reviewbot | |
Col: 13 E128 continuation line under-indented for visual indent |
reviewbot | |
Col: 13 E128 continuation line under-indented for visual indent |
reviewbot | |
Col: 13 E128 continuation line under-indented for visual indent |
reviewbot | |
Col: 13 E128 continuation line under-indented for visual indent |
reviewbot | |
local variable 'e' is assigned to but never used |
reviewbot | |
Col: 18 E127 continuation line over-indented for visual indent |
reviewbot | |
I don't think we need to include this, since the attribute is being added at the same time as the … |
david | |
No blank line necessary here. |
david | |
This is a little cleaner as: except Exception as e: logging.error('Could not run script "%s" for RBTools shell: %s", script_path, … |
david | |
You can skip the conditional if you do this instead; for import_command in config.get('SHELL_IMPORTS', []): |
david | |
A better way to wrap this is to put the stuff in the parens on the next line: readline.set_completer( rlcompleter.Completer(namespace).complete) … |
david | |
'logging' imported but unused |
reviewbot | |
'os' imported but unused |
reviewbot | |
'sys' imported but unused |
reviewbot | |
'Option' imported but unused |
reviewbot | |
'load_config' imported but unused |
reviewbot | |
Col: 1 E302 expected 2 blank lines, found 1 |
reviewbot | |
Col: 9 E124 closing bracket does not match visual indentation |
reviewbot | |
Col: 9 E124 closing bracket does not match visual indentation |
reviewbot | |
I think it would be useful to have a config key that allows you to use ipython or bpython. e.g., … |
brennie | |
"pre-configured" |
brennie | |
I would have these both use the same config key and use store_const e.g. option_list = [ Option('-i', '--ipython', config_key='SHELL', … |
brennie | |
I feel like we should have namespace[config] = self.config namespace[options] = vars(self.options) so that we don't pollute namespace. |
brennie | |
Not necessary. |
brennie | |
Not necessary. |
brennie | |
We should inform the user that, if we can't find their server settings, that client and root are not defined. … |
brennie | |
Not necessary. |
brennie | |
You can move the code.interact() call to below the except: block if you return early in the iPython or bpython … |
brennie | |
Doesn't need parentheses. |
brennie | |
Use a dict literal: namespace = { 'config': self.config, 'options': vars(self.options), } |
brennie | |
Can we add a --quiet/ -q option (that can be controlled by a .reviewboardrc option too) to disable these statements? |
brennie | |
This should use logging.exception and print an error message about what failed. |
brennie | |
Same comment about -q. |
brennie | |
This should probably be: elif shell Otherwise if shell is None (for default Python shell), it will always print this. |
brennie | |
I dont think we need this. |
brennie | |
Python |
brennie | |
Suppress |
brennie | |
Docstring w/ args? |
brennie | |
Blank line between these. |
brennie | |
Blank line between these. |
brennie | |
'spy_on' imported but unused |
reviewbot | |
Col: 1 E302 expected 2 blank lines, found 1 |
reviewbot | |
Your test case should inheirit from SpyAgency. Then in unit tests you can use self.spy_on(foo, ...) without having to set … |
brennie | |
Not too sure what this line is doing. Did the test intend to call shell.ipython() instead of assigning the classmethod … |
SS ssengar | |
local variable 'ipython_module' is assigned to but never used |
reviewbot | |
If the objective of the test was to make sure shell.bpython wasn't called, it should probably spy on it explicitly … |
SS ssengar | |
Again, did the test intend to call shell.bpython() instead of assigning the classmethod to a variable? |
SS ssengar | |
local variable 'bpython_module' is assigned to but never used |
reviewbot | |
If you add SpyAgency as a parent class, you can use self.spy_on(shell.method_name). This has several advantages: you don't have to … |
SS ssengar | |
local variable 'bpython_module' is assigned to but never used |
reviewbot | |
Maybe I'm missing something but is there a need to spy on shell.ipython at the end of the test? If … |
SS ssengar | |
local variable 'ipython_module' is assigned to but never used |
reviewbot | |
Same as comment for line 61. |
SS ssengar | |
local variable 'client' is assigned to but never used |
reviewbot | |
Again, is the spy operation on shell.bpython repated for a particular reason? |
SS ssengar | |
Col: 25 E128 continuation line under-indented for visual indent |
reviewbot | |
Col: 33 E128 continuation line under-indented for visual indent |
reviewbot | |
Col: 80 E501 line too long (80 > 79 characters) |
reviewbot | |
'spy_on' imported but unused |
reviewbot | |
Col: 1 E302 expected 2 blank lines, found 1 |
reviewbot | |
Col: 1 W391 blank line at end of file |
reviewbot | |
Col: 34 W292 no newline at end of file |
reviewbot | |
Can you break these up into multiple test cases, e.g.: def test_ipython(self): # raise SkipTest if we don't have ipython … |
brennie | |
These tests aren't actually testing anything. |
brennie | |
shell.ipython can never return None. maybe update the method to return none if it cant get the shell? |
brennie | |
These tests dont test the results. See above comments. |
brennie | |
These don't test the results of calling log_message. |
brennie | |
Needs "Returns" |
brennie | |
Needs "returns" |
brennie | |
Blank line between these. |
brennie | |
No periods at the end of test cases. Same for all test cases in this file. |
brennie | |
You don't need unspy calls at the end of test -- they will automatically be called. |
brennie | |
Can you split this into multiple tests? |
brennie |
Commit: |
|
||||
---|---|---|---|---|---|
Diff: |
Revision 2 (+32) |
-
-
rbtools/commands/shell.py (Diff revision 2) This looks good as a first draft
You may want to have a look at https://github.com/django/django/blob/master/django/core/management/commands/shell.py
Also, I'd personally love it if this supported iPython and regular Python (via a flag / config option)
-
Commit: |
|
||||
---|---|---|---|---|---|
Diff: |
Revision 3 (+56) |
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
rbtools/commands/shell.py (Diff revision 3) New commands should not use
die()
. They should raise an exception. -
rbtools/commands/shell.py (Diff revision 3) This should be written in the imperitive mood.
i.e., 'Start a ...'
-
rbtools/commands/shell.py (Diff revision 3) How about:
'This command will inject variables into a new interactive shell from your
.reviewboardrc
.` -
rbtools/commands/shell.py (Diff revision 3) You probably don't want to copy wholesale from django's shell for the final version.
-
-
rbtools/commands/shell.py (Diff revision 3) Why not just have
readline
andrlcompleter
be dependencies (insetup.py
) so that we're guaranteed they exist?
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
-
-
rbtools/commands/shell.py (Diff revision 4) Col: 34 E711 comparison to None should be 'if cond is not None:'
-
rbtools/commands/shell.py (Diff revision 4) Col: 36 E711 comparison to None should be 'if cond is not None:'
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
-
-
-
rbtools/commands/shell.py (Diff revision 7) Col: 13 E128 continuation line under-indented for visual indent
-
rbtools/commands/shell.py (Diff revision 7) Col: 13 E128 continuation line under-indented for visual indent
-
rbtools/commands/shell.py (Diff revision 7) Col: 13 E128 continuation line under-indented for visual indent
-
rbtools/commands/shell.py (Diff revision 7) Col: 13 E128 continuation line under-indented for visual indent
-
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
rbtools/commands/shell.py (Diff revision 8) Col: 18 E127 continuation line over-indented for visual indent
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
This is looking pretty great!
-
rbtools/commands/shell.py (Diff revision 9) I don't think we need to include this, since the attribute is being added at the same time as the command as a whole. These are there so we can document when new attributes were added to existing commands.
We probably should have a top-level "added_in" for the command, that will generate the appropriate note in the documentation.
-
-
rbtools/commands/shell.py (Diff revision 9) This is a little cleaner as:
except Exception as e: logging.error('Could not run script "%s" for RBTools shell: %s", script_path, e)
-
rbtools/commands/shell.py (Diff revision 9) You can skip the conditional if you do this instead;
for import_command in config.get('SHELL_IMPORTS', []):
-
rbtools/commands/shell.py (Diff revision 9) A better way to wrap this is to put the stuff in the parens on the next line:
readline.set_completer( rlcompleter.Completer(namespace).complete)
Alternatively, you could do
from rlcompleter import Completer
at the top and then just useCompleter(...)
here.
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py
-
-
-
-
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
-
-
rbtools/commands/shell.py (Diff revision 11) Col: 9 E124 closing bracket does not match visual indentation
-
rbtools/commands/shell.py (Diff revision 11) Col: 9 E124 closing bracket does not match visual indentation
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py
-
Hey, this change looks good. I've got some suggestions for you.
Also, this command will need documentation. Have a look in
docs/rbtools/rbt/commands/
for examples. -
rbtools/commands/shell.py (Diff revision 12) I think it would be useful to have a config key that allows you to use ipython or bpython. e.g., you could set the following in your .reviewboardrc:
SHELL = 'ipython'
or
SHELL = 'bpython'
Then you can have
-i
and-b
override this setting. -
-
rbtools/commands/shell.py (Diff revision 12) I would have these both use the same config key and use
store_const
e.g.
option_list = [ Option('-i', '--ipython', config_key='SHELL', dest='shell', action='store_const', const='ipython', ...), Option('-i', '--bpython', dest='shell', action='store_const', const='bpython', ...) ]
NB: This also takes into account the above suggestion for
SHELL
, which only has to be on one option I believe. -
rbtools/commands/shell.py (Diff revision 12) I feel like we should have
namespace[config] = self.config namespace[options] = vars(self.options)
so that we don't pollute
namespace
. -
-
-
rbtools/commands/shell.py (Diff revision 12) We should inform the user that, if we can't find their server settings, that
client
androot
are not defined. (Or inform them of the opposite when they are defined. Either one is fine.) -
-
rbtools/commands/shell.py (Diff revision 12) You can move the
code.interact()
call to below theexcept:
block if you return early in the iPython or bpython cases.
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
Description: |
|
||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Testing Done: |
|
-
-
-
rbtools/commands/shell.py (Diff revision 15) Use a dict literal:
namespace = { 'config': self.config, 'options': vars(self.options), }
-
rbtools/commands/shell.py (Diff revision 15) Can we add a
--quiet
/-q
option (that can be controlled by a.reviewboardrc
option too) to disable these statements? -
rbtools/commands/shell.py (Diff revision 15) This should use
logging.exception
and print an error message about what failed. -
-
rbtools/commands/shell.py (Diff revision 15) This should probably be:
elif shell
Otherwise if
shell
isNone
(for default Python shell), it will always print this. -
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
Description: |
|
---|
Summary: |
|
||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Description: |
|
||||||||||||||||||||||||||||||||||||
Testing Done: |
|
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: Pyflakes Processed Files: rbtools/commands/shell.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
-
-
rbtools/commands/tests.py (Diff revision 18) local variable 'ipython_module' is assigned to but never used
-
rbtools/commands/tests.py (Diff revision 18) local variable 'bpython_module' is assigned to but never used
-
rbtools/commands/tests.py (Diff revision 18) local variable 'bpython_module' is assigned to but never used
-
rbtools/commands/tests.py (Diff revision 18) local variable 'ipython_module' is assigned to but never used
-
-
Great work, your project seems awesome! In this review, I gave you some feedback on your test cases. I apologize in advance if some of my feedback is irrelevant/incorrect. I'm not very familiar with the rbt shell command, so most of my feedback was based on intuition + my limited knowledge of kgb spies.
-
rbtools/commands/shell.py (Diff revision 18) It's really cool that your shell has so many configuration options!
-
rbtools/commands/shell.py (Diff revision 18) It's super cool that you could smartly re-use existing shell implementations for your project!
-
rbtools/commands/tests.py (Diff revision 18) Not too sure what this line is doing. Did the test intend to call shell.ipython() instead of assigning the classmethod to a variable?
-
rbtools/commands/tests.py (Diff revision 18) If the objective of the test was to make sure shell.bpython wasn't called, it should probably spy on it explicitly using agency.spy_on(shell.bpython) at the start of the test.
-
rbtools/commands/tests.py (Diff revision 18) Again, did the test intend to call shell.bpython() instead of assigning the classmethod to a variable?
-
rbtools/commands/tests.py (Diff revision 18) If you add SpyAgency as a parent class, you can use self.spy_on(shell.method_name). This has several advantages: you don't have to create a new SpyAgency for each test, and it will automatically unspy after each test case (which is both less code and more robust if there are unexpected exceptions). Documentation on how to do this can be found in the README at https://github.com/beanbaginc/kgb
-
rbtools/commands/tests.py (Diff revision 18) Maybe I'm missing something but is there a need to spy on shell.ipython at the end of the test? If not, it's probably a good idea to take this line out.
-
-
rbtools/commands/tests.py (Diff revision 18) Again, is the spy operation on shell.bpython repated for a particular reason?
-
-
rbtools/commands/tests.py (Diff revision 18) Your test case should inheirit from
SpyAgency
. Then in unit tests you can useself.spy_on(foo, ...)
without having to set up a
SpyAgency
each time.
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
rbtools/commands/shell.py (Diff revision 19) Col: 25 E128 continuation line under-indented for visual indent
-
rbtools/commands/shell.py (Diff revision 19) Col: 33 E128 continuation line under-indented for visual indent
-
-
-
-
Description: |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Testing Done: |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Diff: |
Revision 20 (+212) |
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
-
Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
-
rbtools/commands/tests.py (Diff revision 21) Can you break these up into multiple test cases, e.g.:
def test_ipython(self): # raise SkipTest if we don't have ipython installed. pass def test_ipython_not_installed(self): # Ensure that `ipython()` returns `None`. pass # ...
-
-
rbtools/commands/tests.py (Diff revision 21) shell.ipython
can never return None. maybe update the method to return none if it cant get the shell? -
-
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
Tool: Pyflakes Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst Tool: PEP8 Style Checker Processed Files: rbtools/commands/shell.py rbtools/commands/tests.py setup.py Ignored Files: docs/rbtools/rbt/commands/shell.rst
-
-
-
-
-
rbtools/commands/tests.py (Diff revision 23) No periods at the end of test cases. Same for all test cases in this file.
-
rbtools/commands/tests.py (Diff revision 23) You don't need
unspy
calls at the end of test -- they will automatically be called. -