Skip to content

Instantly share code, notes, and snippets.

@TheRolfFR
Last active July 25, 2023 13:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheRolfFR/381df7f3f81fc3850593e5c5db49ad98 to your computer and use it in GitHub Desktop.
Save TheRolfFR/381df7f3f81fc3850593e5c5db49ad98 to your computer and use it in GitHub Desktop.
Quick save, restore and set with multiline lambda

Create temporary setup for pytest with context managers

My need

To create a temporary setup for pytest, you may think fixtures. But my case isn't like yours.
I needed to change tempolarly a value in the middle of my test so I could get faster results.

The clean solution

We will use the with statement. It will make a dedicated "scope" to that temporary setup. I'm sure that you already used them to open files. It is particularly powerful as it creates a context manager, which increases readibility, and protects developers from themselves.

Why do clean? Because it's an addiction since I discovered C++ destructors, but above all, Rust drop trait.

The goal is to have the save and restore code as close as each over to avoid irrelevant variables of saved state. We need to create a class using the __enter__ and __exit__ method defined. We will use __init__ method to define those functions.

The code

""" Quick save and restore context manager (by TheRolf) """
class SaveAndRestore(object):
   def __init__(self, save_fct, restore_fct):
      self.save_fct = save_fct
      self.saved = None
      self.restore_fct = restore_fct
   def __enter__(self):
      self.saved = self.save_fct()
      return self.saved
   def __exit__(self, *_):
      self.restore_fct(self.saved)

Wow so complicated! My god, this is pure genius! /s

You will ask me: This is useless?, we still have to make a function and lambda are very limited. That is where lamba functions come useful. You can write some "anonymous" functions and define on the fly save and restore operations.

With Python 3.8+, you can even make it kinda multiline and local binding!

Use case

To use this, you can simply make your with statement and make your best code. If you want you can use the saved value but it is not mandatory:

with SaveAndRestore(
  lambda: "value", # save function
  lambda saved: print("Value saved was: " + str(saved)) # restore function
 ) as saved:
    print("This is the saved value: " + str(saved))

We can read and interpret this as a human:

  1. [Reading the class name] The following code with save and restore values.
  2. [Reading the save lambda] Okay I save this value.
  3. [Reading the restore lambda] and after it will be restored like that.
  4. [Reading inside the with block] Meanwhile, these operations occur.

Conclusion

Now let your imagination do whatever you want. You can use array or tuple return values to save more data and/or extract some inside the scope.

Only sad part, we cannot have a return value for the with statement for even better lexical code. However you can still use an external variable.

Thanks for reading, hit me on social networks and give me your best feedback or improvements!
TheRolf


Improvements

We could add a new parameter set_fct to the constructor so that change the place where the saved value is stored. And we can get an even cleaner code:

""" Quick save and restore context manager (by TheRolf) """
class SaveAndRestore(object):
   def __init__(self, save_fct, restore_fct, set_fct = lambda: None):
      self.save_fct = save_fct
      self.saved = None
      self.set_fct = set_fct
      self.restore_fct = restore_fct
   def __enter__(self):
      self.saved = self.save_fct()
      self.set_fct()
      return self.saved
   def __exit__(self, *_):
      self.restore_fct(self.saved)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment