Repository: mwouts/jupytext
Pull request #192 (2019-02-27 13:32:23)

Associate `.scm` file extension with Scheme scripts
Scheme support is already in place.  This PR associates the `.scm` file extension with Scheme scripts, and extends the test to test that extension along with the existing `.ss` extension.

codecov[bot]: # [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/192?src=pr&el=h1) Report
> Merging [#192](https://codecov.io/gh/mwouts/jupytext/pull/192?src=pr&el=desc) into [master](https://codecov.io/gh/mwouts/jupytext/commit/219dc18740c0277aaf49ef65e2b7a7ac507d5085?src=pr&el=desc) will **decrease** coverage by `9.06%`.
> The diff coverage is `86.66%`.

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

```diff
@@            Coverage Diff             @@
##           master     #192      +/-   ##
==========================================
- Coverage   99.44%   90.37%   -9.07%     
==========================================
  Files          61       61              
  Lines        5537     5538       +1     
==========================================
- Hits         5506     5005     -501     
- Misses         31      533     +502
```


| [Impacted Files](https://codecov.io/gh/mwouts/jupytext/pull/192?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [jupytext/languages.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-anVweXRleHQvbGFuZ3VhZ2VzLnB5) | `100% <ø> (ø)` | :arrow_up: |
| [tests/test\_read\_simple\_scheme.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9yZWFkX3NpbXBsZV9zY2hlbWUucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [tests/test\_mirror.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9taXJyb3IucHk=) | `36.13% <50%> (-63.87%)` | :arrow_down: |
| [tests/test\_black.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9ibGFjay5weQ==) | `29.47% <0%> (-70.53%)` | :arrow_down: |
| [tests/test\_knitr\_spin.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9rbml0cl9zcGluLnB5) | `45.45% <0%> (-54.55%)` | :arrow_down: |
| [tests/test\_active\_cells.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9hY3RpdmVfY2VsbHMucHk=) | `50% <0%> (-50%)` | :arrow_down: |
| [tests/test\_contentsmanager.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9jb250ZW50c21hbmFnZXIucHk=) | `61.65% <0%> (-38.35%)` | :arrow_down: |
| [tests/test\_rmd\_to\_ipynb.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9ybWRfdG9faXB5bmIucHk=) | `72.72% <0%> (-27.28%)` | :arrow_down: |
| [tests/test\_read\_simple\_rmd.py](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9yZWFkX3NpbXBsZV9ybWQucHk=) | `72.72% <0%> (-27.28%)` | :arrow_down: |
| ... and [10 more](https://codecov.io/gh/mwouts/jupytext/pull/192/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwouts/jupytext/pull/192?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/192?src=pr&el=footer). Last update [219dc18...8a70f4f](https://codecov.io/gh/mwouts/jupytext/pull/192?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

mwouts: Thanks @bzinberg for your contribution. Definitely we should include this!
mwouts: By the way, I see there is an issue with the tests (pypy and Python 3.4, when jupytext is executed through subcommand). The issue is not related to your contribution, please ignore it.

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index e0c0218e..db4c9d09 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,13 @@
 Release History
 ---------------
 
+1.0.2 (2019-02-??)
+++++++++++++++++++++++
+
+**BugFixes**
+- Allow spaces between ``?`` or ``!`` and python or bash command (#189)
+
+
 1.0.1 (2019-02-23)
 ++++++++++++++++++++++
 
diff --git a/jupytext/contentsmanager.py b/jupytext/contentsmanager.py
index e637df55..1020fa3e 100644
--- a/jupytext/contentsmanager.py
+++ b/jupytext/contentsmanager.py
@@ -413,6 +413,14 @@ class TextFileContentsManager(FileContentsManager, Configurable):
 
     def rename_file(self, old_path, new_path):
         """Rename the current notebook, as well as its alternative representations"""
+        if old_path not in self.paired_notebooks:
+            try:
+                # we do not know yet if this is a paired notebook (#190)
+                # -> to get this information we open the notebook
+                self.get(old_path, content=True)
+            except Exception:
+                pass
+
         if old_path not in self.paired_notebooks:
             super(TextFileContentsManager, self).rename_file(old_path, new_path)
             return
diff --git a/jupytext/languages.py b/jupytext/languages.py
index 12b0b78d..25f69fe1 100644
--- a/jupytext/languages.py
+++ b/jupytext/languages.py
@@ -9,6 +9,7 @@ _SCRIPT_EXTENSIONS = {'.py': {'language': 'python', 'comment': '#'},
                       '.jl': {'language': 'julia', 'comment': '#'},
                       '.cpp': {'language': 'c++', 'comment': '//'},
                       '.ss': {'language': 'scheme', 'comment': ';;'},
+                      '.scm': {'language': 'scheme', 'comment': ';;'},
                       '.sh': {'language': 'bash', 'comment': '#'},
                       '.q': {'language': 'q', 'comment': '/'}}
 
diff --git a/jupytext/magics.py b/jupytext/magics.py
index e089abae..3704673f 100644
--- a/jupytext/magics.py
+++ b/jupytext/magics.py
@@ -16,7 +16,7 @@ _MAGIC_NOT_ESC_RE = {_SCRIPT_EXTENSIONS[ext]['language']: re.compile(
 _COMMENT = {_SCRIPT_EXTENSIONS[ext]['language']: _SCRIPT_EXTENSIONS[ext]['comment'] for ext in _SCRIPT_EXTENSIONS}
 
 # Commands starting with a question or exclamation mark have to be escaped
-_PYTHON_HELP_OR_BASH_CMD = re.compile(r"^(# |#)*(\?|!)[A-Za-z]")
+_PYTHON_HELP_OR_BASH_CMD = re.compile(r"^(# |#)*(\?|!)\s*[A-Za-z]")
 
 _PYTHON_MAGIC_CMD = re.compile(r"^(# |#)*({})(\s|$)".format('|'.join(
     # posix
diff --git a/jupytext/version.py b/jupytext/version.py
index 8e49dc8e..86b6eea1 100644
--- a/jupytext/version.py
+++ b/jupytext/version.py
@@ -1,3 +1,3 @@
 """Jupytext's version number"""
 
-__version__ = '1.0.1'
+__version__ = '1.0.2-dev'


Test patch:
diff --git a/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.scm b/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.scm
new file mode 100644
index 00000000..f0b527eb
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.scm
@@ -0,0 +1,443 @@
+;; -*- coding: utf-8 -*-
+;; ---
+;; jupyter:
+;;   kernelspec:
+;;     display_name: Calysto Scheme (Python)
+;;     language: scheme
+;;     name: calysto_scheme
+;; ---
+
+;; %% [markdown]
+;; <img src="images/logo-64x64.png"/>
+;; <h1>Reference Guide for Calysto Scheme</h1>
+;;
+;; [Calysto Scheme](https://github.com/Calysto/calysto_scheme) is a real Scheme programming language, with full support for continuations, including call/cc. It can also use all Python libraries. Also has some extensions that make it more useful (stepper-debugger, choose/fail, stack traces), or make it better integrated with Python.
+;;
+;; In Jupyter notebooks, because Calysto Scheme uses [MetaKernel](https://github.com/Calysto/metakernel/blob/master/README.rst), it has a fully-supported set of "magics"---meta-commands for additional functionality. This includes running Scheme in parallel. See all of the [MetaKernel Magics](https://github.com/Calysto/metakernel/blob/master/metakernel/magics/README.md).
+;;
+;; Calysto Scheme is written in Scheme, and then translated into Python (and other backends). The entire functionality lies in a single Python file: https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py However, you can easily install it (see below).
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
+;;
+;; ## Installation
+;;
+;; You can install Calysto Scheme with Python3:
+;;
+;; ```
+;; pip3 install --upgrade calysto-scheme --user -U
+;; python3 -m calysto_kernel install --user
+;; ```
+;;
+;; or in the system kernel folder with:
+;;
+;; ```
+;; sudo pip3 install --upgrade calysto-scheme -U
+;; sudo python3 -m calysto_kernel install
+;; ```
+;;
+;; Change pip3/python3 to use a different pip or Python. The version of Python used will determine how Calysto Scheme is run.
+;;
+;; Use it in the console, qtconsole, or notebook with IPython 3:
+;;
+;; ```
+;; ipython console --kernel calysto_scheme
+;; ipython qtconsole --kernel calysto_scheme
+;; ipython notebook --kernel calysto_scheme
+;; ```
+;;
+;; In addition to all of the following items, Calysto Scheme also has access to all of Python's builtin functions, and all of Python's libraries. For example, you can use `(complex 3 2)` to create a complex number by calling Python's complex function.
+;;
+;; ## Jupyter Enhancements
+;;
+;; When you run Calysto Scheme in Jupyter (console, notebook, qtconsole, etc.) you get:
+;;
+;; * TAB completions of Scheme functions and variable names
+;; * display of rich media
+;; * stepper/debugger
+;; * magics (% macros)
+;; * shell commands (! command)
+;; * LaTeX equations
+;; * LaTeX-style variables
+;; * Python integration
+
+;; %% [markdown]
+;; ### LaTeX-style variables
+;;
+;; Calysto Scheme allows you to use LaTeX-style variables in code. For example, if you type:
+;;
+;; ```
+;; \beta
+;; ```
+;;
+;; with the cursor right after the 'a' in beta, and then press TAB, it will turn into the unicode character:
+;;
+;; ```
+;; β
+;; ```
+;;
+;; There are nearly 1300 different symbols defined (thanks to the Julia language) and documented here:
+;;
+;; http://docs.julialang.org/en/release-0.4/manual/unicode-input/#man-unicode-input
+;;
+;; Calysto Scheme may not implement all of those. Some useful and suggestive ones:
+;;
+;; * \pi - π
+;; * \Pi - Π
+;; * \Sigma - Σ
+;; * \_i - subscript i, such as vectorᵢ
+
+;; %%
+(define α 67)
+
+;; %%
+α
+
+;; %%
+(define i 2)
+(define vectorᵢ (vector-ref (vector 0 6 3 2) i))
+vectorᵢ
+
+;; %% [markdown]
+;; ### Rich media
+
+;; %%
+(import "calysto.display")
+
+;; %%
+(calysto.display.HTML "This is <b>bold</b>, <i>italics</i>, <u>underlined</u>.")
+
+;; %%
+(import "calysto.graphics")
+
+;; %%
+(define canvas (calysto.graphics.Canvas))
+
+;; %%
+(define ball (calysto.graphics.Circle '(150 150) 100))
+
+;; %%
+(ball.draw canvas)
+
+;; %% [markdown]
+;; ### Shell commands
+
+;; %%
+! ls /tmp
+
+;; %% [markdown]
+;; ### Stepper/Debugger
+;;
+;; Here is what the debugger looks like:
+;;
+;; <img src="images/stepper_debugger.png">
+;;
+;; It has breakpoints (click in left margin). You must press Stop to exit the debugger.
+
+;; %% [markdown]
+;; ```scheme
+;; %%debug
+;;
+;; (begin
+;;  (define x 1)
+;;  (set! x 2)
+;; )
+;; ```
+
+;; %% [markdown]
+;; ### Python Integration
+;;
+;; You can import and use any Python library in Calysto Scheme.
+;;
+;; In addition, if you wish, you can execute expressions and statements in a Python environment:
+
+;; %%
+(python-eval "1 + 2")
+
+;; %%
+(python-exec 
+"
+def mypyfunc(a, b):
+    return a * b
+")
+
+;; %% [markdown]
+;; This is a shared environment with Scheme:
+
+;; %%
+(mypyfunc 4 5)
+
+;; %% [markdown]
+;; You can use `func` to turn a Scheme procedure into a Python function, and `define!` to put it into the shared enviornment with Python:
+
+;; %%
+(define! mypyfunc2 (func (lambda (n) n)))
+
+;; %%
+(python-eval "mypyfunc2(34)")
+
+;; %% [markdown]
+;; # Differences Between Languages
+;;
+;; ## Major differences between Scheme and Python
+;;
+;; 1. In Scheme, double quotes are used for strings and may contain newlines
+;; 1. In Scheme, a single quote is short for (quote ...) and means "literal"
+;; 1. In Scheme, everything is an expression and has a return value
+;; 1. Python does not support macros (e.g., extending syntax)
+;; 1. In Python, "if X" is false if X is None, False, [], (,) or 0. In Scheme, "if X" is only false if X is #f or 0
+;; 1. Calysto Scheme uses continuations, not the call stack. However, for debugging there is a pseudo-stack when an error is raised. You can trun that off with (use-stack-trace #f)
+;; 1. Scheme procedures are not Python functions, but there are means to use one as the other.
+;;
+;; ## Major Differences Between Calysto Scheme and other Schemes
+;;
+;; 1. define-syntax works slightly differently
+;; 1. In Calysto Scheme, #(...) is short for '#(...)
+;; 1. Calysto Scheme is missing many standard functions (see list at bottom)
+;; 1. Calysto Scheme has a built-in amb operator called `choose`
+;; 1. For debugging there is a pseudo-stack when errors are raised in Calysto Scheme. You can trun that off with (use-stack-trace #f)
+;;
+;; ### Stack Trace
+;;
+;; Calysto Scheme acts as if it has a call stack, for easier debugging. For example:
+
+;; %%
+(define fact
+    (lambda (n)
+      (if (= n 1)
+           q
+           (* n (fact (- n 1))))))
+
+;; %%
+(fact 5)
+
+;; %% [markdown]
+;; To turn off the stack trace on error:
+;;
+;; ```scheme
+;; (use-stack-trace #f)
+;; ```
+;; That will allow infinite recursive loops without keeping track of the "stack".
+
+;; %% [markdown]
+;; # Calysto Scheme Variables
+;;
+;; ## SCHEMEPATH
+;; SCHEMEPATH is a list of search directories used with (load NAME). This is a reference, so you should append to it rather than attempting to redefine it.
+
+;; %%
+SCHEMEPATH
+
+;; %%
+(set-cdr! (cdr SCHEMEPATH) (list "/var/modules"))
+
+;; %%
+SCHEMEPATH
+
+;; %% [markdown]
+;; ## Getting Started
+;;
+;; Note that you can use the word `lambda` or \lambda and then press [TAB]
+
+;; %%
+(define factorial
+  (λ (n)
+     (cond
+      ((zero? n) 1)
+      (else (* n (factorial (- n 1)))))))
+
+;; %%
+(factorial 5)
+
+;; %% [markdown]
+;; ## define-syntax
+;; (define-syntax NAME RULES): a method for creating macros
+
+;; %%
+(define-syntax time 
+  [(time ?exp) (let ((start (current-time)))
+                 ?exp
+                 (- (current-time) start))])
+
+;; %%
+(time (car '(1 2 3 4)))
+
+;; %%
+;;---------------------------------------------------------------------
+;; collect is like list comprehension in Python
+
+(define-syntax collect
+  [(collect ?exp for ?var in ?list)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) #t) ?list)]
+  [(collect ?exp for ?var in ?list if ?condition)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) ?condition) ?list)])
+
+(define filter-map
+  (lambda (f pred? values)
+    (if (null? values)
+      '()
+      (if (pred? (car values))
+          (cons (f (car values)) (filter-map f pred? (cdr values)))
+          (filter-map f pred? (cdr values))))))
+
+;; %%
+(collect (* n n) for n in (range 10))
+
+;; %%
+(collect (* n n) for n in (range 5 20 3))
+
+;; %%
+(collect (* n n) for n in (range 10) if (> n 5))
+
+;; %%
+;;---------------------------------------------------------------------
+;; for loops
+
+(define-syntax for
+  [(for ?exp times do . ?bodies)
+   (for-repeat ?exp (lambda () . ?bodies))]
+  [(for ?var in ?exp do . ?bodies)
+   (for-iterate1 ?exp (lambda (?var) . ?bodies))]
+  [(for ?var at (?i) in ?exp do . ?bodies)
+   (for-iterate2 0 ?exp (lambda (?var ?i) . ?bodies))]
+  [(for ?var at (?i ?j . ?rest) in ?exp do . ?bodies)
+   (for ?var at (?i) in ?exp do
+     (for ?var at (?j . ?rest) in ?var do . ?bodies))])
+
+(define for-repeat
+  (lambda (n f)
+    (if (< n 1)
+      'done
+      (begin
+        (f)
+        (for-repeat (- n 1) f)))))
+
+(define for-iterate1
+  (lambda (values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values))
+        (for-iterate1 (cdr values) f)))))
+
+(define for-iterate2
+  (lambda (i values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values) i)
+        (for-iterate2 (+ i 1) (cdr values) f)))))
+
+;; %%
+(define matrix2d
+  '((10 20)
+    (30 40)
+    (50 60)
+    (70 80)))
+
+(define matrix3d
+  '(((10 20 30) (40 50 60))
+    ((70 80 90) (100 110 120))
+    ((130 140 150) (160 170 180))
+    ((190 200 210) (220 230 240))))
+
+;; %%
+(begin 
+ (define hello 0)
+ (for 5 times do (set! hello (+ hello 1)))
+ hello
+ )
+
+;; %%
+(for sym in '(a b c d) do (define x 1) (set! x sym) (print x))
+
+;; %%
+(for n in (range 10 20 2) do (print n))
+
+;; %%
+(for n at (i j) in matrix2d do (print (list n 'coords: i j)))
+
+;; %%
+(for n at (i j k) in matrix3d do (print (list n 'coords: i j k)))
+
+;; %%
+(define-syntax scons
+  [(scons ?x ?y) (cons ?x (lambda () ?y))])
+
+(define scar car)
+
+(define scdr
+  (lambda (s)
+    (let ((result ((cdr s))))
+      (set-cdr! s (lambda () result))
+      result)))
+
+(define first
+  (lambda (n s)
+    (if (= n 0)
+      '()
+      (cons (scar s) (first (- n 1) (scdr s))))))
+
+(define nth
+  (lambda (n s)
+    (if (= n 0)
+      (scar s)
+      (nth (- n 1) (scdr s)))))
+
+(define smap
+  (lambda (f s)
+    (scons (f (scar s)) (smap f (scdr s)))))
+
+(define ones (scons 1 ones))
+
+(define nats (scons 0 (combine nats + ones)))
+
+(define combine
+  (lambda (s1 op s2)
+    (scons (op (scar s1) (scar s2)) (combine (scdr s1) op (scdr s2)))))
+
+(define fibs (scons 1 (scons 1 (combine fibs + (scdr fibs)))))
+
+(define facts (scons 1 (combine facts * (scdr nats))))
+
+(define ! (lambda (n) (nth n facts)))
+
+;; %%
+(! 5)
+
+;; %%
+(nth 10 facts)
+
+;; %%
+(nth 20 fibs)
+
+;; %%
+(first 30 fibs)
+
+;; %% [markdown]
+;; ## for-each
+;; (for-each PROCEDURE LIST): apply PROCEDURE to each item in LIST; like `map` but don't return results
+
+;; %%
+(for-each (lambda (n) (print n)) '(3 4 5))
+
+;; %% [markdown]
+;; ## format
+;; (format STRING ITEM ...): format the string with ITEMS as arguments
+
+;; %%
+(format "This uses formatting ~a ~s ~%" 'apple 'apple)
+
+;; %% [markdown]
+;; ## func
+;;
+;; Turns a lambda into a Python function.
+;;
+;; (func (lambda ...))
+
+;; %%
+(func (lambda (n) n))
+
+;; %% [markdown]
+;; ## There's more!
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
diff --git a/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.scm b/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.scm
new file mode 100644
index 00000000..ba044c3b
--- /dev/null
+++ b/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.scm
@@ -0,0 +1,389 @@
+;; -*- coding: utf-8 -*-
+;; ---
+;; jupyter:
+;;   kernelspec:
+;;     display_name: Calysto Scheme (Python)
+;;     language: scheme
+;;     name: calysto_scheme
+;; ---
+
+;; <img src="images/logo-64x64.png"/>
+;; <h1>Reference Guide for Calysto Scheme</h1>
+;;
+;; [Calysto Scheme](https://github.com/Calysto/calysto_scheme) is a real Scheme programming language, with full support for continuations, including call/cc. It can also use all Python libraries. Also has some extensions that make it more useful (stepper-debugger, choose/fail, stack traces), or make it better integrated with Python.
+;;
+;; In Jupyter notebooks, because Calysto Scheme uses [MetaKernel](https://github.com/Calysto/metakernel/blob/master/README.rst), it has a fully-supported set of "magics"---meta-commands for additional functionality. This includes running Scheme in parallel. See all of the [MetaKernel Magics](https://github.com/Calysto/metakernel/blob/master/metakernel/magics/README.md).
+;;
+;; Calysto Scheme is written in Scheme, and then translated into Python (and other backends). The entire functionality lies in a single Python file: https://github.com/Calysto/calysto_scheme/blob/master/calysto_scheme/scheme.py However, you can easily install it (see below).
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
+;;
+;; ## Installation
+;;
+;; You can install Calysto Scheme with Python3:
+;;
+;; ```
+;; pip3 install --upgrade calysto-scheme --user -U
+;; python3 -m calysto_kernel install --user
+;; ```
+;;
+;; or in the system kernel folder with:
+;;
+;; ```
+;; sudo pip3 install --upgrade calysto-scheme -U
+;; sudo python3 -m calysto_kernel install
+;; ```
+;;
+;; Change pip3/python3 to use a different pip or Python. The version of Python used will determine how Calysto Scheme is run.
+;;
+;; Use it in the console, qtconsole, or notebook with IPython 3:
+;;
+;; ```
+;; ipython console --kernel calysto_scheme
+;; ipython qtconsole --kernel calysto_scheme
+;; ipython notebook --kernel calysto_scheme
+;; ```
+;;
+;; In addition to all of the following items, Calysto Scheme also has access to all of Python's builtin functions, and all of Python's libraries. For example, you can use `(complex 3 2)` to create a complex number by calling Python's complex function.
+;;
+;; ## Jupyter Enhancements
+;;
+;; When you run Calysto Scheme in Jupyter (console, notebook, qtconsole, etc.) you get:
+;;
+;; * TAB completions of Scheme functions and variable names
+;; * display of rich media
+;; * stepper/debugger
+;; * magics (% macros)
+;; * shell commands (! command)
+;; * LaTeX equations
+;; * LaTeX-style variables
+;; * Python integration
+
+;; ### LaTeX-style variables
+;;
+;; Calysto Scheme allows you to use LaTeX-style variables in code. For example, if you type:
+;;
+;; ```
+;; \beta
+;; ```
+;;
+;; with the cursor right after the 'a' in beta, and then press TAB, it will turn into the unicode character:
+;;
+;; ```
+;; β
+;; ```
+;;
+;; There are nearly 1300 different symbols defined (thanks to the Julia language) and documented here:
+;;
+;; http://docs.julialang.org/en/release-0.4/manual/unicode-input/#man-unicode-input
+;;
+;; Calysto Scheme may not implement all of those. Some useful and suggestive ones:
+;;
+;; * \pi - π
+;; * \Pi - Π
+;; * \Sigma - Σ
+;; * \_i - subscript i, such as vectorᵢ
+
+(define α 67)
+
+α
+
+(define i 2)
+(define vectorᵢ (vector-ref (vector 0 6 3 2) i))
+vectorᵢ
+
+;; ### Rich media
+
+(import "calysto.display")
+
+(calysto.display.HTML "This is <b>bold</b>, <i>italics</i>, <u>underlined</u>.")
+
+(import "calysto.graphics")
+
+(define canvas (calysto.graphics.Canvas))
+
+(define ball (calysto.graphics.Circle '(150 150) 100))
+
+(ball.draw canvas)
+
+;; ### Shell commands
+
+! ls /tmp
+
+;; ### Stepper/Debugger
+;;
+;; Here is what the debugger looks like:
+;;
+;; <img src="images/stepper_debugger.png">
+;;
+;; It has breakpoints (click in left margin). You must press Stop to exit the debugger.
+
+;; ```scheme
+;; ;; %%debug
+;;
+;; (begin
+;;  (define x 1)
+;;  (set! x 2)
+;; )
+;; ```
+
+;; ### Python Integration
+;;
+;; You can import and use any Python library in Calysto Scheme.
+;;
+;; In addition, if you wish, you can execute expressions and statements in a Python environment:
+
+(python-eval "1 + 2")
+
+(python-exec 
+"
+def mypyfunc(a, b):
+    return a * b
+")
+
+;; This is a shared environment with Scheme:
+
+(mypyfunc 4 5)
+
+;; You can use `func` to turn a Scheme procedure into a Python function, and `define!` to put it into the shared enviornment with Python:
+
+(define! mypyfunc2 (func (lambda (n) n)))
+
+(python-eval "mypyfunc2(34)")
+
+;; # Differences Between Languages
+;;
+;; ## Major differences between Scheme and Python
+;;
+;; 1. In Scheme, double quotes are used for strings and may contain newlines
+;; 1. In Scheme, a single quote is short for (quote ...) and means "literal"
+;; 1. In Scheme, everything is an expression and has a return value
+;; 1. Python does not support macros (e.g., extending syntax)
+;; 1. In Python, "if X" is false if X is None, False, [], (,) or 0. In Scheme, "if X" is only false if X is #f or 0
+;; 1. Calysto Scheme uses continuations, not the call stack. However, for debugging there is a pseudo-stack when an error is raised. You can trun that off with (use-stack-trace #f)
+;; 1. Scheme procedures are not Python functions, but there are means to use one as the other.
+;;
+;; ## Major Differences Between Calysto Scheme and other Schemes
+;;
+;; 1. define-syntax works slightly differently
+;; 1. In Calysto Scheme, #(...) is short for '#(...)
+;; 1. Calysto Scheme is missing many standard functions (see list at bottom)
+;; 1. Calysto Scheme has a built-in amb operator called `choose`
+;; 1. For debugging there is a pseudo-stack when errors are raised in Calysto Scheme. You can trun that off with (use-stack-trace #f)
+;;
+;; ### Stack Trace
+;;
+;; Calysto Scheme acts as if it has a call stack, for easier debugging. For example:
+
+(define fact
+    (lambda (n)
+      (if (= n 1)
+           q
+           (* n (fact (- n 1))))))
+
+(fact 5)
+
+;; To turn off the stack trace on error:
+;;
+;; ```scheme
+;; (use-stack-trace #f)
+;; ```
+;; That will allow infinite recursive loops without keeping track of the "stack".
+
+;; # Calysto Scheme Variables
+;;
+;; ## SCHEMEPATH
+;; SCHEMEPATH is a list of search directories used with (load NAME). This is a reference, so you should append to it rather than attempting to redefine it.
+
+SCHEMEPATH
+
+(set-cdr! (cdr SCHEMEPATH) (list "/var/modules"))
+
+SCHEMEPATH
+
+;; ## Getting Started
+;;
+;; Note that you can use the word `lambda` or \lambda and then press [TAB]
+
+(define factorial
+  (λ (n)
+     (cond
+      ((zero? n) 1)
+      (else (* n (factorial (- n 1)))))))
+
+(factorial 5)
+
+;; ## define-syntax
+;; (define-syntax NAME RULES): a method for creating macros
+
+(define-syntax time 
+  [(time ?exp) (let ((start (current-time)))
+                 ?exp
+                 (- (current-time) start))])
+
+(time (car '(1 2 3 4)))
+
+;; +
+;;---------------------------------------------------------------------
+;; collect is like list comprehension in Python
+
+(define-syntax collect
+  [(collect ?exp for ?var in ?list)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) #t) ?list)]
+  [(collect ?exp for ?var in ?list if ?condition)
+   (filter-map (lambda (?var) ?exp) (lambda (?var) ?condition) ?list)])
+
+(define filter-map
+  (lambda (f pred? values)
+    (if (null? values)
+      '()
+      (if (pred? (car values))
+          (cons (f (car values)) (filter-map f pred? (cdr values)))
+          (filter-map f pred? (cdr values))))))
+;; -
+
+(collect (* n n) for n in (range 10))
+
+(collect (* n n) for n in (range 5 20 3))
+
+(collect (* n n) for n in (range 10) if (> n 5))
+
+;; +
+;;---------------------------------------------------------------------
+;; for loops
+
+(define-syntax for
+  [(for ?exp times do . ?bodies)
+   (for-repeat ?exp (lambda () . ?bodies))]
+  [(for ?var in ?exp do . ?bodies)
+   (for-iterate1 ?exp (lambda (?var) . ?bodies))]
+  [(for ?var at (?i) in ?exp do . ?bodies)
+   (for-iterate2 0 ?exp (lambda (?var ?i) . ?bodies))]
+  [(for ?var at (?i ?j . ?rest) in ?exp do . ?bodies)
+   (for ?var at (?i) in ?exp do
+     (for ?var at (?j . ?rest) in ?var do . ?bodies))])
+
+(define for-repeat
+  (lambda (n f)
+    (if (< n 1)
+      'done
+      (begin
+        (f)
+        (for-repeat (- n 1) f)))))
+
+(define for-iterate1
+  (lambda (values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values))
+        (for-iterate1 (cdr values) f)))))
+
+(define for-iterate2
+  (lambda (i values f)
+    (if (null? values)
+      'done
+      (begin
+        (f (car values) i)
+        (for-iterate2 (+ i 1) (cdr values) f)))))
+
+;; +
+(define matrix2d
+  '((10 20)
+    (30 40)
+    (50 60)
+    (70 80)))
+
+(define matrix3d
+  '(((10 20 30) (40 50 60))
+    ((70 80 90) (100 110 120))
+    ((130 140 150) (160 170 180))
+    ((190 200 210) (220 230 240))))
+;; -
+
+(begin 
+ (define hello 0)
+ (for 5 times do (set! hello (+ hello 1)))
+ hello
+ )
+
+(for sym in '(a b c d) do (define x 1) (set! x sym) (print x))
+
+(for n in (range 10 20 2) do (print n))
+
+(for n at (i j) in matrix2d do (print (list n 'coords: i j)))
+
+(for n at (i j k) in matrix3d do (print (list n 'coords: i j k)))
+
+;; +
+(define-syntax scons
+  [(scons ?x ?y) (cons ?x (lambda () ?y))])
+
+(define scar car)
+
+(define scdr
+  (lambda (s)
+    (let ((result ((cdr s))))
+      (set-cdr! s (lambda () result))
+      result)))
+
+(define first
+  (lambda (n s)
+    (if (= n 0)
+      '()
+      (cons (scar s) (first (- n 1) (scdr s))))))
+
+(define nth
+  (lambda (n s)
+    (if (= n 0)
+      (scar s)
+      (nth (- n 1) (scdr s)))))
+
+(define smap
+  (lambda (f s)
+    (scons (f (scar s)) (smap f (scdr s)))))
+
+(define ones (scons 1 ones))
+
+(define nats (scons 0 (combine nats + ones)))
+
+(define combine
+  (lambda (s1 op s2)
+    (scons (op (scar s1) (scar s2)) (combine (scdr s1) op (scdr s2)))))
+
+(define fibs (scons 1 (scons 1 (combine fibs + (scdr fibs)))))
+
+(define facts (scons 1 (combine facts * (scdr nats))))
+
+(define ! (lambda (n) (nth n facts)))
+;; -
+
+(! 5)
+
+(nth 10 facts)
+
+(nth 20 fibs)
+
+(first 30 fibs)
+
+;; ## for-each
+;; (for-each PROCEDURE LIST): apply PROCEDURE to each item in LIST; like `map` but don't return results
+
+(for-each (lambda (n) (print n)) '(3 4 5))
+
+;; ## format
+;; (format STRING ITEM ...): format the string with ITEMS as arguments
+
+(format "This uses formatting ~a ~s ~%" 'apple 'apple)
+
+;; ## func
+;;
+;; Turns a lambda into a Python function.
+;;
+;; (func (lambda ...))
+
+(func (lambda (n) n))
+
+;; ## There's more!
+;;
+;; Please see [Calysto Scheme Language](Calysto%20Scheme%20Language.ipynb) for more details on the Calysto Scheme language.
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 6ef6af71..564a118b 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -15,7 +15,7 @@ from jupytext.cli import parse_jupytext_args, jupytext, jupytext_cli, system, st
 from jupytext.compare import compare_notebooks
 from jupytext.paired_paths import paired_paths
 from jupytext.formats import long_form_one_format, JupytextFormatError
-from .utils import list_notebooks, requires_black, requires_flake8
+from .utils import list_notebooks, requires_black, requires_flake8, skip_if_dict_is_not_ordered
 
 
 def test_str2bool():
@@ -304,6 +304,7 @@ def test_convert_to_percent_format_and_keep_magics(nb_file, tmpdir):
     compare_notebooks(nb1, nb2)
 
 
+@skip_if_dict_is_not_ordered
 def test_pre_commit_hook(tmpdir):
     tmp_ipynb = str(tmpdir.join('nb with spaces.ipynb'))
     tmp_py = str(tmpdir.join('nb with spaces.py'))
@@ -334,6 +335,7 @@ def test_pre_commit_hook(tmpdir):
     assert os.path.isfile(tmp_py)
 
 
+@skip_if_dict_is_not_ordered
 def test_pre_commit_hook_py_to_ipynb_and_md(tmpdir):
     tmp_ipynb = str(tmpdir.join('nb with spaces.ipynb'))
     tmp_py = str(tmpdir.join('nb with spaces.py'))
diff --git a/tests/test_escape_magics.py b/tests/test_escape_magics.py
index 77fb01d7..190f9dc8 100644
--- a/tests/test_escape_magics.py
+++ b/tests/test_escape_magics.py
@@ -107,22 +107,22 @@ def test_force_comment_using_contents_manager(tmpdir):
         assert '%pylab inline' in stream.read().splitlines()
 
 
-@pytest.mark.parametrize('magic_cmd', ['ls', '!ls', 'ls -al', '!whoami', '# ls', '# mv a b'])
+@pytest.mark.parametrize('magic_cmd', ['ls', '!ls', 'ls -al', '!whoami', '# ls', '# mv a b', '! mkdir tmp'])
 def test_comment_bash_commands_in_python(magic_cmd):
-    comment_magic([magic_cmd]) == ['# ' + magic_cmd]
-    uncomment_magic(['# ' + magic_cmd]) == magic_cmd
+    assert comment_magic([magic_cmd]) == ['# ' + magic_cmd]
+    assert uncomment_magic(['# ' + magic_cmd]) == [magic_cmd]
 
 
 @pytest.mark.parametrize('not_magic_cmd', ['copy(a)', 'copy.deepcopy'])
 def test_do_not_comment_python_cmds(not_magic_cmd):
-    comment_magic([not_magic_cmd]) == [not_magic_cmd]
-    uncomment_magic([not_magic_cmd]) == not_magic_cmd
+    assert comment_magic([not_magic_cmd]) == [not_magic_cmd]
+    assert uncomment_magic([not_magic_cmd]) == [not_magic_cmd]
 
 
 @pytest.mark.parametrize('magic_cmd', ['ls', '!ls', 'ls -al', '!whoami', '# ls', '# mv a b'])
 def test_do_not_comment_bash_commands_in_R(magic_cmd):
-    comment_magic([magic_cmd], language='R') == ['# ' + magic_cmd]
-    uncomment_magic(['# ' + magic_cmd], language='R') == magic_cmd
+    assert comment_magic([magic_cmd], language='R') == [magic_cmd]
+    assert uncomment_magic([magic_cmd], language='R') == [magic_cmd]
 
 
 def test_markdown_image_is_not_magic():
diff --git a/tests/test_mirror.py b/tests/test_mirror.py
index 100efb99..94a4fb5a 100644
--- a/tests/test_mirror.py
+++ b/tests/test_mirror.py
@@ -109,9 +109,12 @@ def test_ipynb_to_r(nb_file):
     assert_conversion_same_as_mirror(nb_file, '.low.r', 'ipynb_to_script')
 
 
-@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_scheme'))
-def test_ipynb_to_scheme(nb_file):
-    assert_conversion_same_as_mirror(nb_file, 'ss', 'ipynb_to_script')
+@pytest.mark.parametrize('nb_file,extension',
+                         [(nb_file, extension)
+                          for nb_file in list_notebooks('ipynb_scheme')
+                          for extension in ('ss', 'scm')])
+def test_ipynb_to_scheme(nb_file, extension):
+    assert_conversion_same_as_mirror(nb_file, extension, 'ipynb_to_script')
 
 
 @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_bash'))
@@ -169,9 +172,14 @@ def test_ipynb_to_cpp_percent(nb_file):
     assert_conversion_same_as_mirror(nb_file, 'cpp:percent', 'ipynb_to_percent')
 
 
-@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_scheme'))
-def test_ipynb_to_scheme_percent(nb_file):
-    assert_conversion_same_as_mirror(nb_file, 'ss:percent', 'ipynb_to_percent')
+@pytest.mark.parametrize('nb_file,extension',
+                         [(nb_file, extension)
+                          for nb_file in list_notebooks('ipynb_scheme')
+                          for extension in ('ss', 'scm')])
+def test_ipynb_to_scheme_percent(nb_file, extension):
+    assert_conversion_same_as_mirror(nb_file,
+                                     '{}:percent'.format(extension),
+                                     'ipynb_to_percent')
 
 
 @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_bash'))
diff --git a/tests/test_read_simple_percent.py b/tests/test_read_simple_percent.py
index 814c6511..b16de277 100644
--- a/tests/test_read_simple_percent.py
+++ b/tests/test_read_simple_percent.py
@@ -189,3 +189,20 @@ def test_multiple_empty_cells():
     nb2 = jupytext.reads(text, 'py:percent')
     nb2.metadata = nb.metadata
     compare(nb, nb2)
+
+
+def test_first_cell_markdown_191():
+    text = """# %% [markdown]
+# Docstring
+
+# %%
+from math import pi
+
+# %% [markdown]
+# Another markdown cell
+"""
+
+    nb = jupytext.reads(text, 'py')
+    assert nb.cells[0].cell_type == 'markdown'
+    assert nb.cells[1].cell_type == 'code'
+    assert nb.cells[2].cell_type == 'markdown'
diff --git a/tests/test_read_simple_scheme.py b/tests/test_read_simple_scheme.py
index dffa4962..ef532a53 100644
--- a/tests/test_read_simple_scheme.py
+++ b/tests/test_read_simple_scheme.py
@@ -13,15 +13,16 @@ def test_read_simple_file(script=""";; ---
 
 (define a 35)
 """):
-    nb = jupytext.reads(script, 'ss')
-    assert len(nb.cells) == 3
-    assert nb.cells[0].cell_type == 'raw'
-    assert nb.cells[0].source == '---\ntitle: Simple file\n---'
-    assert nb.cells[1].cell_type == 'markdown'
-    assert nb.cells[1].source == 'Here we have some text\n' \
-                                 'And below we have some code'
-    assert nb.cells[2].cell_type == 'code'
-    compare(nb.cells[2].source, '(define a 35)')
+    for file_extension in ('ss', 'scm'):
+        nb = jupytext.reads(script, file_extension)
+        assert len(nb.cells) == 3
+        assert nb.cells[0].cell_type == 'raw'
+        assert nb.cells[0].source == '---\ntitle: Simple file\n---'
+        assert nb.cells[1].cell_type == 'markdown'
+        assert nb.cells[1].source == 'Here we have some text\n' \
+                                     'And below we have some code'
+        assert nb.cells[2].cell_type == 'code'
+        compare(nb.cells[2].source, '(define a 35)')
 
-    script2 = jupytext.writes(nb, 'ss')
-    compare(script, script2)
+        script2 = jupytext.writes(nb, file_extension)
+        compare(script, script2)