Assuming I have the following code
IDLE = 0
STARTED = 1
STOPPED = 2
ERRORED = 3
# additional states as needed
class StateMachine:
def __init__(self)
self.state = IDLE
def start(self):
self.state = STARTED
# do something
def stop(self):
self.state = STOPPED
# do something
def reset(self):
self.state = IDLE
# do something
Our current interface allows a client to change the state of an instance by stating the desired target state, at which point we run certain validation checks and then the appropriate method. I would ideally like to keep a dictionary mapping of the desired target state to the correct method to avoid massive and pointless if-statement blocks. i.e.
if target_state = STARTED:
instance.start()
elif target_state = STOPPED:
instance.stop()
...
But I'm uncertain as to whether or not the following solution is considered good practice or not (it feels a bit whacky calling methods from the class using an instance as arg).
state_mapping = {
IDLE: StateMachine.reset,
STARTED: StateMachine.start,
....
}
And then calling using:
action = state_mapping[target_state]
action(instance)
....
Any thoughts?
Not so whacky.
However, the only thing one has to bear in mind is that action is an unbound method, which may not be very obvious at a first glance of the method call; except I know first-hand how that dictionary is defined.
I think a more readable alternative is to call the method from the instance:
state_mapping = {
IDLE: "reset",
STARTED: "start",
....
}
action = state_mapping[target_state]
getattr(instance, action)()
This will equally improve readability in the case when the method takes more than one argument.
One other alternative.
As your class is called "StateMachine", perhaps it should have a method to execute the state change?
In which case, you can use bound methods in your map
class StateMachine:
...
def ChangeState(self, target):
state_mapping = { IDLE: self.reset, STARTED: self.start, ... }
state_mapping[target]()
You may want to deal with invalid target states, or just let it raise a KEY_ERROR exception.
Related
I would like to understand why using the following snippet leads me to an error:
a) I want to use the following class to create a context manager, as outlined in the link attached below: for me it is very important to keep the "class PrintStop(ExitStack)" form, so please bear in mind when trying to solve this issue, that I already know there are other ways to use ExitStack(), but I am interested in this specific way of using it:
class PrintStop(ExitStack):
def __init__(self, verbose: bool = False):
super().__init__()
self.verbose = verbose
def __enter__(self):
super().__enter__()
if not self.verbose:
sys.stdout = self.enter_context(open(os.devnull, 'w'))
b) when trying to use the class in the more appropriate way, I get the desired effect to stop all the printing within the "with" block, but when trying to print again after that block I get an error:
with PrintStop(verbose=False):
print('this shouldn't be printed') <------ok till here
print('this should be printed again as it is outside the with block) <-----ERROR
c) the error I get is "ValueError: I/O operation on closed file": the reason I guess is the fact that exit method of ExitStack() is not automatically called once we exit the 'with' block, so, how may I change the class to fix this bug?
Here is a quick reference to a similar topic,
Pythonic way to compose context managers for objects owned by a class
ExitStack.__exit__ simply ensures that each context you enter has its __exit__ method called; it does not ensure that any changes you made (like assigning to sys.stdout) inside the corresponding __enter__ is undone.
Also, the purpose of an exit stack is to make it easy to enter contexts that require information not known when the with statement is introduced, or to create a variable number of contexts without having to enumerate them statically.
If you really want to use an exit stack, you'll need something like
class PrintStop(ExitStack):
def __init__(self, verbose: bool = False):
super().__init__()
self.verbose = verbose
def __enter__(self):
rv = super().__enter__()
if not self.verbose:
sys.stdout = self.enter_context(open(os.devnull, 'w'))
return rv
def __exit__(self):
sys.stdout = sys.__stdout__ # Restore the original
return super().__exit__()
Keep in mind that contextlib already provides a context manager for temporarily replacing standard output with a different file, appropriately named redirect_stdout.
with redirect_stdout(open(os.devnull, 'w')):
...
Using this as the basis for PrintStop makes use of composition, rather than inheritance.
from contextlib import redirect_stdout, nullcontext
class PrintStop:
def __init__(self, verbose: bool = False):
super().__init__()
if verbose:
self.cm = redirect_stdout(open(os.devnull, 'w'))
else:
self.cm = nullcontext()
def __enter__(self):
return self.cm.__enter__()
def __exit__(self):
return self.cm.__exit__()
I have a class (AngleInfo) in a file (Display.py) with a self variable (WheelAngle) which is not updated after running a function (GetAngle). This function is being called in a class in a second file (ManageInfo.py) with a trigger based on events. When I try to use the WheelAngle in a second class (AngleProcess) in Display.py, the value doesn't update from the initialization. When the function is triggered in the MessageHandler class, it has access to raw data being represented by m in the GetAngle declaration.
There is another class (SpeedInfo) in a different file (Radar.py) where the self variable (VehicleSpeed) is being updated after running its corresponding information retrieval function (GetSpeed) in the ManageInfo class.
The working case has a threading system, but after replicating it in the non-working case I found no improvement. I don't understand why the WheelAngle is not being updated inside the class and comparing with the working case hasn't brought me closer to the answer.
So basically after I run GetAngle I see WheelAngle has the correct value inside that function but when I call the self variable in the UpdatePlot function of the AngleProcess class in the Display.py file I get the initial value. I even tried to create a different function in the AngleInfo class to access WheelAngle and then call this function in the UpdatePlot function in the AngleProcess class, but the result is the same.
Keep in mind a working example is not possible since it requires live data being sent. Also, even though WheelAngle and VehSpeed don't seem to be used, the code that follows has been ommited for simplicity!
Any ideas? There is a sample of the code below. Thank you!
Display.py
class AngleInfo():
def __init__(self):
self.WheelAngle = 0
def GetAngle(self,m):
self.WheelAngle = float(m) # Angle is correct
class AngleProcess():
def __init__(self):
self.AngleInfoObj = AngleInfo()
def UpdatePlot(self,tupledata):
WheelAngle = self.AngleInfoObj.WheelAngle # Angle is set to initial
Radar.py
class SpeedInfo(threading.Thread):
def __init__(self,page):
threading.Thread.__init__(self)
self.daemon = True
self.start()
self.VehSpeed = 0
def run(self):
VehSpeed = self.VehSpeed # Speed is correct
def GetSpeed(self,m):
self.VehSpeed = float(m) # Speed is correct
ManageInfo.py
from AurixCAN import Receiver
from Radar import SpeedInfo
from Display import AngleInfo
class MessageHandler:
def __init__(self,page):
self.SpeedInfo = SpeedInfo(page)
self.AngleInfo = AngleInfo()
DataSet = Receiver(canIDsandCalls={0xE:[self.SpeedInfo.GetSpeed,self.AngleInfo.GetAngle]})
I struggled to think of a good title so I'll just explain it here. I'm using Python in Maya, which has some event callback options, so you can do something like on save: run function. I have a user interface class, which I'd like it to update when certain events are triggered, which I can do, but I'm looking for a cleaner way of doing it.
Here is a basic example similar to what I have:
class test(object):
def __init__(self, x=0):
self.x = x
def run_this(self):
print self.x
def display(self):
print 'load user interface'
#Here's the main stuff that used to be just 'test().display()'
try:
callbacks = [callback1, callback2, ...]
except NameError:
pass
else:
for i in callbacks:
try:
OpenMaya.MEventMessage.removeCallback(i)
except RuntimeError:
pass
ui = test(5)
callback1 = OpenMaya.MEventMessage.addEventCallback('SomeEvent', ui.run_this)
callback2 = OpenMaya.MEventMessage.addEventCallback('SomeOtherEvent', ui.run_this)
callback3 = ......
ui.display()
The callback persists until Maya is restarted, but you can remove it using removeCallback if you pass it the value that is returned from addEventCallback. The way I have currently is just check if the variable is set before you set it, which is a lot more messy than the previous one line of test().display()
Would there be a way that I can neatly do it in the function? Something where it'd delete the old one if I ran the test class again or something similar?
There are two ways you might want to try this.
You can an have a persistent object which represents your callback manager, and allow it to hook and unhook itself.
import maya.api.OpenMaya as om
import maya.cmds as cmds
om.MEventMessage.getEventNames()
class CallbackHandler(object):
def __init__(self, cb, fn):
self.callback = cb
self.function = fn
self.id = None
def install(self):
if self.id:
print "callback is currently installed"
return False
self.id = om.MEventMessage.addEventCallback(self.callback, self.function)
return True
def uninstall(self):
if self.id:
om.MEventMessage.removeCallback(self.id)
self.id = None
return True
else:
print "callback not currently installed"
return False
def __del__(self):
self.uninstall()
def test_fn(arg):
print "callback fired 2", arg
cb = CallbackHandler('NameChanged', test_fn)
cb.install()
# callback is active
cb.uninstall()
# callback not active
cb.install()
# callback on again
del(cb) # or cb = None
# callback gone again
In this version you'd store the CallbackHandlers you create for as long as you want the callback to persist and then manually uninstall them or let them fall out of scope when you don't need them any more.
Another option would be to create your own object to represent the callbacks and then add or remove any functions you want it to trigger in your own code. This keeps the management entirely on your side instead of relying on the api, which could be good or bad depending on your needs. You'd have an Event() class which was callable (using __call__() and it would have a list of functions to fire then its' __call__() was invoked by Maya. There's an example of the kind of event handler object you'd want here
I have a large python application which is running on a Django service. I need to turn off permission tests for certain operations so I created this context manager:
class OverrideTests(object):
def __init__(self):
self.override = 0
def __enter__(self):
self.override += 1
# noinspection PyUnusedLocal
def __exit__(self, exc_type, exc_val, exc_tb):
self.override -= 1
assert not self.override < 0
#property
def overriding(self):
return self.override > 0
override_tests = OverrideTests()
Various parts of the application can then overide the tests using the context manager:
with override_tests:
do stuff
...
Within the do stuff, the above context manager may be used multiple times in different functions. The use of the counter keeps this under control and it seems to work fine... until threads get involved.
Once there are threads involved, the global context manager gets re-used and as a result, tests may be incorrectly over-ridden.
Here is a simple test case - this works fine if the thread.start_new_thread(do_id, ()) line is replaced with a simple do_it but fails spectacularly as shown:
def stat(k, expected):
x = '.' if override_tests.overriding == expected else '*'
sys.stdout.write('{0}{1}'.format(k, x))
def do_it_inner():
with override_tests:
stat(2, True)
stat(3, True) # outer with context makes this true
def do_it():
with override_tests:
stat(1, True)
do_it_inner()
stat(4, False)
def do_it_lots(ntimes=10):
for i in range(ntimes):
thread.start_new_thread(do_it, ())
How can I make this context manager thread safe so that in each Python thread, it is consistently used even though it is re-entrant?
Here is a way that seems to work: make your OverrideTests class a subclass of threading.local. For safety, you should then call the superclass __init__ in your __init__ (although it seems to work even if you don't):
class OverrideTests(threading.local):
def __init__(self):
super(OverrideTests, self).__init__()
self.override = 0
# rest of class same as before
override_tests = OverrideTests()
Then:
>>> do_it_lots()
1.1.1.2.2.1.1.1.1.1.1.3.3.2.2.2.2.2.2.4.4.3.1.3.3.3.3.4.3.2.4.4.2.4.3.4.4.4.3.4.
However, I wouldn't put money on this not failing in some kind of corner case, especially if your real application is more complex than the example you showed here. Ultimately, you really should rethink your design. In your question, you are focusing on how to "make the context-manager threadsafe". But the real problem is not just with your context manager but with your function (stat in your example). stat is relying on global state (the global override_tests), which is inherently fragile in a threaded environment.
I'm trying to override the DaemonRunner in the python standard daemon process library (found here https://pypi.python.org/pypi/python-daemon/)
The DaemonRunner responds to command line arguments for start, stop, and restart, but I want to add a fourth option for status.
The class I want to override looks something like this:
class DaemonRunner(object):
def _start(self):
...etc
action_funcs = {'start': _start}
I've tried to override it like this:
class StatusDaemonRunner(DaemonRunner):
def _status(self):
...
DaemonRunner.action_funcs['status'] = _status
This works to some extent, but the problem is that every instance of DaemonRunner now have the new behaviour. Is it possible to override it without modifying every instance of DaemonRunner?
I would override action_functs to make it a non-static member of class StatusDaemonRunner(DaemonRunner).
In terms of code I would do:
class StatusDaemonRunner(runner.DaemonRunner):
def __init__(self, app):
self.action_funcs = runner.DaemonRunner.action_funcs.copy()
self.action_funcs['status'] = StatusDaemonRunner._status
super(StatusDaemonRunner, self).__init__(app)
def _status(self):
pass # do your stuff
Indeed, if we look at the getter in the implementation of DaemonRunner (here) we can see that it acess the attribute using self
def _get_action_func(self):
""" Return the function for the specified action.
Raises ``DaemonRunnerInvalidActionError`` if the action is
unknown.
"""
try:
func = self.action_funcs[self.action]
except KeyError:
raise DaemonRunnerInvalidActionError(
u"Unknown action: %(action)r" % vars(self))
return func
Hence the previous code should do the trick.