Repository: ipython/ipython

Issue:
magic with spaces in quotes fails
The magic argument parser doesn't seem to handle spaces in quotes very well. Trying to execute `%debug print(' ')`:

```
Python 3.7.4 (default, Sep  7 2019, 18:27:02) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: %debug print(' ')                                                                                                                                 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-e73341fe4191> in <module>
----> 1 get_ipython().run_line_magic('debug', "print(' ')")

/usr/local/lib/python3.7/site-packages/IPython/core/interactiveshell.py in run_line_magic(self, magic_name, line, _stack_depth)
   2312                 kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
   2313             with self.builtin_trap:
-> 2314                 result = fn(*args, **kwargs)
   2315             return result
   2316 

</usr/local/lib/python3.7/site-packages/decorator.py:decorator-gen-57> in debug(self, line, cell)

/usr/local/lib/python3.7/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

/usr/local/lib/python3.7/site-packages/IPython/core/magics/execution.py in debug(self, line, cell)
    464 
    465         """
--> 466         args = magic_arguments.parse_argstring(self.debug, line)
    467 
    468         if not (args.breakpoint or args.statement or cell):

/usr/local/lib/python3.7/site-packages/IPython/core/magic_arguments.py in parse_argstring(magic_func, argstring)
    162     """ Parse the string of arguments for the given magic function.
    163     """
--> 164     return magic_func.parser.parse_argstring(argstring)
    165 
    166 

/usr/local/lib/python3.7/site-packages/IPython/core/magic_arguments.py in parse_argstring(self, argstring)
    133         """ Split a string into an argument list and parse that argument list.
    134         """
--> 135         argv = arg_split(argstring)
    136         return self.parse_args(argv)
    137 

/usr/local/lib/python3.7/site-packages/IPython/utils/_process_common.py in arg_split(s, posix, strict)
    200     while True:
    201         try:
--> 202             tokens.append(next(lex))
    203         except StopIteration:
    204             break

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shlex.py in __next__(self)
    293 
    294     def __next__(self):
--> 295         token = self.get_token()
    296         if token == self.eof:
    297             raise StopIteration

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shlex.py in get_token(self)
    103             return tok
    104         # No pushback.  Get a token.
--> 105         raw = self.read_token()
    106         # Handle inclusions
    107         if self.source is not None:

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shlex.py in read_token(self)
    185                         print("shlex: I see EOF in quotes state")
    186                     # XXX what error should be raised here?
--> 187                     raise ValueError("No closing quotation")
    188                 if nextchar == self.state:
    189                     if not self.posix:

ValueError: No closing quotation

In [2]:    
```


Discussion:
I'm not too surprise, the code to handle magics is brittle; it would be nice to fix; and it will be a matter of finding someone with time to dive into this. 

Resolving patch:
diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py
--- a/IPython/core/magics/execution.py
+++ b/IPython/core/magics/execution.py
@@ -451,7 +451,13 @@ def debug(self, line='', cell=None):
 
         if not (args.breakpoint or args.statement or cell):
             self._debug_post_mortem()
+        elif not (args.breakpoint or cell):
+            # If there is no breakpoints, the line is just code to execute
+            self._debug_exec(line, None)
         else:
+            # Here we try to reconstruct the code from the output of
+            # parse_argstring. This might not work if the code has spaces
+            # For example this fails for `print("a b")`
             code = "\n".join(args.statement)
             if cell:
                 code += "\n" + cell


Test patch:
diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py
--- a/IPython/core/tests/test_magic.py
+++ b/IPython/core/tests/test_magic.py
@@ -32,6 +32,7 @@
 from IPython.utils.tempdir import (TemporaryDirectory,
                                     TemporaryWorkingDirectory)
 from IPython.utils.process import find_cmd
+from .test_debugger import PdbTestInput
 
 
 @magic.magics_class
@@ -599,6 +600,18 @@ def doctest_precision():
     Out[5]: '3.141593e+00'
     """
 
+def test_debug_magic():
+    """Test debugging a small code with %debug
+    
+    In [1]: with PdbTestInput(['c']):
+       ...:     %debug print("a b") #doctest: +ELLIPSIS
+       ...:
+    ...
+    ipdb> c
+    a b
+    In [2]:
+    """
+
 def test_psearch():
     with tt.AssertPrints("dict.fromkeys"):
         _ip.run_cell("dict.fr*?")