Environment variables

1

I need to create an environment variable to share information between two Python scripts.

Let me explain, I have created three scripts in Python to check the operation or rather verify that the environment variable is created. These files are:

blink.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time, os,sys
GPIO.setmode(GPIO.BCM)
GPIO.setup(7, GPIO.OUT)

repeticion = 0
try:
    #os.system('export CONTADOR = "0"')
    os.environ["CONTADOR"] = str(repeticion)
except:
    print sys.exc_info()[0]

def blink():
    if os.path.isfile("stop_blink"):
        os.system("sudo rm stop_blink")
    print "Ejecucion iniciada..."
    global repeticion
    while not os.path.isfile("stop_blink"):
        GPIO.output(7, True) # Enciende el 7
        time.sleep(0.5) # Espera 1/2 segundo
        GPIO.output(7, False) # Apaga el 7
        time.sleep(0.5) # Espera 1/2 segundo
        if (repeticion>= 10000):
            repeticion= 0
        repeticion= repeticion+ 1
        print ("Vuelta " + str(repeticion))
        try:
            print ("Contador {}".format(os.environ.get("CONTADOR","No Funciona")))
        except:
            print sys.exc_info()[0]
        try:
            print ("El valor de cuenta es: {}".format(os.environ["CONTADOR"]))
        except:
            print sys.exc_info()[0]
        try:
            os.environ["CONTADOR"] = str(repeticion)
            #os.system('export CONTADOR = ' +str(repeticion))
        except:
            print sys.exc_info()[0]
    print "Ejecucion finalizada"
    os.environ["CONTADOR"] = "0"
    GPIO.cleanup() #limpieza de los GPIO

blink() #llamada a la funcion blink

consult.py

    #!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys

try:
    print ("1.- Contador {}".format(os.environ.get("CONTADOR","No Funciona")))
except:
    print "1.1.- ", sys.exc_info()[0]
try:
    print ("2.- El valor de cuenta es: {}".format(os.environ["CONTADOR"]))
except:
    print "2.1.- ", sys.exc_info()[0]

try:
    print ("3.- El valor de cuenta es: {}".format(os.environ.get("CONTADOR","No Funciona")))
except:
    print "3.1.- ", sys.exc_info()[0]
try:
    print ("4.- El valor de cuenta es: {}".format(os.getenv("CONTADOR")))
except:
    print "4.1.- ", sys.exc_info()[0]


if ("CONTADOR" in os.environ):
    print "Existe en 2.7"
if (os.environ.has_key("CONTADOR")):
    print "Existe en 3.1"

for param in os.environ.keys():
    print("%s: %s " % (param, os.environ[param])

parar_blink.py

    #!/usr/bin/python
# -*- coding: utf-8 -*-
import os

print ("Creando archivo stop_blink")
fichero = open ( 'stop_blink', 'w' ) 
fichero.write('Archivo de parada de blink') 
fichero.close()
print ("Archivo stop_blink creado")

The theoretical operation is:

  • The stop_blink.py script creates a file called stop_blink that of be found by the blink.py script this will exit the loop.
  • The blink.py script creates an environment variable called COUNTER and enters a loop that only stops when the file exists stop_blink The only thing this script does is activate and deactivate an output every 1/2 second and update the environment variable with a value that is the number of repeats of the loop.
  • The crypt query.py when launched should read the environment variable COUNTER and print its value (in the example, later it will be used to perform actions). All the code in this script are simply different ways to capture said variable and show the result. The final for what it does is show all the current variables.
  • In the way it is developed, the blink.py script correctly creates the environment variable and updates it, but since it was created from the script, the scope of it is only for the current process and its subprocesses, with what when launching the query.py script does not find this variable.

    The correct thing would be for this variable to be created in the /etc/profile that I understand, is executed at the beginning of the system independently of the user, or in the ~/.bash_profile that is the next to be executed or if it does not exist it would follow by ~/.bash_login and ~/.profile following this order and executing the orders of the first one that is found and is readable.

    The problem is that I have tried with all, and in none the query.py script finds that the environment variable was created, not even when including it in ~/.bashrc that would be the specific shell of the current user.

    I have tried writing the variable according to the examples that I have found Googling:

    Mode a):

    CONTADOR = "XX"
    export CONTADOR
    

    Mode b):

    export CONTADOR = "XX"
    

    C mode:)

    echo "export AS='name'" >> ~/.bash_profile
    

    In all cases I tried with and without source ~/.bash_profile after the variable to update the shell.

    I can think of an alternative to using the environment variable and is to create a text file, for example, in which the desired value is updated and accessed by the other script to collect that value, but this option does not It seems the most "decent", and I think it would consume many more resources than using a variable.

    Any suggestions? Am I trying to load the variable in the wrong place or incorrectly? Is there any other alternative to perform this action, maybe the two scripts can share data while running in different threads (I I do not know)?

    Or maybe I should raise it with another focus. Let me explain:

    What I intend to do is to have a Python script working in a continuous loop taking a series of measurements from sensors connected to the Raspberry pins and depending on these sensors, and other parameters, a certain action is performed on peripherals connected to other pins (that is, activate outputs).

    The inconvenience I have when I have to take values of measures that this script is handling for another script to do its work, I also need to pass a series of arbitrary values to the main script (entered either by the user, or by another script) .

    I had thought that the most efficient way were the environment variables, but I would appreciate any other approach that could give a little light to this black hole I'm in. The truth is that I do not know if it is possible and / or feasible for these scripts to be included in modules so that they work from the same parent thread, although the user's input is still behind me, logically the script can not stop waiting for the user , but it must be an interruption or something like that since this value is not constant.

        
    asked by gAb 03.03.2018 в 23:49
    source

    1 answer

    1

    I do not see the need to run the blink and the consulta in two separate processes. Both could be two threads of the same process, one executing the function blink() and the other executing another function (to be programmed) that we could call consultar() .

    Having two threads allows you to execute these two functions in parallel, but being within the same process you could share global variables, which will avoid the mess of the environment variable.

    First version, easy, contains a bug

    The first idea would therefore be the following: make the contador be a global variable, that the thread blink() is increasing while the thread consultar() is reading it, for example every 5 seconds. I also make a global variable parar that both threads query to know when to exit the loop (and the thread changes blink() when it detects the appropriate file).

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import RPi.GPIO as GPIO
    import time, os,sys
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(7, GPIO.OUT)
    
    contador = 0
    parar = False
    
    def blink():
        global contador
        global parar
    
        if os.path.isfile("stop_blink"):
            os.system("sudo rm stop_blink")
            parar = False
        print("Ejecucion iniciada...")
    
        while not parar:
            parar = os.path.isfile("stop_blink")
            GPIO.output(7, True) # Enciende el 7
            time.sleep(0.5) # Espera 1/2 segundo
            GPIO.output(7, False) # Apaga el 7
            time.sleep(0.5) # Espera 1/2 segundo
            contador = (contador + 1) % 10000
            print ("Vuelta " + str(contador))
        print("Ejecucion finalizada")
        contador = 0
        GPIO.cleanup() #limpieza de los GPIO
    
    def consultar():
        global contador
        global parar
        while not parar:
            time.sleep(5)  # Esperar 5 segundos
            print("El valor de cuenta es: {}".format(contador))
    
    # Función principal que arranca ambos hilos
    import threading
    hilo_blink = threading.Thread(target = blink)
    hilo_consultar = threading.Thread(target = consultar)
    
    hilo_blink.start()
    hilo_consultar.start()
    

    I recommend that you use python3 to run this program, since python2 does not allow to stop its execution with Ctrl-C because it is multithreaded (you would need to kill the process with kill ), whereas in python3 that aspect has been corrected.

    But as an announcement in the title, this solution has a bug . Which? Multi-threaded programming is delicate when threads share information through global variables, as in this case. Since each thread is executed independently and at the same time, it could be the case that both threads try to access the global variable at the same time. In some cases this can cause a problem, especially if both threads try to modify that variable.

    Think for example that two threads try to increase the counter at the same time. Since to modify the counter first it is read, then its new value is calculated and then the result is written into memory, if in the time between read and write another thread it also reads it, increases it and writes it, our later writing could overwrite the result of the other thread. Thus the variable instead of being increased by 2 (once per thread), would be increased only by 1.

    In your case there is no such problem because one thread only reads, while another only writes. However, if you want to do it right, the code gets complicated. The counter could no longer be a simple integer, but it would need to be an object with methods incrementar() and obtener_valor() , and these methods should include locks ( locks ) to prevent two threads from trying to enter the time in those methods.

    Good solution, with locks

    A "robust" counter to these problems could be the following:

    class Contador:
        def __init__(self, inicial=0, reiniciar_con = 10000):
            self.valor = inicial
            self.reiniciar_con = reiniciar_con
            self._cerrojo = threading.Lock()
    
        def incrementar(self):
            with self._cerrojo:
                self.valor = (self.valor + 1) % self.reiniciar_con
            return self.valor
    
        def obtener_valor(self):
            with self._cerrojo:
                return self.valor
    

    Now the global variable would no longer be contador = 0 , but:

    contador = Contador()
    

    To increase it from blink() it will be enough to do:

    contador.incrementar()
    

    And to consult it to print:

    print("El valor de cuenta es: {}".format(contador.obtener_valor()))
    
        
    answered by 15.03.2018 в 13:34