Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I split a long function into separate steps while maintaining the relationship between said steps?

Tags:

python

I have a very long function func which takes a browser handle and performs a bunch of requests and reads a bunch of responses in a specific order:

def func(browser):
    # make sure we are logged in otherwise log in
    # make request to /search and check that the page has loaded
    # fill form in /search and submit it
    # read table of response and return the result as list of objects

Each operation require a large amount of code due to the complexity of the DOM and they tend to grow really fast.

What would be the best way to refactor this function into smaller components so that the following properties still hold:

  • the execution flow of the operations and/or their preconditions is guaranteed just like in the current version
  • the preconditions are not checked with asserts against the state, as this is a very costly operation
  • func can be called multiple times on the browser

?

like image 302
Shoe Avatar asked Mar 12 '23 22:03

Shoe


1 Answers

Just wrap the three helper methods in a class, and track which methods are allowed to run in an instance.

class Helper(object):

    def __init__(self):
        self.a = True
        self.b = False
        self.c = False

    def funcA(self):
        if not self.A:
            raise Error("Cannot run funcA now")
        # do stuff here
        self.a = False
        self.b = True
        return whatever

    def funcB(self):
        if not self.B:
            raise Error("Cannot run funcB now")
        # do stuff here
        self.b = False
        self.c = True
        return whatever

    def funcC(self):
        if not self.C:
            raise Error("Cannot run funcC now")
        # do stuff here
        self.c = False
        self.a = True
        return whatever

def func(...):
    h = Helper()
    h.funcA()
    h.funcB()
    h.funcC()

# etc

The only way to call a method is if its flag is true, and each method clears its own flag and sets the next method's flag before exiting. As long as you don't touch h.a et al. directly, this ensures that each method can only be called in the proper order.

Alternately, you can use a single flag that is a reference to the function currently allowed to run.

class Helper(object):
    def __init__(self):
        self.allowed = self.funcA
    def funcA(self):
        if self.allowed is not self.funcA:
            raise Error("Cannot run funcA now")
        # do stuff
        self.allowed = self.funcB
        return whatever
    # etc
like image 55
chepner Avatar answered May 04 '23 11:05

chepner