├── .gitignore ├── .project ├── .pydevproject ├── README ├── basic_examples ├── extra_stuff.py ├── my_module.py ├── python_booleans.py ├── python_control.py ├── python_conversions.py ├── python_dictionary.py ├── python_exceptions.py ├── python_files.py ├── python_functions.py ├── python_lists.py ├── python_logical_lines.py ├── python_modules.py ├── python_numbers.py ├── python_objects.py ├── python_os_module.py ├── python_read.py ├── python_sequences.py ├── python_star_args_and_kwargs.py ├── python_string_interpolation.py ├── python_strings.py ├── python_tuples.py └── python_write.py ├── mini_projects └── adaptive_backup.py └── tdd ├── TDDWithPython.odp ├── iostubbing ├── all_tests.py ├── test_write_user_details.py ├── test_write_user_details_iostub.py ├── test_write_user_details_istub.py ├── test_write_user_details_mock.py ├── write_user_details.py └── write_user_details_di.py ├── tdd_python_plan.txt └── transformations ├── MailerProg.py ├── Msg2Xml.py ├── TestMailerProg.py ├── VerifyServers.py ├── email1.msg ├── email2.msg ├── email3.msg ├── email4.msg └── email5.msg /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | python-examples 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.wst.common.project.facet.core.builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.wst.common.project.facet.core.nature 21 | org.python.pydev.pythonNature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Default 6 | python 2.7 7 | 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | **About** 2 | 3 | This project contains a series of Python examples to help someone new to Python learn how to program in Python. 4 | 5 | I am listing the order in which you may want to read these examples. The order below starts with simple concepts, moving on to more advanced ones. 6 | 7 | **Introduction To Python** 8 | 9 | - python_strings.py 10 | - python_numbers.py 11 | - python_booleans.py 12 | - python_conversions.py 13 | - using_modules.py 14 | - string_interpolation.py 15 | - python_lists.py 16 | - python_dictionary.py 17 | - python_tupes.py 18 | - python_sequences,py 19 | - python_functions.py 20 | - python_logical_lines.py 21 | - python_string_interpolation.py 22 | - python_modules.py 23 | - python_os_module.py 24 | - python_star_args_and_kwargs.py 25 | - python_objects.py 26 | 27 | **TDD With Python** 28 | 29 | Slides for introduction to TDD With Python 30 | 31 | Code Samples 32 | 33 | - write_user_details.py 34 | - test_write_user_details.py 35 | - test_write_user_details_istub.py 36 | - test_write_user_details_iostub.py 37 | - write_user_details_di.py 38 | - test_write_user_details_mock.py 39 | -------------------------------------------------------------------------------- /basic_examples/extra_stuff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | 5 | #If there is only one statement that would have gone in the block then it can 6 | #be specified in the same line 7 | if os.name == 'posix': print 'You are cool' 8 | 9 | def say_hello(): return 'hello' 10 | print say_hello() 11 | 12 | #List comprehensions 13 | class Employee: 14 | 15 | def __str__(self): 16 | return self.name + ' ' + str(self.salary) 17 | 18 | def __init__(self, name, salary): 19 | self.name = name 20 | self.salary = salary 21 | 22 | 23 | employees = [] 24 | employees.append(Employee('Tom', 30000)) 25 | employees.append(Employee('Satish', 40000)) 26 | employees.append(Employee('Harry', 50000)) 27 | 28 | #Let's get a list of all employees whose salaries are greater than 30000 29 | #using list comprehensions 30 | sal_more_than_40k = [e for e in employees if e.salary > 40000] 31 | print 'Listing ' + str(len(sal_more_than_40k)) + ' employees with sal > 40k' 32 | for emp in sal_more_than_40k: 33 | print emp 34 | 35 | #Now let's get something similar using lambda expressions 36 | sal_more_than_30k = filter(lambda e : e.salary > 30000, employees) 37 | print "Listing " + str(len(sal_more_than_30k)) + " employees with sal > 30k" 38 | for emp in sal_more_than_30k: 39 | print emp 40 | 41 | #`` and repr return a canonical form of an object in the form if a string 42 | #We can control what repr returns with the __repr__() method 43 | print repr(sal_more_than_30k) 44 | print `sal_more_than_30k` 45 | eval(repr(sal_more_than_30k)) 46 | -------------------------------------------------------------------------------- /basic_examples/my_module.py: -------------------------------------------------------------------------------- 1 | def sayHello(): 2 | print 'Hello' 3 | 4 | print 'Module ', __name__, ' initialized' 5 | -------------------------------------------------------------------------------- /basic_examples/python_booleans.py: -------------------------------------------------------------------------------- 1 | print "Python has 10 type of booleans", True, " and ", False 2 | print "They are normally the result of comparisons" 3 | 4 | num = 8 5 | if num < 10: 6 | print num, " is less than 10" 7 | else: 8 | print num, " is not less than 10" 9 | 10 | 11 | print "What we just saw are conditional statements. 12 | print "It's ok if you do not understand them, we will look into them in greater details very soon." 13 | -------------------------------------------------------------------------------- /basic_examples/python_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | age = int(raw_input('Enter your age ')) 4 | #If else statements in Python 5 | if age < 6: 6 | print 'Hello little one' 7 | elif age >= 6 and age < 10: 8 | print 'Are you enjoying school?' 9 | elif age >= 10 and age < 13: 10 | print 'You are a Tween now' 11 | elif age >= 13 and age < 20: 12 | print 'Now you are officially a teenager' 13 | else: 14 | print 'Welcome to the real world' 15 | 16 | #PYTHON DOES NOT HAVE A SWITCH STATEMENT 17 | 18 | #Python while loop 19 | number = age 20 | fact = 1 21 | st = 'abc' 22 | while(age < 2): 23 | fact = fact * age 24 | age = age - 1 25 | else: 26 | print 'Python while loops have a redundant else' 27 | print 'factorial of your age is: ', fact 28 | 29 | #The for loop in Python essentially iterates over a sequence. This is very 30 | #similar to the for(i : collection) loop in Java 31 | print 'printing all integers from 1 to 5' 32 | for i in range(1,5): 33 | print i 34 | 35 | #We can also have a stepping factpr in the iteration 36 | print 'printing alternate numbers from 1 to 10' 37 | for i in range(1,11, 2): 38 | print i 39 | 40 | #Python also has the break and continue statements which work just like in Java 41 | -------------------------------------------------------------------------------- /basic_examples/python_conversions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #num is of type int 4 | num = 8 5 | #eight is of type str 6 | eight = str(num) 7 | print "eight is of type ", type(eight) 8 | 9 | #num8 is of type int 10 | num8 = int(eight) 11 | print "num8 is of type ", type(num8) 12 | 13 | t = True 14 | strt = str(t) 15 | print "strt is of type ", type(strt) -------------------------------------------------------------------------------- /basic_examples/python_dictionary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #A Python dictionary is a collection which contains key value pairs, where the 4 | #key must be an immutable object 5 | 6 | # you can also run this in the interpretor 7 | 8 | phonebook = {'joe':'568-564-1109', 'ashish':'657-097-7862'} 9 | 10 | print "\nPrinting the phonebook" 11 | print phonebook 12 | print "\nPrinting joe's phone number" 13 | phonebook['joe'] 14 | 15 | print "\nAdding an element to the phonebook" 16 | phonebook['joel'] = '657' 17 | print "\nPrinting the phone number of the newly added contact joel" 18 | print phonebook['joel'] 19 | 20 | print "\nItearating across the phonebook with it's keys" 21 | for k in phonebook.keys(): 22 | print k, phonebook[k] 23 | 24 | print "\nHere's how we remove an element from the dictionary - removing 'joel'" 25 | del(phonebook['joel']) 26 | 27 | print "\nIterating across the phonebook with key and value" 28 | for k, v in phonebook.items(): 29 | print k, v 30 | 31 | 32 | -------------------------------------------------------------------------------- /basic_examples/python_exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | class MyMethodNotImplementedError(Exception): 4 | def __init__(self): 5 | Exception.__init__(self) 6 | 7 | #We will raise an Error if this method is called. The purpose is to show which 8 | #Error to use to signal that a method os not yet implemented and also to show 9 | #how to raise Exceptions 10 | def some_method(): 11 | raise NotImplementedError 12 | 13 | def another_method(): 14 | raise MyMethodNotImplementedError() 15 | 16 | try: 17 | some_method() 18 | except NotImplementedError, e: 19 | print 'Method is not implemented ', e 20 | 21 | another_method() 22 | -------------------------------------------------------------------------------- /basic_examples/python_files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | 5 | def read_file(name): 6 | print 'printing contents of file ' + name 7 | f = None 8 | #We use try...finally to ensure that the file is closed even if there 9 | #is a problem while processing it 10 | try: 11 | f = file(name, 'r') 12 | line = f.readline() 13 | while(len(line) != 0): 14 | print line, #notice the , to prevent printing a newline character 15 | line = f.readline() 16 | finally: 17 | if f != None: 18 | f.close() 19 | 20 | def create_module(name): 21 | print 'creating module ' + name 22 | f = None 23 | #We use try...finally to ensure that the file is closed even if there 24 | #is a problem while processing it 25 | try: 26 | f = file(name, 'w') 27 | #Notice that a newline is not inserted automatically 28 | #There is no println in Python 29 | f.write('#!/usr/bin/python\n') 30 | f.write('# Filename : ' + name + '\n') 31 | f.write("'''Class comment to be completed.'''\n") 32 | f.write('\n') 33 | f.write('def __init__():\n') 34 | f.write('\n') 35 | f.write('def __str__():\n') 36 | f.write('\n') 37 | finally: 38 | if f != None: 39 | f.close() 40 | 41 | name = '/home/pshah/tmp/tmp.py' 42 | #Use exceptions to make the program fail gracefully and provide a meaningful 43 | #message to the user 44 | try: 45 | create_module('/home/pshah/tmp1/tmp.py') 46 | read_file(name) 47 | #Remove the file so we do not pollute the disk 48 | os.remove(name) 49 | except Exception, e: 50 | print 'An error ocurred in this program ', e 51 | -------------------------------------------------------------------------------- /basic_examples/python_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | maxCalled = 0 4 | minCalled = 0 5 | 6 | 7 | 8 | 9 | def max_val(a,b): 10 | '''Returns the maximum of the specified arguments. 11 | 12 | Arguments must be numbers''' 13 | 14 | global maxCalled 15 | maxCalled = maxCalled + 1 16 | 17 | if(a > b): 18 | return a 19 | elif(b > a): 20 | return b 21 | else: 22 | return a 23 | 24 | def min_val(a,b): 25 | '''Returns the minimum of the specified arguments. 26 | 27 | Arguments must be numbers''' 28 | 29 | global minCalled 30 | minCalled = minCalled + 1 31 | 32 | if(a < b): 33 | return a 34 | elif(b < a): 35 | return b 36 | else: 37 | return a 38 | 39 | def print_usage(init_msg, max_val=True, min_val=True): 40 | global maxCalled, minCalled 41 | print init_msg 42 | if max_val: 43 | print 'functin max_val was called', maxCalled, ' times' 44 | if min_val: 45 | print 'function min_val was called', minCalled, ' times' 46 | 47 | 48 | print 'Calling function max_val' 49 | print max_val.__doc__ 50 | max_val(1,4) 51 | max_val(2,b=1) 52 | max_val(b=4,a=3) 53 | 54 | print 'Calling function min_val' 55 | print min_val.__doc__ 56 | min_val(1,4) 57 | min_val(2,4) 58 | min_val(4,b=9) 59 | 60 | print_usage('The usage of functions min_val and max_val') 61 | -------------------------------------------------------------------------------- /basic_examples/python_lists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | jvm_langs = ['Java', 'Jython', 'Groovy', 'Scala', 'Jruby'] 4 | 5 | #How many JVM langs do you know ? 6 | print 'I know of ', jvm_langs.__len__(), 'langs that can run on the JVM' 7 | 8 | #It's not a good idea to directly us __xxx__ methods 9 | #A better way is. Remember there is usually a top level function which 10 | #is the idoimatic way to access the __xxx__method 11 | print 'I know of ', len(jvm_langs), 'langs that can run on the JVM' 12 | 13 | print 'Oops I forgot Clojure' 14 | jvm_langs.append('Clojure') 15 | 16 | #Let's iterate across the list 17 | for lang in jvm_langs: 18 | print lang 19 | 20 | #Can we get the 3rd element of the list ? 21 | print "The 3rd JVM language is ", jvm_langs[2] 22 | print "The first 3 JVM languages are ", jvm_langs[:3] 23 | print "The 2nd to 4th JVM languages are ", jvm_langs[1:4] 24 | 25 | print "let's sort these languages" 26 | jvm_langs.sort() 27 | print jvm_langs 28 | 29 | 30 | -------------------------------------------------------------------------------- /basic_examples/python_logical_lines.py: -------------------------------------------------------------------------------- 1 | #This physical like is also a logical line 2 | i = 3 3 | print i 4 | 5 | #This physical line actually contrains two logical lines 6 | i = 8; j = 0 7 | print i + j 8 | 9 | #Now we show multiple physical lines that represent one logical line 10 | print "this line may appear to be have a newline\ 11 | but in reality it does not" 12 | -------------------------------------------------------------------------------- /basic_examples/python_modules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import my_module 5 | 6 | #printing all the command line arguments provided to this program 7 | print "cmd args to this program are '", sys.argv, "'" 8 | 9 | print 'The PYTHONPATH is ', sys.path 10 | 11 | #Let's call a function from my_module 12 | my_module.sayHello() 13 | -------------------------------------------------------------------------------- /basic_examples/python_numbers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | print "8 is an integer ", 8 4 | 5 | num = 8 6 | print "8 is an integer and 'num' is a variable referring to an integer", num 7 | 8 | print "8/4 is 2. let's check ", num/4 9 | print "But why is 8/3 also 2 ? ", num/3 10 | print """ 11 | Maybe because when integers are divided, the result is also an integer. 12 | Which means the result is truncated. Maybe if we used floats we might get 13 | a better answer. 14 | """ 15 | 16 | num = 8.0 17 | print "8.0/3.0 = ", num/3.0 18 | 19 | print """ 20 | ok great. BTW I hope you realized how we were able to assign 'num' to a float 21 | after assigning it to an int. This is because Python is not a statically 22 | typed language. We could have assigned 'num' to a String as well. 23 | """ 24 | 25 | #What is the default type of numbers in Python... compare this with Groovy 26 | max64BitInt = 2**64 - 1 27 | bigNum = max64BitInt + 126 28 | print 'This is a big number ', bigNum 29 | 30 | #Python has the floor dividion operator as well as the power operator 31 | #I do not think Java has any of these. Groovy probably has the power op 32 | print '4//3 = ', 4//3 33 | 34 | #See the GroovyNumbers program and verify that all concepts appear here as well 35 | -------------------------------------------------------------------------------- /basic_examples/python_objects.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | class InvoiceItem: 4 | '''An item within the invoice''' 5 | def __str__(self): 6 | return self.text + ' ' + str(self.amt) 7 | 8 | def __init__(self, **kwargs): 9 | self.text = '' 10 | self.amt = 0 11 | if kwargs['text'] != None: 12 | self.text = kwargs['text'] 13 | if kwargs['amt'] != None: 14 | self.amt = kwargs['amt'] 15 | 16 | class Invoice: 17 | # This is a static variable 18 | my_company_name = "DIYComputerScience" 19 | 20 | # This is a static method 21 | @staticmethod 22 | def get_service_tax_per(): 23 | return 12.5 24 | 25 | '''An invoice.''' 26 | def __str__(self): 27 | return self.number + ' ' + str(self.amt()) 28 | 29 | def __init__(self, **kwargs): 30 | self.number = '' 31 | self.client = '' 32 | self.date = '' 33 | self.invoice_items = [] 34 | 35 | def add_invoice_item(self, invoice_entry): 36 | self.invoice_items.append(invoice_entry) 37 | 38 | def amt(self): 39 | amt = 0 40 | for item in self.invoice_items: 41 | amt = amt + item.amt 42 | return amt 43 | 44 | invoice = Invoice() 45 | invoice.number = '20080422_01' 46 | invoice.client = 'Sun Microsystems' 47 | invoice.date = '22/04/2008' 48 | 49 | invoice_item = InvoiceItem(text='consulting April', amt=2000) 50 | invoice.add_invoice_item(invoice_item) 51 | 52 | print invoice 53 | -------------------------------------------------------------------------------- /basic_examples/python_os_module.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #The os module exposes various operating system related details and 4 | #functionality. In this module we will explore some of the more commonly 5 | #needed functionality 6 | 7 | import os 8 | 9 | print 'Your operating system: ', os.name 10 | print 'Your username: ', os.getlogin() 11 | print 'Your current working directory is: ', os.getcwd() 12 | print 'Your system path is: ', os.getenv('PATH') 13 | -------------------------------------------------------------------------------- /basic_examples/python_read.py: -------------------------------------------------------------------------------- 1 | # from sys module import a member called 'argv' 2 | from sys import argv 3 | #unpack 4 | script, filename = argv 5 | fp = open(filename) 6 | print "Reading file %r " % fp 7 | print fp.read() 8 | fp.close() 9 | -------------------------------------------------------------------------------- /basic_examples/python_sequences.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #Note: lists, tuples, and strings are all sequences in Python. Sequences allow 4 | #indexing and slicing operations 5 | #When a sequence is sliced the resulting object is a brand new object 6 | 7 | jvm_langs = ['Java', 'Jython', 'Groovy', 'JRuby', 'Scala', 'Clojure'] 8 | print 'The first JVM language was ', jvm_langs[0] 9 | 10 | #Let's slice the list. Slicing can be done with positive and negative indexes 11 | print 'The second and third JVM languages are ', jvm_langs[1:3] 12 | print 'The last 2 JVM languages are ', jvm_langs[-2:] 13 | print 'The first 2 JVM languages are ', jvm_langs[:2] 14 | 15 | name = 'Parag Shah' 16 | print 'The first name is ', name[0:5] 17 | -------------------------------------------------------------------------------- /basic_examples/python_star_args_and_kwargs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | def add_all(*list_of_nums): 4 | sum = 0 5 | for num in list_of_nums: 6 | sum = sum + num 7 | print 'sum ', ' + '.join(str(n) for n in list_of_nums), ' = ', sum 8 | 9 | add_all(2,3,4) 10 | 11 | nums = (2,3,4) 12 | add_all(*nums) 13 | 14 | nums = [2,3,4] 15 | add_all(*nums) 16 | 17 | def add_all_msg(*args, **kwargs): 18 | print kwargs['before'] 19 | add_all(*args) 20 | print kwargs['after'] 21 | 22 | add_all_msg(2,3,4,before='adding numbers', after='added all numbers') 23 | -------------------------------------------------------------------------------- /basic_examples/python_string_interpolation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from string import Template 4 | 5 | greeting = 'Hello' 6 | name = 'John' 7 | 8 | print("%s %s how are you?" %(greeting, name)) 9 | 10 | print "%(greeting)s %(name)s how are you?" % locals() 11 | 12 | print Template('$greeting $name how are you?').substitute(locals()) 13 | 14 | #For Python 3.0 15 | #print("{greeting} {name} how are you?".format(**locals())) 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /basic_examples/python_strings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #Note: Python strings are immutable 4 | 5 | #Strings can be enclosed in single quotes 6 | print 'Hello World in single quotes' 7 | 8 | #Strings can also be enclosed in double quotes 9 | print "Hello World in double quotes" 10 | 11 | #Strings can also be enclosed in triple quotes 12 | print """ This is a multiline string 13 | Such strings are also possible in Groovy. 14 | These strings can contain ' as well as " quotes """ 15 | 16 | #Strings can be continued on the next line 17 | print "This string is continued on the\ 18 | next line (in the source) but a newline is not added" 19 | 20 | #Raw strings 21 | print r"The newline character is represented with \n" 22 | 23 | #The following is a unicode string, but here it is a literal. 24 | #How do we specify that a string read from a file should be held 25 | #as a unicode string. Also the +uXXXX notation does not work in 26 | #Python 27 | print u"This is a unicode string u0600" 28 | 29 | #See how the strings are concatenated 30 | print 'hello ' 'world' 31 | 32 | #Notice how the print statement accepts , separated values 33 | print '2 + 3 = ', (2+3) 34 | 35 | #Getting the length of a string 36 | greeting = "Hello my dear friend how are you?" 37 | print "the length of string '" ,greeting, "' is ", len(greeting) 38 | 39 | #Let's check if the string contains the word 'Hello' 40 | if('Hello' in greeting): 41 | print 'This greeting seems to be in English' 42 | 43 | if(greeting.find('Hello') != -1): 44 | print 'This greeting seems to be in English' 45 | -------------------------------------------------------------------------------- /basic_examples/python_tuples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #Note: Python tuples are immutable just like strings 4 | 5 | jvm_langs = ('Java', 'Jython', 'JRuby') 6 | print jvm_langs 7 | 8 | print 'Oops I forgot Scala and Groovy but I cannot add to an existing tuple' 9 | 10 | jvm_langs_revised = ('Scala', 'Groovy', 'Clojure', jvm_langs) 11 | #Notice that the earlier sequence is retained and not flattened 12 | print jvm_langs_revised 13 | 14 | -------------------------------------------------------------------------------- /basic_examples/python_write.py: -------------------------------------------------------------------------------- 1 | # from sys module import a member called 'argv' 2 | from sys import argv 3 | #unpack 4 | script, filename = argv 5 | fp = open(filename, "w") 6 | print "Writing to file %r " % fp 7 | fp.write("Hello World") 8 | fp.close() 9 | -------------------------------------------------------------------------------- /mini_projects/adaptive_backup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Filename : adaptive_backup.py 3 | 4 | import os 5 | import time 6 | import tarfile 7 | 8 | #List of directories and files to backup 9 | bk_src = ['/home/pshah/Documents', 10 | '/home/pshah/Templates'] 11 | 12 | #Directory where the backup will be stored 13 | bk_dest = '/home/pshah/bk/' 14 | 15 | bk_fn = bk_dest + time.strftime('%Y%m%d%H%M%S') + '.tgz' 16 | zip_cmd = "zip -qr '%s' %s" % (bk_fn, ' '.join(bk_src)) 17 | 18 | tar_file = tarfile.open(bk_fn, 'w:gz') 19 | for file in bk_src: 20 | tar_file.add(file) 21 | tar_file.close() 22 | 23 | -------------------------------------------------------------------------------- /tdd/TDDWithPython.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaptives/python-examples/ae5e003151be8fcb3a0bfb8924a8e8d5a1d71a81/tdd/TDDWithPython.odp -------------------------------------------------------------------------------- /tdd/iostubbing/all_tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import test_write_user_details 3 | import test_write_user_details_istub 4 | 5 | suite1 = unittest.TestLoader().loadTestsFromTestCase(test_write_user_details.TestUserDetails) 6 | suite1.addTests(unittest.TestLoader().loadTestsFromTestCase(test_write_user_details_istub.TestUserDetails)) 7 | 8 | unittest.TextTestRunner(verbosity=2).run(suite1) -------------------------------------------------------------------------------- /tdd/iostubbing/test_write_user_details.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | class TestUserDetails(unittest.TestCase): 4 | 5 | def setUp(self): 6 | super(TestUserDetails, self).setUp() 7 | 8 | 9 | def tearDown(self): 10 | super(TestUserDetails, self).tearDown() 11 | 12 | 13 | def test_output_file_contents(self): 14 | fp = open('Priyanka.txt') 15 | name = fp.readline() 16 | age = fp.readline() 17 | city = fp.readline() 18 | self.assertEqual(name[0:len(name)-1], 'Name : Priyanka') 19 | self.assertEqual(age[0:len(age)-1], 'Age : 33') 20 | self.assertEqual(city[0:len(city)-1], 'City : Mumbai') 21 | 22 | if __name__ == "__main__": 23 | unittest.main() -------------------------------------------------------------------------------- /tdd/iostubbing/test_write_user_details_iostub.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import write_user_details 3 | import sys 4 | 5 | def gen_open_stub(output_stub): 6 | def open(fpath, mode): 7 | return output_stub 8 | return open 9 | 10 | class InputStub(object): 11 | def __init__(self, values): 12 | self.values = values 13 | self.cnt = 0 14 | 15 | def readline(self): 16 | if self.cnt < len(self.values): 17 | ret_val = self.values[self.cnt] 18 | self.cnt += 1 19 | return ret_val 20 | else: 21 | raise StopError() 22 | 23 | class OutputStub(object): 24 | def __init__(self): 25 | self.values = [] 26 | 27 | def write(self, value): 28 | self.values.append(value) 29 | 30 | def close(self): 31 | self.closed = True 32 | 33 | 34 | class TestUserDetails(unittest.TestCase): 35 | 36 | def test_output_file_contents(self): 37 | values = ['Priyanka', '33', 'Mumbai'] 38 | output_stub = OutputStub() 39 | write_user_details.open = gen_open_stub(output_stub) 40 | sys.stdin = InputStub(values) 41 | write_user_details.accept_and_write() 42 | name = output_stub.values[0] 43 | age = output_stub.values[1] 44 | city = output_stub.values[2] 45 | self.assertEqual(name[0:len(name)-1], 'Name : Priyanka') 46 | self.assertEqual(age[0:len(age)-1], 'Age : 33') 47 | self.assertEqual(city[0:len(city)-1], 'City : Mumbai') 48 | 49 | if __name__ == "__main__": 50 | unittest.main() -------------------------------------------------------------------------------- /tdd/iostubbing/test_write_user_details_istub.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import write_user_details 3 | import sys 4 | 5 | class InputStub(object): 6 | def __init__(self, values): 7 | self.values = values 8 | self.cnt = 0 9 | 10 | def readline(self): 11 | if self.cnt < len(self.values): 12 | ret_val = self.values[self.cnt] 13 | self.cnt += 1 14 | return ret_val 15 | else: 16 | raise StopError() 17 | 18 | class TestUserDetails(unittest.TestCase): 19 | 20 | def test_output_file_contents(self): 21 | values = ['Priyanka', '33', 'Mumbai'] 22 | sys.stdin = InputStub(values) 23 | write_user_details.accept_and_write() 24 | fp = open('Priyanka.txt') 25 | name = fp.readline() 26 | age = fp.readline() 27 | city = fp.readline() 28 | self.assertEqual(name[0:len(name)-1], 'Name : Priyanka') 29 | self.assertEqual(age[0:len(age)-1], 'Age : 33') 30 | self.assertEqual(city[0:len(city)-1], 'City : Mumbai') 31 | 32 | if __name__ == "__main__": 33 | unittest.main() -------------------------------------------------------------------------------- /tdd/iostubbing/test_write_user_details_mock.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import write_user_details_di 3 | import sys 4 | import pymock 5 | 6 | class TestUserDetails(pymock.PyMockTestCase): 7 | 8 | def test_output_file_contents(self): 9 | #mock the input proxy 10 | raw_input_provider = self.mock() 11 | mock_raw_input = raw_input_provider.raw_input 12 | self.expectAndReturn(mock_raw_input('What is your name ? '), 'Joe') 13 | self.expectAndReturn(mock_raw_input('What is your age ? '), '40') 14 | self.expectAndReturn(mock_raw_input('Which city do you stay in ? '), 'Pune') 15 | 16 | #mock the file handle 17 | mock_opener_interface = self.mock() 18 | mock_file = self.mock() 19 | self.expectAndReturn(mock_opener_interface.open('Joe.txt', 'w'), mock_file) 20 | mock_file.write('Name : Joe\n') 21 | mock_file.write('Age : 40\n') 22 | mock_file.write('City : Pune\n') 23 | mock_file.close() 24 | 25 | #replay 26 | self.replay() 27 | 28 | #call the actual method 29 | write_user_details_di.accept_and_write(iproxy=raw_input_provider.raw_input, opener=mock_opener_interface.open) 30 | 31 | #verify 32 | self.verify() 33 | 34 | 35 | if __name__ == "__main__": 36 | unittest.main() -------------------------------------------------------------------------------- /tdd/iostubbing/write_user_details.py: -------------------------------------------------------------------------------- 1 | """ 2 | In this file we accept some details from the user and write them 3 | to a file. 4 | """ 5 | 6 | def accept_and_write(): 7 | name = raw_input('What is your name ? ') 8 | age = raw_input('What is your age ? ') 9 | city = raw_input('Which city do you stay in ? ') 10 | 11 | fp = open(name+'.txt', 'w') 12 | fp.write("Name : %s\n" % name) 13 | fp.write("Age : %s\n" % age) 14 | fp.write("City : %s\n" % city) 15 | fp.close() 16 | 17 | if __name__ == "__main__": 18 | accept_and_write() 19 | -------------------------------------------------------------------------------- /tdd/iostubbing/write_user_details_di.py: -------------------------------------------------------------------------------- 1 | """ 2 | In this file we accept some details from the user and write them 3 | to a file. 4 | """ 5 | 6 | class InputProxy(): 7 | def raw_input(self, msg): 8 | return raw_input(msg) 9 | 10 | 11 | def accept_and_write(iproxy=raw_input, opener=open): 12 | name = iproxy('What is your name ? ') 13 | age = iproxy('What is your age ? ') 14 | city = iproxy('Which city do you stay in ? ') 15 | 16 | fp = opener(name+'.txt', 'w') 17 | fp.write("Name : %s\n" % name) 18 | fp.write("Age : %s\n" % age) 19 | fp.write("City : %s\n" % city) 20 | fp.close() -------------------------------------------------------------------------------- /tdd/tdd_python_plan.txt: -------------------------------------------------------------------------------- 1 | There are two ways to us the PyMock framework. As standalone or as a unittest testcase. If used in standalone mode, we need to create a controller, and when used as a unittest testcase, the controller is created automatically for us. 2 | 3 | Stana 4 | 5 | 6 | ============================================= 7 | 1. Explain and run write_user_details.py 8 | 2. Explaina and Test the output file with test_write_user_details.py 9 | 3. Explain the drawbacks of the plumbing we have to do with this type of test. Explain why we need to automate the accepting of UserInput 10 | 4. Explain and run test_write_user_details_istub.py and show it's benefits. This is a one step process. However, we are still writing to a file. This too is not good. 11 | 5. Explain and run test_write_user_details_iostub.py 12 | 6. This is good but we cannot wrote stubs for everything in a large project. It will take too long to do it 13 | 7. Here is where mocking comes in. Explain and run test_write_user_details_mock.py 14 | 8. We cannot manually run so many test cases. Here is where test suites come in the picture 15 | 9. unittest also supports test discovery 16 | python -m unittest discover 17 | 18 | 19 | class MyTestCase(unittest.TestCase): 20 | 21 | @unittest.skip("demonstrating skipping") 22 | def test_nothing(self): 23 | self.fail("shouldn't happen") 24 | 25 | @unittest.skipIf(mylib.__version__ < (1, 3), 26 | "not supported in this library version") 27 | def test_format(self): 28 | # Tests that work for only a certain version of the library. 29 | pass 30 | 31 | @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") 32 | def test_windows_support(self): 33 | # windows specific testing code 34 | pass 35 | 36 | @unittest.skip("showing class skipping") 37 | class MySkippedTestCase(unittest.TestCase): 38 | def test_not_run(self): 39 | pass 40 | 41 | class ExpectedFailureTestCase(unittest.TestCase): 42 | @unittest.expectedFailure 43 | def test_fail(self): 44 | self.assertEqual(1, 0, "broken") 45 | 46 | Resources: 47 | ========== 48 | Python unittest http://docs.python.org/library/unittest.html 49 | -------------------------------------------------------------------------------- /tdd/transformations/MailerProg.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | """ 5 | def find_msg_files(d): 6 | msg_files = [] 7 | for f in os.listdir(d): 8 | if f.endswith('.msg'): 9 | msg_files.append(f) 10 | return msg_files 11 | """ 12 | 13 | def get_all_msgs(): 14 | msgs = [] 15 | msg_file_names = find_msg_files('c:/temp') 16 | for msg_file_name in msg_file_names: 17 | msgs.append(parse_msg_file(msg_file_name)) 18 | return msgs 19 | 20 | def find_msg_files(d): 21 | return [f for f in os.listdir(d) if f.endswith('.msg')] 22 | 23 | def parse_msg_file(fname): 24 | fp = open(fname, 'r') 25 | reading_body = False 26 | ffrom = "" 27 | to = "" 28 | cc = "" 29 | tstamp = "" 30 | mime = "" 31 | body = "" 32 | for line in fp.readlines(): 33 | if reading_body: 34 | body += line 35 | else: 36 | if line.startswith('to: '): 37 | to = line[len('to: '):] 38 | to = __remove_newline_chars(to) 39 | elif line.startswith('from: '): 40 | ffrom = line[len('from: '):] 41 | ffrom = __remove_newline_chars(ffrom) 42 | elif line.startswith('cc: '): 43 | cc = line[len('cc: '):] 44 | cc = __remove_newline_chars(cc) 45 | elif line.startswith('tstamp: '): 46 | tstamp = line[len('tstamp: '):] 47 | tstamp = __remove_newline_chars(tstamp) 48 | elif line.startswith('mime: '): 49 | mime = line[len('mime: '):] 50 | mime = __remove_newline_chars(mime) 51 | elif line.startswith('body:'): 52 | reading_body = True 53 | 54 | return Msg(to=to, ffrom=ffrom, cc=cc, tstamp=tstamp, mime=mime, body=body) 55 | 56 | def __remove_newline_chars(s): 57 | s = s.replace('\n','') 58 | s = s.replace('\r', '') 59 | return s 60 | 61 | class Msg(object): 62 | def __init__(self, ffrom="", to="", cc="", tstamp="", mime="", body=""): 63 | self.ffrom = ffrom 64 | self.to = [] 65 | self.cc = [] 66 | self.tstamp = tstamp 67 | self.mime = mime 68 | self.body = body 69 | self.to = re.split(',\s*', to) 70 | if '' in self.to: 71 | self.to.remove('') 72 | self.cc = re.split(',\s*', cc) 73 | if '' in self.cc: 74 | self.cc.remove('') 75 | 76 | def __str__(self): 77 | return """ 78 | from: %s 79 | to: %s 80 | cc: %s 81 | mime: %s 82 | tstamp: %s 83 | body: %s""" % (self.ffrom, self.to, self.cc, self.mime, self.tstamp, self.body) 84 | 85 | 86 | 87 | 88 | if "__main__" == __name__: 89 | print find_msg_files('c:/temp') 90 | -------------------------------------------------------------------------------- /tdd/transformations/Msg2Xml.py: -------------------------------------------------------------------------------- 1 | from xml.etree.ElementTree import Element, SubElement, Comment, tostring 2 | 3 | top = Element('top') 4 | 5 | comment = Comment('Generated for PyMOTW') 6 | top.append(comment) 7 | 8 | child = SubElement(top, 'child') 9 | child.text = 'This child contains text.' 10 | 11 | child_with_tail = SubElement(top, 'child_with_tail') 12 | child_with_tail.text = 'This child has regular text.' 13 | child_with_tail.tail = 'And "tail" text.' 14 | 15 | child_with_entity_ref = SubElement(top, 'child_with_entity_ref') 16 | child_with_entity_ref.text = 'This & that' 17 | 18 | print tostring(top) -------------------------------------------------------------------------------- /tdd/transformations/TestMailerProg.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import MailerProg 3 | import os 4 | 5 | class TestMailerProg(unittest.TestCase): 6 | 7 | def setUp(self): 8 | super(TestMailerProg, self).setUp() 9 | 10 | def tearDown(self): 11 | super(TestMailerProg, self).tearDown() 12 | 13 | def test_find_msg_files(self): 14 | fqn = os.path.abspath(__file__) 15 | msg_files = MailerProg.find_msg_files(fqn[0:fqn.rfind('/')]) 16 | self.assertEqual(5, len(msg_files)) 17 | self.assertTrue('email1.msg' in msg_files) 18 | self.assertTrue('email2.msg' in msg_files) 19 | self.assertTrue('email3.msg' in msg_files) 20 | self.assertTrue('email4.msg' in msg_files) 21 | self.assertTrue('email5.msg' in msg_files) 22 | 23 | def test_parse_msg_file(self): 24 | fqn = os.path.abspath(__file__) 25 | curr_dir = fqn[0:fqn.rfind('/')] 26 | msg = MailerProg.parse_msg_file(os.path.join(curr_dir,'email1.msg')) 27 | self.assertEqual(msg.to[0], 'user1@domaina.com') 28 | 29 | 30 | if __name__ == "__main__": 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tdd/transformations/VerifyServers.py: -------------------------------------------------------------------------------- 1 | import urllib2 2 | 3 | servers = ['http://localhost:8080'] 4 | 5 | for server in servers: 6 | try: 7 | urllib.urlopen(server) 8 | except urllib2.HTTPError, e: 9 | send_mail(e) 10 | except urllib2.URLError, e: 11 | send_mail(e) 12 | 13 | def send_mail(e): 14 | #send an email to the admin with the details of this exception 15 | pass -------------------------------------------------------------------------------- /tdd/transformations/email1.msg: -------------------------------------------------------------------------------- 1 | to: user1@domaina.com 2 | from: user1@domainb.com 3 | mime: plain/text 4 | tstamp: 26 June, 2012 5 | body: 6 | Hi there how are you ? 7 | -- 8 | Thanks 9 | 10 | -------------------------------------------------------------------------------- /tdd/transformations/email2.msg: -------------------------------------------------------------------------------- 1 | to: user2@domaina.com 2 | from: user1@domainc.com 3 | cc: user1@domainb.com 4 | mime: plain/text 5 | tstamp: 26 June, 2012 6 | body: 7 | Hi there how are you ? 8 | -- 9 | Thanks 10 | 11 | -------------------------------------------------------------------------------- /tdd/transformations/email3.msg: -------------------------------------------------------------------------------- 1 | to: user4@domaina.com 2 | from: user1@domainc.com 3 | mime: plain/text 4 | tstamp: 26 June, 2012 5 | body: 6 | Hi there how are you ? 7 | -- 8 | Thanks 9 | 10 | -------------------------------------------------------------------------------- /tdd/transformations/email4.msg: -------------------------------------------------------------------------------- 1 | to: user3@domaina.com 2 | from: user1@domainc.com 3 | cc: user1@domainb.com 4 | mime: plain/text 5 | tstamp: 26 June, 2012 6 | body: 7 | Hi there how are you ? 8 | -- 9 | Thanks 10 | -------------------------------------------------------------------------------- /tdd/transformations/email5.msg: -------------------------------------------------------------------------------- 1 | to: user3@domaina.com 2 | from: user4@domainc.com 3 | cc: user5@domainb.com 4 | mime: plain/text 5 | tstamp: 26 June, 2012 6 | body: 7 | Hi there how are you ? 8 | -- 9 | Thanks 10 | --------------------------------------------------------------------------------