V&V: Design by Contract Coursework Due March 15, 2021 @ 4:00PM (UK time) (5% of the Total Course Mark) 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. 1 import bisect import random x = 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. Assessment We will manually assess your submission. Be sure to meet the following require- ments: 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 icon- tract integration. 3. DbC does not require loop invariants. Nonetheless, for this assignment, formulate loop invariants for each loop in each component in your coursework. Do not publish your solution to GitHub or any similar platform, even after the module completes. You are, of course, welcome, even encouraged, to use private repos for this coursework. 2 Appendix: Guidance for icontract, threading, and xm- prlc 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 < 3) 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. import time import threading # create the lock lock = threading.Lock() # define a global resource global_resource = [None] * 5 def 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() 3 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 SimpleXMLRPCServer import icontract import time import threading # create a random function def add(balance, deposit): return balance + deposit @icontract.require(lambda x: x < 3) def some_func(x, y): return 2 * x + y # create the lock lock = threading.Lock() # define a global resource global_resource = [None] * 5 def 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 server if __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) 4 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 ServerProxy if __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 precondition x < 3, the running outputs for server and client terminals follow: 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 32 9 None And 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 - 5 terminal 2 Fault:
:File /home/bingchai/comp0103_2021/serverSample/py, line 17 in :\nx<3: x was 5"> 6 欢迎咨询51作业君