Browse Source

add --xargs and --help support to search..

main
John-Mark Gurney 2 months ago
parent
commit
c93d24e210
2 changed files with 129 additions and 2 deletions
  1. +50
    -0
      ui/fixtures/cmd.search.json
  2. +79
    -2
      ui/medashare/cli.py

+ 50
- 0
ui/fixtures/cmd.search.json View File

@@ -20,6 +20,17 @@
"title": "create host",
"cmd": [ "hosts" ]
},
{
"title": "search --help works",
"cmd": [ "search", "--help" ],
"stdout_re": "^usage:.*"
},
{
"title": "search --unkonwn errors with usage",
"cmd": [ "search", "--unkown" ],
"exit": 1,
"stderr_re": "^Unknown option: --unkown\nusage:.*"
},
{
"title": "add tag a",
"cmd": [ "modify", "+tag=foo", "+dc:creator=John-Mark Gurney", "newfile.txt" ]
@@ -136,5 +147,44 @@
"title": "search exclusion w/ inclusion properly",
"cmd": [ "search", "file", "+tag=bar", "-other" ],
"stdout": "/foobar/newfile.txt\n"
},
{
"title": "create a file with space in it",
"special": "create file",
"file": "file name space.txt",
"contents": "a file name with space in it.\n"
},
{
"title": "create a file with newline in it",
"special": "create file",
"file": "new\nline'.txt",
"contents": "a file name with new line in it.\n"
},
{
"title": "create another file with newline in it",
"special": "create file",
"file": "another\nnew\nline\".txt",
"contents": "another file name with new line in it.\n"
},
{
"title": "create yet another file with newline in it",
"special": "create file",
"file": "yet another\nnew\nline\".txt",
"contents": "yet file name with new line in it.\n"
},
{
"title": "add special tag",
"cmd": [ "modify", "+special=tag", "file name space.txt", "new\nline'.txt", "another\nnew\nline\".txt", "yet another\nnew\nline\".txt" ]
},
{
"title": "without any special args, it is skipped with one warning",
"cmd": [ "search", "file", "+special=tag" ],
"stdout_re": "^.*file name space.txt\n$",
"stderr": "Warning: file with neline in name was skipped.\n"
},
{
"title": "with --xargs arg",
"cmd": [ "search", "--xargs", "file", "+special=tag" ],
"stdout_re": "^'.*file name space.txt'\n.*new\\\\\n\"line'.txt\"\n.*another\\\\\nnew\\\\\n'line\".txt'\n'.*yet another'\\\\\nnew\\\\\n'line\".txt'\n$"
}
]

+ 79
- 2
ui/medashare/cli.py View File

@@ -1630,9 +1630,48 @@ def cmd_drop(options, persona, objstr, cache):
for i in options.uuids:
objstr.drop_uuid(i)

def do_xargs_escape(s):
sset = set(s)
escapeset = set(' \t\n"\'\\')
if not (escapeset & sset):
return s

needescape = sset & escapeset

if '\n' not in needescape and len(needescape & set('"\'')) <= 1:
# simple escape works
if "'" in sset and '"' not in sset:
escape_char = '"'
else:
escape_char = "'"
return escape_char + s + escape_char
elif '\n' not in needescape:
# be lazy and escape everything with backslash
return re.sub('([%s\\\\])' % ''.join(escapeset - set('\\')), r'\\\1', s)

# XXX handle s w/ both ' and "

# split on new lines, as they have to be manually escaped
segs = (do_xargs_escape(x) for x in s.split('\n'))

return '\\\n'.join(segs)

@init_datastructs
def cmd_search(options, persona, objstr, cache):
args = options.args.copy()
do_xargs = False

while args[0].startswith('--'):
if args[0] == '--help':
options.search_argparser.print_help()
sys.exit(0)
elif args[0] == '--xargs':
do_xargs = True
args.pop(0)
else:
print('Unknown option: %s' % args[0], file=sys.stderr)
options.search_argparser.print_usage(sys.stderr)
sys.exit(1)

_type = args.pop(0)

@@ -1822,7 +1861,16 @@ def cmd_search(options, persona, objstr, cache):
else:
raise ValueError('unhandled type: %s' % repr(_type))

printed_warning = False
for i in r:
i = str(i)
if do_xargs:
i = do_xargs_escape(i)
elif '\n' in i:
if not printed_warning:
print('Warning: file with neline in name was skipped.', file=sys.stderr)
printed_warning = True
continue
print(i)

def main():
@@ -1936,6 +1984,7 @@ def main():
parser_search.set_defaults(func=cmd_search)

options = parser.parse_args()
options.search_argparser = parser_search

if options.debug:
enable_debug()
@@ -2105,6 +2154,23 @@ class _TestMigrations(unittest.IsolatedAsyncioTestCase):

self.assertEqual(c, 2)

class _TestPureFunctions(unittest.TestCase):
def test_do_xargs_escape(self):
# To verify with xargs:
# a, b = testcase
# proc = subprocess.run('xargs -n 1 -J % printf %s %'.split(),
# input=a.encode('utf-8'), stdout=subprocess.PIPE)
# b == proc.stdout.decode('utf-8')
for s, es in [
('"foo', "'\"foo'"),
("'bar", '"\'bar"'),
('dfkj sdfkj', "'dfkj sdfkj'"),
('dfkj\nsdfkj', "dfkj\\\nsdfkj"),
('dfkj\n', "dfkj\\\n"),
('d f\'k\\j\n\\\n\'\n"\n\', "dfkj\\\n', '"d f\'k\\j"\\\n\'\\\'\\\n"\'"\\\n\'"\'\\\n\\\',\\ \\"dfkj\\\\\\\n'),
]:
self.assertEqual(do_xargs_escape(s), es)

class _TestCases(unittest.TestCase):
def setUp(self):
self.fixtures = pathlib.Path('fixtures').resolve()
@@ -2735,6 +2801,8 @@ class _TestCases(unittest.TestCase):
elif special == 'delete files':
for i in cmd['files']:
os.unlink(i)
elif special == 'create file':
(self.tempdir / cmd['file']).write_text(cmd['contents'])
elif special == 'setup file':
shutil.copy(self.fixtures /
cmd['file'], self.tempdir)
@@ -2786,6 +2854,17 @@ class _TestCases(unittest.TestCase):
# XXX - Minor hack till other tests fixed
sys.exit(0)

# with the correct err output
err = cmd.get('stderr')
errre = cmd.get('stderr_re')
if not errre and not err:
err = ''

if err:
self.assertEqual(stderr.getvalue(), err)
if errre:
self.assertRegex(stderr.getvalue(), errre)

# with the correct output
self.maxDiff = None
outeq = cmd.get('stdout')
@@ -2803,8 +2882,6 @@ class _TestCases(unittest.TestCase):
stdout.seek(0)
self.objcompare(_json_objstream(stdout), outcheck)

self.assertEqual(stderr.getvalue(), cmd.get('stderr', ''))

self.assertEqual(cm.exception.code, cmd.get('exit', 0))

# any store commands:


Loading…
Cancel
Save