Making code go oops on purpose

I recently wrote syncing software that takes data from the school database and updates the user accounts on the tools the school uses on a daily basis. It’s one of those things that every school or organization needs, but can be a bit laborious to put together.

It’s what happens when lots of different tools are used, and different tools are the norm. There is no such thing as a one-fit-solution, instead you have to write the “glue code” to make it all work.

There are different approaches to getting these updates, but the way I chose is the one I unceremoniously call “compare the databases for changes, then make them.” The main problem I had though was to write the code in a way that it was completely clear what was going on. I didn’t want to get fancy I just wanted a block of code where someone down the road, when they need to adjust the syncing, can do so without hunting around forever.

It’s actually complicated, because syncing makes a lot of assumptions when the updates are sent across. If a student’s passport data has changed, for example, the method handling that change assumes that that student already has an account. That’s a dependency, and I’d like that to come through in the code too.

What I came up with is using Python’s exception handling. Whenever an update needs to happen, an error is raised and the right handler is called. To ensure that all the possible checks happen, I use a while loop. The first thing the while loop does is send a message to the section of code that executes the checks. Another section of code then does the update. They pass each other the object in question, usually an instance of a student class, so all the information is all there.

The exceptions are ordered by dependencies, so that the actions that do the updating can safely assume that the other previous updates have happened.

                continue_until_no_errors = True
                times_through = 0
                while continue_until_no_errors:
                    try:
                        # Compares to the actual database, raises errors if there is anything different than what is expected
                        server_information.check_student(student)

                    except NoStudentInMoodle:
                        print("Student does not have an account:\n{}".format(student))
                        modify.new_student(student)

                    except NoEmailAddress:
                        modify.no_email(student)

                    except NoParentAccount:
                        modify.new_parent(student)

                    except ParentAccountNotAssociated:
                        modify.parent_account_not_associated(student)

                    except StudentChangedName:
                        print("Student has had his or her account name changed: {}, {}".format(student.num, student.username))
                        modify.change_name(student)

                    except MustExit:
                        print("Got a really heinus error when going through modify")
                        continue_until_no_errors = False

                    else:
                        # executed when no errors are raised
                        continue_until_no_errors = False

                    times_through += 1
                    if times_through > 2:
                        print("Infinite Loop detected when processing student\n{}".format(student))
                        continue_until_no_errors = False

Exception handling isn’t supposed to work this way, but it’s far clearer than using a bunch of callbacks, if you ask me. The really bad part, though, is that checks are needed to detect an infinite loop. Thats just nasty.

Comments are closed.