7d.nz

A Python interactive guide on Org-babel

[2020-12-18 Fri]

  python org

org source

Intro

This is a guide to learn how to program with python on org-babel.

To start on the right foot, advanced coders can take a look at python.el (Python's flying circus support for Emacs), which provides python-mode, and relies on comint. Beginners can also read the official tutorial and the guide. https://docs.python.org/3/using https://docs.python.org/3/tutorial

This guide features some of the interactions org-babel provides over code source blocks. It was initially written with Emacs 26.3 and Org-mode 9.3.6 running on Debian 9 (stretch).

Note that the file isn't intended to be read on github (or briefly) as their render of org-mode is incomplete (the following results for instance). You can read it in online in different formats on 7d.nz.

Present configuration is :

9.5
GNU Emacs 28.0.50 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo version 1.14.8, Xaw scroll bars)
 of 2021-08-04
Linux 4.9.0-16-amd64 SMP Debian (2021-07-19)

Typing C-c C-c (which means keeping Control pressed while typing c twice) on the lines above will call and execute the code blocks declared below. It also works when the cursor is on the code blocks.

  (org-version)
  (emacs-version)
  uname -a

Emacs will ask: evaluate this (elisp or python) code block on your system ?

Depending on your symbol definition, you'll have to type "yes" or "no", "y" or "n" the two latter comes by changing the following predicate

(fset 'yes-or-no-p 'y-or-n-p) ;  y/n instead of yes/no

Configuring ob-python

We will be running Python source blocks, that is, blocks starting with #+begin_src python. For this we need to instruct org-babel about what languages can be evaluated in the buffer, and in what mode and/or languages.

You can run the code block below, or set the variable : (customize-variable 'org-babel-load-languages)

  (org-babel-do-load-languages
    'org-babel-load-languages
    '((python . t)))

(defun do-org-confirm-babel-evaluations (lang body)
  (not
  (or
     (string= lang "python")
     (string= lang "calc"))))
(setq org-confirm-babel-evaluate 'do-org-confirm-babel-evaluations)

The other function in the block above will instruct babel to skip the confirmation when running python or calc in org-babel source blocks.

More radical : (setq org-confirm-babel-evaluate nil) see (customize-variable 'org-confirm-babel-evaluate)

The interpreter

Python is distributed with every Linux system. Many bash and perl programs, parts of the operating system, have been rewritten with it.

(setq python-shell-interpreter "python3")

Now you can check your python system interpreter

    import sys
  return sys.version

If the answer is 2.7, you'll have to make one more customization tweak. The official Python distribution is now Python 3. The 2.7 interpreter is still around for historical reasons, but will be dropped from now on.

(customize-variable 'org-babel-python-command)

to python3, and now we are set.

While for proper shell to start interactively outside of org-babel, the variable will be

(customize-variable 'python-shell-interpreter)

type M-x : run-python (where M stands for Meta, which is Alt) and the shell (the CPython shell) starts.

If somehow, somewhere the interpreter already started — typically when a session code block gets evaluated, then the shell is already open in a buffer named *Python*.

Notice it tells you python.el completion is loaded. You should have basic syntax coloring and completion by pressing tab. M-n and M-p loops through the history of commands.

Another python interpreter you should be aware of is ipython which provides some extra features : a zsh prompt on the very same command line and automodule reloading, but we'll save that for later.

Python is often said to be an interpreted programming language, but a language is not "interpreted" or "compiled" as such. A specific implementation can be an interpreter or a compiler. Over-simplification, but still: when a program is run, every line of code feeds, in sequence, the interpreter/compiler which translates the program into byte-code. The last step generates files, landing in the __pycache__ folder which in turn contains .pyc files extensions named after your program. You can completely ignore that folder, the interpreter will manage it.

Basic types and definitions

Instructions comes in two forms : definitions, that don't return anything and statements, that do return something.

definitions

x=1 defines a variable x holding the value 1 As you can see in the interpreter this definition does not return any value, while the statement x does return the value of the variable

>>> x=1
>>> x
1
>>>

In the same way a function definition when declared doesn't return anything : do not confuse with the return statement of the function, but with the return value of an expression given to the interpreter.

>>> def f(): return 256

Asking for the value of f will return a string representation of that function as its type, name and memory address.

>>> f
<function f at 0x7fbdaace2e18>

And of course actually calling that function is expressed with f().

Python variables are dynamically typed. Any variable can hold any type from any variable, and — along with its value — the type it holds changes during the execution of the program. We talk about binding variables.

Asking for an undefined variable raise an error : NameError: name 'y' is not defined We can initialize y with "text" and ask for it's length :

>>> y="text"
>>> len(y)

then give the same variable another value of a different type and ask again for it's length. For instance :

>>> y=2
>>> len(2)

This time we would get a type error, with the explanation : TypeError: object of type 'int' has no len()

The type of a variable is implicit and stricly related to the value it holds.

The type of a variable can be returned by the type function.

>>> x=2.1
>>> type(x)
<class 'float'>

which is the same as asking the type of the corresponding value (since a variable returns its value).

>>> type(2.1)
<class 'float'>

And what is the type of that? Well it's a type again, denoted through a class which is a reserved keyword, and which indicates the nature of the type construct: this is object oriented design.

>>> type(type(x))
<class 'type'>

Any variable, function, declared in the interpreter can be queried, modifed, or manipulated, even built-in functions, so good care is advised to not inadvertently change the semantics of the program in its course (say, with an embarassing type=x for instance). Due to the local scoping however, if a parameter takes the name of an existing function (a commonplace beeing file), then it exists only for the scope it was defined.

  
def proceeed():
  def open(file):
    print("opening %s" % file)

Yes, it's possible to define functions inside of functions (so you can call functions while calling functions…). Here open and file already exists as built-ins, but the definition of file designates the parameter only inside the open function, while this definition of open only exists in proceed. Out of that scope, both open and file keep refering to their original definitions.

Getting help

The function help() will spit out the help text of a function or a module.

    help(type)

Wich is defined à la lisp, with a string as first statement in a function definition.

Here I'm skipping forward a bit assuming you know about function definitions, scopes and indentations. Otherwise, here it is, in the official tutorial. https://docs.python.org/3.6/tutorial/controlflow.html#defining-functions

    def greetings(name):
     """ Here we can describe what the function does """
     print ("Hello %s" % name)

  greetings('Vincent')
  help(greetings)

Session

Every code block is an independent program

   x=0
   x+=1

unless we enter session mode.

Session mode in org-python is slightly different from non-session mode, because in session mode you are talking to a single "interactive" python session. In python's interactive mode, blank lines are special: they indicate the end of an indented block. So you have to write your org-mode python code a little different when using session mode. Besides, the return type is implicit, it is the last expression, as in an interactive shell.

[2020-12-19 Sat 17:35] I don't know if this holds anymore since org 9.3

   x
  x+=1
x

The above program can be run repeatdly (with C-c) and the result will keep increasing.

Memo noweb

  return n*2
  return n*2
return n

Every block is considered as a function with its own scope and variable namespace

  return n*2
return n
  x = 12
return x
  return int(y)+1

Passing variables between blocks

    n
    from math import sqrt
  print(n)
  n+=i
  (n+sqrt(5))/2

When babel doesn't provide the expected output, the error might be silenced, — which isn't much in the spirit of python — or displayed in the *Org-Babel Error Output* buffer. In the first block, changing the variable to n="3" will raise an error in the second block, which in turn won't provide any result. The error appears in the *Python* buffer, but it's not the best place to investigate. Changing the :results from value to output will make the situation explicit.

For debugging at least : if the intent is to use that result to feed another function, then output won't provide the computed golden ratio, but the print statement output, which is the integer 3 in this example.

Babel provides an extra layer to organize the code, so in the end it's only a matter of opening the proper channels to direct the results in the proper buffers.

Printing output

sys.stdout

    import sys
  sys.stdout.write("1..")
  sys.stdout.write("2")
  sys.stdout.flush()
    import random
  dogs ="Max Charlie Cooper Buddy Jack Rocky Oliver Bear Duke".split()
  head = ["Name", "str",  "agi",  "int"]
  fmt1 ="{0:8s}| {1:3s} | {2:3s} | {3:3s}"
  fmt2 ="{0:8s}| {1:3d} | {2:3d} | {3:3d}"
  return [[ fmt1.format(*head)],
	*([ fmt2.format(*([name]+ random.sample( range(0,20), 3)))]
	    for name in dogs )]

Formatting example : tables of squares and cubes

  
  print(' {4:2s}    {0:5s} {1:7s}{2:4s}  {3:10s}'.format('x','x^2','ko','x^3','n'))

  for n in range(0, 11):
       x=2<<n
       ko=int(x**2/1024)
       print('{4:2d} {0:5d} {1:7d} {2:4d}{3:12d}'.format(x,x**2,ko,x**3,n))

  print('-'*33)
  # right justify as string (through repr or str)
  for x in range(1, 12):
       print(repr(x).rjust(2), repr(x*x).rjust(3), end=' | ')
       # Note use of 'end' on previous line, instead of '\n' by default
       print(repr(x**3).rjust(4))

Formatting tables

and adding a prologue header

A basic table output can be return by value

    import random
  dogs ="Max Charlie Cooper Buddy Jack Rocky Oliver Bear Duke".split()
  head = ["Name", "str",  "agi",  "int"]
  fmt1 ="{0:8s}| {1:3s} | {2:3s} | {3:3s}"
  fmt2 ="{0:8s}| {1:3d} | {2:3d} | {3:3d}"
  return [[ fmt1.format(*head)],
	*([ fmt2.format(*([name]+ random.sample( range(0,20), 3)))]
	    for name in dogs )]

the :prologue attributes can be used to insert something before the result; It requires however the result to be an output, an so it needs a slight modification.

Name str agi int
Max 0 16 6
Charlie 8 18 14
Cooper 11 18 9
Buddy 18 9 4
Jack 4 12 3
Rocky 15 2 8
Oliver 16 13 7
Bear 7 11 10
Duke 19 5 0

an other alternative is the :post attribute

    echo "#+ATTR_LATEX: :center nil :align |p{5cm}|l|l|l|"
  echo "$data"

Here the origin of the *this* should be investigated

    import random
  dogs ="Max Charlie Cooper Buddy Jack Rocky Oliver Bear Duke".split()
  head = "Name", "str",  "agi",  "int"
  fmt1 ="|{0:8s}| {1:3s} | {2:3s} | {3:3s}"
  fmt2 ="|{0:8s}| {1:3d} | {2:3d} | {3:3d}"
  print(  fmt1.format(*head))
  for name in dogs :
      print ( fmt2.format(*([name]+ random.sample( range(0,20), 3))))
Name str agi int
Max 14 15 1
Charlie 19 17 4
Cooper 18 12 2
Buddy 14 12 8
Jack 4 9 6
Rocky 0 8 17
Oliver 2 5 3
Bear 5 12 18
Duke 12 18 6

Lambda expressions

  def g(n, f=lambda i:0):
   return [f(i) for i in range(1,n+1)]

g(10, f=lambda x:2**x), g(10, f=lambda x:3**x)

Files

initializing a file buffer

    f = open(file,'w+')
  f.write(mark)
  f.close ()

Be careful with the file keyword though, as it's already a bound function.

Write permissions

  
  f1 = open(file,'r+')
  n = f1.read()
  i = int(n)
  i += 1
  f1.seek(0)
  f1.write(str(i))
  f1.close()

  # woops error silenced
  f2 = open(testfile,'w')
  f2.write('')
  f2.close()
  f2 = open(testfile, 'rb+')
  f2.write(b'0123456789abcdef') #noticed the bYTE ?
  a = f2.seek(5)      # Go to the 6th byte in the file
  b = f2.read(1)
  #5
  f2.seek(-3, 2)  # Go to the 3rd byte before the end
  c = f2.read(1)
  #d
  f2.close()
  # pour conclure correctement le test, vérifier simplement
  # les types de retour (je les ai gardé lisible pour mémo, on va s'en resservir)
  # et les octets attendus

  print('\n'.join([str(i),str(a),str(b),str(c),str(f1),str(f2)]))
    def fib(n):    # Write Fibonacci series up to n.
      """ Print a Fibonacci series up to n."""
      a, b = 0, 1
      while a < n:
          print(a, end=' ')
          a, b = b, a+b
      print()

  fib(100)

global level and modules

    from pprint import pprint
  pprint(globals())

Objects

Every object is backed up by a __dict__ object which acts as a namespace for that object.

Init, Enter, Exit

    class aClass :
      def __init__(self,v):
          self.v=v
      def __enter__(self):
          print(__class__, "__enter__", self)
          return self.v
      def __exit__(self, type, value, traceback):
          print(__class__, '__exit__', value, traceback)
      def __del__(self):
          print(__class__, '__del__', self)

  print('>')
  with aClass(42) as value:
      print ("\ninside of block 'with'", value)

Note that self isn't a keyword. The following is still respectable python

    class MyClass :
      def __init__(λ,v):
          λ.v=v
      def __enter__(λ):
          print(__class__,"__enter__")
          return λ.v
      def __exit__(λ,type, value, traceback):
          print(__class__,'__exit__',value,traceback)
      def __del__(λ):
          print(__class__,'__del__')

  print('\r')
  with MyClass(42) as value:
      print ("in block 'with' and",value)

The output of the execution order isn't however guaranteed The output of "ok" and True will follow that order, but the call to __del__ may appear before "ok" : the function gets a copy, yet the object is deleted.

    class aClass :
      def __init__(self,v):
          self.v=v
      def __enter__(self):
          print(__class__,"__enter__")
          return self.v
      def __exit__(self,type,value,traceback):
          print(__class__,'__exit__',value,traceback)
      def __del__(self):
          print('__del__')
      def p(self):
          print("ok",self)
          return True

  f = aClass(1).p
  print(f())

session output in

__del__
ok <__main__.aClass object at 0x7f75451a4910>
True

non-session output :

ok <__main__.aClass object at 0x7f0059834fa0>
True
__del__

Generic Function single dispatch

Java-like Polymorphism with decorators

    from functools import singledispatch
  @singledispatch
  def F(arg):
      return "default"

  @F.register(int)
  @F.register(float)
  def _(arg):
      return "for a number"

  class C: pass

  @F.register(C)
  def _(arg):
      return "for a an objet C"

  print( ( F("x"), F([]), F(1), F(C()) ) )
  print(F.registry.keys())

Converting a raw dictionnary to an object

  class Struct:
    def __init__(_,rawdat) :
        _.__dict__ = rawdat
        for k,v in rawdat.items() :
            if isinstance(v,dict):
                _.__dict__[k] = Struct(v)
            if isinstance(v,list):
                if all(type(x) is dict for x in v):
                    _.__dict__[k] = [Struct(i) for i in v]
                else:
                    _.__dict__[k] = v

org-tables

org#Environment of a Code Block

a
b
c
   return [[val + '*' for val in row] for row in tab]

Serialization with pickle

    from pickle import dumps, load

  class A:
      def __init__(self,v):
          self.v=v

  F = testfile="/tmp/serialize.dump"

  a = A(15)
  f = open(F,'wb')
  f.write(dumps(a))
  f.close()

  f = open(F,'rb')
  o = load(f)
  f.close()

  print (o.v)

Non blocking logging with thread safety

  
class Log :
   def __init__(_,_file):
      _.a = open(_file, 'a')
      _.ready = True

   def read():
      _.r = open('r', file)
      pass

   def write(event):
      queue.put(event)

   def stream(_):
      # thread_safe. Non blocking
      # chrono
      ms=0
      while not queue.empty() and _.ready:
          T = queue.get()
          #atomicité de l'opération write
          nb+=_.a.write(T)  # et en cas d'interruption ?
      # nb/ms

   def close(_):
      _.ready = false;
      close(_.a)
      close(_.r)


from threading import Thread

L = Log("/tmp/a.log")

def writeGibberish():
    global L
    print('.',end="-")
    for i in range(16):
        L.write(i)
        L.stream()

for i in range(128):
    t=Thread(target=writeGibberish)
    t.start()
    print(i)

print('?')

file:///tmp/a.log

reading the standard output

  # http://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python?rq=1
  import sys
  from subprocess import PIPE, Popen
  from threading  import Thread

  try:
      from Queue import Queue, Empty
  except ImportError:
      from queue import Queue, Empty  # python 3.x

  ON_POSIX = 'posix' in sys.builtin_module_names

  def enqueue_output(out, queue):
      for line in iter(out.readline, b''):
          queue.put(line)
      out.close()

  p = Popen(['./veryverbose'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
  q = Queue()
  t = Thread(target=enqueue_output, args=(p.stdout, q))
  t.daemon = True # thread dies with the program
  t.start()

  # ... do other things here

  # read line without blocking
  try:  line = q.get_nowait() # or q.get(timeout=.1)
  except Empty:
      print('no output yet')
  else: # got line
      # ... do something with line
      print('line')

strftime reference

Note: Examples are based on datetime.datetime(2013, 9, 30, 7, 6, 5)

Code Meaning Example
%a Weekday as locale’s abbreviated name. Mon
%A Weekday as locale’s full name. Monday
%w Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. 1
%d Day of the month as a zero-padded decimal number. 30
%-d Day of the month as a decimal number. (Platform specific) 30
%b Month as locale’s abbreviated name. Sep
%B Month as locale’s full name. September
%m Month as a zero-padded decimal number. 09
%-m Month as a decimal number. (Platform specific) 9
%y Year without century as a zero-padded decimal number. 13
%Y Year with century as a decimal number. 2013
%H Hour (24-hour clock) as a zero-padded decimal number. 07
%-H Hour (24-hour clock) as a decimal number. (Platform specific) 7
%I Hour (12-hour clock) as a zero-padded decimal number. 07
%-I Hour (12-hour clock) as a decimal number. (Platform specific) 7
%p Locale’s equivalent of either AM or PM. AM
%M Minute as a zero-padded decimal number. 06
%-M Minute as a decimal number. (Platform specific) 6
%S Second as a zero-padded decimal number. 05
%-S Second as a decimal number. (Platform specific) 5
%f Microsecond as a decimal number, zero-padded on the left. 000000
%z UTC offset in the form +HHMM or -HHMM  
  (empty string if the the object is naive).  
%Z Time zone name (empty string if the object is naive).  
%j Day of the year as a zero-padded decimal number. 273
%-j Day of the year as a decimal number. (Platform specific) 273
%U Week number of the year (Sunday as the first day of the week)  
  as a zero padded decimal number. All days in a new year preceding  
  the first Sunday are considered to be in week 0. 39
%W Week number of the year (Monday as the first day of the week)  
  as a decimal number. All days in a new year preceding the first  
  Monday are considered to be in week 0. 39
%c Locale’s appropriate date and time representation. Mon Sep 30 07:06:05 2013
%x Locale’s appropriate date representation. 09/30/13
%X Locale’s appropriate time representation. 07:06:05
%% A literal '%' character. %
  from time import time
t = time()
print(t)
  import time

def countdown(t):
    while t:
        mins, secs = divmod(t, 60)
        timeformat = '{:02d}:{:02d}'.format(mins, secs)
        print(timeformat, end='\r')
        time.sleep(1)
        t -= 1
    print('Goodbye!\n\n\n\n\n')

timeit

Timing code execution

  from timeit import timeit

def rev(n,L):
    for x in reversed(L):
        n += x
    return(n)

def rev1(n,L):
    for x in L[::-1]:
        n += x
    return(n)

def rev2(n,L):
    for i in range(len(L)-1, 0, -1):
        n += L[i]
    return(n)


def loop(f,x):
    n=0
    x = f(n,L)

a=b=c=0
L = [x for x in range(300000)]

def tit(x):
    return timeit(x,number=100)

print(tit(lambda:loop(rev,a)))
print(tit(lambda:loop(rev1,b)))
print(tit(lambda:loop(rev2,c)))

on iPython :

  
%alias_magic t timeit

L = [x for x in range(3000000)]

def rev(n,L):
    for x in reversed(L):
	n += x
    return(n)

def rev1(n,L):
    for x in L[::-1]:
	n += x
    return(n)

def rev2(n,L):
    for i in range(len(L)-1, 0, -1):
	n += L[i]
    return(n)

def loop(f,x):
    n=0
    x = f(n,L)

a=b=c=0
%t loop(rev,a)
%t loop(rev1,b)
%t loop(rev2,c)

Memory analysis with tracemalloc

Every objects has a _sizeof_() function.

10 biggest objects

    import tracemalloc
  tracemalloc.start()

  #run the application...

  snapshot = tracemalloc.take_snapshot()
  top_stats = snapshot.statistics('lineno')
  [[str(t)] for t in top_stats[:10]]

Memory leak search :

    import tracemalloc
  tracemalloc.start()

  #run the application...
  snapshot2 = tracemalloc.take_snapshot()
  top_stats = snapshot.compare_to(snapshot,'lineno')
  print("[ Top 10 différences ] ")
  [[str(stat)] for stat in top_stats[:10]]

ipython autoreload

  '''
enable autoreload of a library whenever a change occurs
'''
%load_ext autoreload
%autoreload 2
%aimport pyorgmode
# set the locale for correct date handling (%a)
import locale
locale.setlocale(locale.LC_TIME, "")
# ^ you'll need it to properly handle
# date format such as <2017-03-24 Fri> or <2017-03-24 ven.>

from pyorgmode import *
org = OrgDataStructure()
org.load_from_file("tests/orgs/test.org")
topnodes = org.toplevel_nodes()
headings = [T.heading for T in topnodes]
print(headings)
for it in topnodes :
    print (it.level, it.todo, it.priority, it.heading, it.tags)

Compiling Python

In further versions of Python are introduced new language features. For instance as of 3.6 was introduced the matrix multiplication operator (@).

At the end of 2020, Python latest version is 3.9.1 Every release brings a significant amount or improvements.

https://www.python.org/doc/versions/

The following compilation options enable

  • the creation and loading C shared library
  • history in the shell
  • PGO
  • link time optimization

https://stackoverflow.com/questions/41405728/what-does-enable-optimizations-do-while-compiling-python

./configure --enable-shared --enable-loadable-sqlite-extensions --enable-optimizations --with-lto

https://www.python.org/ https://www.python.org/downloads/release/python-391/ https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz.asc

Flycheck

  ;;; (add-hook 'python-mode-common-hook 'flycheck-mode)
;(require 'flymake-python-pyflakes)
;(add-hook 'python-mode-hook 'flymake-python-pyflakes-load)
; (global-flycheck-mode 1)  ;; << will globally bind C-c !
(with-eval-after-load 'flycheck
  (add-hook 'flycheck-mode-hook #'flycheck-pycheckers-setup))
(add-hook 'before-save-hook 'delete-trailing-whitespace)

Graphics'n Gui

[66%] Tk interlude

  • DONE default bindings ?
      
      def key(event):
          print ("pressed", repr(event.keysym))
    
      def Esc(event):
          quit()
    
      def mouseCallback(evt):
          Log.put({'type':evt.type,'widget':evt.widget,'x':evt.x,'y':evt.y, 'btn':evt.num})
          # x and y root left aside
    
      def callback(evt):
          print (evt.type)
    
      def ignore(event):
          # avoid this for toplevel as is will mute the event
          return "break"
    
      def windows_callback(evt):
      # a <configure> event
      #
      # evt 22 = configure (windows_event). peu utile comme évènement, niveau trace/debug
          Log.put(evt.type,{'width':evt.width,'height':evt.height,'x_root':evt.x_root,'y_root':evt.y_root})
      # no ? filter event logging base on their type : better, pipe it to the tkinter filter
      # rem : not very pythonic
      # let's see later about dnd'
    
    
      def defaultbindings(frame):
          frame.bind("<Key>",key)
      #   The user pressed any key. The key is provided in the char member of the event object passed to the callback (this is an empty string for special keys).
    
      #    a
      #   The user typed an “a”. Most printable characters can be used as
      #   is. The exceptions are space (<space>) and less than
      #   (<less>). Note that 1 is a keyboard binding, while <1> is a
      #   button binding.
          frame.bind("<Escape>",Esc)
          frame.bind("<Button-1>", callback)
          frame.bind("<Button-2>", callback)
          # think about the Menu button
          frame.bind("<Button-2>", callback)
          frame.bind("<Double-Button-1>", callback)
          # Note that if you bind to both a single click (<Button-1>)
          # and a double click, both bindings will be called.
    
          frame.bind("<Enter>", callback)
          # The mouse pointer entered the widget (this event doesn’t mean that
          # the user pressed the Enter key!).
    
          frame.bind("<Leave>", callback)
          # The mouse pointer left the widget.
    
          frame.bind("<FocusIn>", callback)
          # Keyboard focus was moved to this widget, or to a child of this widget.
          frame.bind("<FocusOut>", callback)
          # Keyboard focus was moved from this widget to another widget.
    
          frame.bind("<Return>", callback)
    
          # The user pressed the Enter key. You can bind to virtually all keys on the keyboard. For an ordinary 102-key PC-style keyboard, the special keys are Cancel (the Break key), BackSpace, Tab, Return(the Enter key), Shift_L (any Shift key), Control_L (any Control key), Alt_L (any Alt key), Pause, Caps_Lock, Escape, Prior (Page Up), Next (Page Down), End, Home, Left, Up, Right, Down, Print, Insert, Delete, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Num_Lock, and Scroll_Lock.
    
          frame.bind("<Shift-Up>", callback)
    
          # The user pressed the Up arrow, while holding the Shift key
          # pressed. You can use prefixes like Alt, Shift, and Control.
    
          frame.bind("<Configure>", windows_callback)
    
          # The widget changed size (or location, on some platforms). The
          # new size is provided in the width and height attributes of the
          # event object passed to the callback.
    
      "defaults bindings set"
    

    =defaults bindings set

  • DONE TkTest ?
      
      #from Tkinter import *
      from tkinter import *
      from tkinter import messagebox
      import sys
    
      def quit():
          print("exiting now")
          if messageBox.askokcancel("Quit", "Do you really wish to quit?"):
              # make sure widget instances are deleted
              root.destroy()
          # event is automatically sent to the log
          #top.protocol("WM_DELETE_WINDOW", top.destroy)
    
      def XColorString(color) :
          return '#%02x%02x%02x' % color
    
      if __name__ == '__main__':
          root=Tk()
          root.geometry("%dx%d+%d+%d" % (360,200,900,600))
          root.protocol("WM_DELETE_WINDOW", quit)
          root.bind('<Escape>', Esc)
          #Log=LogBuffer('./test.log')
          #frame = Frame(root)
          grey=(180, 180, 0)
          m = PanedWindow(master=root,orient=VERTICAL, background=XColorString(grey))
          m.background = XColorString(grey)
          m.pack(fill=BOTH, expand=1)
          top = Label(m, text="top pane")
          m.add(top)
          bottom = Label(m, text="bottom pane")
          m.add(bottom)
          defaultbindings(root)
          #defaultbindings(frame)
          #frame.pack()
          #top.protocol("WM_TAKE_FOCUS", top.takefocus)
          root.mainloop()
          True
    
  • The tk color picker ?

    how to gracefully extend that program so the selected color is tried and tested before it is applied? It sounds simple at first, but this where we would discover the importance of the design choices in the former module: is the color picker blocking ? Does it return a value only upon validation? Should the host program be modified to get the event ?

tk, buttons & dnd

When you press down a mouse button over a widget, Tkinter will automatically “grab” the mouse pointer, and subsequent mouse events (e.g. Motion and Release events) will then be sent to the current widget as long as the mouse button is held down, even if the mouse is moved outside the current widget.

FrameBSP

    # Binary Tree Levelorder Traversal (visitor pattern)
  def traverse_levelorder (tree_node):
    queue.put(tree_node)
    while not queue.empty():
      T = queue.get()
      if t is not None:
        visit(t)
        queue.put(t.left)
        queue.put(t.right)
        queue.put(t)
  queue.put(t)

SDL2+OpenGL with a non blocking input

  # moduile name
DEFAULT_MODULE = 'sdl2ogl'
thismodule = DEFAULT_MODULE
# loading it
IGL = __import__(thismodule)
t = Thread(target=IGL.start)
t.start()
# r goes
# and we can give it new values
# read by the simulation

sdl+ogl

  """OpenGL rendering"""
import sys
import ctypes

from OpenGL import GL, GLU
import sdl2

clearcolor=(0,0,0)
def justdoit(): # call <module>.justdoit() from pyshell
    global clearcolor
    clearcolor=(0,1,0)

def run():
    if sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) != 0:
        print(sdl2.SDL_GetError())
        return -1

    window = sdl2.SDL_CreateWindow(b"OpenGL demo",
                                   sdl2.SDL_WINDOWPOS_UNDEFINED,
                                   sdl2.SDL_WINDOWPOS_UNDEFINED, 800, 600,
                                   sdl2.SDL_WINDOW_OPENGL)
    if not window:
        print(sdl2.SDL_GetError())
        return -1

    context = sdl2.SDL_GL_CreateContext(window)

    GL.glMatrixMode(GL.GL_PROJECTION | GL.GL_MODELVIEW)
    GL.glLoadIdentity()
    GL.glOrtho(-400, 400, 300, -300, 0, 1)

    x = 0.0
    y = 30.0

    event = sdl2.SDL_Event()
    running = True
    while running:
        while sdl2.SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == sdl2.SDL_KEYDOWN :
                if event.key.keysym.sym == sdl2.SDLK_F2:
                    print ('>now what ?\n')
                    running = False
                if event.key.keysym.sym == sdl2.SDLK_ESCAPE:
                    running = False
            if event.type == sdl2.SDL_QUIT:
                running = False

        GL.glClearColor(clearcolor[0], clearcolor[1], clearcolor[2], 1)
        GL.glClear(GL.GL_COLOR_BUFFER_BIT)
        GL.glRotatef(10.0, 0.0, 0.0, 1.0)
        GL.glBegin(GL.GL_TRIANGLES)
        GL.glColor3f(1.0, 0.0, 0.0)
        GL.glVertex2f(x, y + 90.0)
        GL.glColor3f(0.0, 1.0, 0.0)
        GL.glVertex2f(x + 90.0, y - 90.0)
        GL.glColor3f(0.0, 0.0, 1.0)
        GL.glVertex2f(x - 90.0, y - 90.0)
        GL.glEnd()

        sdl2.SDL_GL_SwapWindow(window)
        sdl2.SDL_Delay(10)
    sdl2.SDL_GL_DeleteContext(context)
    sdl2.SDL_DestroyWindow(window)
    sdl2.SDL_Quit()
    return 0

# don't
# if __name__ == "__main__":
#      sys.exit(run())
def start():
    print ('starting\n')
    sys.exit(run())
# here it's already out unless a root tk is still flying around

Idle and PyShell invocation

http://stackoverflow.com/questions/39543888/python-pygame-can-you-run-a-program-whilst-having-a-pygame-window-that-can-stil/39573442?noredirect=1#comment66863541_39573442

Some details of what you must do may depend on what you want to do with IDLE's Shell once you have it running. I would like to know more about that. But let us start simple and make the minimum changes to pyshell.main needed to make it run with other code.

Note that in 3.6, which I use below, PyShell.py is renamed pyshell.py. Also note that everything here amounts to using IDLE's private internals and is 'use at your own risk'.

I presume you want to run Shell in the same process (and thread) as your tkinter code. Change the signature to

def main(tkroot=None):

Change root creation (find # setup root) to You should be able to call pyshell.main whenever you want.

  
tkroot=None
if not tkroot:
    root = Tk(className="Idle")
    root.withdraw()
else:
    root = tkroot

# In current 3.6, there are a couple more lines to be indented under if not tkroot:

    if use_subprocess and not testing:
        NoDefaultRoot()

# Guard mainloop and destroy (at the end) with

if not tkroot:
    while flist.inversedict:  # keep IDLE running while files are open.
        root.mainloop()
    root.destroy()
# else leave mainloop and destroy to caller of main
"""
The above adds 'dependency injection' of a root window to the
function. I might add it in 3.6 to make testing (an example of 'other
code') easier.
"""
#The follow tkinter program now runs, displaying the both the root window and an IDLE shell.

from tkinter import Tk
from idlelib import pyshell

root = Tk()
Label(root, text='Root id is '+str(id(root))).pack()
root.update()
def later():
    pyshell.main(tkroot=root)
    Label(root, text='Use_subprocess = '+str(pyshell.use_subprocess)).pack()

root.after(0, later)
root.mainloop()

if/else vs. list comprehension

dissassembling and timing two code variants

  import sys
def get_datasets(observatoryGroup=None, instrumentType=None, observatory=None,
                 instrument=None,
                 startDate=None, stopDate=None, idPattern=None, labelPattern=None, notesPattern=None):

        return [ f'{x}={y}' for (x,y) in [
            ("observatory",observatory),
            ("observatoryGroup",observatoryGroup),
            ("instrumentType",instrumentType),
            ("instrument",instrument),
            ("startDate",startDate),
            ("stopDate", stopDate ),
            ("idPattern", idPattern ),
            ("labelPattern", labelPattern ),
            ("notesPattern", notesPattern )
        ] if y is not None]


# get_datasets(observatoryGroup=False)

def distest() :
    import dis

    dis.dis("""args = [ f'{x}={y}' for (x,y) in [
                ('observatory',observatory),
                ('observatoryGroup',observatoryGroup),
                ('instrumentType',instrumentType),
                ('instrument',instrument),
                ('startDate',startDate),
                ('stopDate', stopDate ),
                ('idPattern', idPattern ),
                ('labelPattern', labelPattern ),
                ('notesPattern', notesPattern )
            ] if y is not None]""" )


    print("-----------")

    dis.dis("""args = []
    if observatory is not None:
        args.append(f'observatory={observatory}')
    if observatoryGroup is not None:
        args.append(f'observatoryGroup={observatoryGroup}')
    if instrumentType is not None:
        args.append(f'instrumentType={instrumentType}')
    if instrument is not None:
        args.append(f'instrument={instrument}')
    if startDate is not None:
        args.append(f'startDate={startDate}')
    if stopDate is not None:
        args.append(f'stopDate={stopDate}')
    if idPattern is not None:
        args.append(f'idPattern={idPattern}')
    if labelPattern is not None:
        args.append(f'labelPattern={labelPattern}')
    if notesPattern is not None:
        args.append(f'notesPattern={notesPattern}')
        """
     )


def get_datasets0(observatoryGroup=None, instrumentType=None, observatory=None,
                 instrument=None,
                 startDate=None, stopDate=None, idPattern=None, labelPattern=None, notesPattern=None):

    args = []
    if observatory is not None:
        args.append(f'observatory={observatory}')
    if observatoryGroup is not None:
        args.append(f'observatoryGroup={observatoryGroup}')
    if instrumentType is not None:
        args.append(f'instrumentType={instrumentType}')
    if instrument is not None:
        args.append(f'instrument={instrument}')
    if startDate is not None:
        args.append(f'startDate={startDate}')
    if stopDate is not None:
        args.append(f'stopDate={stopDate}')
    if idPattern is not None:
        args.append(f'idPattern={idPattern}')
    if labelPattern is not None:
        args.append(f'labelPattern={labelPattern}')
    if notesPattern is not None:
        args.append(f'notesPattern={notesPattern}')

    return args


import timeit

print(timeit.timeit(lambda:get_datasets(observatoryGroup=1, instrumentType=2, observatory=3)))
print(timeit.timeit(lambda:get_datasets0(observatoryGroup=1, instrumentType=2, observatory=3)))

print(timeit.timeit(lambda:get_datasets(idPattern=1, labelPattern=2, notesPattern=3)))
print(timeit.timeit(lambda:get_datasets0(idPattern=1, labelPattern=2, notesPattern=3)))

yield from

yield from can be used to delegate iteration

    def countdown(n):
      while n > 0:
          yield n
          n -= 1

  def countup(stop):
      n = 1
      while n < stop:
          yield n
          n += 1

  def up_and_down(n):
      yield from countup(n)
      yield from countdown(n)

  for x in up_and_down(3) :
      print(x)

Maximum Nesting Depth of the Parentheses

https://yewtu.be/watch?v=zrOIQEN3Wkk

  import re,operator
s="(+(2*3)+((8)/4))+1"
def scan(f, state, it):
     for x in it: state = f(state, x)
       yield state
return max(list(scan(operator.add, 0, [(1 if x=="(" else -1) for x in re.findall("[()]",s)])))