0

Is there an easy way to execute time delay (like time.sleep(3)) between every statement of Python code without having to explicitly write between every statement?

Like in the below Python Script which performs certain action on SAP GUI window. Sometimes, the script continues to the next statement before the previous statement is complete. So, I had to add a time delay between every statement so that it executes correctly. It is working with time delay, but I end up adding time.sleep(3) between every line. Just wondering if there is a better way?

import win32com.client
import time

sapgui = win32com.client.GetObject("SAPGUI").GetScriptingEngine
session = sapgui.FindById("ses[0]")

def add_record(employee_num, start_date, comp_code):
    try:
        time.sleep(3)
        session.findById("wnd[0]/tbar[0]/okcd").text = "/npa40"
        time.sleep(3)
        session.findById("wnd[0]").sendVKey(0)
        time.sleep(3)
        session.findById("wnd[0]/usr/ctxtRP50G-PERNR").text = employee_num
        time.sleep(3)
        session.findById("wnd[0]").sendVKey(0)
        time.sleep(3)
        session.findById("wnd[0]/usr/ctxtRP50G-EINDA").text = start_date
        time.sleep(3)
        session.findById("wnd[0]/usr/tblSAPMP50ATC_MENU_EVENT/ctxtRP50G-WERKS[1,0]").text = comp_code
        time.sleep(3)
        session.findById("wnd[0]/usr/tblSAPMP50ATC_MENU_EVENT/ctxtRP50G-PERSG[2,0]").text = "1"
        time.sleep(3)
        session.findById("wnd[0]/usr/tblSAPMP50ATC_MENU_EVENT/ctxtRP50G-PERSK[3,0]").text = "U1"
        time.sleep(3)
        session.findById("wnd[0]/usr/tblSAPMP50ATC_MENU_EVENT").getAbsoluteRow(0).selected = True
        time.sleep(3)
        return "Pass"
    except:
        return "failed"
Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
spchu
  • 29
  • 7
  • 6
    What do you consider to be a "step"? – roganjosh Aug 15 '18 at 19:40
  • Have you considered using a Python debugger instead? `pdb` is in the Python standard library. It's [well documented](https://docs.python.org/3/library/pdb.html) and has the ability to step through a program one line at a time. – Daniel Pryden Aug 15 '18 at 19:46
  • If you want to step through the lines of code, use a debugger. There is one built in called `pdb`. – Klaus D. Aug 15 '18 at 19:47
  • 1
    corrected, every line of code – spchu Aug 15 '18 at 19:47
  • 2
    You would be surprised how many steps that would actually be just for something that looks very simple on the surface like an assignment. – Klaus D. Aug 15 '18 at 19:49
  • It is a Python Script which performs certain action on my computer. Sometimes, the script continues to next step before the previous step is fully complete. So, I want to add time delay at every step so that it executes correctly. It is working with time delay, but I will end up adding 'time.sleep(2)' at every line. Just wondering if there is a better way? – spchu Aug 15 '18 at 19:52
  • 2
    Thank you for telling us the X of your X Y problem. There should be a better way to do that. Please show us some of your troubling code! – Klaus D. Aug 15 '18 at 19:54
  • Every line, or every statement? For example, if you write a two-line expression, like `result = function_with_lots_of_args(arg1, arg2, arg3,,` and then `arg4, arg5, arg6)` on the next line, where should the sleep happen? – abarnert Aug 15 '18 at 19:55
  • @chundu: I agree with Klaus D., you need to show us some code -- there certainly is a better way. The general principle is to actually wait for whatever postcondition needs to be true for each step before starting the next one. I seriously doubt that you want to sleep *exactly* 2 seconds each time: instead you want to sleep "until it's ready" -- and you need to define what "it's ready" means for your program. – Daniel Pryden Aug 15 '18 at 19:56
  • 1
    Also, @chundu: Could you [edit] the information from your most recent comment into your question? Without it, the question is easily misunderstood, and most of the answers you've received so far aren't really addressing your real problem as a result. – Daniel Pryden Aug 15 '18 at 19:59
  • @chundu that can't happen unless you're using threads or multiple processes – roganjosh Aug 15 '18 at 20:00
  • @roganjosh Or using an event loop where the "step" is just starting a coroutine or starting an `after` function that does part of the work and calls `after` to continue, or similar things. (In which case sleeping is even worse than in a multithreading or multiprocessing program…). – abarnert Aug 15 '18 at 20:09
  • @abarnert then I have something to learn too, but I have a feeling we're over thinking the issue and that the OP has misdiagnosed. I could well be wrong, though :) – roganjosh Aug 15 '18 at 20:14
  • @roganjosh Unless the problem is just a matter of `flush`ing a file or something, I'm pretty sure the real problem is waiting on some "background" task; the question is whether that's a thread or process, or a job sent to a thread or process or asyncio pool, or an undifferentiated chunk of a thread or process's work, or a chain of `after`-style callbacks on an async or GUI event loop, or… – abarnert Aug 15 '18 at 20:19
  • In your edited question, what is that `session`? If whatever library it comes from has some way to wait for something to finish, that's what you want to do. But without any idea of what library that is, it's impossible to give you a more detailed suggestion. – abarnert Aug 15 '18 at 20:22
  • @abarnert updated – spchu Aug 15 '18 at 20:35
  • @chundu: If your code is automating a UI task like sending a key (which is what it appears to be doing, although I don't know anything about the SAP GUI), then simply sending the keystroke doesn't mean that the corresponding operation has completed. Most likely there is something visible that happens in the GUI to indicate that the operation is complete, and what you should be doing is waiting for that to happen. Or perhaps you shouldn't be trying to script a GUI at all, and should instead be using some lower-level API? Unfortunately I don't know enough about SAP to actually answer that. – Daniel Pryden Aug 15 '18 at 20:44

4 Answers4

3

The right way to do what you asked for is almost certainly to use the debugger, pdb.

The right way to do what you want is probably something completely different: find some signal that tells you that the step is done, and wait for that signal. With problems like this, almost any time you pick will be way, way too long 99% of the time, but still too short 1% of the time. That signal may be joining a thread, or waiting on a (threading or multiprocessing) Condition, or getting from a queue, or awaiting a coroutine or future, or setting the sync flag on an AppleEvent, or… It really depends on what you're doing.


But if you really want to do this, you can use settrace:

def sleeper(frame, event, arg):
    if event == 'line':
        time.sleep(2)
    return sleeper

sys.settrace(sleeper)

One small problem is that the notion of line used by the interpreter may well not be what you want. Briefly, a 'line' trace event is triggered whenever the ceval loop jumps to a different lnotab entry (see lnotab_notes.txt in the source to understand what that means—and you'll probably need at least a passing understanding of how bytecode is interpreted, at least from reading over the dis docs, to understand that). So, for example, a multiline expression is a single line; the line of a with statement may appear twice, etc.1


And there's probably an even bigger problem.

Sometimes, the script continues to next step before the previous step is fully complete.

I don't know what those steps are, but if you put the whole thread to sleep for 2 seconds, there's a good chance the step you're waiting for won't make any progress, because the thread is asleep. (For example, you're not looping through any async or GUI event loops, because you're doing nothing at all.) If so, then after 2 seconds, it'll still be just as incomplete as it was before, and you'll have wasted 2 seconds for nothing.


1. If your notion of "line" is closer to what's described in the reference docs on lexing and parsing Python, you could create an import hook that walks the AST and adds an expression statement with a Call to time.sleep(2) after each list element in each body with a module, definition, or compound statement (and then compiles and execs the result as usual).

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 1
    Based on the comments, it's clear that the questioner doesn't want an interactive debugging solution, they just want to wait at each step for the previous step to be complete. – Daniel Pryden Aug 15 '18 at 20:00
  • @DanielPryden But they have no idea _how_ to wait at each step, and the way they want to solve that (which is not a good solution, of course) is to sleep after each one. And this shows how to do exactly that without an interactive debugging session, while also (hopefully) explaining why it isn't a great idea. – abarnert Aug 15 '18 at 20:03
2

Anything you want to happen in a program has to be explicitly stated - this is the nature of programming. This is like asking if you can print hello world without calling print("hello world").

nick
  • 1,090
  • 1
  • 11
  • 24
darrahts
  • 365
  • 1
  • 10
  • 3
    This is true, but the reality in a language like Python is that *lots* of things happen without being explicitly stated. In fact, it's possible in CPython to use `sys.settrace()` to register a function which will get called for every line of code. You probably *shouldn't* do such a thing (let a debugger do that for you), but you *can*. – Daniel Pryden Aug 15 '18 at 19:54
1

I think the best advice to give you here is: don't think in terms of "lines", but think in term of functions.

PascalVKooten
  • 20,643
  • 17
  • 103
  • 160
-2

use debugging mode and watch each and every line executing line by line.