While playing with the spirograph app between regex posts, I discovered it was not working as I expected. And, I thought I had tested the particular functionality that was failing. Failing miserably!

When I used the form to make a selection for a given parameter (e.g. using the same curve or using datamutlipliers for mosaic-like images), I epected that parameter to remain as chosen until such time as I accessed the form to change it. Well, if I selected Yes for “Use same curve again:” and generated the image, it did indeed use the same curve as the previous image. But, if I used the “Draw another one!” button a new set of curve data would be generated. This was not what I wanted. Similar situation for using data multipliers. Or pretty much any other form field.

Determine Source of Bug

I went through (refactored) my code over and over and over and… Nothing changed. On the subsequent image request all the parameters went back to their defaults. I continued going over (refactoring) my code over and over and over and… Finally I did some more specific googling and found my error.

I had a dictionary keyed on form fields with values that were the related user variables in the global module, g_vars.py. Here’s that bit of code defining the dictionary.

  fld_2_glbl = {
    'u_agn': g.u_again,
    'wheels': g.u_whl,
    'shape': g.u_shp,
    'shp_mlt': g.u_shps,
    'torb': g.u_torb,
    'drows': g.u_drws,
    'c_nbr': g.u_cnbr,
    'r_mlt': g.u_rmlt,
    'ln_sz': g.u_lw,
    'cmap': g.u_cmap,
    'do_bg': g.u_bg,
    'ntr': g.u_ntr,
    'do_drpt': g.u_dt2t,
    'clw_pct': g.u_lwpct,
    'clwc': g.u_lwcyc,
    'clwpp': g.u_dppc,
    'npts': g.u_pts,
    'ani_step': g.ani_step,
  }

I assumed the dictionary values would be references to the appropriate global value. So that when I did something like the following the global variable would be updated.

  # e.g. fld = 'u_agn', f_data is the data return from the form submission
  fld_2_glbl[fld] = f_data[fld]

In other languages I have played with, the dictionary’s declaration would have assigned a reference to the variable in each dictionary value. Well at least I thought it would. Python most certainly does not. Each dictionary value was simply assigned the current value of the specific global variable. The assignment above simply changed the value for the specified field in the fld_2_glbl dictionary. The global value was never changed, as I had expected.

Fix Bug

I then got creative (?) and tried using lists (arrays) for each global value. That would result in the dictionary declaration getting references to the specific global variable. But what a nightmare the code started to become. And the amount of refactoring was quickly becoming absolutely ridiculous.

I, within a relatively short period of time, gave up and reverted to the last working version of the three main files.

New Functions

I considered using Python’s exec function. But instead decided to use getter and setter functions for the curve specification form’s related global variables. Those terms are likely incorrect in this case, but they do describe what the functions do. The functions are, of course, lengthy if blocks; each with many elif blocks. I will just show the setter version. The parameter gvar is the specific field name from the form data related to the global being updated.

def set_global(gvar, gval):
  if gvar == 'u_agn':
    g.u_again = gval
  elif gvar == 'wheels':
    g.u_whl = gval
  elif gvar == 'shape':
    g.u_shp = gval
  elif gvar == 'shp_mlt':
    g.u_shps = gval
  elif gvar == 'torb':
    g.u_torb = gval
  elif gvar == 'drows':
    g.u_drws = gval
  elif gvar == 'c_nbr':
    g.u_cnbr = gval
  elif gvar == 'r_mlt':
    g.u_rmlt = gval
  elif gvar == 'ln_sz':
    g.u_lw = gval
  elif gvar == 'cmap':
    g.u_cmap = gval
  elif gvar == 'do_bg':
    g.u_bg = gval
  elif gvar == 'ntr':
    g.u_ntr = gval
  elif gvar == 'do_drpt':
    g.u_dt2t = gval
  elif gvar == 'clw_pct':
    g.u_lwpct = gval
  elif gvar == 'clwc':
    g.u_lwcyc = gval
  elif gvar == 'clwpp':
    g.u_dppc = gval
  elif gvar == 'npts':
    g.u_pts = gval
  elif gvar == 'ani_step':
    g.ani_step = gval

I should probably return something (e.g. success status or the new value of the global) from the function, but for now am not doing so. The getter of course returns the value of the requested global (again passing in the related form field name). And, looking at it now, seems to me to be a situation where the eval or exec functions might improve things. Though would need to deal with security issues related to user supplied form field values. Easier for now to leave as is.

Refactor fini_curve_form()

I am not going to show the whole refactored fini_curve_form(), in the sp_app_lib module. Just a sample portion. I am sure you can sort the whole function on your own.

  for fld in dflts:
    g_f_val = get_global(fld)
    if fld == 'shape' or fld == 'shp_mlt':
      continue
    do_fill = False

    if fld in f_data:
      cmplt[fld] = f_data[fld]
      if f_data[fld] is None or f_data[fld] == '':
        do_fill = True
    else:
      do_fill = True

    if do_fill:
      if g_f_val is not None:
        cmplt[fld] = g_f_val
      else:
        cmplt[fld] = dflts[fld]
    
    # set the appropriate global to current form field value
    set_global(fld, cmplt[fld])

Test

That seems to work, more or less, as expected. But, I now have some other issues. If I set ‘use again’ to true, too many options do not change on subsequent redraws. For example, if using multipliers was selected, I’d like to have new ones generated on each redraw. That does not currently happen. The same ones are reused for each subsequent redraw. At least I think that’s what I would like.

Done

That said, I think I will leave this post as it currently stands. Perhaps one of my shorter ones. Lesson being learned. Do thoroughly design your app before your fingers start drumming. Just goes to show you, I may be able to do some programming. But a developer I not am.

May any bugs you encounter be less troublesome.