Update object inside a multiprocessing.Manager.dict() - python

I was wondering how I could update an object assigned as a shared dictionary value between different process. I have the following class:
class Task:
STATUS_PROCESSING = 0
STATUS_EXECUTING = 1
STATUS_QUEUED = 2
STATUS_TERMINATED = 3
STATUS_HALTED = 4
STATUS_STOPPED = 5
def __init__(self, id: str, uuid: str, options: dict):
self.id = id
self.uuid = uuid
self.options = options
self.state = 0
# Some properties...
def execute(self):
""" Executes the task
"""
# Set self status to Executing
self.state = Task.STATUS_EXECUTING
print('Executing...')
self.state = Task.STATUS_TERMINATED
It just creates a new task with a given ID and executes its core method when execute() is called. I have another class with staticmethods that is used to append a new pair (id, task) to the dict, and read the dict executing all its tasks until the main program stops:
class DummyList:
#staticmethod
def submit_task(d: dict, uuid: str, options: dict):
""" Submit a new task
"""
# If invalid UUID
if not Task.is_valid_uuid(uuid):
return False
# If more than 20 tasks
if len(d) > 19:
return False
# Create random ID (simplified for question)
r_id = str(random.randint(1, 2000000))
if r_id in d:
return False
# Add task to the dictionary
d[r_id] = Task(r_id, uuid, options)
# Set status to queue
d[r_id].state = Task.STATUS_QUEUED
# Return the created ID
return r_id
#staticmethod
def execute_forever(d):
try:
while True:
for i in d.values():
print(i.state)
i.execute()
time.sleep(5)
except KeyboardInterrupt:
pass
The thing is that the DummyList.execute_forever() will be called from another process, while the main one will execute the submit_task(...) function to add new tasks. Like this:
# Create a shared dict
m = multiprocessing.Manager()
shared_d = m.dict()
# Start the Task shared list execution in another process
p = multiprocessing.Process(target=DummyList.execute_forever, args=(shared_d,))
# Set the process to exit when the main halts
p.daemon = True
p.start()
........
# From another place
# The message variable is not important
DummyList.submit_task(shared_d, message['proc'], message['options'])
It works! The task is created, assigned to the dictionary and executed, but the following lines (which are seen in the above code) do not execute properly:
self.state = Task.STATUS_EXECUTING
self.state = Task.STATUS_TERMINATED
d[r_id].state = Task.STATUS_QUEUED
If we would try to write ìf shared_d[<some_id>].state == 0 all over the code, it will always be True, because the property does not update
I suppose that's because the shared dictionary does not update when the object properties are modified, maybe because the dictionary only understands he has to update when his getitem or setitem methods are called. Do you know if there is any way to change this behaivor?
Thank you very much!

I finally found a solution. The objects inside the dictionary were not updating unless the __getitem__ or __setitem__ methods from the proxy dictionary were called. That's why I changed the following lines:
Task
The execute() method ends with return self. The self.state must be changed throughout the execution.
TaskManager
Method changed to:
#staticmethod
def execute_forever(d):
""" Infinite loop reading the queued tasks and executing all of them.
"""
try:
while True:
# Notice the loop using the keys
for i in d.keys():
# Execute and re-assign item
d[i] = d[i].execute()
time.sleep(5)
except KeyboardInterrupt:
pass

Related

How to pass arguments to class after initialized?

I'm trying to create threads to run a class method. However, when I try to pass one class to another, it tries to initialize the class and never gets threaded.
I'm taking a list of tuples and trying to pass that list to the cfThread class, along with the class method that I want to use. From here, I'd like to create a separate thread to run the classes method and take action on one of tuples from the list. The REPLACEME is a placeholder because the class is looking for a tuple but I don't have one to pass to it yet. My end goal is to be able to pass a target (class / function) to a thread class that can create it's own queue and manage the threads without having to manually do it.
Below is a simple example to hopefully do a better job of explaining what I'm trying to do.
#!/bin/python3.10
import concurrent.futures
class math:
def __init__(self, num) -> None:
self.num = num
def add(self):
return self.num[0] + self.num[1]
def sub(self):
return self.num[0] - self.num[1]
def mult(self):
return self.num[0] * self.num[1]
class cfThread:
def __init__(self, target, args):
self.target = target
self.args = args
def run(self):
results = []
with concurrent.futures.ThreadPoolExecutor(10) as execute:
threads = []
for num in self.args:
result = execute.submit(self.target, num)
threads.append(result)
for result in concurrent.futures.as_completed(threads):
results.append(result)
return results
if __name__ == '__main__':
numbers = [(1,2),(3,4),(5,6)]
results = cfThread(target=math(REPLACEME).add(), args=numbers).run()
print(results)
target has to be a callable; you want to wrap your call to add in a lambda expression.
results = cfThread(target=lambda x: math(x).add(), args=numbers)

How to get values back from multithreading?

class Return_Thread_Value(object):
def __init__(self,target = None,args = (),**kwargs):
self._que = queue.Queue()
self._t = Thread(target = lambda q,arg1,kwargs1: q.put(target(*arg1,**kwargs1)),
args=(self._que,args,kwargs), )
self._t.start()
def Return_Value(self):
self._t.join()
return self._que.get()
Thread_1 = Return_Thread_Value(target = Walking_Inputs,args = (
WINDOW,CLOCK,Hero,FRAME,INTERACTING,TOP_SCREEN,POSITION_DATA,BACKGROUND,
FOREGROUND_OPAQUE,FOREGROUND_TRANSLUCENT,INPUT,INPUT_SHIFT,PROMPT_SHIFT,Input,
ENTERED))
INTERACTING,TOP_SCREEN,Input,ENTERED = Thread_1.Return_Value()
Thread_2 = Return_Thread_Value(target = Key_Inputs,args = (
WINDOW,ENTERED,PROMPT_SHIFT,INPUT,INPUT_SHIFT,CAPITAL,Input))
ENTERED,PROMPT_SHIFT,INPUT,INPUT_SHIFT,CAPITAL,Input = Thread_2.Return_Value()
Trying to run two functions, one that lets you walk about the village and another that accepts key inputs, both functions are running, but I'm not sure if the values are being returned.
They threads will each have their own scopes, to pass data back to their parent thread, the easiest way is to define any object for example a dict return_value = {} and pass that as an arg to your thread.
Set the value you want to return as a key in the dict (
return_value['thread1return']='something'), and you should be able to access it in the parent thread

Items appended to object's list append to every object's list

I'm building a simple blockchain/cryptocurrency to learn about python and blockchain programming.
I've run into an issue regarding appending transaction objects to the list variable 'transactions' in my Block objects.
For whatever reason, when adding a transaction to a block, it is added to every block on the chain.
I have uploaded my code to a github repo:
The project consists of 3 class files: Blockchain.py, Block.py & Transaction.py
I also have a testing file 'test1.py' which reproduces the error.
https://github.com/swooperior/blockchain-py
I suspect the issue is in the Block class file:
#Not intended behaviour. addTransaction seems to add to every block in self.chain
from datetime import datetime
import hashlib
class Block:
hash = ''
txIndex = 0
transactions = []
timeStamp = ''
previous_hash = ''
nonce = 0
def calculateHash(self):
self.hash = str(hashlib.sha256(repr([self.transactions,self.previous_hash,self.nonce]).encode('utf-8')).hexdigest())
def getHash(self):
return self.hash
def addTransaction(self,tx):
#Validate transaction, then pass to transactions list
tx.id = self.txIndex
self.transactions.append(tx)
self.txIndex += 1
def printDetails(self):
print('Block Hash: '+self.getHash())
print('Nonce: '+str(self.nonce))
print('Created: '+ str(datetime.fromtimestamp(self.timeStamp)))
print('Prev_hash: '+self.previous_hash)
print('Transactions ('+str(len(self.transactions))+'):')
self.printTransactions()
def printTransactions(self):
c = 1
for tx in self.transactions:
print('Transaction:'+ str(c))
tx.printDetails()
c += 1
def __init__(self,txlist=[],prev_hash=''):
self.txIndex = 0
self.previous_hash = prev_hash
for tx in txlist:
self.addTransaction(tx)
self.timeStamp = datetime.timestamp(datetime.now())
self.nonce = 1
self.calculateHash()
#print(self.printDetails())
The transactions attribute is a class attribute for all instances of the class. When you instantiate the class, you should create an instance variable instead. You also shouldn’t use a mutable default argument.
class Block:
...
def __init__(self, txlist=None, prev_hash=''):
self.transactions = []
txlist = txlist or []
self.previous_hash = prev_hash
for tx in txlist:
self.addTransaction(tx)
self.timeStamp = datetime.timestamp(datetime.now())
self.nonce = 1
self.calculateHash()
Function defaults are only evaluated once so each instance uses the same default argument unless you give it another one. This only happens to mutable objects as re-assigning them doesn’t copy them.

Python error 'NoneType' object has no attribute '<....>'

I'm having trouble with the following code:
import tweepy
from tweet import TweetBuilder
from libs.session import Session
class GameHandler:
open_sessions = []
def get_session(self, sessionname):
for session in GameHandler.open_sessions:
#FOLLOWING STATEMENT GOES WRONG
if session.roomname == sessionname:
return session
return None
def session_create(self, sessionname, owner_id, owner_name):
new = Session(sessionname, owner_id, owner_name).add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
return TweetBuilder.new_session(sessionname, owner_name)
def session_join(self, sessionname, player_id, player_name):
session = self.get_session(sessionname)
if session != None:
session.add_player(player_id, player_name)
return TweetBuilder.join_session(session, player_name)
return ""
Also part of the Session class:
class Session:
def __init__(self, name, owner_id, owner_name):
#keep track of tweets
self.tweetid_start = None
self.tweetid_current = None
#game elements
self.roomname = name
#THIS LINE WORKS CORRECTLY
print(self.roomname)
self.players = []
self.currentround = None
self.roundnumber = 0
self.players.append(Player(owner_id, owner_name))
When I call session_create() everything works fine. The app runs Session.__init__(), the print statement prints self.roomname.
When I call session_join(), and session_join() calls get_session() problems arise. The for loop is supposed to iterate over the Session-array called open_sessions, but the moment it tries to access the Session-attribute called 'roomname' it gives me the following error:
'NoneType' object has no attribute 'roomname'
Why are my Session-objects suddenly NoneType?
Thanks in advance.
The problem is here:
new = Session(sessionname, owner_id, owner_name).add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
By immediately calling add_player on the newly created Session, new is not the Session but the result of add_player, and whatever that does, it seems to return None. Thus you are adding a bunch of None objects to your open_sessions list. Use this instead:
new = Session(sessionname, owner_id, owner_name)
new.add_player(owner_id, owner_name)
GameHandler.open_sessions.append(new)
Or if you want to keep it the way it was, you could change your Session class to provide kind of a "fluent interface" and have add_player (and other methods) return self:
class Session:
...
def add_player(self, id_, name):
....
return self

How to Instantiate a Completely Fresh Temporary Object that Contains Lists

I'm currently using Python to parse CAN database files. I ran into a problem with lists during implementation and gave it a quick patch that makes it work, but it's kind of ugly and seems as if there's a more elegant solution.
I have defined an object CAN database and one of it's methods takes the file to be parsed, which contains definitions of messages in the database. I loop through each line in the file and when I come across a line indicating a message description, I create a temporary variable referencing an object I've defined for CAN messages, some of the members of which are lists. I put elements in these lists with a method based on the next handful of lines in the file.
Now when I'm done with this temporary object, I add it to the CAN database object. Since I no longer need the data referenced by this variable, I assign the value None to it and reinstantiate the clean slate variable on the next iteration through that detects a message descriptor. Or that was the plan.
When I go through the next iteration and need to use this variable, I add some values to these lists and find that they're not actually empty. It seems that despite assigning the variable to reference None the values in the lists persisted and were not cleaned up.
Below you can see my solution which was to stack more methods on specifically to get rid of the persisting list elements.
Here's some relevant portions of the file:
Parsing Loop
for line in file:
line = line.rstrip('\n')
line_number += 1 # keep track of the line number for error reporting
if line.startswith("BU_:"):
self._parseTransmittingNodes(line)
elif line.startswith("BO_"):
can_msg = self._parseMessageHeader(line).ResetSignals().ResetAttributes()
building_message = True
elif line.startswith(" SG_") and building_message:
can_msg.AddSignal(self._parseSignalEntry(line))
# can_msg.updateSubscribers()
elif line == "":
if building_message:
building_message = False
self._messages += [can_msg]
can_msg = None
Reset Methods
def ResetSignals(self):
"""
Flushes all the signals from the CANMessage object.
"""
self._signals = []
return self
def ResetAttributes(self):
"""
Flushes all the attributes from the CANMessage object.
"""
self._attributes = []
return self
How can I make this variable a fresh object every time? Should I have a method that clears all of it's internals instead of assigning it None like the IDispose interface in C#?
EDIT: Here's the full source for the CANMessage object:
class CANMessage:
"""
Contains information on a message's ID, length in bytes, transmitting node,
and the signals it contains.
"""
_name = ""
_canID = None
_idType = None
_dlc = 0
_txNode = ""
_comment = ""
_signals = list()
_attributes = list()
_iter_index = 0
_subscribers = list()
def __init__(self, msg_id, msg_name, msg_dlc, msg_tx):
"""
Constructor.
"""
self._canID = msg_id
self._name = msg_name
self._dlc = msg_dlc
self._txNode = msg_tx
def __iter__(self):
"""
Defined to make the object iterable.
"""
self._iter_index = 0
return self
def __next__(self):
"""
Defines the next CANSignal object to be returned in an iteration.
"""
if self._iter_index == len(self._signals):
self._iter_index = 0
raise StopIteration
self._iter_index += 1
return self._signals[self._iter_index-1]
def AddSignal(self, signal):
"""
Takes a CANSignal object and adds it to the list of signals.
"""
self._signals += [signal]
return self
def Signals(self):
"""
Gets the signals in a CANMessage object.
"""
return self._signals
def SetComment(self, comment_str):
"""
Sets the Comment property for the CANMessage.
"""
self._comment = comment_str
return self
def CANID(self):
"""
Gets the message's CAN ID.
"""
return self._canID
def AddValue(self, value_tuple):
"""
Adds a enumerated value mapping to the appropriate signal.
"""
for signal in self:
if signal.Name() == value_tuple[0]:
signal.SetValues(value_tuple[2])
break
return self
def AddAttribute(self, attr_tuple):
"""
Adds an attribute to the message.
"""
self._attributes.append(attr_tuple)
return self
def ResetSignals(self):
"""
Flushes all the signals from the CANMessage object.
"""
self._signals = []
return self
def ResetAttributes(self):
"""
Flushes all the attributes from the CANMessage object.
"""
self._attributes = []
return self
def Name(self):
return self._name
def TransmittingNode(self):
return self._txNode
def DLC(self):
return self._dlc
The problem you're seeing is because you used class attributes instead of instance attributes. If you move the initialization of the attributes you don't pass to __init__ from class scope into __init__, each instance will have its own set of lists.
Here's what that would look like:
class CANMessage:
"""
Contains information on a message's ID, length in bytes, transmitting node,
and the signals it contains.
"""
def __init__(self, msg_id, msg_name, msg_dlc, msg_tx):
"""
Constructor.
"""
self._canID = msg_id
self._name = msg_name
self._dlc = msg_dlc
self._txNode = msg_tx
self._name = ""
self._canID = None
self._idType = None
self._dlc = 0
self._txNode = ""
self._comment = ""
self._signals = list()
self._attributes = list()
self._iter_index = 0
self._subscribers = list()
# the rest of the class is unchanged, and not repeated here...

Categories