From e08519b04dbe2d96a448484664a4e3065bc988d4 2021-04-17 00:47:54 From: Matthias Bussonnier Date: 2021-04-17 00:47:54 Subject: [PATCH] Merge pull request #12708 from adityausathe/12107_parse_opts Issue #12107 fix: additional spaces while parsing timeit-magic options --- diff --git a/IPython/core/magic.py b/IPython/core/magic.py index e64d3aa..03b76cb 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -618,6 +618,9 @@ class Magics(Configurable): posix = kw.get('posix', os.name == 'posix') strict = kw.get('strict', True) + preserve_non_opts = kw.get("preserve_non_opts", False) + remainder_arg_str = arg_str + # Check if we have more than one argument to warrant extra processing: odict = {} # Dictionary with options args = arg_str.split() @@ -629,10 +632,18 @@ class Magics(Configurable): try: opts,args = getopt(argv, opt_str, long_opts) except GetoptError as e: - raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str, - " ".join(long_opts))) from e - for o,a in opts: - if o.startswith('--'): + raise UsageError( + '%s ( allowed: "%s" %s)' % (e.msg, opt_str, " ".join(long_opts)) + ) from e + for o, a in opts: + if mode is "string" and preserve_non_opts: + # remove option-parts from the original args-string and preserve remaining-part. + # This relies on the arg_split(...) and getopt(...)'s impl spec, that the parsed options are + # returned in the original order. + remainder_arg_str = remainder_arg_str.replace(o, "", 1).replace( + a, "", 1 + ) + if o.startswith("--"): o = o[2:] else: o = o[1:] @@ -649,7 +660,10 @@ class Magics(Configurable): # Prepare opts,args for return opts = Struct(odict) if mode == 'string': - args = ' '.join(args) + if preserve_non_opts: + args = remainder_arg_str.lstrip() + else: + args = " ".join(args) return opts,args diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index e2d550e..9b49d79 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -1073,8 +1073,9 @@ class ExecutionMagics(Magics): does not matter as long as results from timeit.py are not mixed with those from %timeit.""" - opts, stmt = self.parse_options(line,'n:r:tcp:qo', - posix=False, strict=False) + opts, stmt = self.parse_options( + line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True + ) if stmt == "" and cell is None: return diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 66ba309..226c460 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -472,6 +472,23 @@ def test_parse_options(): nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') +def test_parse_options_preserve_non_option_string(): + """Test to assert preservation of non-option part of magic-block, while parsing magic options.""" + m = DummyMagics(_ip) + opts, stmt = m.parse_options( + " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True + ) + nt.assert_equal(opts, {"n": "1", "r": "13"}) + nt.assert_equal(stmt, "_ = 314 + foo") + + +def test_run_magic_preserve_code_block(): + """Test to assert preservation of non-option part of magic-block, while running magic.""" + _ip.user_ns["spaces"] = [] + _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])") + assert _ip.user_ns["spaces"] == [[0]] + + def test_dirops(): """Test various directory handling operations.""" # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')