Walkthrough Video 006 - The Process Of Doing Tedious Code Changes
Published: 2022-12-30 14:57:37

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](https://www.youtube.com/watch?v=QneOXVwMin0)

## 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](https://gitlab.com/geraldew/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:

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

```

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`

``` Python
	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() )

```

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.

``` Python
	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() )

```

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.

``` Python
	t_t01_old_combo_DeletionMode = True
	if t_t01_old_combo_DeletionMode :
		# the old code
	else :
		# the new code

```

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:

``` Python
	t_t01_old_combo_DeletionMode = True
	if t_t01_old_combo_DeletionMode :

```

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:

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

```

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:

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

```

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

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

```

Another,

``` Python
			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

```

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

``` Python
				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

```

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.

``` Python
	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

```

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.

``` Python
			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()

```

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

``` Python
			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 )

```

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

``` Python
				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() )

```

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.

``` Python
			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

```

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.

``` Python
				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()

```

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

``` Python
	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() )

```

## 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

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

```

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

``` Python
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() )
```

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:

``` Python
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() )

```

and then doing this:

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

```

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.