miércoles, 26 de abril de 2017

Internacionalización del código (I)

El módulo locale


Cuando se instala un sistema operativo una de las primeras opciones de configuración que se decide es la localización geográfica del equipo o dispositivo. Normalmente, el idioma del sistema operativo se corresponderá con el país seleccionado y dicha elección será también empleada para fijar una serie de convenciones que definen para ese lugar el modo de expresar los números, las cantidades monetarias, los números positivos y negativos, las fechas y horas, etc.

Estas convenciones establecen, entre otros, el carácter que se utiliza en los números para separar la parte entera de la decimal (",", ".", etc.); el carácter que se usa como separador de millares (".", ",", etc.), el símbolo monetario (€, £, $, ¥, etc.), el formato de las fechas y horas, etc.

Afortunadamente, estas definiciones pueden ser utilizadas (y cambiadas) por una aplicación para adaptar la forma en que presentan sus datos a cada territorio. Particularmente, en Python ese es el cometido principal que tiene el módulo locale, incluido en la librería estándar como parte del soporte que ofrece el lenguaje a la internacionalización de las aplicaciones.

Herramienta de Configuración Regional y de Idioma

Hoy, los distintos sistemas operativos proporcionan herramientas gráficas que permiten modificar de forma fácil las opciones comentadas. En Windows se utiliza la herramienta de "Configuración Regional y de Idioma" y en GNU/Linux ésta, a veces, se denomina de igual forma (como en Ubuntu GNOME) o puede estar integrada en otra herramienta como la de "Soporte de Idiomas" (como sucede en Xubuntu).

Configuración Regional en Registro de Windows
 
Según sea el caso, en Windows las definiciones vigentes del entorno de un usuario son almacenadas en el Registro de Windows (en la rama "HKEY_CURRENT_USER\Control Panel\International") y en un sistema GNU/Linux en los archivos donde se declaran las variables de entorno (LC_ALL, LC_CTYPE, LANG, LANGUAGE) que son ejecutados al iniciar un equipo. Cada plataforma tiene sus propios mecanismos.

En GNU/Linux para consultar desde la línea de comandos las variables de entorno utilizar los comandos env o printenv. En Windows, el comando systeminfo muestra información básica de la configuración regional.


Obtener/establecer la configuración regional: setlocate()


La función setlocate() devuelve o establece la configuración regional del entorno de un usuario. El argumento category representa una categoría determinada de configuración (una categoría agrupa un conjunto de definiciones de configuración). Y locale es una cadena que representa el idioma de una localización geográfica determinada:

locale.setlocale(category, locale=None)

Las definiciones de configuración se agrupan en las siguientes categorías:
  • locale.LC_ALL: combinación de todas las categorias.
  • locale.LC_NUMERIC: formatos de números.
  • locale.LC_TIME: formatos de fechas/horas.
  • locale.LC_MONETARY: formatos de valores monetarios.
  • locale.LC_COLLATE: para ordenación de cadenas.
  • locale.LC_CTYPE: para funciones de tipo de carácter.
  • locale.LC_MESSAGES: para visualización de mensajes. (Python no admite mensajes específicos)

Ver valores del argumento locale en Windows

En GNU/Linux Ubuntu se pueden consultar los idiomas disponibles con el comando locale -a.

También, se pueden instalar más idiomas con el comando sudo apt-get install language-pack-XXX. Por ejemplo, para instalar la especificación de Japón: sudo apt-get install language-pack-ja. (Es equivalente a utilizar la herramienta de "Soporte de Idiomas").

A continuación, varios ejemplos para fijar la configuración regional en todas las categorías (locale.LC_ALL). Si la cadena del idioma es incorrecta o el idioma no está disponible se producirá la siguiente excepción:

Error: unsupported locale setting.

Cuando se desarrolla una aplicación para que funcione en cualquier país teniendo en cuenta la configuración regional, después de importar el módulo locale se establecerá la configuración que tenga el entorno de trabajo del propio usuario. Para ello, el argumento locale de setlocate() debe ser una cadena vacía: ''

Así, si un usuario en España imprime importes utilizando la configuración regional, el símbolo monetario mostrado será el símbolo del euro '€'; si es un londinense, el símbolo de la libra '£'; un japonés, el símbolo del Yen '¥', etc.

import locale

# Establecer la configuración que tenga el entorno del usuario
locale.setlocale(locale.LC_ALL, '')  

# Establecer configuraciones de países concretos:

# Establecer configuración para España en un sistema Windows
locale.setlocale(locale.LC_ALL, 'esp')  

# Establecer configuración para España en Ubuntu
locale.setlocale(locale.LC_ALL, 'es_ES.utf8')  

# Establecer configuración para España en otros sistemas GNU/Linux
locale.setlocale(locale.LC_ALL, 'es_ES')  

# Establecer configuración para Japón en Ubuntu
locale.setlocale(locale.LC_ALL, 'ja_JP.utf8') 
 
# Establecer configuración para Japón en otros sistemas GNU/Linux 
locale.setlocale(locale.LC_ALL, 'ja_JP')  


Obtener diccionario con configuración actual: locale.localeconv()


La función localeconv() devuelve un diccionario con todas las definiciones de la configuración regional actual.

import locale
import pprint

# Establecer la configuración del entorno de usuario 
# En este ejemplo se corresponde con 'es_ES.utf8' (España)
locale.setlocale(locale.LC_ALL, '')

# Obtener definiciones de la configuración actual
configuracion = locale.localeconv()

# Imprimir definiciones con pprint para una
# lectura agradable:
imprimir = pprint.PrettyPrinter()
imprimir.pprint(configuracion)

'''
Salida para España:

{'currency_symbol': '€',
 'decimal_point': ',',
 'frac_digits': 2,
 'grouping': [3, 3, 0],
 'int_curr_symbol': 'EUR ',
 'int_frac_digits': 2,
 'mon_decimal_point': ',',
 'mon_grouping': [3, 3, 0],
 'mon_thousands_sep': '.',
 'n_cs_precedes': 0,
 'n_sep_by_space': 1,
 'n_sign_posn': 1,
 'negative_sign': '-',
 'p_cs_precedes': 0,
 'p_sep_by_space': 1,
 'p_sign_posn': 1,
 'positive_sign': '',
 'thousands_sep': '.'}
'''

# Establecer configuración para Japón
# en Ubuntu. 
locale.setlocale(locale.LC_ALL, 'ja_JP.utf8')
# (En Windows utilizar la cadena 'jpn')

# Obtener definiciones de Japón
configuracion = locale.localeconv()

# Imprimir definiciones con pprint para una
# lectura agradable
imprimir.pprint(configuracion)

'''
Salida para Japón:

{'currency_symbol': '¥',
 'decimal_point': '.',
 'frac_digits': 0,
 'grouping': [3, 0],
 'int_curr_symbol': 'JPY ',
 'int_frac_digits': 0,
 'mon_decimal_point': '.',
 'mon_grouping': [3, 0],
 'mon_thousands_sep': ',',
 'n_cs_precedes': 1,
 'n_sep_by_space': 0,
 'n_sign_posn': 4,
 'negative_sign': '-',
 'p_cs_precedes': 1,
 'p_sep_by_space': 0,
 'p_sign_posn': 4,
 'positive_sign': '',
 'thousands_sep': ','}
'''


Funciones para aplicar las definiciones de configuración


Para aplicar las definiciones de una configuración regional utilizar las siguientes funciones:

import locale
locale.setlocale(locale.LC_ALL, '')

# Formatear números de acuerdo con la configuración
# actual de LC_NUMERIC
valor = 123456.78
locale.format('%10.2f', valor, grouping=True)   # '123.456,78'
locale.format('%10.2f', valor, grouping=False)  # ' 123456,78'

# Formatear procesando especificadores de formato %
locale.format_string('%i', valor, grouping=True)  # '123.456'
locale.format_string('%.1f', valor, grouping=True)  # '123.456,8'

# Formatear números de acuerdo con la 
# configuración LC_MONETARY actual.
locale.currency(valor, symbol=True, grouping=False)  # '123456,78 €'
# Establecer configuración para Reino Unido
locale.setlocale(locale.LC_ALL, 'en_IN') 
locale.currency(valor, symbol=True, grouping=False)  # '₹ 123456.78'
# Establecer configuración predeterminada
locale.setlocale(locale.LC_ALL, '') 

# Formatear flotantes como str pero con el carácter
# predeterminado para el punto decimal
locale.str(valor)  # '123456,78'

# Comparar dos cadenas de acuerdo con la 
# configuración LC_COLLATE actual
locale.strcoll('Guadalquivir', 'Guadalhorce')  # 10
locale.strcoll('Guadalquivir', 'Guadalupe')  # -4
locale.strcoll('Guadalupe', 'Guadalupe')  # 0

# Definir clave de ordenación de acuerdo a la
# configuración actual
cadenas = ['Guadalquivir', 'Guadalhorce', 'Guadalupe']
cadenas.sort(key=locale.strxfrm)
print(cadenas)  # ['Guadalhorce', 'Guadalquivir', 'Guadalupe']

# Convertir cadena en cadena numérica normaliza
# de acuerdo con LC_NUMERIC. 
# Disponible a partir de Python 3.5
cadena = '6543,21'
locale.delocalize(cadena)  # '6543.21'


Obtener información específica de configuración: locale.nl_langinfo()


La función nl_langinfo() retorna una cadena con información específica de la configuración regional actual. Esta función no está disponible en todos los sistemas y el conjunto de opciones posibles también puede variar entre plataformas.

import locale
from datetime import datetime

# Establecer configuración del entorno de usuario 
# En este ejemplo se corresponde con 'es_ES.utf8' (España)
locale.setlocale(locale.LC_ALL, '')

# Obtener codificación de los caracteres utilizada
codificacion = locale.nl_langinfo(locale.CODESET)  # 'UTF-8'

# En este caso la codificación se utiliza para
# convertir una cadena de tipo Unicode a Byte
pais = bytes("España", codificacion)
print(pais)  # b'Espa\xc3\xb1a' 
# El formato Byte acepta sólo caracteres ASCII y
# facilita la creación de archivos de texto.

# Obtener formato de fecha y hora 
formato1 = locale.nl_langinfo(locale.D_T_FMT)  # '%a %d %b %Y %T %Z'

# Obtener fecha y hora actual e imprimir utilizando
# el formato de fecha-hora de la configuración actual
hoy = datetime.today()
print(hoy.strftime(formato1)) # mié 26 abr 2017 17:24:42 

# Obtener formato de fecha e imprimir utilizando
# el formato de fecha de la configuración actual
formato2 = locale.nl_langinfo(locale.D_FMT)  # '%d/%m/%y'
print(hoy.strftime(formato2)) # 26/04/17

# Obtener formato de hora e imprimir utilizando
# el formato de hora de la configuración actual
formato3 = locale.nl_langinfo(locale.T_FMT)  # '%T'
print(hoy.strftime(formato3))  # 17:24:42

# Obtener cadena para representar la hora con 
# el formato AM/PM. 
# Si devuelve cadena vacía se expresa con 24 horas
formato4 = locale.nl_langinfo(locale.T_FMT_AMPM)  # ''

# Obtener nombre completo y abreviado de los días de 
# la semana en el idioma de la configuración actual
for dia in range(1, 8):
    nombre_dia = 'locale.DAY_' + str(dia)
    abrev_dia = 'locale.ABDAY_' + str(dia)
    print(nombre_dia, ':', 
          locale.nl_langinfo(eval(nombre_dia)),
          '-',
          locale.nl_langinfo(eval(abrev_dia)))

'''
Salida:

locale.DAY_1 : domingo - dom
locale.DAY_2 : lunes - lun
locale.DAY_3 : martes - mar
locale.DAY_4 : miércoles - mié
locale.DAY_5 : jueves - jue
locale.DAY_6 : viernes - vie
locale.DAY_7 : sábado - sáb
'''

# Obtener nombre completo y abreviado de los meses  
# en el idioma de la configuración actual
for mes in range(1, 13):
    nombre_mes = 'locale.MON_' + str(mes)
    abrev_mes = 'locale.ABMON_' + str(mes)
    print(nombre_mes, ':', 
          locale.nl_langinfo(eval(nombre_mes)),
          '-',
          locale.nl_langinfo(eval(abrev_mes)))   

'''
Salida:

locale.MON_1 : enero - ene
locale.MON_2 : febrero - feb
locale.MON_3 : marzo - mar
locale.MON_4 : abril - abr
locale.MON_5 : mayo - may
locale.MON_6 : junio - jun
locale.MON_7 : julio - jul
locale.MON_8 : agosto - ago
locale.MON_9 : septiembre - sep
locale.MON_10 : octubre - oct
locale.MON_11 : noviembre - nov
locale.MON_12 : diciembre - dic
'''

# Obtener carácter que se emplea en los números
# para separar la parte entera de la decimal de
# la configuración actual
locale.nl_langinfo(locale.RADIXCHAR)  # ','

# Obtener carácter separador de millares
locale.nl_langinfo(locale.THOUSEP)  # '.'

# Obtener expresión regular que se puede usar con la función regex
# para reconocer una respuesta positiva o negativa a una pregunta.
locale.nl_langinfo(locale.YESEXPR)  # '^[sSyY].*'
locale.nl_langinfo(locale.NOEXPR)  # '^[nN].*'

# Obtener símbolo monetario
# El signo '-' indica que el símbolo aparecerá delante del valor
# El signo '+' indica que el símbolo aparecerá detrás del valor
# El '.' indica que el símbolo aparecerá en la posición del
# punto o coma decimal
locale.nl_langinfo(locale.CRNCYSTR)  # '+€'


Funciones para obtener el idioma y la codificación


Las funciones siguientes permiten obtener el idioma y la codificación de las cadenas de texto de distintos modos:

import locale
locale.setlocale(locale.LC_ALL, '')

# Determinar configuración a partir de variable de entorno
locale.getdefaultlocale()  # ('es_ES', 'UTF-8')
locale.getdefaultlocale(['LANG'])  # ('es_ES', 'UTF-8')

# Obtener configuración del entorno local a partir de categoria
locale.getlocale()  #  ('es_ES', 'UTF-8')
locale.getlocale(locale.LC_NUMERIC)  # ('es_ES', 'UTF-8')

# Obtener codificación que se utiliza para representar
# las cadenas de texto
locale.getpreferredencoding()  # 'UTF-8'

# Obtener código de configuración regional normalizado
locale.normalize('es_ES')  #  'es_ES.ISO8859-1'

# Establecer configuración regional para la categoria indicada
locale.resetlocale(locale.LC_NUMERIC)



Ir al índice del tutorial de Python

viernes, 17 de marzo de 2017

Programar eventos para ejecutar tareas



El módulo sched


El módulo sched implementa un programador o planificador de eventos para ejecutar tareas después de que transcurra un tiempo específico, o bien, para ejecutar en un momento determinado (por ejemplo, en una fecha y hora concretas).

El módulo sched se basa en la clase scheduler  que tiene métodos para declarar un programador; definir, ejecutar y cancelar eventos; y para consultar información sobre eventos pendientes de ejecución.


Declarar un programador


Para declarar un programador se utiliza el método constructor scheduler() que tiene como primer argumento una función para obtener la hora actual -que por defecto es monotonic()- y una función para introducir los tiempos de espera -que de forma predeterminada es sleep()-. Las dos funciones son del módulo time:

sched.scheduler(timefunc=time.monotonic, delayfunc=time.sleep)

Se pueden utilizar otras funciones para los argumentos de scheduler() pero las dos deben trabajar con la misma escala temporal.

# Declarar un programador con los valores por defecto:

programador1 = sched.scheduler()


Programar eventos y poner en marcha el programador


Para programar los eventos se emplean dos métodos: enterabs() y enter(). Mientras que el primero define los eventos de un programador para que se ejecuten en un momento específico, el segundo establece la ejecución para después de un tiempo de espera.

# Definir un evento para ejecutar en un momento específico

scheduler.enterabs(time, priority, action, argument=(), kwargs={})

# Definir un evento para ejecutar después de un tiempo de espera

scheduler.enter(delay, priority, action, argument=(), kwargs={})

Los dos métodos tienen los siguientes argumentos:

  • time/delay: en entertabs() define el momento de ejecución del evento y en enter() el tiempo de espera. El valor expresado debe ser compatible con el valor devuelto por la función indicada como primer argumento del constructor scheduler().
  • priority: establece la prioridad de ejecución cuando dos o más eventos tienen el mismo momento de ejecución. Cuanto más bajo es el número mayor prioridad tiene.
  • action: es la función a ejecutar.
  • argument: normalmente, es una tupla con los valores de los argumentos de la función de action. Opcional.
  • kwargs: es un diccionario que se puede pasar como argumento de la función de action. Opcional.

Los métodos enterabs() y enter() devuelven un valor que se puede utilizar para cancelar con posterioridad un evento con el método cancel(), siempre que el evento no haya iniciado su ejecución o, dicho de otro modo, cuando el evento esté en cola esperando a ejecutarse. El método empty() se utiliza para conocer si quedan eventos pendientes. La clase scheduler también cuenta con el atributo queue, muy útil, para consultar información sobre los eventos pendientes de ejecución.

Después de definir los eventos hay que poner en marcha el programador con el método run(). Este método hace que el programador espere (valiéndose de la función de retardo especificada en el método constructor) a que los eventos se vayan ejecutando, uno a uno, en el tiempo establecido y hasta que no quede ninguno pendiente. El método run() espera la ejecución porque tiene el argumento blocking con el valor True, por defecto. Si el valor de este argumento es False se ejecutarán sólo los eventos que vencen inmediatamente (por ejemplo, eventos con tiempo de espera en 0), devolviendo run(), si existe, el momento límite de la siguiente ejecución programada. Este argumento está disponible a partir de Python 3.3.


Programar eventos para ejecutar en un momento determinado


En el siguiente ejemplo se declara un programador con dos eventos para ejecutar una tarea 1 segundo después de poner en marcha el programador y la misma tarea cinco segundos después. El programador se crea con la función time.time() que devuelve el tiempo expresado en segundos. (Cualquier fecha-hora se puede expresar en segundos y viceversa. Ver: El módulo time)

import sched
import time

# Función que es llamada por los eventos
def abrir_cerrar(estado):
    print('TIEMPO:', int(time.time()))
    if estado:
        print("Abriendo compuertas...")
    else:
        print("Cerrando compuertas...")

# Declarar el programador
programador = sched.scheduler(time.time, time.sleep)

# Asignar el tiempo de comienzo (en segundos)
comienzo = int(time.time())
t1 = comienzo + 1   # Tiempo para abrir compuertas (1")
t2 = t1 + 4         # Tiempo para cerrar compuertas (5")
print('PROGRAMADOR INICIADO:', comienzo)

# Definir los dos eventos indicando: momento de ejecución, 
# prioridad, función a la que se llama y el valor que se
# pasa al argumento de la función:
programador.enterabs(t1, 1, abrir_cerrar, (1,))
programador.enterabs(t2, 1, abrir_cerrar, (0,))

# Poner en marcha el programador.
# El programa permanece a la espera hasta que se
# ejecuten los dos eventos. La ejecución se producirá
# cuando se alcance el momento definido.
programador.run()
print('PROGRAMADOR FINALIZADO:', int(time.time()))

'''
Salida:

PROGRAMADOR INICIADO: 1489659568
TIEMPO: 1489659569
Abriendo compuertas...
TIEMPO: 1489659573
Cerrando compuertas...
PROGRAMADOR FINALIZADO: 1489659573
'''


Programar eventos con posibilidad de cancelación


A continuación, un ejemplo que declara un programador con dos eventos de las mismas características que en el ejemplo anterior. En este caso se utiliza el valor de retorno del método enterabs() para cancelar el segundo evento cuando el número aleatorio devuelto por la función randint() es impar.

import sched
import time
import random

# Función que es llamada por los eventos
def abrir_cerrar(estado, mantener):
    print('TIEMPO:', int(time.time()))
    if mantener and not programador.empty():
        programador.cancel(ev2)
        
    if estado:
        print("Abriendo compuertas...")
    else:
        print("Cerrando compuertas...")
    
# Obtener un número aleatorio entre 1 y 5
# Si el valor es impar: no cerrar compuertas
valor = random.randint(1, 5)
if valor % 2:
    print("No mantener compuertas abiertas")
    mantener_abiertas = False
else:
    print("Mantener compuertas abiertas")   
    mantener_abiertas = True

# Declarar el programador
programador = sched.scheduler(time.time, time.sleep)

# Asignar el tiempo de comienzo (en segundos)
comienzo = int(time.time())
t1 = comienzo + 1   # Tiempo para abrir compuertas
t2 = t1 + 4         # Tiempo para cerrar compuertas
print('PROGRAMADOR INICIADO:', comienzo)

# Definir los dos eventos indicando: momento de ejecución, 
# prioridad, función a la que se llama y valores que se
# pasan a los argumentos de la función:
ev1 = programador.enterabs(t1, 1, abrir_cerrar, 
                           (1, mantener_abiertas))
ev2 = programador.enterabs(t2, 1, abrir_cerrar, 
                           (0, mantener_abiertas))

# Poner en marcha el programador.
# El programa permanece a la espera hasta que se
# ejecuten los dos eventos. La ejecución se producirá
# cuando se alcance el momento definido.
programador.run()
print('PROGRAMADOR FINALIZADO:', int(time.time()))

'''
Salidas posibles:

1) Si el número devuelto por randint() es par

No mantener compuertas abiertas
PROGRAMADOR INICIADO: 1489659900
TIEMPO: 1489659901
Abriendo compuertas...
TIEMPO: 1489659905
Cerrando compuertas...
PROGRAMADOR FINALIZADO: 1489659905

2) Si el número devuelto por randint() es impar

Mantener compuertas abiertas
PROGRAMADOR INICIADO: 1489659972
TIEMPO: 1489659973
Abriendo compuertas...
PROGRAMADOR FINALIZADO: 1489659973

'''


Programar eventos para ejecutar después de un tiempo de espera


Seguidamente otro ejemplo con un programador con tres eventos para ejecutar la misma tarea después de esperar 1, 2 y 3 segundos, respectivamente. El programador se crea con time.monotonic() que es la función que utiliza por defecto el constructor, que devuelve el tiempo expresado en segundos. En este caso el primer argumento de la función enter() representa el valor del tiempo de espera que utiliza la función sleep() en el constructor.

import sched
import time

def tarea(nombre, comienzo):
    ahora = time.time()
    diferencia = int(ahora - comienzo)
    print('MOMENTO :', int(ahora), 
          'Diferencia:', diferencia, 'segundos', 
          'Tarea:', nombre)

programador = sched.scheduler()
comienzo = time.time()
print('COMIENZO:', comienzo)

programador.enter(1, 1, tarea, ('TAREA_1', comienzo))
programador.enter(2, 1, tarea, ('TAREA_2', comienzo))
programador.enter(3, 1, tarea, ('TAREA_3', comienzo))
programador.run()

print('FINAL   :', time.time())

'''
Salida:

COMIENZO: 1489579919.1849022
MOMENTO : 1489579920 Diferencia: 1 segundos Tarea: TAREA_1
MOMENTO : 1489579921 Diferencia: 2 segundos Tarea: TAREA_2
MOMENTO : 1489579922 Diferencia: 3 segundos Tarea: TAREA_3
FINAL   : 1489579922.1852293
'''


Programar eventos para ejecutar considerando la prioridad


En el siguiente ejemplo se programan tres eventos para ejecutar la misma tarea. Uno de ellos, la ejecuta después de 1 segundo de espera y los otros dos eventos como tienen el mismo tiempo de espera (3 segundos) utilizan el argumento de la prioridad para establecer el orden de ejecución. En este caso el tercero que tiene una prioridad más alta se ejecuta antes que el segundo.

El programador se crea con la función time.time() y la función tiempo() se utiliza para 'humanizar' la salida del tiempo que se expresa en esta ocasión con el siguiente formato de fecha/hora: '%d-%m-%Y %H:%M:%S'.

import sched
import time

def tiempo():
    formato = '%d-%m-%Y %H:%M:%S'
    
    # Obtiene fecha/hora local como tupla struct_time    
    st_tiempo = time.localtime()
    
    # Convierte fecha/hora a segundos
    tiempo = time.mktime(st_tiempo)
    
    # Convierte fecha/hora a cadena
    str_tiempo = time.strftime(formato, st_tiempo)
    return tiempo, str_tiempo    

def tarea(nombre, comienzo):
    ahora, str_ahora = tiempo()
    diferencia = int(ahora - comienzo)
    print('MOMENTO :', str_ahora, 
          'Diferencia:', diferencia, 'segundos', 
          'Tarea:', nombre)

programador = sched.scheduler(time.time, time.sleep)
comienzo, str_comienzo = tiempo()
print('COMIENZO:', str_comienzo)

programador.enter(1, 1, tarea, ('TAREA_1', comienzo))
programador.enter(3, 2, tarea, ('TAREA_2', comienzo))
programador.enter(3, 1, tarea, ('TAREA_3', comienzo))
programador.run()

final, str_final = tiempo()
print('FINAL   :', str_final)

'''
Salida:

COMIENZO: 15-03-2017 11:36:07
MOMENTO : 15-03-2017 11:36:08 Diferencia: 1 segundos Tarea: TAREA_1
MOMENTO : 15-03-2017 11:36:10 Diferencia: 3 segundos Tarea: TAREA_3
MOMENTO : 15-03-2017 11:36:10 Diferencia: 3 segundos Tarea: TAREA_2
FINAL   : 15-03-2017 11:36:10
'''


Programar eventos para ejecutar controlando el bloqueo


Para finalizar, un ejemplo en el que se utiliza el argumento blocking del método run() para bloquear el programa para que espere a que finalice la ejecución de todos los eventos o, bien, para que se ejecuten sólo los eventos de ejecución inmediata (son aquellos que tienen el valor 0 en el tiempo de espera.

En este caso se utiliza también la función randint() para generar un número aleatorio. Si el número obtenido es par blocking toma el valor True y el programa espera la ejecución de todos los eventos. Si el número es impar sólo se ejecutan los eventos inmediatos.

El programador se crea con la función time.time() y la función tiempo() se utiliza para 'humanizar' la salida del tiempo que se expresa en esta ocasión con el siguiente formato de fecha/hora: '%d-%m-%Y %H:%M:%S'.

import sched
import time
import random

def tiempo():
    formato = '%d-%m-%Y %H:%M:%S'
    
    # Obtiene fecha/hora local como tupla struct_time    
    st_tiempo = time.localtime()
    
    # Convierte fecha/hora a segundos
    tiempo = time.mktime(st_tiempo)
    
    # Convierte fecha/hora a cadena
    str_tiempo = time.strftime(formato, st_tiempo)
    return tiempo, str_tiempo    

def tarea(nombre, comienzo):
    ahora, str_ahora = tiempo()
    diferencia = int(ahora - comienzo)
    print('MOMENTO :', str_ahora, 
          'Diferencia:', diferencia, 'segundos', 
          'Tarea:', nombre)

# Obtener un número aleatorio entre 1 y 5
# Si el valor es impar: ejecutar todos los eventos
valor = random.randint(1, 5)
if valor % 2:
    print("Ejecutar todos los eventos")
    bloquear = True
else:
    print("Ejecutar sólo eventos inmediatos")   
    bloquear = False

programador = sched.scheduler(time.time, time.sleep)
comienzo, str_comienzo = tiempo()
print('COMIENZO:', str_comienzo)

programador.enter(0, 1, tarea, ('TAREA_1', comienzo))
programador.enter(0, 1, tarea, ('TAREA_2', comienzo))
programador.enter(3, 1, tarea, ('TAREA_3', comienzo))
programador.run(blocking=bloquear)

final, str_final = tiempo()
print('FINAL   :', str_final)

'''
Salidas posibles:

1) Si el número devuelto por randint() es impar:

Ejecutar todos los eventos
COMIENZO: 16-03-2017 12:13:44
MOMENTO : 16-03-2017 12:13:44 Diferencia: 0 segundos Tarea: TAREA_1
MOMENTO : 16-03-2017 12:13:44 Diferencia: 0 segundos Tarea: TAREA_2
MOMENTO : 16-03-2017 12:13:47 Diferencia: 3 segundos Tarea: TAREA_3
FINAL   : 16-03-2017 12:13:47

2) Si el número devuelto por randint() es par:

Ejecutar sólo eventos inmediatos
COMIENZO: 16-03-2017 12:14:11
MOMENTO : 16-03-2017 12:14:11 Diferencia: 0 segundos Tarea: TAREA_1
MOMENTO : 16-03-2017 12:14:11 Diferencia: 0 segundos Tarea: TAREA_2
FINAL   : 16-03-2017 12:14:11
'''



Ir al índice del tutorial de Python