Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
d01561fc
Commit
d01561fc
authored
Jul 07, 2013
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
RedisCli improvements
parent
25fc7b8d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
168 additions
and
9 deletions
+168
-9
rediscli.py
flask_admin/contrib/rediscli.py
+95
-2
rediscli.css
flask_admin/static/admin/css/rediscli.css
+4
-0
rediscli.js
flask_admin/static/admin/js/rediscli.js
+63
-7
response.html
flask_admin/templates/admin/rediscli/response.html
+6
-0
No files found.
flask_admin/contrib/rediscli.py
View file @
d01561fc
...
...
@@ -3,7 +3,6 @@ import shlex
import
warnings
from
flask
import
request
from
flask.json
import
jsonify
from
jinja2
import
Markup
...
...
@@ -12,7 +11,28 @@ from flask.ext.admin.babel import gettext
from
flask.ext.admin._compat
import
VER
class
CommandError
(
Exception
):
"""
RedisCli error exception.
"""
pass
class
TextWrapper
(
str
):
"""
Small text wrapper for result formatter to distinguish between
different string types.
"""
pass
class
RedisCli
(
BaseView
):
"""
Simple redis console.
To use it, simply pass `Redis` connection object to the constructor.
"""
shlex_check
=
True
"""
shlex from stdlib does not work with unicode on 2.7.2 and lower.
...
...
@@ -26,6 +46,11 @@ class RedisCli(BaseView):
List of redis remapped commands.
"""
excluded_commands
=
set
((
'pubsub'
,
'set_response_callback'
,
'from_url'
))
"""
List of excluded commands.
"""
def
__init__
(
self
,
redis
,
name
=
None
,
category
=
None
,
endpoint
=
None
,
url
=
None
):
"""
...
...
@@ -51,6 +76,7 @@ class RedisCli(BaseView):
self
.
commands
=
{}
self
.
_inspect_commands
()
self
.
_contribute_commands
()
if
self
.
shlex_check
and
VER
<
(
2
,
7
,
3
):
warnings
.
warn
(
'Warning: rediscli uses shlex library and it does not work with unicode until Python 2.7.3. '
+
...
...
@@ -58,14 +84,34 @@ class RedisCli(BaseView):
'to False.'
)
def
_inspect_commands
(
self
):
"""
Inspect connection object and extract command names.
"""
for
name
in
dir
(
self
.
redis
):
if
not
name
.
startswith
(
'_'
):
attr
=
getattr
(
self
.
redis
,
name
)
if
callable
(
attr
):
if
callable
(
attr
)
and
name
not
in
self
.
excluded_commands
:
doc
=
(
getattr
(
attr
,
'__doc__'
,
''
)
or
''
)
.
strip
()
self
.
commands
[
name
]
=
(
attr
,
doc
)
for
old
,
new
in
self
.
remapped_commands
:
self
.
commands
[
new
]
=
self
.
commands
[
old
]
def
_contribute_commands
(
self
):
"""
Contribute custom commands.
"""
self
.
commands
[
'help'
]
=
(
self
.
_cmd_help
,
'Help!'
)
def
_execute_command
(
self
,
name
,
args
):
"""
Execute single command.
:param name:
Command name
:param args:
Command arguments
"""
# Do some remapping
new_cmd
=
self
.
remapped_commands
.
get
(
name
)
if
new_cmd
:
...
...
@@ -79,6 +125,12 @@ class RedisCli(BaseView):
return
self
.
_result
(
handler
(
*
args
))
def
_parse_cmd
(
self
,
cmd
):
"""
Parse command by using shlex module.
:param cmd:
Command to parse
"""
if
VER
<
(
2
,
7
,
3
):
# shlex can't work with unicode until 2.7.3
return
tuple
(
x
.
decode
(
'utf-8'
)
for
x
in
shlex
.
split
(
cmd
.
encode
(
'utf-8'
)))
...
...
@@ -86,19 +138,58 @@ class RedisCli(BaseView):
return
tuple
(
shlex
.
split
(
cmd
))
def
_error
(
self
,
msg
):
"""
Format error message as HTTP response.
:param msg:
Message to format
"""
return
Markup
(
'<div class="error">
%
s</div>'
%
msg
)
def
_result
(
self
,
result
):
"""
Format result message as HTTP response.
:param msg:
Result to format.
"""
return
self
.
render
(
'admin/rediscli/response.html'
,
type_name
=
lambda
d
:
type
(
d
)
.
__name__
,
result
=
result
)
# Commands
def
_cmd_help
(
self
,
*
args
):
"""
Help command implementation.
"""
if
not
args
:
help
=
'Usage: help <command>.
\n
List of supported commands: '
help
+=
', '
.
join
(
n
for
n
in
sorted
(
self
.
commands
))
return
TextWrapper
(
help
)
cmd
=
args
[
0
]
if
cmd
not
in
self
.
commands
:
raise
CommandError
(
'Invalid command.'
)
help
=
self
.
commands
[
cmd
][
1
]
if
not
help
:
return
TextWrapper
(
'Command does not have any help.'
)
return
TextWrapper
(
help
)
# Views
@
expose
(
'/'
)
def
console_view
(
self
):
"""
Console view.
"""
return
self
.
render
(
'admin/rediscli/console.html'
)
@
expose
(
'/run/'
,
methods
=
(
'POST'
,))
def
execute_view
(
self
):
"""
AJAX API.
"""
try
:
cmd
=
request
.
form
.
get
(
'cmd'
)
.
lower
()
if
not
cmd
:
...
...
@@ -109,6 +200,8 @@ class RedisCli(BaseView):
return
self
.
_error
(
'Cli: Failed to parse command.'
)
return
self
.
_execute_command
(
parts
[
0
],
parts
[
1
:])
except
CommandError
as
err
:
return
self
.
_error
(
'Cli:
%
s'
%
err
)
except
Exception
as
ex
:
logging
.
exception
(
ex
)
return
self
.
_error
(
'Cli:
%
s'
%
ex
)
flask_admin/static/admin/css/rediscli.css
View file @
d01561fc
...
...
@@ -33,10 +33,14 @@
.console
.cmd
{
background-color
:
#f5f5f5
;
padding
:
2px
;
margin
:
1px
;
}
.console
.response
{
background-color
:
#f0f0f0
;
padding
:
2px
;
margin
:
1px
;
}
.console
.error
{
...
...
flask_admin/static/admin/js/rediscli.js
View file @
d01561fc
var
RedisCli
=
function
(
postUrl
)
{
// Constants
var
KEY_UP
=
38
;
var
KEY_DOWN
=
40
;
var
MAX_ITEMS
=
128
;
var
$con
=
$
(
'.console'
);
var
$container
=
$con
.
find
(
'.console-container'
);
var
$input
=
$con
.
find
(
'input'
);
var
history
=
[];
var
historyPos
=
0
;
function
resizeConsole
()
{
var
height
=
$
(
window
).
height
();
...
...
@@ -11,12 +19,16 @@ var RedisCli = function(postUrl) {
}
function
scrollBottom
()
{
$container
.
animate
({
scrollTop
:
$container
.
height
()
},
100
);
$container
.
animate
({
scrollTop
:
$container
[
0
].
scrollHeight
},
100
);
}
function
createEntry
(
cmd
)
{
var
$entry
=
$
(
'<div>'
).
addClass
(
'entry'
).
appendTo
(
$container
);
$entry
.
append
(
$
(
'<div>'
).
addClass
(
'cmd'
).
html
(
cmd
));
if
(
$container
.
find
(
'>div'
).
length
>
MAX_ITEMS
)
$container
.
find
(
'>div:first-child'
).
remove
();
scrollBottom
();
return
$entry
;
}
...
...
@@ -25,14 +37,25 @@ var RedisCli = function(postUrl) {
$entry
.
append
(
$
(
'<div>'
).
addClass
(
'response'
).
html
(
response
));
scrollBottom
();
}
function
addError
(
$entry
,
response
)
{
$entry
.
append
(
$
(
'<div>'
).
addClass
(
'response'
).
addClass
(
'error'
).
html
(
response
));
scrollBottom
();
}
function
submitCommand
()
{
var
val
=
$input
.
val
().
trim
();
if
(
!
val
.
length
)
return
false
;
function
addHistory
(
cmd
)
{
history
.
push
(
cmd
);
if
(
history
>
MAX_ITEMS
)
history
.
splice
(
0
,
1
);
historyPos
=
history
.
length
;
}
function
sendCommand
(
val
)
{
var
$entry
=
createEntry
(
'> '
+
val
);
addHistory
(
val
);
$
.
ajax
({
type
:
'POST'
,
url
:
postUrl
,
...
...
@@ -41,20 +64,53 @@ var RedisCli = function(postUrl) {
addResponse
(
$entry
,
response
);
},
error
:
function
()
{
add
Response
(
$entry
,
'Failed to communicate with server.'
);
add
Error
(
$entry
,
'Failed to communicate with server.'
);
}
});
return
false
;
}
function
submitCommand
()
{
var
val
=
$input
.
val
().
trim
();
if
(
!
val
.
length
)
return
false
;
sendCommand
(
val
);
$input
.
val
(
''
);
}
return
false
;
function
onKeyPress
(
e
)
{
if
(
e
.
keyCode
==
KEY_UP
)
{
historyPos
-=
1
;
if
(
historyPos
<
0
)
historyPos
=
0
;
if
(
historyPos
<
history
.
length
)
$input
.
val
(
history
[
historyPos
]);
}
else
if
(
e
.
keyCode
==
KEY_DOWN
)
{
historyPos
+=
1
;
if
(
historyPos
>=
history
.
length
)
{
$input
.
val
(
''
);
historyPos
=
history
.
length
;
}
else
{
$input
.
val
(
history
[
historyPos
]);
}
}
}
// Setup
$con
.
find
(
'form'
).
submit
(
submitCommand
);
$input
.
keydown
(
onKeyPress
);
$
(
window
).
resize
(
resizeConsole
);
resizeConsole
();
$input
.
focus
();
sendCommand
(
'ping'
);
};
flask_admin/templates/admin/rediscli/response.html
View file @
d01561fc
...
...
@@ -19,6 +19,12 @@
"{{ item }}"
{% elif type == 'binary' %}
"{{ item.decode('utf-8') }}"
{% elif type == 'TextWrapper' %}
<pre>
{{ item }}
</pre>
{% elif type == 'dict' %}
{% for k, v in item.items() %}
{{ loop.index }}) {{ k }} - {{ render(v, depth + 1) }}
<br/>
{% endfor %}
{% else %}
{{ item }}
{% endif %}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment