Is it possible to create a dynamic localized area in Python?

I have a scenario in which I dynamically execute functions at runtime and need to track the "localized" area. In the example below, "startScope" and "endScope" will actually create levels of "nesting" (in fact, the material contained in this localized area is not print instructions ... these are function calls that send data elsewhere, but nesting tracked there.startScope / endScope just sets the control flags that are used to start / end the current nesting depth).

This all works great for tracking nested data, but exceptions are another matter. Ideally, an exception will cause the current localized scope to β€œdrop out” rather than terminate the entire function (myFunction in the example below).

def startScope(): #Increment our control object (not included in this example) nesting depth control.incrementNestingDepth() def endScope(): #Decrement our control object (not included in this example) nesting depth control.decrementNestingDepth() def myFunction(): print "A" print "B" startScope() print "C" raise Exception print "D" print "This print statement and the previous one won't get printed" endScope() print "E" def main(): try: myFunction() except: print "Error!" 

Running this will (theoretically) output the following:

 >>> main() A B C Error! E >>> 

I am absolutely sure that this is impossible, because I wrote it above - I just wanted to paint a picture of the final result that I am trying to achieve.

Is something like this possible in Python?

Edit: A more appropriate (albeit long) example of how this is used:

 class Log(object): """ Log class """ def __init__(self): #DataModel is defined elsewhere and contains a bunch of data structures / handles nested data / etc... self.model = DataModel() def Warning(self, text): self.model.put("warning", text) def ToDo(self, text): self.model.put("todo", text) def Info(self, text): self.model.put("info", text) def StartAdvanced(self): self.model.put("startadvanced") def EndAdvanced(self): self.model.put("endadvanced") def AddDataPoint(self, data): self.model.put("data", data) def StartTest(self): self.model.put("starttest") def EndTest(self): self.model.put("endtest") def Error(self, text): self.model.put("error", text) #myScript.py from Logger import Log def test_alpha(): """ Crazy contrived example In this example, there are 2 levels of nesting...everything up to StartAdvanced(), and after EndAdvanced() is included in the top level...everything between the two is contained in a separate level. """ Log.Warning("Better be careful here!") Log.AddDataPoint(fancyMath()[0]) data = getSerialData() if data: Log.Info("Got data, let continue with an advanced test...") Log.StartAdvanced() #NOTE: If something breaks in one of the following methods, then GOTO (***) operateOnData(data) doSomethingCrazy(data) Log.ToDo("Fill in some more stuff here later...") Log.AddDataPoint(data) Log.EndAdvanced() #(***) Ideally, we would resume here if an exception is raised in the above localized scope Log.Info("All done! Log some data and wrap everything up!") Log.AddDataPoint({"data": "blah"}) #Done #framework.py import inspect from Logger import Log class Framework(object): def __init__(self): print "Framework init!" self.tests = [] def loadTests(self, file): """ Simplifying this for the sake of clarity """ for test in file: self.tests.append(test) def runTests(self): """ Simplifying this for the sake of clarity """ #test_alpha() as well as any other user tests will be run here for test in self.tests: Log.StartTest() try: test() except Exception,e : Log.Error(str(e)) Log.EndTest() #End 
+5
source share
1 answer

You can achieve a similar effect with the context manager using the with statement. Here I use contextlib.contextmanager decorator:

 @contextlib.contextmanager def swallower(): try: yield except ZeroDivisionError: print("We stopped zero division error") def foo(): print("This error will be trapped") with swallower(): print("Here comes error") 1/0 print("This will never be reached") print("Merrily on our way") with swallower(): print("This error will propagate") nonexistentName print("This won't be reached") >>> foo() This error will be trapped Here comes error We stopped zero division error Merrily on our way This error will propagate Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> foo() File "<pyshell#3>", line 10, in foo nonexistentName NameError: global name 'nonexistentName' is not defined 

This cannot be done using a regular function call, as in your example. In your example, the startScope function returns before the rest of the body myFunction , so startScope cannot affect it. To handle exceptions, you need some kind of explicit structure (either the with statement or the usual try/except ) inside myFunction ; there is no way to make a simple function call magically catch exceptions that occur in its caller.

You should read the context managers as they seem to match what you are trying to do. The context manager __enter__ and __exit__ methods will match your startScope and endScope . Whether it will do exactly what you want depends on what you want from these β€œmanager” functions, but you are probably more fortunate with the context manager than trying to do this with simple function calls.

+3
source

Source: https://habr.com/ru/post/1232590/