0% found this document useful (0 votes)
18 views4 pages

Python Developer Test: Decorators & IPC

This document contains 3 questions related to Python programming. Question 1 asks to write a decorator to log function details like name, arguments, output, and time taken. Question 2 asks to write unit tests for a singleton design pattern implementation in Python. Question 3 asks to build inter-process communication between 2 Python processes to send/replace/update dictionaries and receive IDs.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views4 pages

Python Developer Test: Decorators & IPC

This document contains 3 questions related to Python programming. Question 1 asks to write a decorator to log function details like name, arguments, output, and time taken. Question 2 asks to write unit tests for a singleton design pattern implementation in Python. Question 3 asks to build inter-process communication between 2 Python processes to send/replace/update dictionaries and receive IDs.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Note:

1. All 3 Questions are must to attempt in Python.


2. Time Slot :90 min

[Link] a decorator for logging the function. The decorator also consumes an optional
argument which denotes the file path to write logs. If it is not provided, print it to
console.
It should log below details related to the function it was attached: 
a. function name 
b. function arguments 
c. output of the function 
d. time took to execute the function 
E.g.  

from time import time


def myDecorator(func):
'''Decorator to print function call details - parameters names and effective
values'''
def wrapper(*func_args, **func_kwargs):
print("Method - " + func.__name__)
args = list(func_args) if len(list(func_args)) > 0 else None
kwargs = list(func_kwargs) if len(list(func_kwargs)) > 0 else None
print("Arguments -", args)
print("kwargs -", kwargs)
start = time()
retval = func(*func_args,**func_kwargs)
print("Output -", retval)
print("Time taken -",time() - start, "seconds")
return wrapper

 
@mydecorator('/tmp/[Link]') 
def subtract(a, b): 
    return a - b 
  
  
result = subtract(10 , 2)
  
In the '/tmp/[Link]', you should see below details: 
Method - subtract 
arguments - [10, 2] 
kwargs - None 
Output - 8 
Time Taken - 0.2086548805236816 seconds 
 
@mydecorator() 
def add(a, b=1): 
    return a + b 
  
result = add(a=10) 
  
In the output console, you should see below details: 
Method - add 
arguments - None 
kwargs - {'a': 10} 
Output - 11 
Time Taken - 0.2086548805236816 seconds 
 
[Link] unit tests (no integration tests) for singleton design pattern implementation in
python.

# Singleton Borg pattern


class SingleTonClass:
    __shared_state = dict()
 
    # constructor method
    def __init__(self):
        self.__dict__ = self.__shared_state
        [Link] = ‘Apple’
 
    def __str__(self):
        return [Link]
 
# main method
if __name__ == "__main__":
value1 = SingleTonClass()
value2 = SingleTonClass()
value3 = SingleTonClass()
 
    [Link] = 'Mango' # person1 changed the state
    [Link] = 'Melon'     # person2 changed the state
 
    print(value1)    # output --> Melon
    print(value2)    # output --> Melon
 
    [Link] = 'Grape'
 
    print(value1)    # output --> Grape
    print(value2)    # output --> Grape
    print(value3)    # output --> Grape

[Link] a inter process communication between two python processes where: 


a. Process 1 can send a new dictionary to process 2 and gets an
ID along with saved dictionary in response. 
b. Process 1 can replace dictionary for a given ID and gets the same ID along with
replaced dictionary in response. In case no dictionary exists before, an error
message is sent. 
c. Process 1 can update partial content of the dictionary for a given ID and gets
the same ID along with updated dictionary in response. In case no dictionary
exists before, an error message is sent.
Example
Data from P1 to P2: {'data': {'a': {'c': 3}}, 'action': 'create'} 
Response from P2 to P1: {'data': {'a': {'c': 3}}, 'id': 45} 
 
Data from P1 to P2: {'data': {'a': {'b': 4}}, 'id': 45, 'action': 'replace'} 
Response from P2 to P1: {'data': {'a': {'b': 4}}, 'id': 45} 
 
Data from P1 to P2: {'data': {'a': {'d': 10}, 'e': 15}, 'id': 45, 'action': 'update'} 
Response from P2 to P1: {'data': {'a': {'b': 4, 'd': 10}, 'e': 15}, 'id': 45} 
 
Data from P1 to P2: {'data': {'a': {'b': 4}}, 'id': 15, 'action': 'replace'} 
Response from P2 to P1: {'error': 'No data is present for id 15.'} 
 
Data from P1 to P2: {'data': {'a': {'d': 10}, 'e': 15}, 'id': 15, 'action': 'update'} 
Response from P2 to P1: {'error': 'No data is present for id 15.'} 

allData = {}
def doAction(data):
if(data['action'] == 'create'):
temp = {}
temp[data] = data['data']
while(1):
randInt = [Link]()
if randInt not in allData:
break
temp['id'] = randInt
allData[randInt] = temp
return temp

if(data['action'] == 'replace'):
key = data['id']
if(key not in allData):
print("Key does not exist for {}".format(key))
else:
allData[key] = data

if(action['action'] == 'update'):
key = data['id']
if(key not in allData):
print("Key does not exist for {}".format(key))
else:
f

Common questions

Powered by AI

Potential challenges include handling file I/O operations correctly, ensuring thread-safety when writing logs to the same file, and correctly configuring and validating the file path argument. Additionally, handling exceptions during logging, such as file permission issues or full disk space, might also complicate implementation .

Process 1 can interact with process 2 by sending a dictionary for three main actions: creating a new dictionary; replacing an existing dictionary for a given ID; or updating parts of a dictionary for a given ID. Process 2 returns the dictionary and ID after a create, a replaced dictionary and ID after a replace, or an updated dictionary and ID after an update. If no dictionary exists for the given ID during replace or update, process 2 returns an error message .

The shared state in the Singleton Borg pattern allows different instances of a class to maintain and update a single state, which can simplify state management but also introduces potential drawbacks such as increased coupling, potential difficulties in managing state changes, and challenges in ensuring thread safety in concurrent environments. These effects necessitate careful design considerations to ensure the pattern is beneficial rather than a source of bugs or performance issues .

Decorators for logging offer advantages, such as separation of concerns, reducing repetitive code, and enhancing readability. By employing decorators, logging logic is centralized and can be applied consistently across multiple functions, promoting code modularity and usability. This enables easy maintenance and changes to the logging process without modifying the original function code .

Process 2 might return an error if process 1 attempts to replace or update a dictionary with an ID that doesn’t exist in process 2's storage. Process 1 should handle this by implementing error checking to confirm the existence of the ID in process 2 before attempting these actions, and by implementing error logging or alerting mechanisms to notify when such errors occur .

The logging decorator in Python is designed to automatically log specific details related to the function it is attached to. It captures the function name, function arguments, the output of the function, and the time it took to execute the function. This helps in debugging and monitoring the behavior of functions during runtime .

The Singleton Borg pattern ensures a single shared state across instances by assigning the same dictionary to the instance’s `__dict__` attribute, which means all instances share the same state. Each instance, therefore, automatically references and modifies the shared state, ensuring that any change in one instance is reflected across others .

The design supports robust data management by providing clear protocols for data creation, replacement, and updates across processes, each action paired with appropriate responses or error messages. This promotes consistency and transparency in data operations. However, robustness is contingent on error handling, verification checks, and concurrency management to handle data inconsistencies and communication errors efficiently .

Unit tests can ensure the correct implementation of the Singleton pattern by creating multiple instances of the Singleton class and verifying that all instances share the same state. Tests should check that changes in one instance reflect in others, indicating shared state. This involves asserting that attributes of different instances have identical values, confirming that only one state is being maintained across all instances .

Process 1 must first verify that the dictionary ID exists in process 2, then send an update request including the dictionary ID and the data to update. Potential pitfalls include network latency or failures, ensuring atomicity of updates to prevent data corruption, handling concurrency issues if multiple updates occur simultaneously, and ensuring data validation to maintain dictionary integrity .

You might also like