0
 import urllib.request
    from bs4 import BeautifulSoup
    import urllib
    import RPi.GPIO as GPIO
    import time
    import os
    import sys
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)

    light_1 = 5

    GPIO.setup(light_1, GPIO.OUT)

    GPIO.output(light_1, 0)

    req = urllib.request.urlopen("http://lapi.transitchicago.com/api/1.0/ttpositions.aspx?key=6b78297846g28427e4359b886742561183677876&rt=red,blue,G,pink,Brn,Org,P,Y")

    content = BeautifulSoup(req, 'xml')

    def Red(xml):
    train_1 = xml.find('rn')
    trainStation = xml.find('nextStaNm')
    trainApp = xml.find('isApp')
    direction = xml.find('destNm')


    if trainApp.text == '1':
    print('Train number '+ train_1.text + ' RED line train to',direction.text,'is now approching ' + trainStation.text)
    time.sleep(1)
    GPIO.output(light_1, True)
    else:
    print('There is no train arriving yet at '+ trainStation.text)
    GPIO.output(light_1, False)
    time.sleep(1)

    def Blue(xml):
    train_2 = xml.select_one('route[name="blue"] train rn').get_text()
    TrainStation = xml.select_one('route[name="blue"] train nextStaNm').get_text()
    TrainApp= xml.select_one('route[name="blue"] train isApp').get_text()
    direction= xml.select_one('route[name="blue"] train destNm').get_text()


    if TrainApp == '1':
    print('Train number '+ train_2 + ' BLUE line train to',direction,'is now 

    approching ' + TrainStation)

    else:
    print('There is no train arriving yet at '+ TrainStation)


    def Green(xml):
    train_3 = xml.select_one('route[name="g"] train rn').get_text()
    TrainStation = xml.select_one('route[name="g"] train nextStaNm').get_text()
    TrainApp= xml.select_one('route[name="g"] train isApp').get_text()
    direction= xml.select_one('route[name="g"] train destNm').get_text()

    if TrainApp == '1':
    print('Train number '+ train_3 + ' GREEN line train to',direction,'is now approching ' + TrainStation)

else:
print('There is no train arriving yet at '+ TrainStation)

def Pink(xml):
train_4 = xml.select_one('route[name="pink"] train rn').get_text()
TrainStation = xml.select_one('route[name="pink"] train nextStaNm').get_text()
TrainApp= xml.select_one('route[name="pink"] train isApp').get_text()
direction= xml.select_one('route[name="pink"] train destNm').get_text()

if TrainApp == '1':
print('Train number '+ train_4 + ' PINK line train to',direction,'is now approching ' + TrainStation)

else:
print('There is no train arriving yet at '+ TrainStation)

    def Brown(xml):
    train_5 = xml.select_one('route[name="brn"] train rn').get_text()
    TrainStation = xml.select_one('route[name="brn"] train 

    nextStaNm').get_text()
        TrainApp= xml.select_one('route[name="brn"] train isApp').get_text()
        direction= xml.select_one('route[name="brn"] train destNm').get_text()

        if TrainApp == '1':
        print('Train number '+ train_5 + ' BROWN line train to',direction,'is now approching ' + TrainStation)

    else:
    print('There is no train arriving yet at '+ TrainStation)


    def Orange(xml):
    train_6 = xml.select_one('route[name="org"] train rn').get_text()
    TrainStation = xml.select_one('route[name="org"] train nextStaNm').get_text()
    TrainApp= xml.select_one('route[name="org"] train isApp').get_text()
    direction= xml.select_one('route[name="org"] train destNm').get_text()

    if TrainApp == '1':
    print('Train number '+ train_6 + ' ORANGE line train to',direction,'is now approching ' + TrainStation)

    else:
    print('There is no train arriving yet at '+ TrainStation)


    def Purple(xml):
    train_7 = xml.select_one('route[name="p"] train rn').get_text()
    TrainStation = xml.select_one('route[name="p"] train nextStaNm').get_text()
    TrainApp= xml.select_one('route[name="p"] train isApp').get_text()
    direction= xml.select_one('route[name="p"] train destNm').get_text()

    if TrainApp == '1':
    print('Train number '+ train_7 + ' PURPLE line train to',direction,'is now approching ' + TrainStation)

    else:
    print('There is no train arriving yet at '+ TrainStation)

    def Yellow(xml):
    train_8 = xml.select_one('route[name="y"] train rn').get_text()
    TrainStation = xml.select_one('route[name="y"] train nextStaNm').get_text()
    TrainApp= xml.select_one('route[name="y"] train isApp').get_text()
    direction= xml.select_one('route[name="y"] train destNm').get_text()

    if TrainApp == '1':
    print('Train number '+ train_8 + ' YELLOW line train to',direction,'is now approching ' + TrainStation)

    else:
    print('There is no train arriving yet at '+ TrainStation)


    if __name__=='__main__':

    Red(content)
    Blue(content)
    Green(content)
    Pink(content)
    Brown(content)
    Orange(content)
    Purple(content)
    ## Yellow(content)
    os.execv(__file__, sys.argv)

My goal is to make this code self runnable and continue to update itself, so I figured this way would work (at the bottom) Yet when I run it, it gives me [Errno 13] Permisson denied... Not sure what went wrong. But if any one has a solution or another way to approach this, I am willing for other suggestions.

I just want this code to run by itself and continue to update. As if I were to be hitting run each time. Not a WHILE loop.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • To be able to exec a file it must have the execute bit set (`chmod +x your_file.py`). But that begs the question why are you doing it this way? Why not just wrap all this logic in a function and call it from a while loop (maybe with a delay after each call). That would then be self updating. – Bailey Parker Jan 19 '18 at 07:11
  • Also `CamelCase` is almost always reserved for class names in python. PEP8 encourages you to stick to `snake_case` for variables and function names. – Bailey Parker Jan 19 '18 at 07:12
  • I used the while loop but the outputs do not update. I am using an xml file that is updating every minute and my plan is to run the script once and see the shell run the program and continue to restart and run again with new data – Daniel Palacios Jan 19 '18 at 07:13
  • What do you mean by "the outputs do not update"? If I had to make a guess, you didn't put the urllib request inside the loop (so you were running your functions on the same `req`--which obviously will give you the same results) – Bailey Parker Jan 19 '18 at 07:16
  • When I run the program, The shell only runs once. My plan is for it to run once, restart, then run again automatically with new data – Daniel Palacios Jan 19 '18 at 07:19
  • You should probably change your API key too if that's supposed to be private, because you just shared that with the world ;) – Bailey Parker Jan 19 '18 at 07:19
  • and for it to continue to do that – Daniel Palacios Jan 19 '18 at 07:19

1 Answers1

0

To be able to exec a file it must have the execute bit set (chmod +x your_file.py). Also, since it is a script, you'll need to make the first line of your script a shebang:

#!/usr/bin/env python3

As I mentioned in the comments, you really shouldn't need to be exec-ing yourself. What you should probably be doing is something like this:

from time import sleep

if __name__ == '__main__':
    while True:
        xml = request_train_status()
        print(get_red_status(xml))
        print(get_blue_status(xml))
        # etc.
        sleep(1) # Don't constantly slam the API with requests

Had a few minutes so I refactored your code a tad. You had a lot of duplicated logic. Made it very difficult to reason about what was happening when and how you could repeat your process. I pulled out the GPIO stuff, request to the API, and pulling the status out of the XML into separate functions. I also introduced an Enum for line colors and namedtuple for the status to make things more obvious:

from collections import namedtuple
from enum import Enum
import RPi.GPIO as GPIO
from time import sleep
from urllib import request


LIGHT_1 = 5


def setup_gpio():
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)

    GPIO.setup(LIGHT_1, GPIO.OUT)
    GPIO.output(LIGHT_1, 0)


def set_light(on):
    GPIO.output(LIGHT_1, on)


def request_train_status():
    req = request.urlopen('http://lapi.transitchicago.com/api/1.0/' +
                          'ttpositions.aspx?key=' +
                          '5c78297a2f28427e9b87435118367766&' +
                          'rt=red,blue,G,pink,Brn,Org,P,Y')

    return BeautifulSoup(req, 'xml')


class Line(Enum):
    BLUE = 'blue'
    GREEN = 'g'
    PINK = 'pink'
    BROWN = 'brn'
    ORANGE = 'org'
    PURPLE = 'p'
    YELLOW = 'y'


LineStatus = namedtuple('LineStatus', ('approaching', 'num', 'station',
                                       'direction'))


def get_line_status(xml, line):
    color = line.value
    train = xml.select_one(f'route[name="{color}"] train')

    approaching = train.select_one('isApp').get_text() == '1'
    num = train.select_one('rn').get_text()
    station = train.select_one('nextStaNm').get_text()
    direction = train.select_one('destNm').get_text()

    return LineStatus(approaching, num, station, direction)


def print_line_status(status):
    if status.approaching:
        print(f'Train number {status.num} {color.name} line train to ' +
              f'{status.direction} is now approching f{status.station}')
    else:
        print(f'There is no train arriving yet at {status.station}')


def main():
    while True:
        xml = request_train_status()

        for line in Line:
            status = get_line_status(xml, line)
            print_line_status(status)

            if line == Line.RED:
                set_light(status.approaching)

        sleep(5)


if __name__=='__main__':
    main()

Obviously, I can't test this, but something like the above should definitely work (print new data every 5 seconds, give or take the API response time).

Bailey Parker
  • 15,599
  • 5
  • 53
  • 91
  • Yeah, I got that part. I run my script and it only runs the program once... doesnt seem to run again with new data... Which is what I am trying to get. – Daniel Palacios Jan 19 '18 at 07:17
  • Yes and don't use exec for this. Wrap it in a while loop, I'd recommend structuring your code in the way I described. This way you reuse a lot of your logic. In fact give me a moment and I can rewrite your script in about 10 lines that will be much easier to reason about (w/ respect to this problem) – Bailey Parker Jan 19 '18 at 07:22
  • Alright take a look at my refactoring. As you can see in `main()` you should just be able to run the same steps (request XML from api, for each line get and print the status, set the light for the RED line) in a while loop with a little bit of delay. – Bailey Parker Jan 19 '18 at 07:44
  • so in the get_line_status function, should I repeat those same lines for the other trains I am requesting? – Daniel Palacios Jan 19 '18 at 07:51
  • Nope! Notice how in `main()` we do `for line in Line:`. This means `get_line_status()` will get called for each line color (see the `Line` enum; it's `BLUE`, `GREEN`, `PINK`, etc.). The nice thing about this approach is if you need to change how you get the station name, for example, you don't have modify it for each of the lines (you only have to modify it once). – Bailey Parker Jan 19 '18 at 07:53
  • Oh sweet! Yeah that totally makes sense!. Thank you for taking your time do help me out on this! its been bugging me for a while. – Daniel Palacios Jan 19 '18 at 07:55
  • No problem! Good luck – Bailey Parker Jan 19 '18 at 08:14
  • one last thing... I've noticed you put 'f' in some lines... Whats that about? – Daniel Palacios Jan 19 '18 at 08:20
  • Those are [f strings](https://cito.github.io/blog/f-strings/) (new in Python 3.6). tl;dr `f'Train number {num} is late' == "Train number {} is late".format(num) == 'Train number ' + str(num) + ' is late'`. But you should never do the last one. Before Python 3.6, [use the `.format()` function on strings](https://docs.python.org/3.4/library/string.html#format-examples). – Bailey Parker Jan 19 '18 at 08:23
  • This .Value attribute confuses me... I would understand that python wont know that object... therefore wont go through all the Line's in the class – Daniel Palacios Jan 19 '18 at 16:43
  • `line` is just a member of the `Line` enum. Try this: `python3` and then just paste in the `class Line(Enum): #...` declaration. Now try: `print(Line.ORANGE)`, `print(Line.ORANGE.name)`, and `print(Line.ORANGE.value)`. `name` will give you the name of the member (the name of `ORANGE` is `'ORANGE'`) and `value` will give it's value (the value of `ORANGE` is `'org'`). See: https://docs.python.org/3/library/enum.html – Bailey Parker Jan 19 '18 at 22:47