Skip to content
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

Implemented new command: tree #204

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion http_prompt/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# '/foo/bar' => ('/foo/bar', 'bar')
# '/foo/bar/' => ('/foo/bar/', '')
# 'foo/bar' => ('foo/bar', 'bar')
(r'(ls|cd)\s+(/?(?:[^/]+/)*([^/]*)/?)$', 'urlpaths'),
(r'(ls|cd|tree)\s+(/?(?:[^/]+/)*([^/]*)/?)$', 'urlpaths'),
(r'^\s*[^\s]*$', 'root_commands')
]

Expand Down
1 change: 1 addition & 0 deletions http_prompt/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
('rm -q', 'Remove querystring parameter'),
('rm -q *', 'Remove all querystring parameters'),
('source', 'Load environment from a file'),
('tree', 'Show tree structure'),
])

ACTIONS = OrderedDict([
Expand Down
53 changes: 52 additions & 1 deletion http_prompt/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
command = mutation / immutation

mutation = concat_mut+ / nonconcat_mut
immutation = preview / action / ls / env / help / exit / exec / source / clear / _
immutation = preview / action / ls / env / help /
exit / exec / source / clear / tree /_

concat_mut = option_mut / full_quoted_mut / value_quoted_mut / unquoted_mut
nonconcat_mut = cd / rm
Expand All @@ -49,6 +50,7 @@
help = _ "help" _
exit = _ "exit" _
ls = _ "ls" _ (urlpath _)? (redir_out)?
tree = _ "tree" _ (urlpath _)? (redir_out)?
env = _ "env" _ (redir_out)?
source = _ "source" _ filepath _
exec = _ "exec" _ filepath _
Expand Down Expand Up @@ -352,6 +354,55 @@ def visit_ls(self, node, children):
self.output.write('\n'.join(lines))
return node

def _formattreename(self, node, color=False):
if color:
col = Name
if node.data.get("type") == "dir":
col = String
return self._colorize(node.name, col)
else:
return node.name

def _fetchtreenode(self, node, children, filtertypes=[], prepend="",
colorize=True):
ret = []
ppsym = " "

cnodes = sorted(sorted([c for c in node.children if c.data.get("type")
not in filtertypes], key=lambda x: x.name),
key=lambda y: y.data.get("type"), reverse=True)

for i, c in enumerate(cnodes):
if i == len(cnodes)-1:
ppsym = "└── "
postsym = " "
else:
ppsym = "├── "
postsym = "│ "
ret.append(prepend + ppsym + self._formattreename(c, colorize))
ret += self._fetchtreenode(c,
children,
filtertypes,
prepend + postsym,
colorize)

return ret

def visit_tree(self, node, children):
path = urlparse(self.context_override.url).path
path = filter(None, path.split('/'))
topnode = self.context.root.findtopnode(*path)
lines = []
lines.append(self._formattreename(topnode, self.output.isatty()))
lines += self._fetchtreenode(topnode,
children,
["file"],
"",
self.output.isatty())
if lines:
self.output.write('\n'.join(lines))
return node

def visit_env(self, node, children):
text = format_to_http_prompt(self.context)
self.output.write(text)
Expand Down
12 changes: 9 additions & 3 deletions http_prompt/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def find_child(self, name, wildcard=True):

return None

def ls(self, *path):
def findtopnode(self, *path):
success = True
cur = self
for name in path:
Expand All @@ -74,5 +74,11 @@ def ls(self, *path):
success = False
break
if success:
for node in sorted(cur.children):
yield node
return cur

def ls(self, *path):
topnode = self.findtopnode(*path)
if not topnode:
return
for node in sorted(topnode.children):
yield node
18 changes: 12 additions & 6 deletions tests/context/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,19 @@ def test_override():
orgs_methods = list(sorted(list(root_children)[0].children))
# path parameters are used even if no method parameter
assert len(orgs_methods) == 2
assert next(filter(lambda i:i.name == 'username', orgs_methods), None) is not None
assert next(filter(lambda i:i.name == 'Accept', orgs_methods), None) is not None
assert next(filter(lambda i: i.name == 'username', orgs_methods), None)\
is not None
assert next(filter(lambda i: i.name == 'Accept', orgs_methods), None)\
is not None

users_methods = list(sorted(list(root_children)[1].children))
# path and methods parameters are merged
assert len(users_methods) == 4
assert next(filter(lambda i:i.name == 'username', users_methods), None) is not None
assert next(filter(lambda i:i.name == 'custom1', users_methods), None) is not None
assert next(filter(lambda i:i.name == 'custom2', users_methods), None) is not None
assert next(filter(lambda i:i.name == 'Accept', users_methods), None) is not None
assert next(filter(lambda i: i.name == 'username', users_methods), None)\
is not None
assert next(filter(lambda i: i.name == 'custom1', users_methods), None)\
is not None
assert next(filter(lambda i: i.name == 'custom2', users_methods), None)\
is not None
assert next(filter(lambda i: i.name == 'Accept', users_methods), None)\
is not None
4 changes: 3 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ def test_spec_basePath(self):
self.assertEqual(lv3_names, set(['users', 'orgs']))

def test_spec_from_http(self):
spec_url = 'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json'
spec_url = 'https://raw.githubusercontent.com/github/'\
'rest-api-description/main/descriptions/api.github.com/'\
'api.github.com.json'
result, context = run_and_exit(['https://api.github.com', '--spec',
spec_url])
self.assertEqual(result.exit_code, 0)
Expand Down
76 changes: 75 additions & 1 deletion tests/test_execution.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import hashlib
import io
import json
import shutil
import os
Expand Down Expand Up @@ -767,6 +766,81 @@ def test_reset(self):
self.assertFalse(self.context.body_json_params)


class TestExecution_tree(ExecutionTestCase):

def test_root(self):
execute('tree', self.context)
self.assert_stdout("root\n" +
"├── orgs\n" +
"│ └── {org}\n" +
"│ ├── events\n" +
"│ └── members\n" +
"└── users\n" +
" └── {username}\n" +
" ├── events\n" +
" └── orgs\n")

def test_relative_path(self):
self.context.url = 'http://localhost/users'
execute('tree 101', self.context)
self.assert_stdout("{username}\n" +
"├── events\n" +
"└── orgs\n")

def test_absolute_path(self):
self.context.url = 'http://localhost/users'
execute('tree /orgs/1', self.context)
self.assert_stdout("{org}\n" +
"├── events\n" +
"└── members\n")

def test_redirect_write(self):
filename = self.make_tempfile()

# Write something first to make sure it's a full overwrite
with open(filename, 'w', encoding="utf-8") as f:
f.write('hello world\n')

execute('tree > %s' % filename, self.context)

with open(filename, encoding="utf-8") as f:
content = f.read()
self.assertEqual(content, "root\n" +
"├── orgs\n" +
"│ └── {org}\n" +
"│ ├── events\n" +
"│ └── members\n" +
"└── users\n" +
" └── {username}\n" +
" ├── events\n" +
" └── orgs")

def test_redirect_append(self):
filename = self.make_tempfile()

# Write something first to make sure it's an append
with open(filename, 'w', encoding="utf-8") as f:
f.write('hello world\n')

execute('tree >> %s' % filename, self.context)

with open(filename, encoding="utf-8") as f:
content = f.read()
self.assertEqual(content, "hello world\nroot\n" +
"├── orgs\n" +
"│ └── {org}\n" +
"│ ├── events\n" +
"│ └── members\n" +
"└── users\n" +
" └── {username}\n" +
" ├── events\n" +
" └── orgs")

def test_grep(self):
execute('tree | grep users', self.context)
self.assert_stdout('└── users\n')


class TestExecution_ls(ExecutionTestCase):

def test_root(self):
Expand Down