Good way to writing unit test in python - python

I have simply 'to do' app in python 2.7 and I wrote for this some unit test. This is my first time with python unit test and I just wanna to know idea of unit test.
Someone can tell me whether I'm going in the right direction?
How to improve this tests?
How to check message in IndexError is correct? for this ("IndexError('Note doesn\'t exist')" or IndexError('Returned more then one entry') )
App:
# coding: utf-8
from __future__ import unicode_literals
from shutil import copyfile
import json
import os
DATABASE = 'notes_data/notes.json'
BOARDS = ['to do', 'in progress', 'done']
class NotesManagerMixin(object):
def count(self):
return len(self.notes)
def filter(self, *args, **kwargs):
result = self.notes
for key, value in kwargs.iteritems():
result = [
note for note in result
if getattr(note, key, None) == value or
note.message.startswith(str(value)) or
note.message.endswith(str(value))
]
return NotesQueryset(result)
def get(self, *args, **kwargs):
notes = self.filter(*args,**kwargs)
if notes.count() == 0:
raise IndexError('Note doesn\'t exist')
elif notes.count() == 1:
return notes[0]
else:
raise IndexError('Returned more then one entry')
def first(self):
return self.notes[0]
def last(self):
return self.notes[-1]
class NotesQueryset(NotesManagerMixin):
def __init__(self, notes):
self.notes = [note for note in notes]
def update(self, *args, **kwargs):
for note in self.notes:
for key, value in kwargs.items():
setattr(note, key, value)
note.save()
return self
def delete(self):
for note in self.notes:
note.delete()
return self
def __getitem__(self, idx):
return self.notes[idx]
def __str__(self):
return str(self.notes)
def __repr__(self):
return self.__str__()
class NotesManager(NotesManagerMixin):
def __init__(self):
self.notes = []
def __iter__(self):
return self.next()
def __generate_id(self):
"""
Funkcja pomocnicza do pobrania pewnej wolnej wartości indexu.
"""
try:
return max(note.id for note in self.notes) + 1
except ValueError:
return 1
def all(self):
return NotesQueryset(self.notes)
def add(self, idx, board, message):
self.notes.append(Note(idx=idx, board=board, message=message))
def create(self, board, message):
note = Note(
idx=self.__generate_id(),
board=board,
message=message
)
note.clean()
self.notes.append(note)
note.save()
return note
def next(self):
for note in self.notes:
yield note
def to_dict(self):
return [note.to_dict() for note in self.notes]
class Note(object):
objects = NotesManager()
def __init__(self, idx, board, message):
self.id = idx
self.board = board
self.message = message
def __str__(self):
return 'ID: {}, Board: {}, Message: {}'.format(
self.id,
self.board,
self.message
)
def __repr__(self):
return self.__str__()
def clean(self):
if not self.message:
raise ValueError('Message is required')
if self.board not in BOARDS:
raise ValueError('Board "{}" doesn\'t exists'.format(self.board))
if type(self.id) != int:
raise ValueError('Note id "{}" is invalid'.format(self.id))
def save(self):
for key, note in enumerate(self.objects):
if note.id == self.id:
self.objects.notes[key] = self
break
with open(DATABASE, 'w') as database_file:
json.dump(self.objects.to_dict(), database_file, indent=4)
return True
def delete(self):
for key, note in enumerate(self.objects.notes):
if note.id == self.id:
self.objects.notes.pop(key)
with open(DATABASE, 'w') as database_file:
json.dump(self.objects.to_dict(), database_file, indent=4)
def to_dict(self):
return {
'id': self.id,
'message': self.message,
'board': self.board
}
def load_initial_data():
with open(DATABASE, 'r') as database_file:
json_data = json.load(database_file, encoding='utf-8')
for item in json_data:
Note.objects.add(
idx=item['id'],
board=item['board'],
message=item['message'],
)
load_initial_data()
unit tests:
import unittest
from notes_manager_v2 import NotesQueryset, Note, load_initial_data, NotesManagerMixin
class TestNotesQueryset(unittest.TestCase):
def test_filter(self):
actual = Note.objects.all().filter(board='in progress') # get all notes with board 'in progress'
expected = []
for note in Note.objects.all():
if note.board == 'in progress':
expected.append(note)
self.assertItemsEqual(actual, expected)
def test_get(self):
actual = [Note.objects.all().get(id=1)] # get note with method get
expected = []
for note in Note.objects.all(): # search note with index 1
if note.id == 1:
expected.append(note)
self.assertEqual(actual, expected)
def test_get_fail_1(self):
self.assertRaises(IndexError, lambda:Note.objects.all().get(id=9868976)) # thos note dont exist should raise IndexError
def test_update(self):
from_board = 'to do'
to_board = 'done'
before_change = Note.objects.filter(board=from_board) # use filter method to get all notes with board 'to do'
actual = Note.objects.filter(board=from_board).update(board=to_board) # update all board
self.assertNotEqual(before_change, actual) # check for difference
notes = Note.objects.all()
for note in actual:
self.assertIn(note, notes) # check notes are updated
def test_delete(self):
to_delete = Note.objects.filter(id=2).delete() # find note with filter method and delete it
notes = Note.objects.all()
self.assertNotIn(to_delete, notes)
def test_create(self):
new_note = Note.objects.create(message='lorem ipsum', board='in progress') # create new note
notes = Note.objects.all()
self.assertIn(new_note, notes) #
if __name__ == '__main__':
unittest.main()

Have you looked at the documentation? See doctest. They are an easy way to integrate unit tests into python code. Another option is the unittest framework.

Related

Code not hashing? <bound method Block.hash of <__main__.Block object at 0x032F04A8>>

I'm trying to make my own cryptocurrency and blockchain. Made a few edits and now it's not hashing?
Can someone with hashlib knowledge help me?
# -*- coding: utf-8 -*-
from hashlib import sha256
def updatehash(*args):
hashing_text = ""; h = sha256()
for arg in args:
hashing_text += str(args)
h.update(hashing_text.encode("utf-8"))
return h.hexdigest()
class Block():
data = None
hash = None
nonce = 0
previous_hash = "0" * 64
def __init__(self, data, number=0):
self.data = data
self.number = number
def hash(self):
return updatehash(
self.number,
self.previous_hash,
self.data,
self.nonce,
)
def __str__(self):
return str("Block#: %s\nHash: %s\nPrevious: %s\nData: %s\nNonce: %s\n" %(
self.number,
self.hash,
self.previous_hash,
self.data,
self.nonce
)
)
class Blockchain():
difficulty = 4
def __init__(self, chain=[]):
self.chain = chain
def add(self, block):
self.chain.append(block)
def remove(self, block):
self.chain.remove(block)
def mine(self, block):
try: block.previous_hash = self.chain[-1].hash()
except IndexError: pass
while True:
if block.hash()[:self.difficulty] == "0" * self.difficulty:
self.add(block); break
else:
block.nonce += 1
def main():
blockchain = Blockchain()
database = ["hello", "goodbye", "test", "DATA here"]
num = 0
for data in database:
num += 1
blockchain.mine(Block(data, num))
for block in blockchain.chain:
print(block)
if __name__ == '__main__':
main()
This is what the first block looks like. The hash isn't showing but only saying
main.Block object at 0x032C9AA8>>...
Hash: <bound method Block.hash of <__main__.Block object at 0x032C9AA8>>
Previous: 0000000000000000000000000000000000000000000000000000000000000000
Data: hello
Nonce: 78787
I need help in getting the Hash to work again...
def hash(self):
hash is a function. You forgot a () to get the result:
def __str__(self):
return str("Block#: %s\nHash: %s\nPrevious: %s\nData: %s\nNonce: %s\n" %(
self.number,
self.hash(),
self.previous_hash,
self.data,
self.nonce
)
)

Proxy Class in Python 3

I wrote a simple Proxy class in python3, but I have a problem with "was_called" function
class Proxy:
last_invoked = ""
calls = {}
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item):
attrs = dir(self._obj)
if item in attrs:
Proxy.last_invoked = item
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
return getattr(self._obj, item)
else:
raise Exception('No Such Method')
def last_invoked_method(self):
if Proxy.last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return Proxy.last_invoked
def count_of_calls(self, method_name):
if method_name in Proxy.calls.keys():
return Proxy.calls[method_name]
return 0
def was_called(self, method_name):
if method_name in Proxy.calls.keys():
if Proxy.calls[method_name] > 0: return True
return False
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Radio()
radio_proxy = Proxy(radio)
radio.number = 3
radio_proxy.number = 3
radio_proxy.power()
print(radio_proxy.was_called("number"))
print(radio_proxy.was_called("power"))
"was_called" function is work for functions and attributes that is in radio at first such as "power", but it's not work for new attributes that we add such as "number".
I expect for both print "True", because both of "power" and "number" is called. but first print return False!
What do you suggest?
def Proxy(class_type):
class ProxyClass(class_type):
def __init__(self, *args, **kwargs):
# Set your _calls and _last_invoked here, so that they are not class attributes (and are instead instance attributes).
self._calls = {}
self._last_invoked = ""
# Pass the arguments back to the class_type (in our case Radio) to initialize the class.
super().__init__(*args, **kwargs)
def __getattribute__(self, item):
# We must do this prelimary check before continuing on to the elif statement.
# This is since _calls and _last_invoked is grabbed when self._last_invoked/self._calls is called below.
if item in ("_calls", "_last_invoked"):
return super(ProxyClass, self).__getattribute__(item)
elif not item.startswith("_"):
self._last_invoked = item
self._calls[item] = 1 if item not in self._calls.keys() else self._calls[item] + 1
return super(ProxyClass, self).__getattribute__(item)
def __setattr__(self, item, val):
# Wait until _calls is initialized before trying to set anything.
# Only set items that do not start with _
if not item == "_calls" and not item.startswith("_"):
self._calls[item] = 0
super(ProxyClass, self).__setattr__(item, val)
def last_invoked_method(self):
if self._last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return self._last_invoked
def count_of_calls(self, method_name):
return self._calls[method_name] if method_name in self._calls.keys() else 0
def was_called(self, method_name):
return True if method_name in self._calls.keys() and self._calls[method_name] > 0 else False
return ProxyClass
#Proxy
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Proxy(Radio)()
radio.number = 3 # Notice that we are only setting the digit here.
radio.power()
print(radio._calls)
print(radio.number) # Notice that this when we are actually calling it.
print(radio._calls)
outputs:
{'is_on': 0, 'volume': 0, 'number': 0, 'power': 1}
3
{'is_on': 0, 'volume': 0, 'number': 1, 'power': 1}
A few modifications here and there, but you should be able to see the bigger idea by reading through the code. From here you should be able to modify the code to your liking. Also note that any variable that starts with _ is automatically removed from the _calls dictionary.
If you rather not use the decorator #Proxy, you may initialize your Radio class (as a proxy) like so:
# Second parentheses is where your Radio args go in.
# Since Radio does not take any args, we leave it empty.
radio_proxy = Proxy(Radio)()
Also, make sure to understand the difference between class attributes, and instance attributes.
Edit:
class Test:
def __init__(self, var):
self.var = var
self.dictionary = {}
def __getattribute__(self, item):
print("we are GETTING the following item:", item)
# If we don't do this, you end up in an infinite loop in which Python is
# trying to get the `dictionary` class to do `self.dictionary['dictionary'] = ...`
if item == "dictionary":
super(Test, self).__getattribute__(item)
else:
self.dictionary[item] = "Now we can use this!"
return super(Test, self).__getattribute__(item)
def __setattr__(self, item, key):
print("we are SETTING the following item:", item)
super(Test, self).__setattr__(item, key)
Notice:
test = Test(4)
outputs:
we are SETTING the following item: var
we are SETTING the following item: dictionary
then following it:
test.var
outputs:
we are GETTING the following item: var
we are GETTING the following item: dictionary

Unable to import Excel to database

I have a system whereby it allows to import the student attendance file into the system. However, it did not import it successfully. I tried to debug and i found out that when it goes to the importer.py function, it does not go to the "def _do_save(self, row)", hence that is the problem why it was not saved into database.
importer.py
class AttendanceImporter(CsvImporter):
field_names=["username", "mark"]
#it does not go to the method.
def _handle_row(self,row):
print("this")
if (not self._is_row_valid(row)):
return self._FAILED
username=row["username"]
if (self._is_username_exist(username)):
if (self._is_mark_exist(username)):
if (self._do_update(row)):
return self._UPDATED
else:
return self._FAILED
else:
if (self._do_save(row)):
return self._CREATED
else:
return self._FAILED
else:
return self._FAILED
def _is_row_valid(self, row):
for item in self.field_names:
if (len(row[item])==0):
return False
return True
def _is_username_exist(self, username):
return len(User.objects.filter(username=username))>0
print(username)
def _is_mark_exist(self, username):
user = User.objects.get(username=username)
return len(Attendance.objects.filter(user=user))>0
print(username)
def _do_save(self, row):
# create attendace mark
try:
attendance=Attendance()
user=User.objects.get(username=row["username"])
attendance=Attendance.objects.create(user=user, mark=row["mark"])
print("save?")
attendance.save()
except:
return False
return True
def _do_update(self, row):
# update attendance mark
try:
user=User.objects.get(username=row["username"])
attendance=Attendance.objects.get(user=user)
attendance.mark = row["mark"]
attendance.save()
except Exception as e:
print(e)
return False
return True
Views.py
#transaction.atomic
#csrf_exempt
def data_import(request, file_type):
# TODO: currently only support importing users, later can support importing groups
fields_required = None
if(file_type == "user"):
fields_required = "username, password, email, matric_number, fullname, groups"
elif (file_type == "attendance"):
fields_required = "username, mark"
if request.FILES:
successful = False
try:
im = None
if(file_type == "user"):
upload_file = request.FILES['user_file']
file_path = save_uploaded_file(request.FILES['file'], filename=generate_unique_file_name(extension="csv"),
filedir=USER_DATA_UPLOAD_PATH)
im = StudentImporter(source=open(file_path))
elif (file_type == "attendance"):
upload_file = request.FILES['attendance_file']
file_path = save_uploaded_file(upload_file, filename=generate_unique_file_name(extension="csv"),
filedir=USER_DATA_UPLOAD_PATH)
im = AttendanceImporter(source=open(file_path))
successful, result = im.import_to_database()
except Exception:
pass
if successful:
messages.info(request, "The import is successful!\n" + result)
else:
messages.warning(request, "The import is NOT successful, no data is imported!")
return HttpResponseRedirect(reverse("student_user_profile_list"))
return render(request,
"app-appglobal/import-data.html",
{'type': file_type, 'fields_required': fields_required})
Model.py:
class Attendance(models.Model):
user=models.OneToOneField(User, on_delete=models.CASCADE)
mark=models.IntegerField(default=0)
class Meta:
ordering=['user']
def get_mark(self):
return self.mark
Urls
url(r'^student/attendance/$' ,views.data_import,{'file_type':'attendance'},name='attendance_import'),
CSVIMPORTER
class CsvImporter(object, metaclass=ABCMeta):
def __init__(self, source):
"""
:param source: a file object
:return:
"""
self.source=source
def import_to_database(self):
"""
:return: (successful:boolean,result:string)
"""
pass
example:

Making the file_names unique with timestamp in django image upoads

Intro: I have a small piece of code that takes any image that is being added and makes it smaller and saves it. I am using a external library called Filepond for this.
The Issue: If 2 users add same names to their images(different images). The second users image replaces the 1st users image and both users see the same image.
What I want: Add unique image names. My tries are below the present code. I need the best solution for this so the names are not too big but are unique
Present Code:
fields.py:
class FilePondField(forms.FileField):
widget = forms.TextInput(attrs={'class': 'fileid'})
def __init__(self, name, *args, **kwargs):
super(FilePondField, self).__init__(*args, **kwargs)
self.name = name
def prepare_value(self, data):
if not data:
return None
if isinstance(data, str):
try:
tu = TU.objects.get(upload_id=data)
except TU.DoesNotExist:
return None
return tu.upload_id
name = data.name
base = os.path.basename(name)
file_id = "%s_%s" % (self.name, data.instance.pk)
try:
tu = TU.objects.get(file_id=file_id)
except TU.DoesNotExist:
upload_id = uuid()
tu = TU(upload_id=upload_id, file_id=file_id, # uuid(),
upload_name=base, upload_type=TU.FILE_DATA)
try:
with data.storage.open(name, 'rb') as f:
rd_data = File(f)
tu.file.save(tu.file_id, rd_data, True)
tu.save()
except:
pass
return tu.upload_id
def clean(self, data, initial=None):
self.initial = initial
if not data:
if self.required:
raise ValidationError(self.error_messages['required'], code='required')
return None
return data
def save_cb(self, instance, modfld, tu):
prename = os.path.join(modfld.upload_to, tu.upload_name)
ffile = ImageFieldFile(instance, modfld, prename)
try:
with open(tu.get_file_path(), 'rb') as f:
data = File(f)
ffile.save(tu.upload_name, data, False)
except:
pass
return ffile
def do_tmp(self, instance, modfld, value, cb):
try:
tu = TU.objects.get(upload_id=value)
ffile = cb(instance, modfld, tu) if cb else None
except TU.DoesNotExist:
ffile = None
else:
tu.delete()
file_id = "%s_%s" % (self.name, instance.pk)
try:
ogtu = TU.objects.get(file_id=file_id)
except TU.DoesNotExist:
pass
else:
ogtu.delete()
return ffile
def save(self, instance, modfld, value):
return self.do_tmp(instance, modfld, value, self.save_cb)
def del_tmp(self, instance, modfld, value):
self.do_tmp(instance, modfld, value, None)
def bound_data(self, data, initial):
return data
def has_changed(self, initial, data):
if not initial:
return data
return initial != data
forms.py
class ImageForm(forms.ModelForm):
img_fields = []
def __init__(self, *args, **kwargs):
super(ImageForm, self).__init__(*args, **kwargs)
for (fld, fargs) in self.img_fields:
self.fields[fld] = FilePondField(fld, **fargs)
def save(self, *args, **kwargs):
commit = kwargs.get('commit', True)
for (fld_nm, fargs) in self.img_fields:
fld = dict([(f.name, f) for f in self._meta.model._meta.fields])[fld_nm]
if isinstance(self.fields[fld_nm], FilePondField):
self.fields[fld_nm] = self.fields[fld_nm].save(self.instance, fld, self.cleaned_data[fld_nm])
return super(ImageForm, self).save(*args, **kwargs)
def del_tmp (self):
for (fld_nm, fargs) in self.img_fields:
fld = dict([(f.name, f) for f in self._meta.model._meta.fields])[fld_nm]
if isinstance(self.fields[fld_nm], FilePondField):
self.fields[fld_nm].del_tmp(self.instance, fld, self.cleaned_data[fld_nm])
My Approach:
in fields.py I import
In the function def prepare_value(self, data): and def do_tmp(self, instance, modfld, value, cb): I make the below changes
...
file_id = "%s_%s_%s" % (self.name, data.instance.pk, datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f"))
...
Can someone comment on this or suggest a better alternative
Just use datetime.now() value for the file name such as below:
from datetime import datetime
open(str(datetime.now()) + ".txt", "w+")
Result: It creates a file named 2019-04-22 00:21:31.862001.txt
make the name a variable like this
name = "Your-General-Name-{}".format((int(time.time())))
and then put it in your loop so that the time.time() value changes each time. You obviously dont have to use time.time(). you could use datetime.datetime.now() etc. but then you'd just replace the time function.

How to use the methods of the parent class?

Here the SMS_Store inherits the Message, but every time I call the parent method there is the error:
NoneType object does not have attribute 'read_message'.
and also the object stored in the list returns a None value??
class Message():
def __init__(self, from_number, time_arrived, text_of_sms):
self.has_been_viewed=False
self.from_number= from_number
self.time_arrived = time_arrived
self.text_of_sms = text_of_sms
def getTime(self):
return self.time_arrived
def getText(self):
return self.text_of_sms
def setView(self):
self.has_been_viewed = True
def check_read(self):
return self.has_been_viewed
class SMS_Store(Message):
def __init__(self):
self.list_=[]
def add_new_arrivals(self,from_number, time_arrived, text_of_sms):
self.list_.append(Message.__init__(self,from_number,time_arrived,text_of_sms))
def message_count(self):
return len(self.list_)
def get_unread_index(self):
unread =[]
for i in range(len(self.list_)):
if(super(SMS_Store,self).check_read() == False):
unread.append(i)
return unread
def read_message(self,index):
self.list_[index].getText()
def delete(self,ints):
a = int(ints)
self.list_.pop(a)
def clear():
for i in list:
self.list_.pop(i)
inbox = SMS_Store()
inbox.add_new_arrivals(12,9,'some nights i stay up')
inbox.add_new_arrivals(12,5,'cashing in my bad luck')
inbox.add_new_arrivals(14,7,'somenights i call it a draw')
inbox.add_new_arrivals(56,0,'somenights i wish that my lips could built a castle.')
inbox.read_message(0)
It's unclear where that error could be coming from since read_message() is only called by the inbox.read_message(0) statement at the end of your code, which wouldn't produce it (although the return value of the read_message() method is always None, since there's no explicit return statement in it).
Regardless, to answer the titular question, to do that explicitly you would use the super() method—but that's not the problem here which is that the SMS_Store class shouldn't be a subclass of Message at all, since it's not a specialization of the Message class...rather it's a container of Message instances.
Below is reworked version of your code that shows how to separate these two classes and get most if not all of the methods working. It also now mostly follows PEP 8 - Style Guide for Python Code recommendations.
class Message:
def __init__(self, from_number, time_arrived, text_of_sms):
self.has_been_viewed = False
self.from_number = from_number
self.time_arrived = time_arrived
self.text_of_sms = text_of_sms
def get_time(self):
return self.time_arrived
def get_text(self):
return self.text_of_sms
def set_viewed(self):
self.has_been_viewed = True
def check_read(self):
return self.has_been_viewed
class SMS_Store:
def __init__(self):
self.list_ = []
def add_new_arrival(self, from_number, time_arrived, text_of_sms):
self.list_.append(Message(from_number, time_arrived, text_of_sms))
def message_count(self):
return len(self.list_)
def get_unread_index(self):
unread = []
for (i, message) in enumerate(self.list_):
if not message.check_read():
unread.append(i)
return unread
def read_message(self, index):
message_text = self.list_[index].get_text()
self.list_[index].set_viewed()
return message_text
def delete(self, index):
self.list_.pop(index)
def clear():
self.list_.clear()
inbox = SMS_Store()
inbox.add_new_arrival(12, 9, 'some nights i stay up')
inbox.add_new_arrival(12, 5, 'cashing in my bad luck')
inbox.add_new_arrival(14, 7, 'somenights i call it a draw')
inbox.add_new_arrival(56, 0, 'somenights i wish that my lips could built a castle.')
print(inbox.get_unread_index()) # -> [0, 1, 2, 3]
print(inbox.read_message(0)) # -> some nights i stay up
print(inbox.get_unread_index()) # -> [1, 2, 3]
You shouldn't use inheritance in this case. Here's corrected version (also fixed code style):
class Message:
def __init__(self, from_number, time_arrived, text_of_sms):
self.has_been_viewed = False
self.from_number = from_number
self.time_arrived = time_arrived
self.text_of_sms = text_of_sms
def get_time(self):
return self.time_arrived
def get_text(self):
return self.text_of_sms
def set_view(self):
self.has_been_viewed = True
def check_read(self):
return self.has_been_viewed
class SMSStore:
def __init__(self):
self._list = []
def add_new_arrivals(self, from_number, time_arrived, text_of_sms):
self._list.append(Message(from_number, time_arrived, text_of_sms))
def message_count(self):
return len(self._list)
def get_unread_index(self):
return list(filter(lambda m: not m.check_read(), self._list))
def read_message(self, index):
return self._list[index].get_text()
def delete(self, index):
a = int(index)
self._list.pop(a)
def clear(self):
self._list = []
inbox = SMSStore()
inbox.add_new_arrivals(12, 9, 'some nights i stay up')
inbox.add_new_arrivals(12, 5, 'cashing in my bad luck')
inbox.add_new_arrivals(14, 7, 'somenights i call it a draw')
inbox.add_new_arrivals(56, 0, 'somenights i wish that my lips could built a castle.')
print(inbox.read_message(0))

Categories

Resources