1

I'm unsure again with something what I'm sure is going to be very simple...

Basically, I'm trying to make my first script call/execute a bunch of other scripts but the problem is I want each individual script to contain its own functions not be called from the first secript...

First script/main script:

from datetime import date, timedelta
from sched import scheduler
from time import time, sleep, strftime
import random

s = scheduler(time, sleep)
random.seed()

def periodically(runtime, intsmall, intlarge, function):
     ## Get current time
    currenttime = strftime('%H:%M:%S')

    ## If currenttime is anywhere between 23:40 and 23:50 then...
    if currenttime > '23:40:00' and currenttime < '23:50:00':
        ## Call clear
        clear()
        ## Update time
        currenttime = strftime('%H:%M:%S')

    ## Idle time
    while currenttime > '23:40:00' and currenttime < '23:59:59' or currenttime >= '00:00:00' and currenttime < '01:30:00':
        ## Update time
        currenttime = strftime('%H:%M:%S')

    runtime += random.randrange(intsmall, intlarge)
    s.enter(runtime, 1, function, ())
    s.run()

def callscripts():
    print "Calling Functions"

    errors = open('ERROR(S).txt', 'a')
    try: 
        execfile("data/secondary.py")
    except Exception as e:
        errors.write(str(e))
        errors.write("""
""")         
    errors.close()


while True:
    periodically(2, -1, +1, callscripts)

Below is secondary.py

import win32con
from win32api import *
from win32gui import *

class WindowsBalloonTip:
    def __init__(self, title, msg):
        message_map = { win32con.WM_DESTROY: self.OnDestroy,}

        # Register the window class.
        wc = WNDCLASS()
        hinst = wc.hInstance = GetModuleHandle(None)
        wc.lpszClassName = 'PythonTaskbar'
        wc.lpfnWndProc = message_map # could also specify a wndproc.
        classAtom = RegisterClass(wc)

        # Create the window.
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        self.hwnd = CreateWindow(classAtom, "Taskbar", style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, hinst, None)
        UpdateWindow(self.hwnd)

        # Icons managment
        iconPathName = "icon1.ico" ## LOCATION TO THE ICON FILE
        icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
        try:
            hicon = LoadImage(hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags)
        except:
            hicon = LoadIcon(0, win32con.IDI_APPLICATION)
        flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
        nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, 'Tooltip')

        # Notify
        Shell_NotifyIcon(NIM_ADD, nid)
        Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, win32con.WM_USER+20, hicon, 'Balloon Tooltip', msg, 200, title))
        # self.show_balloon(title, msg)
        sleep(5)

        # Destroy
        DestroyWindow(self.hwnd)
        classAtom = UnregisterClass(classAtom, hinst)
    def OnDestroy(self, hwnd, msg, wparam, lparam):
        nid = (self.hwnd, 0)
        Shell_NotifyIcon(NIM_DELETE, nid)
        PostQuitMessage(0) # Terminate the app.

# Function
def balloon_tip(title, msg):
    w=WindowsBalloonTip(title, msg)


balloon_tip("test test", "Running")

def hi():
    print "hi"

hi()

Error:

global name 'WindowsBalloonTip' is not defined

Full Error:

Traceback (most recent call last):
  File "C:\Main.py", line 48, in <module>
    periodically(2, -1, +1, callscripts)
  File "C:\Main.py", line 27, in periodically
    s.run()
  File "C:\Python27\lib\sched.py", line 117, in run
    action(*argument)
  File "Main.py", line 34, in callscripts
    execfile("data/secondary.py")
  File "data/secondary.py", line 93, in <module>
    balloon_tip("test test", "Running")
  File "data/secondary.py", line 78, in balloon_tip
    w=WindowsBalloonTip(title, msg)
NameError: global name 'WindowsBalloonTip' is not defined

How would I go about fixing this?

Thanks in advance Hyflex

Ryflex
  • 5,559
  • 25
  • 79
  • 148
  • Please post the **full** error traceback - the information in it is intended to help debug your code, so you should post it. – Brionius Aug 15 '13 at 20:48
  • That is the full error traceback from the file, if you run the secondary.py on its own it runs and works fine but if you run it via the main.py as intended it fails to work. The two scripts above can recreate the error perfectly. – Ryflex Aug 15 '13 at 20:59
  • Can't really determine the problem without a traceback. But I think you might want to look into how to write modules and use the import statement. Rather than defining functions in a file, and calling those functions at module level you should should just import your module from within your first script like `import secondary` and call functions in it like `secondary.balloon_tip()`. This is the correct way to write modular code. – Iguananaut Aug 15 '13 at 21:00
  • 1
    @Hyflex the reason that's the only output you get is because in the `except` statement in your primary script you're catching all exceptions and printing them. Instead modify that except block to re-raise the exception (with `raise`) and you'll get a full traceback. – Iguananaut Aug 15 '13 at 21:02
  • @Iguananaut Thanks for your reply, I am wanting to be able to call each "module" as shown because each of those modules has many different functions in it. My example uses `balloon_tip("test test", "Running")` and `hi()` but in my actual code each module is already set out to run a series of functions. How would I be able to call the script on its own to run as it is without redefining each individual function... – Ryflex Aug 15 '13 at 22:57
  • @Iguananaut I've edited my first post to include the full error as you explained in your second comment. – Ryflex Aug 15 '13 at 22:58
  • @Hyflex I think you need to read about modules and the import statement in Python: http://docs.python.org/2/tutorial/modules.html – Iguananaut Aug 16 '13 at 13:13
  • @Hyflex What you're showing is not your actual code. In your first script it reads `execfile("data/secondary.py")` But in the traceback it shows `execfile("secondary.py")`. Also there's really no reason this should happen if you're calling `balloon_top` before `WindowsBalloonTip` is defined. – Iguananaut Aug 16 '13 at 13:20
  • @Iguananaut The issue with doing in that format is calling each individual part of the python code when within each "module" there are functions which interact with one another. Ignore the fact one shows it coming from the folder /data that was me trying to play around with the code getting stuff to work and yes, exactly it shouldn't happen but it is and I have no idea why. – Ryflex Aug 16 '13 at 15:08
  • Does `secondary.py` work when run manually, or do you get the same exception? – Blckknght Aug 17 '13 at 22:59
  • @Blckknghb running secondary on its own manually does NOT create an exception. – Ryflex Aug 18 '13 at 00:05

2 Answers2

3

First of all,

class WindowsBalloonTip:

should be

class WindowsBalloonTip(object):

because the former is an old style class, which has disappeared in Python 3 and is in recent versions of Python 2.x only for backwards compatibility.

Ethan's answer is correct, but probably unclear to you if you're asking this question. A full explanation is here.

When ballon_tip() is run, it first searches the local namespace -- balloon_tip()'s namespace -- for something called WindowsBalloonTip. When it can't find it, it searches the global namespace. Since you didn't provide anything to the globals parameter to execfile(), it defaults to the namespace of callscripts(), which doesn't have anything named WindowsBaloonTip inside of it, and errors.

To fix this, you can pass globals() as an argument to execfile, but this will pollute the global namespace of your main script, which you probably don't want. You can also declare everything inside of secondary.py to be global, but you probably don't want to do that since the whole point is to test secondary.py.

The issue is execfile. execfile is an ugly, hack-y way to do things. import is better. One solution would be to write something like this inside secondary.py:

def test_harness():
    balloon_tip("test test", "Running")
    hi()

then, import secondary, traceback inside your main script, and change callscripts() like this:

def callscripts():
        print "Calling Functions"
        errors = open("ERRORS(S).txt", "a")

        try:
            secondary.test_harness()
        except:
            errors.write(traceback.format_exc() + '\n')

EDIT in response to comment: At the top of your script, import traceback, then:

def callscripts():
        print "Calling Functions"
        errors = open("ERRORS(S).txt", "a")

        try:
            execfile("data/secondary.py", {})
        except:
            errors.write(traceback.format_exc() + '\n')
Community
  • 1
  • 1
Patrick Collins
  • 10,306
  • 5
  • 30
  • 69
  • Thanks for your very detailed response, the only issue with doing this is I'm still having to effectively create a function to run a series of functions... Ideally I want to be able to manually run secondary.py and it to instantly yeild results and then if ran via the main.py for it to again yeild the same results without having to specifically call certain functions within the import... Does that make sense? – Ryflex Aug 18 '13 at 14:09
  • 1
    Try `execfile(script_path, {})`. It works on my local machine. – Patrick Collins Aug 18 '13 at 14:27
  • how would I be able to use that execfile method you just mentioned with the same sort of traceback writing as your previous answer as a full traceback would be better. – Ryflex Aug 18 '13 at 15:10
  • 1
    I edited my reply to show the full function, see above. `traceback.format_exc()` gets you the full stack trace, calling `execfile(script_path, {})` gives the file an empty `globals()` to work with. – Patrick Collins Aug 18 '13 at 15:15
  • Thank you ever so much, both options work great with the latter working the best. I've awarded you your points and upvoted you everywhere in this post :) – Ryflex Aug 18 '13 at 16:37
1

The problem is execfile.

Basically, execfile is a fancy exec with a file. So when you call execfile 'secondary.py' Python executes all the lines of secondary.py in the context of where execfile was called. Which, in this case, is inside the callscripts function.

What you probably want is subprocess.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • Are you able to give me an example of how to use subprocess in these circumstances; to work with my code listed above? – Ryflex Aug 18 '13 at 14:06