13

Similarly to this question I would like to evaluate (in org mode) Python source code blocks containing "input" instructions but I can't find a way to have an interactive evaluation (with user input) during evaluation or to give it some input known in advance (stored in a file for instance).

My constraint is to use explicitly the input instruction since all this should be included in a textbook for students.

Example of code :

#+BEGIN_SRC python :results output 
a= input("Value") 
print(a)
#+END_SRC

Is it possible to have such an interactive evaluation or to simulate it (by giving to the source code a fake input) ?

Lgen
  • 1,380
  • 10
  • 19
  • Will you be using org-mode `export` to generate your example code for the students into some other format, e.g. html? – Melioratus Sep 06 '16 at 05:10
  • Python's [input()](https://docs.python.org/2/library/functions.html#input) function only takes quoted text on single line, e.g. "hello" or "hi\nhello\nhowdy", correct? – Melioratus Sep 06 '16 at 05:29
  • @Melioratus Thaks for you comments; yes I use the org-mode export (to LaTeX/pdf) and I export both the code and the result. You can also use multilines (""" ... """) text as a parameter for the input function. My problem is to introduce values to the "input" function during the execution of the code. – Lgen Sep 06 '16 at 17:12
  • Thanks for the clarification! I'll post an answer that uses the literate programming features , i.e. `noweb`, of org-mode that will allow you to test & export your code with results. – Melioratus Sep 06 '16 at 18:14
  • Thanks, this could be an alternative to the solution proposed by John Kitchin (may be avoiding the tangling step ?). – Lgen Sep 06 '16 at 20:15
  • Correct you will not need to `tangle` any files unless you want to provide students your code samples as standalone scripts. Also you can choose if you want to be prompted for input when exporting by using elisp to ask you. I find creating and maintaining code examples as literate programs is much easier in the long term. – Melioratus Sep 07 '16 at 14:43

5 Answers5

9

Here is an alternative approach that uses a non-exported, tangled file to replace the input function.

#+BEGIN_SRC python :session :exports none :tangle example1.py
def input(x): 
    if x == 'Value of a':
        return 3
    elif x == 'Value of b':
        return 4 

#+END_SRC 

#+RESULTS: 

Tip: Press C-cC-vt or use the M-xorg-babel-tangle command to create, i.e. tangle, the example1.py file.


#+BEGIN_SRC python :results output :preamble from example1 import *
a = input('Value of a')
b = input('Value of b')
print(a + b) 
#+END_SRC 

#+RESULTS:
: 7

Note: The example1.py file that was created from the previous python SRC block will be imported into the current block using the builtin :preamble header and the value from example1 import *.

Melioratus
  • 4,504
  • 1
  • 25
  • 43
John Kitchin
  • 11,555
  • 1
  • 19
  • 41
  • Very interesting solution, thanks. I accept it as the solution and I will also propose a variant based on python generator and returning systematically a "str" object to mimic the input(...) function. – Lgen Sep 06 '16 at 20:04
8

Evaluate python code blocks using literate programming in org-mode.

Use :var header to assign variables and test your code.

Note: If desired use elisp (read-string "Prompt: ") and (read-number "Prompt: ") to prompt for user input inside emacs.


Example 1 - print(a)

  • Assign hello world to a.


    #+name: ex1-code  
    #+header: :var a="hello world"  
    #+begin_src python :results verbatim replace output :exports results  
      print(a)  
    #+end_src
    
    #+begin_src python :eval never :exports code :noweb yes   
      a = input('Value of a')  
      <<ex1-code>>  
    #+end_src  
    
    #+results: ex1-code
    : hello world
    

Example 2 - print(a + b)

  • Assign 1 to a.

  • Assign 2 to b.


    #+name: ex2-code
    #+header: :var a=1 
    #+header: :var b=2 
    #+begin_src python :results replace output  :exports results 
      print(a + b)
    #+end_src
    
    #+begin_src python :eval never :exports code :noweb yes 
      a = input('Value of a')
      b = input('Value of b')
      <<ex2-code>>
    #+end_src  
    
    #+results: ex2-code
    : 3
    

Example 3 - print(a,b,c)

  • When prompted for Value of a enter Thanks
  • When prompted for Value of b enter 4.
  • When prompted for Value of c enter your question.


    #+NAME: ex3-code
    #+header: :var a=(read-string "Value of a ") 
    #+header: :var b=(read-number "Value of b ") 
    #+header: :var c=(read-string "Value of c ") 
    #+begin_src python :results replace output   :exports results 
      print a,b,c
    #+end_src  
    
    #+begin_src python :eval never :exports code :noweb yes 
      a = input('Value of a')
      b = input('Value of b')
      c = input('Value of c')
      <<ex3-code>>
    #+end_src  
    
    #+results: ex3-code
    : Thanks 4 your question
    

When you export your org file, the output should look similar to the example below


Example 1 - print(a)

  • Assign hello world to a.

    a = input('Value of a')
    print(a)
    
    hello world
    

Example 2 - print(a + b)

  • Assign 1 to a.
  • Assign 2 to b.

    a = input('Value of a')
    b = input('Value of b')
    print(a + b)
    
    3
    

Example 3 - print(a,b,c)

  • When prompted for Value of a enter Thanks
  • When prompted for Value of b enter 4.
  • When prompted for Value of c enter your question.

    a = input('Value of a')
    b = input('Value of b')
    c = input('Value of c')
    print a,b,c
    
    Thanks 4 your question
    


This code was tested with
GNU Emacs 24.5.1 (x86_64-unknown-cygwin, GTK+ Version 3.14.13)
Org-Mode Version: 8.3.2
and github.

Melioratus
  • 4,504
  • 1
  • 25
  • 43
  • Very interesting solution; it took time for me to understand (I'm not used to literate programming) that the instructions in the second python source bloc (e.g. a = input('Value of a')) were only documentation text and were not treated as instructions. – Lgen Sep 07 '16 at 15:10
  • @Lgen - Thanks! The literate programming, `noweb`, in org-mode features are amazing and incredibly useful! Please let me know if you would like additional code examples. If you need something that doesn't fit well into the Q&A format, I'm happy to post to my GitHub repository. – Melioratus Sep 07 '16 at 15:41
6

I don't think it is possible to get truly interactive Python input with org-babel.

You could use a preamble to redefine the input function so it returns what you want to simulate the use of input, e.g. here we make it look like the user typed in "3".

#+BEGIN_SRC python :results output :preamble def input(x): return 3
a = input("value ")
print(a)
#+END_SRC

#+RESULTS:
: 3

Depending on what students see that is exported, they might not see that you have done this.

John Kitchin
  • 11,555
  • 1
  • 19
  • 41
  • Thanks John for you proposed answer. I was wondering if it is possible to extend it to a multiple call to the input function in the code block (e.g. a=input("Value of a") b=input("Value of b")). To build the texbook I generally use and ":export both" in the "#+BEGIN_SRC python" statement so this line should not be visible in the exported text. – Lgen Sep 06 '16 at 17:17
  • If you use a session, it is possible: #+BEGIN_SRC python :session :exports none def input(x): if x == 'Value of a': return 3 elif x == 'Value of b': return 4 #+END_SRC #+RESULTS: #+BEGIN_SRC python :results output :session a = input('Value of a') b = input('Value of b') print(a + b) #+END_SRC #+RESULTS: : : >>> >>> 7 – John Kitchin Sep 06 '16 at 18:45
2

As a complement of John Kitchin's solution, I propose to use a generator to provide the successive values that will "feed" the input(...) functions and to return a strobject systematically.

#+BEGIN_SRC python :session :exports none :tangle example2.py :results none
def generate(lst):
    for element in lst:
        yield str(element)

generator =  generate(["Thanks",4,"your help"])

def input(x):
     return generator.__next__()
#+END_SRC 


#+BEGIN_SRC python :results output :preamble from example2 import * :exports both
a = input('Value of a')
b = input('Value of b')
c = input('Value of c')
print(a,b,c)
#+END_SRC 
Lgen
  • 1,380
  • 10
  • 19
0

Another way is to use tkinter and to input the value in an external window:

#+name: tkinput
#+begin_src python :results output :exports none
from tkinter.simpledialog import askstring
from tkinter import Tk

def input(message):
    root = Tk()
    root.withdraw()
    entered = askstring("", message)
    root.destroy()
    return entered
#+end_src

#+name: test-tkinput
#+begin_src python :results output :exports code :noweb strip-export
<<tkinput>>
x = input("Test1")
y = input("Test2")
print(x,y)
#+end_src
Lgen
  • 1,380
  • 10
  • 19