Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion IPython/core/magic_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def error(self, message):
def parse_argstring(self, argstring):
""" Split a string into an argument list and parse that argument list.
"""
argv = arg_split(argstring)
argv = arg_split(argstring, posix=True, strict=False)
return self.parse_args(argv)


Expand Down
5 changes: 1 addition & 4 deletions IPython/core/magics/osm.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,10 +842,7 @@ def writefile(self, line, cell):
The file will be overwritten unless the -a (--append) flag is specified.
"""
args = magic_arguments.parse_argstring(self.writefile, line)
if re.match(r'^(\'.*\')|(".*")$', args.filename):
filename = os.path.expanduser(args.filename[1:-1])
else:
filename = os.path.expanduser(args.filename)
filename = os.path.expanduser(args.filename)

if os.path.exists(filename):
if args.append:
Expand Down
30 changes: 21 additions & 9 deletions IPython/core/tests/test_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,11 +860,17 @@ def test_file_single_quote():
"""Basic %%writefile with embedded single quotes"""
ip = get_ipython()
with TemporaryDirectory() as td:
fname = os.path.join(td, '\'file1\'')
ip.run_cell_magic("writefile", fname, u'\n'.join([
'line1',
'line2',
]))
fname = os.path.join(td, "'file1'")
ip.run_cell_magic(
"writefile",
'"%s"' % fname,
u"\n".join(
[
"line1",
"line2",
]
),
)
s = Path(fname).read_text()
nt.assert_in('line1\n', s)
nt.assert_in('line2', s)
Expand All @@ -875,10 +881,16 @@ def test_file_double_quote():
ip = get_ipython()
with TemporaryDirectory() as td:
fname = os.path.join(td, '"file1"')
ip.run_cell_magic("writefile", fname, u'\n'.join([
'line1',
'line2',
]))
ip.run_cell_magic(
"writefile",
"'%s'" % fname,
u"\n".join(
[
"line1",
"line2",
]
),
)
s = Path(fname).read_text()
nt.assert_in('line1\n', s)
nt.assert_in('line2', s)
Expand Down
18 changes: 18 additions & 0 deletions IPython/core/tests/test_magic_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,21 @@ def test_magic_arguments():
assert_equal(real_name(foo), 'foo')
assert_equal(foo(None, ''), argparse.Namespace(foo=None))
assert hasattr(foo, 'has_arguments')


@magic_arguments()
@argument("filepath", type=str, nargs=1, help="An argument with possible spaces")
def magic_one_arg(args):
return parse_argstring(magic_one_arg, args)


def test_spaces_and_quotes():
"Test for GH-12729"
assert_equal(
parse_argstring(magic_one_arg, '"this is one arg"'),
argparse.Namespace(filepath=["this is one arg"]),
)
assert_equal(
parse_argstring(magic_one_arg, "this\ is\ also\ one\ arg"),
argparse.Namespace(filepath=["this is also one arg"]),
)
35 changes: 35 additions & 0 deletions docs/source/whatsnew/pr/incompat-quotes-magic-argument.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Incompatible change to the way magics handle spaces and quotes
--------------------------------------------------------------

Magics that previously split on whitespace for arguments now preserve
backslash-escaped whitespace and whitespace inside of quotes. This allows for
filenames that have spaces to be treated the same as files without spaces.
However, this change is backwards incompatible with previous behavior of
preserving un-escaped quotes in the `writefile` magic, for example.

So if you previously had code like

`%%writefile File"With"Quotes`

You must now write

`%%writefile 'File"With"Quotes`

or

`%%writefile File\"With\"Quotes'`


It was also the case that, due to some special-casing, surrounding quotes for
a filename containing spaces for writefile did work (`%%writefile "File with
spaces"`), the escaping of the same file name did not (`%%writefile File\
with\ spaces` would fail with "UsageError: unrecognized arguments: with\
spaces"). Now, both cases work the same.

The underlying change was to have
:meth:`~IPython.core.magics_arguments.parse_argstring` pass `posix=True` and
`strict=False` when calling `arg_split`.

This change fixes :ghissue:`12729`, for full details, please see
:ghpull:`13027`