Question
Use iContract, a Design by Contract library for Python3, and two Python3's standard libraries xmlrpc and threading, to implement a client-server banking application. This application
Use iContract, a Design by Contract library for Python3, and two Python3\'s standard libraries xmlrpc and threading, to implement a client-server banking application. This application has three parts: a server, clients, and a test harness.
Server
The server supports the following operations
1. openAccount
2. closeAccount
3. deposit
4. withdraw
5. changeAddress
6. changePassword
7. resetPassword (for lost or forgotten passwords)
8. transfer
9. cancelTransfer
10. getStatements
11. listAccounts
The listAccounts operation is not realistic, of course, because it violates privacy, but we need it in this coursework to support transfers without needless complexity. Another simplification, in addition to listAccounts, is that users can have only one account. This server must correctly handle concurrent calls to its operations.
Client
Each client takes a starting amount of money to deposit and a probability distribution over the server\'s banking operations, augmented with the special operation noAction, which, as its name suggests, does nothing. Each client starts by opening an account and depositing money. It then loops using its probability distribution to select operations, which it performs concurrently to other clients, until the harness kills it. The clients must correctly handle errors, like attempting to withdraw more money than it has.
Action distribution: Server supports 11 operations, which grows to 12 with noAction. For this assignment, therefore, a client probability distribution is a list of 12 floats that sum to 1. This list assigns probabilities to the actions above in order, again augmented with noAction. Thus, the first element of the list is the probability of an account opening and the last element is the probability that the client takes no action.
test.py
import bisectimport randomx = random.uniform(0, 1)probs = [0.1, 0.4, 0.3, 0.2]intervals = [sum(probs[:i+1]) for i in range(len(probs))]bisect.bisect_right(intervals, x)Harness
The harness takes a number of clients to create, a list of action distributions to use when creating each client, and a bound on the simulation. It should default to creating two clients with a reasonable probability distribution over actions,i.e.one which favours deposits over closing accounts.
We will manually assess your submission. Be sure to meet the following requirements:
1. When we statically check your submission for errors using pyicontract-lint, it should be clean.
2. Your submission must include a test suite written using Hypothesis and its icontract integration.
Appendix: Guidance for icontract, threading, and xmprlc
icontract
The installation of iContract is straightforward: install icontract using pip with sudo:
sudo pip3 install icontract
Here is a brief example of icontract:
import icontract @icontract.require(lambda x: x def some_func(x: int, y: int = 5): print(x)some_func(2)threading
threading is a python built-in module, so it comes with python. Here is an example of how parallelism is handled using the threading package.
threadingTest.py
import timeimport threading# create the locklock = threading.Lock()# define a global resourceglobal_resource = [None] * 5def change_resource(para, sleep): # request the lock lock.acquire() global global_resource for i in range(len(global_resource)): global_resource[i] = para time.sleep(sleep) print(\"global_resource to be:\", global_resource) # release the lock lock.release()def main(): thread_hi = threading.Thread(target = change_resource, args = (\"hi\", 2)) thread_hello = threading.Thread(target = change_resource, args = (\"hello\", 1)) thread_hi.start() thread_hello.start()if __name__ == \'__main__\': main()xmlrpc
xmlrpc is also a python built-in module, so it is installed along with python. The scripts below are two examples showing how xmlrpc works together with icontract and threading packages.
serverSample.py
from xmlrpc.server import SimpleXMLRPCServerimport icontractimport timeimport threading# create a random functiondef add(balance, deposit): return balance + deposit@icontract.require(lambda x: x def some_func(x, y): return 2 * x + y# create the locklock = threading.Lock()# define a global resourceglobal_resource = [None] * 5def change_resource(para, sleep): # request the lock lock.acquire() global global_resource for i in range(len(global_resource)): global_resource[i] = para time.sleep(sleep) print(\"global_resource to be:\", global_resource) # release the lock lock.release()def lock_test(): thread_hi = threading.Thread(target = change_resource, args = (\"hi\", 2)) thread_hello = threading.Thread(target = change_resource, args = (\"hello\", 1)) thread_hi.start() thread_hello.start()# test the xmlrpc serverif __name__ == \'__main__\': s = SimpleXMLRPCServer((\'127.0.0.1\', 9000), allow_none = True) s.register_introspection_functions() # register the functions of \"add\", \"pow\", \"some_func\" and \"lock_test \"to the server s.register_function(add) s.register_function(pow) s.register_function(some_func) s.register_function(lock_test) # start the main loop of the server s.serve_forever()clientSample.py
from xmlrpc.client import ServerProxyif __name__ == \'__main__\': # create a client instance s = ServerProxy(\"http://127.0.0.1:9000\") print(s.system.listMethods()) # execute functions print(s.add(100, 101)) print(s.pow(2, 5)) print(s.some_func(5, 5)) print(s.lock_test())We can run serverSample.py in terminal 1 and run clientSample.py in terminal 2. If, in the clientSample.py, we have some_func(x= 2,y= 5) and the preconditionx
terminal 1
127.0.0.1 - - [17/Feb/2021 01:09:41] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:09:41] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:09:41] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:09:41] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:09:41] \"POST /RPC2 HTTP/1.1\" 200 -global_resource to be: [\'hi\', \'hi\', \'hi\', \'hi\', \'hi\']global_resource to be: [\'hello\', \'hello\', \'hello\', \'hello\', \'hello\']terminal 2
[\'add\', \'lock_test\', \'pow\', \'some_func\', \'system.listMethods\', \'system.methodHelp\', \'system.methodSignature\']201 329 NoneAnd if, in the clientSample.py, we have some_func(x= 5,y= 5), then the running outputs for the terminals are:
terminal 1
127.0.0.1 - - [17/Feb/2021 01:11:30] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:11:30] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:11:30] \"POST /RPC2 HTTP/1.1\" 200 -127.0.0.1 - - [17/Feb/2021 01:11:30] \"POST /RPC2 HTTP/1.1\" 200 -terminal 2
Fault: :File/home/bingchai/comp0103_2021/serverSample/py, line 17 in : x 3:>Sol152:
Design by Contract with iContract: Design by Contract (DbC) is a software development technique that involves specifying the behavior of software components in terms of preconditions, postconditions, and invariants. The iContract library for Python3 provides decorators for specifying preconditions and postconditions on functions and methods.
To use iContract, you need to import the library and decorate the relevant functions with the @icontract.require and @icontract.ensure decorators. For example, to specify that a function should take a positive integer argument, you can use:
import icontract
@icontract.require(lambda x: x > 0)
def some_function(x):
# ...
This specifies that the function requires its argument to be greater than zero.
Threading: The threading module in Python provides a way to create multiple threads of execution within a single program. Each thread runs in parallel with the others, allowing for concurrent execution of tasks.
To use threading, you need to create a Thread object for each task you want to perform concurrently, and then start the threads with the start(
)
method. For example, to perform two tasks concurrently, you can use:
import threading
def task1():
# ...
def task2():
# ...
thread1 = threading.Thread(target=task1)
thread2 = threading.Thread(target=task2)
thread1.start()
thread2.start()
XML-RPC: XML-RPC is a remote procedure call (RPC) protocol that uses XML to encode its calls and responses. The Python standard library includes an implementation of XML-RPC that can be used for inter-process communication.
To use XML-RPC, you need to create a server and register the functions that you want to make available to clients. You also need to create a client and use it to make requests to the server. For example:
# server.py
from xmlrpc.server import SimpleXMLRPCServer
def add(x, y):
return x + y
server = SimpleXMLRPCServer((\'localhost\', 8000))
server.register_function(add)
server.serve_forever()
# client.py
from xmlrpc.client import ServerProxy
server = ServerProxy(\'http://localhost:8000\')
result = server.add(1, 2)
print(result)
This code creates a server that exposes an add(
)
function, and a client that connects to the server and calls the add()
function with arguments 1 and 2. The result of the function call is printed to the console.
With these tools, you can start building your client-server banking application. You will need to define the functions that perform the banking operations and decorate them with preconditions and postconditions using iContract. You will also need to create a server that exposes these functions using XML-RPC, and a client that connects to the server and calls the functions with appropriate arguments. Finally, you will need to use threading to perform multiple concurrent requests to the server.
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started