Walkthrough Video 006 - The Process Of Doing Tedious Code Changes

techocodger

techocodger

Posted on December 30, 2022

Walkthrough Video 006 - The Process Of Doing Tedious Code Changes

Let's be blunt: sometimes, coding is just tedious. It is tedious and requires an unglamorous commitment to working through something consistently.

The classic situation for this, is where you want to achieve some kind of uniformity so as to make later changes less bothersome. Even though, there's some chance that you will never end up making another change. That's merely one of the paradoxes of good coding - or at least, what I consider "good coding" to mean.

This is the companion post for the video: TechoCodger - Walkthrough Video 006

The Example

I wasn't happy with how fiddly a particular pattern inside my program was proving to be. This revolved around the instances of a combination of:

  • a drop-down aka pull-down menu control
  • the values displayed on that control versus the internal representation of the value.

I had an idea that there was a better way to integrate those two things so that they both referenced the same internal concept. I did some experiments and found a solution that I liked - but it would require rewriting code every single place where the original mixed-concept was used so that it could be replaced by using the new concept.

But, this "internal concept" is itself only an archetype - throughout the program there were multiple similar combinations that I wanted to change over from the old method to the new style.

The purpose of this article is to walk through the example of making that change for the initial instance - and in such a way that we're ready to do the same for the other instances. It is about accepting that it will be tedious, and finding a good-enough way to work through the whole process.

As an extra requirement, I want:

  • to always be able to run the current code;
  • to be able to quickly flick a switch between using the old code and the new;
  • to keep all the multiple "flick a switch" options viable until the very last instance has been achieved.

The reasoning behind that last point is that my initial "new method" was based on making something "good enough" for the first instance. I won't really know it is good enough for all of the instances until the last has been done.

What that means in Python code

The example comes from work on the program Foldatry.

  • btw this is not a veiled promotion for that program, which I only consider to be a prototype, something written for my own use, and gets used as an example simply because the idea for this article came during work on its code.

So, for this program I'm writing, I recently made two coincident changes:

  • changed the definition and features for an Enumeration type (in the module "commontry")
  • made a reworked "Combobox" control by making a new class based on the Tkinter Combobox control.

With those as available new features, I then wrote and proved successful the usage of them.

For this walk-through, we'll take it as read that there is an Enumeration defined as:

@unique
class DeletionMode(Enum):
    DryRun = "DelMod_Dry"
    DeleteNow = "DelMod_Delete"
    Bash = "DelMod_Bash"

Enter fullscreen mode Exit fullscreen mode

The enumeration is supported by some additional functions, but we'll let those be mentioned in context as they arise.

What I will show here is how I implemented the first instance, but with a mind to being a process I knew I would have to repeat for similar adaptations.

Preparing

As the basis for the change will include
I could search for DeletionMode_GetTuple

    t01_str_prunatry_mode = m_tkntr.StringVar() 
    t01_cb_prunatry_modes = m_tkntr_ttk.Combobox( t01_frame11, state="readonly", width = 20, textvariable = t01_str_prunatry_mode) 
    t01_cb_prunatry_modes['values'] = cmntry.DeletionMode_GetTuple()
    t01_str_prunatry_mode.set( cmntry.DeletionMode_Default_Value() )
    t01_cb_prunatry_modes.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )

Enter fullscreen mode Exit fullscreen mode

The code that will replace this, will be to create a Combobox object from the new type PairTupleCombobox with its associated calls to new features for the Enumeration class ( DeletionMode_LstPairTpls and DeletionMode_Default )

i.e.

    t01_cb_prunatry_mode = PairTupleCombobox( t01_frame11, cmntry.DeletionMode_LstPairTpls(), cmntry.DeletionMode_Default(), state="readonly", width = 50) 
    t01_cb_prunatry_mode.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )

Enter fullscreen mode Exit fullscreen mode

But, as an at-home amateur coder, I want my code to keep working as is until I'm ready to throw a switch and then test the replacements.

So what I do is:

  • leave the old aspects of the Enumeration intact and merely add the new support functions
  • bracket the changes I make in Foldatry with boolean flags.
    t_t01_old_combo_DeletionMode = True
    if t_t01_old_combo_DeletionMode :
        # the old code
    else :
        # the new code

Enter fullscreen mode Exit fullscreen mode

Now, one detail to note here. In most other languages I would add my boolean control flag: t_t01_old_combo_DeletionMode at a global level. That way when it comes time to "throw the swtich" I just change a single True to a False. For reasons I won't go into, I don't trust the scoping of Python to be clear enough that I want to do that here. ([^1])

So what I will do - seeing as these control switches are all going to be temporary - is to always have the line pair:

    t_t01_old_combo_DeletionMode = True
    if t_t01_old_combo_DeletionMode :

Enter fullscreen mode Exit fullscreen mode

in every single place where I'm making these changes. In practice it will merely mean that I will perform the changeover - from True to False - using a "replace all" in my editor instead of editing a single line. It's not a big deal.

First Phase - Locate and Insert If Blocks

The first time through, what I am doing is finding all the places where there will need to be a controllable switch between old and new methods.

For each I will insert the line pair before the existing code and put an empty else : clause after the existing code. Here is the first case:

            t_t01_old_combo_DeletionMode = True
            if t_t01_old_combo_DeletionMode :
                setting_value = cmntry.DeletionMode_EnumOfString( t01_str_prunatry_mode.get() )
            else :
                pass

Enter fullscreen mode Exit fullscreen mode

The orginal was just the line: setting_value = cmntry.DeletionMode_EnumOfString( t01_str_prunatry_mode.get() )

Note that Python syntax requires a pass otherwise the else will cause an error.

Here is the next one:

            t_t01_old_combo_DeletionMode = True
            if t_t01_old_combo_DeletionMode :
                t01_str_prunatry_mode.set( cmntry.DeletionMode_EnumValueString( val ) )
            else :
                pass

Enter fullscreen mode Exit fullscreen mode

We can repeat these edits, mainly ignoring what the line actually does or needs.

                t_t01_old_combo_DeletionMode = True
                if t_t01_old_combo_DeletionMode :
                    t_prunatry_mode_show = t01_str_prunatry_mode.get()
                else :
                    pass

Enter fullscreen mode Exit fullscreen mode

Another,

            t_t01_old_combo_DeletionMode = True
            if t_t01_old_combo_DeletionMode :
                t_prunatry_mode_ok = t01_str_prunatry_mode.get() in cmntry.DeletionMode_GetList()
            else :
                pass

Enter fullscreen mode Exit fullscreen mode

and another - we will come back to what each these really does, when we come around again to write the replacements.

                t_t01_old_combo_DeletionMode = True
                if t_t01_old_combo_DeletionMode :
                    i_enum_prunatry_mode = cmntry.DeletionMode_EnumOfString( t01_str_prunatry_mode.get() )
                else :
                    pass

Enter fullscreen mode Exit fullscreen mode

Finally - due to the way I like to structure my Tkinter code, the actual creation of the Tkinger GUI objects comes last - so here we see the creation of the Combobox GUI control.

    t_t01_old_combo_DeletionMode = True
    if t_t01_old_combo_DeletionMode :
        t01_str_prunatry_mode = m_tkntr.StringVar() 
        t01_cb_prunatry_modes = m_tkntr_ttk.Combobox( t01_frame11, state="readonly", width = 20, textvariable = t01_str_prunatry_mode) 
        t01_cb_prunatry_modes['values'] = cmntry.DeletionMode_GetTuple()
        t01_str_prunatry_mode.set( cmntry.DeletionMode_Default_Value() )
        t01_cb_prunatry_modes.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )
    else :
        pass

Enter fullscreen mode Exit fullscreen mode

To state the obvious, all of those if statements are set to evaluate as True and thereby the code at run time remains unaffected.

Second Phase - Write In Else Clauses

Having identified all the places where all the replacements will need to be inserted, we can now work through the process of writing them all.

Here is the first one. This was about reading the state of the Combobox for then saving settings into a configuration file.

            t_t01_old_combo_DeletionMode = False
            if t_t01_old_combo_DeletionMode :
                setting_value = cmntry.DeletionMode_EnumOfString( t01_str_prunatry_mode.get() )
            else :
                setting_value = t01_cb_prunatry_mode.getSelectedKey()

Enter fullscreen mode Exit fullscreen mode

The second. This was about using settings that had been loaded from a configuration file to reset the Combobox.

            t_t01_old_combo_DeletionMode = False
            if t_t01_old_combo_DeletionMode :
                t01_str_prunatry_mode.set( cmntry.DeletionMode_EnumValueString( val ) )
            else :
                t01_cb_prunatry_mode.setSelectedKey( val )

Enter fullscreen mode Exit fullscreen mode

The third - more about this later. This was just to have a string to display in the status window and logs.

                t_t01_old_combo_DeletionMode = False
                if t_t01_old_combo_DeletionMode :
                    t_prunatry_mode_show = t01_str_prunatry_mode.get()
                else :
                    t_prunatry_mode_show = cmntry.DeletionMode_String( t01_cb_prunatry_mode.getSelectedKey() )

Enter fullscreen mode Exit fullscreen mode

The fourth. This was to feed a Boolean check of whether an acceptable value had been set in the Combobox. As it happens, the change to the new type of Combobox means that this check is unnecessary, but for now we'll not tackle removing this part.

            t_t01_old_combo_DeletionMode = False
            if t_t01_old_combo_DeletionMode :
                t_prunatry_mode_ok = t01_str_prunatry_mode.get() in cmntry.DeletionMode_GetList()
            else :
                t_prunatry_mode_ok = t01_cb_prunatry_mode.getSelectedKey() in cmntry.DeletionMode

Enter fullscreen mode Exit fullscreen mode

The fifth. This is where the actual mode value - as an Enumeration - has to be pulled from the Combobox. In the old method I was getting a string from the Combobox and then having to call a function to get the matching Enumeration. In the replacement I directly get an Enumeration from the new class object.

                t_t01_old_combo_DeletionMode = False
                if t_t01_old_combo_DeletionMode :
                    i_enum_prunatry_mode = cmntry.DeletionMode_EnumOfString( t01_str_prunatry_mode.get() )
                else :
                    i_enum_prunatry_mode = t01_cb_prunatry_mode.getSelectedKey()

Enter fullscreen mode Exit fullscreen mode

Finally, here is where the actual Combobox control gets created.

    t_t01_old_combo_DeletionMode = False
    if t_t01_old_combo_DeletionMode :
        t01_str_prunatry_mode = m_tkntr.StringVar() 
        t01_cb_prunatry_modes = m_tkntr_ttk.Combobox( t01_frame11, state="readonly", width = 20, textvariable = t01_str_prunatry_mode) 
        t01_cb_prunatry_modes['values'] = cmntry.DeletionMode_GetTuple()
        t01_str_prunatry_mode.set( cmntry.DeletionMode_Default_Value() )
        t01_cb_prunatry_modes.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )
    else :
        t01_cb_prunatry_mode = PairTupleCombobox( t01_frame11, cmntry.DeletionMode_LstPairTpls(), cmntry.DeletionMode_Default(), state="readonly", width = 50) 
        t01_cb_prunatry_mode.pack( side = m_tkntr.LEFT, padx=tk_padx(), pady=tk_pady() )

Enter fullscreen mode Exit fullscreen mode

Complexities

Some those changes were slightly more complex than they might seem.

For example, in the third example above, the original was a one-line with a call to fetch the visible value of the Combobox, which ironically is no longer a valid thing to do with the new Combox

t02_label_paths_process_status.configure( text = "Abandoned processing in mode:" + t01_str_prunatry_mode.get())

Enter fullscreen mode Exit fullscreen mode

In which case, one apparent way to tackle this is to replicate the configuring of the label for True and False - i.e.

t_t01_old_combo_DeletionMode = False
if t_t01_old_combo_DeletionMode :
    t02_label_paths_process_status.configure( text = "Abandoned processing in mode:" + t01_str_prunatry_mode.get())
else :
    t02_label_paths_process_status.configure( text = "Abandoned processing in mode:" + cmntry.DeletionMode_String( t01_cb_prunatry_mode.getSelectedKey() )
Enter fullscreen mode Exit fullscreen mode

But I didn't like the feel of doing that, so I reworked it to set an interim variable as the thing governed by the if and then have the label configuration appear only once.

Hence, first doing this:

t_t01_old_combo_DeletionMode = False
if t_t01_old_combo_DeletionMode :
    t_prunatry_mode_show = t01_str_prunatry_mode.get()
else :
    t_prunatry_mode_show = cmntry.DeletionMode_String( t01_cb_prunatry_mode.getSelectedKey() )

Enter fullscreen mode Exit fullscreen mode

and then doing this:

t02_label_paths_process_status.configure( text = "Abandoned processing in mode:" + t_prunatry_mode_show )

Enter fullscreen mode Exit fullscreen mode

Now you might (rightly) ask: why bother with such sophistries when this is all code that will eventually be deleted after all the changes prove themselves successful?

To me, this is about establishing and holding various "good routine practices". And the answer to the "why?" question is that I'm going to have quite a few of these sequences of changes to work through. I'm therefore interested in using a method where there is less that can go wrong.

This is one of many coding paradoxes - extra work to reduce risk, or to reduce future extra work - and I assume that all coders go about these in ways that suit themselves. I can tell you what I do and why I do it, but not whether you should also do it.

Footnotes:

([^1]: It is perhaps more accurate to say I don't trust my clarity about the scoping rationale in Python. This being a problem of writing in multiple languages.

💖 💪 🙅 🚩
techocodger
techocodger

Posted on December 30, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024