Repository: mwouts/jupytext

Issue:
exclamation mark not correctly handled
I am using
```bash
pip freeze | grep jupytext
jupytext==1.2.4
```
and the following MWE file (say `Untitled.md`)
```markdown
try:
    !echo jo
except:
    pass
```
(which is intended to be run as a jupyter notebook inside a jupyter server using the jupytext extension, which works well, btw.) is not correctly converted to python on the command line. I.e., the output of
```bash
jupytext --to py Untitled.ipynb
```
yields the following `Untitled.py`:
```bash
cat Untitled.py 
# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.4'
#       jupytext_version: 1.2.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

try:
    !echo jo
except:
    pass
```

I would expect the `!echo jo` statement to be commented out, but the presence of the `try:` seems to hinder this...
This is related to #149, but not the same.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=h1) Report
> Merging [#439](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=desc) into [master](https://codecov.io/gh/mwouts/jupytext/commit/7fd991f2dd402a900c40cb385e7bfc586790094e?src=pr&el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/mwouts/jupytext/pull/439/graphs/tree.svg?width=650&token=a1pNjsiILi&height=150&src=pr)](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #439      +/-   ##
==========================================
+ Coverage   97.97%   97.99%   +0.01%     
==========================================
  Files          77       77              
  Lines        7720     7725       +5     
==========================================
+ Hits         7564     7570       +6     
+ Misses        156      155       -1
```


| [Impacted Files](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/test\_read\_simple\_python.py](https://codecov.io/gh/mwouts/jupytext/pull/439/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9yZWFkX3NpbXBsZV9weXRob24ucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [jupytext/magics.py](https://codecov.io/gh/mwouts/jupytext/pull/439/diff?src=pr&el=tree#diff-anVweXRleHQvbWFnaWNzLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [jupytext/cell\_metadata.py](https://codecov.io/gh/mwouts/jupytext/pull/439/diff?src=pr&el=tree#diff-anVweXRleHQvY2VsbF9tZXRhZGF0YS5weQ==) | `98.66% <0%> (+0.33%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=footer). Last update [7fd991f...36a518e](https://codecov.io/gh/mwouts/jupytext/pull/439?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e325459..90ac8aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 **Fixed**
 - `jupytext --to script *.ipynb` now computes the script extension for each notebook (#428) 
 - Fix shebang handling for languages with non-# comments, by Jonas Bushart (#434)
+- Indented bash commands are now commented out (#437)
 
 
 1.3.3 (2020-01-27)
diff --git a/jupytext/magics.py b/jupytext/magics.py
index 4fc3527..2b49064 100644
--- a/jupytext/magics.py
+++ b/jupytext/magics.py
@@ -26,7 +26,7 @@ _MAGIC_FORCE_ESC_RE['csharp'] = re.compile(r"^(// |//)*#![a-zA-Z](.*)//\s*escape
 _MAGIC_FORCE_ESC_RE['csharp'] = re.compile(r"^(// |//)*#![a-zA-Z](.*)//\s*noescape")
 
 # Commands starting with a question or exclamation mark have to be escaped
-_PYTHON_HELP_OR_BASH_CMD = re.compile(r"^(# |#)*(\?|!)\s*[A-Za-z]")
+_PYTHON_HELP_OR_BASH_CMD = re.compile(r"^(# |#)*\s*(\?|!)\s*[A-Za-z]")
 
 # A bash command not followed by an equal sign or a parenthesis is a magic command
 _PYTHON_MAGIC_CMD = re.compile(r"^(# |#)*({})($|\s$|\s[^=,])".format('|'.join(


Test patch:
diff --git a/tests/test_read_simple_python.py b/tests/test_read_simple_python.py
index 63417ef..fd03a8a 100644
--- a/tests/test_read_simple_python.py
+++ b/tests/test_read_simple_python.py
@@ -935,3 +935,24 @@ interpreter = 'python'
     compare_notebooks(nb, ref)
     py = jupytext.writes(nb, 'py')
     compare(py, text)
+
+
+def test_indented_bash_command(no_jupytext_version_number,
+                               nb=new_notebook(cells=[new_code_cell("""try:
+    !echo jo
+    pass
+except:
+    pass""")]),
+                               text="""try:
+#     !echo jo
+    pass
+except:
+    pass
+"""
+
+                               ):
+    """Reproduces https://github.com/mwouts/jupytext/issues/437"""
+    py = jupytext.writes(nb, 'py')
+    compare(py, text)
+    nb2 = jupytext.reads(py, 'py')
+    compare_notebooks(nb2, nb)