3

Say I have a function or method that does something repetitive, like checking a value, before performing every operation it does, like so:

def myfunc():
    if mybool:
        do_operation_1()
    else:
        return

    if mybool:
        do_operation_2()
    else:
        return

    ...

These checks get repetitive, and end up wasting a lot of time and keyboard springs, especially when they are needed very often.

If you have control over the operation functions, like, do_operation_N you can decorate the functions with something that checks the boolean.

But what if you don't have control over the individual do_operation_N operations? If, for each line in a function or method, I want the same check to be performed, is there some way to "insert" it without explicitly writing it in on each operation line? For example, is there some decorator magic by which I could do the following?

def magic_decorator(to_decorate):
    def check(*args, **kwargs):
        for call in to_decorate: #magic
            if mybool:
                to_decorate.do_call(call) #magic
            else:
                return #or break, raise an exception, etc
    return check

@magic_decorator
def myfunc():
    do_operation_1()
    do_operation_2()
    ...

If there is a way to achieve this, I don't care if it uses decorators or not; I just want some way to say "for every line in function/method X, do Y first".

The "magic" example of a do_call method above is shorthand for what I'm after, but it would encounter serious problems with out-of-order execution of individual lines (for example, if a function's first line was a variable assignment, and its second was a use of that variable, executing them out of order would cause problems).

To be clear: the ability to externally control the line-by-line order of a function's execution is not what I'm trying to achieve: ideally, I'd just implement something that, in the natural execution order, would perform an operation each time myfunc does something. If "does something" ends up being limited to "calls a function or method" (excluding assignments, if checks, etc), that is fine.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Zac B
  • 3,796
  • 3
  • 35
  • 52
  • Unless you oversimplified and `mybool` can change in between (or even depends on the line), you can simply make the call to `myfunc` conditional. I suppose it's not that simple, so could you clarify? –  Nov 02 '12 at 16:01
  • My goal is, given a check (like `mybool`) and an arbitrary, potentially very long, function/method, to apply the conditional/check *without* manually making each operation in the function conditional. I'm not the best at articulating these things, so please edit/suggest edits to make my aim clearer. – Zac B Nov 02 '12 at 16:06
  • What I'm not clear on is whether the condition is always the same and applies to all lines. If so, you can significantly simplify it by not mucking with individual lines (just call/don't call the whole function instead). –  Nov 02 '12 at 16:10
  • `mybool` could indeed change between checks. – Zac B Nov 02 '12 at 16:10

2 Answers2

6

Store your operations in a sequence, then use a loop:

ops = (do_operation_1, do_operation_2, do_operation_3)

for op in ops:
    if mybool:
        op()
    else:
        return
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

Essentially, you can extract the file and line number from the decorated function, go re-read the function, compile it to an AST, insert nodes in the AST, and then compile the AST and use that as the function.

This method can be used for very long functions, which is a problem if you are using the approach above.

ironstein
  • 421
  • 5
  • 16