diff --git a/tests/python/python.bats b/tests/python/python.bats new file mode 100755 index 0000000..7b00c71 --- /dev/null +++ b/tests/python/python.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats +# *-*- Mode: sh; c-basic-offset: 8; indent-tabs-mode: nil -*-* + +load ../utils + +@test "python3 string" { + docker run --rm -v "$PWD":/src -w /src clearlinux/python python3 testcase/string3.py +} + +@test "python3 list" { + docker run --rm -v "$PWD":/src -w /src clearlinux/python python3 testcase/list3.py +} + +@test "python3 count" { + docker run --rm -v "$PWD":/src -w /src clearlinux/python python3 testcase/wordcount3.py --count testcase/string3.py +} + +@test "python3-basic pip3 check" { + docker run --rm -v "$PWD":/src -w /src clearlinux/python pip3 check +} + diff --git a/tests/python/testcase/list3.py b/tests/python/testcase/list3.py new file mode 100644 index 0000000..19c1190 --- /dev/null +++ b/tests/python/testcase/list3.py @@ -0,0 +1,119 @@ +#!/usr/bin/python -tt +# Copyright 2010 Google Inc. +# Licensed under the Apache License, Version 2.0 +# http://www.apache.org/licenses/LICENSE-2.0 + +# Google's Python Class +# http://code.google.com/edu/languages/google-python-class/ + +# Basic list exercises +# Fill in the code for the functions below. main() is already set up +# to call the functions with a few different inputs, +# printing 'OK' when each function is correct. +# The starter code for each function includes a 'return' +# which is just a placeholder for your code. +# It's ok if you do not complete all the functions, and there +# are some additional functions to try in list2.py. + +# A. match_ends +# Given a list of strings, return the count of the number of +# strings where the string length is 2 or more and the first +# and last chars of the string are the same. +# Note: python does not have a ++ operator, but += works. +def match_ends(words): + # +++your code here+++ + # LAB(begin solution) + count = 0 + for word in words: + if len(word) >= 2 and word[0] == word[-1]: + count = count + 1 + return count + # LAB(replace solution) + # return + # LAB(end solution) + + +# B. front_x +# Given a list of strings, return a list with the strings +# in sorted order, except group all the strings that begin with 'x' first. +# e.g. ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] yields +# ['xanadu', 'xyz', 'aardvark', 'apple', 'mix'] +# Hint: this can be done by making 2 lists and sorting each of them +# before combining them. +def front_x(words): + # +++your code here+++ + # LAB(begin solution) + # Put each word into the x_list or the other_list. + x_list = [] + other_list = [] + for w in words: + if w.startswith('x'): + x_list.append(w) + else: + other_list.append(w) + return sorted(x_list) + sorted(other_list) + # LAB(replace solution) + # return + # LAB(end solution) + + +# LAB(begin solution) +# Extract the last element from a tuple -- used for custom sorting below. +def last(a): + return a[-1] +# LAB(end solution) + +# C. sort_last +# Given a list of non-empty tuples, return a list sorted in increasing +# order by the last element in each tuple. +# e.g. [(1, 7), (1, 3), (3, 4, 5), (2, 2)] yields +# [(2, 2), (1, 3), (3, 4, 5), (1, 7)] +# Hint: use a custom key= function to extract the last element form each tuple. +def sort_last(tuples): + # +++your code here+++ + # LAB(begin solution) + return sorted(tuples, key=last) + # LAB(replace solution) + # return + # LAB(end solution) + + +# Simple provided test() function used in main() to print +# what each function returns vs. what it's supposed to return. +def test(got, expected): + if got == expected: + prefix = ' OK ' + else: + prefix = ' X ' + print(('%s got: %s expected: %s' % (prefix, repr(got), repr(expected)))) + + +# Calls the above functions with interesting inputs. +def main(): + print('match_ends') + test(match_ends(['aba', 'xyz', 'aa', 'x', 'bbb']), 3) + test(match_ends(['', 'x', 'xy', 'xyx', 'xx']), 2) + test(match_ends(['aaa', 'be', 'abc', 'hello']), 1) + + print() + print('front_x') + test(front_x(['bbb', 'ccc', 'axx', 'xzz', 'xaa']), + ['xaa', 'xzz', 'axx', 'bbb', 'ccc']) + test(front_x(['ccc', 'bbb', 'aaa', 'xcc', 'xaa']), + ['xaa', 'xcc', 'aaa', 'bbb', 'ccc']) + test(front_x(['mix', 'xyz', 'apple', 'xanadu', 'aardvark']), + ['xanadu', 'xyz', 'aardvark', 'apple', 'mix']) + + + print() + print('sort_last') + test(sort_last([(1, 3), (3, 2), (2, 1)]), + [(2, 1), (3, 2), (1, 3)]) + test(sort_last([(2, 3), (1, 2), (3, 1)]), + [(3, 1), (1, 2), (2, 3)]) + test(sort_last([(1, 7), (1, 3), (3, 4, 5), (2, 2)]), + [(2, 2), (1, 3), (3, 4, 5), (1, 7)]) + + +if __name__ == '__main__': + main() diff --git a/tests/python/testcase/string3.py b/tests/python/testcase/string3.py new file mode 100644 index 0000000..b9df0aa --- /dev/null +++ b/tests/python/testcase/string3.py @@ -0,0 +1,140 @@ +#!/usr/bin/python -tt +# Copyright 2010 Google Inc. +# Licensed under the Apache License, Version 2.0 +# http://www.apache.org/licenses/LICENSE-2.0 + +# Google's Python Class +# http://code.google.com/edu/languages/google-python-class/ + +# Basic string exercises +# Fill in the code for the functions below. main() is already set up +# to call the functions with a few different inputs, +# printing 'OK' when each function is correct. +# The starter code for each function includes a 'return' +# which is just a placeholder for your code. +# It's ok if you do not complete all the functions, and there +# are some additional functions to try in string2.py. + + +# A. donuts +# Given an int count of a number of donuts, return a string +# of the form 'Number of donuts: ', where is the number +# passed in. However, if the count is 10 or more, then use the word 'many' +# instead of the actual count. +# So donuts(5) returns 'Number of donuts: 5' +# and donuts(23) returns 'Number of donuts: many' +def donuts(count): + # +++your code here+++ + # LAB(begin solution) + if count < 10: + return 'Number of donuts: ' + str(count) + else: + return 'Number of donuts: many' + # LAB(replace solution) + # return + # LAB(end solution) + + +# B. both_ends +# Given a string s, return a string made of the first 2 +# and the last 2 chars of the original string, +# so 'spring' yields 'spng'. However, if the string length +# is less than 2, return instead the empty string. +def both_ends(s): + # +++your code here+++ + # LAB(begin solution) + if len(s) < 2: + return '' + first2 = s[0:2] + last2 = s[-2:] + return first2 + last2 + # LAB(replace solution) + # return + # LAB(end solution) + + +# C. fix_start +# Given a string s, return a string +# where all occurences of its first char have +# been changed to '*', except do not change +# the first char itself. +# e.g. 'babble' yields 'ba**le' +# Assume that the string is length 1 or more. +# Hint: s.replace(stra, strb) returns a version of string s +# where all instances of stra have been replaced by strb. +def fix_start(s): + # +++your code here+++ + # LAB(begin solution) + front = s[0] + back = s[1:] + fixed_back = back.replace(front, '*') + return front + fixed_back + # LAB(replace solution) + # return + # LAB(end solution) + + +# D. MixUp +# Given strings a and b, return a single string with a and b separated +# by a space ' ', except swap the first 2 chars of each string. +# e.g. +# 'mix', pod' -> 'pox mid' +# 'dog', 'dinner' -> 'dig donner' +# Assume a and b are length 2 or more. +def mix_up(a, b): + # +++your code here+++ + # LAB(begin solution) + a_swapped = b[:2] + a[2:] + b_swapped = a[:2] + b[2:] + return a_swapped + ' ' + b_swapped + # LAB(replace solution) + # return + # LAB(end solution) + + +# Provided simple test() function used in main() to print +# what each function returns vs. what it's supposed to return. +def test(got, expected): + if got == expected: + prefix = ' OK ' + else: + prefix = ' X ' + print(('%s got: %s expected: %s' % (prefix, repr(got), repr(expected)))) + + +# Provided main() calls the above functions with interesting inputs, +# using test() to check if each result is correct or not. +def main(): + print('donuts') + # Each line calls donuts, compares its result to the expected for that call. + test(donuts(4), 'Number of donuts: 4') + test(donuts(9), 'Number of donuts: 9') + test(donuts(10), 'Number of donuts: many') + test(donuts(99), 'Number of donuts: many') + + print() + print('both_ends') + test(both_ends('spring'), 'spng') + test(both_ends('Hello'), 'Helo') + test(both_ends('a'), '') + test(both_ends('xyz'), 'xyyz') + + + print() + print('fix_start') + test(fix_start('babble'), 'ba**le') + test(fix_start('aardvark'), 'a*rdv*rk') + test(fix_start('google'), 'goo*le') + test(fix_start('donut'), 'donut') + + print() + print('mix_up') + test(mix_up('mix', 'pod'), 'pox mid') + test(mix_up('dog', 'dinner'), 'dig donner') + test(mix_up('gnash', 'sport'), 'spash gnort') + test(mix_up('pezzy', 'firm'), 'fizzy perm') + + +# Standard boilerplate to call the main() function. +if __name__ == '__main__': + main() diff --git a/tests/python/testcase/wordcount3.py b/tests/python/testcase/wordcount3.py new file mode 100644 index 0000000..17460e3 --- /dev/null +++ b/tests/python/testcase/wordcount3.py @@ -0,0 +1,114 @@ +#!/usr/bin/python -tt +# Copyright 2010 Google Inc. +# Licensed under the Apache License, Version 2.0 +# http://www.apache.org/licenses/LICENSE-2.0 + +# Google's Python Class +# http://code.google.com/edu/languages/google-python-class/ + +"""Wordcount exercise +Google's Python class + +The main() below is already defined and complete. It calls print_words() +and print_top() functions which you write. + +1. For the --count flag, implement a print_words(filename) function that counts +how often each word appears in the text and prints: +word1 count1 +word2 count2 +... + +Print the above list in order sorted by word (python will sort punctuation to +come before letters -- that's fine). Store all the words as lowercase, +so 'The' and 'the' count as the same word. + +2. For the --topcount flag, implement a print_top(filename) which is similar +to print_words() but which prints just the top 20 most common words sorted +so the most common word is first, then the next most common, and so on. + +Use str.split() (no arguments) to split on all whitespace. + +Workflow: don't build the whole program at once. Get it to an intermediate +milestone and print your data structure and sys.exit(0). +When that's working, try for the next milestone. + +Optional: define a helper function to avoid code duplication inside +print_words() and print_top(). + +""" + +import sys + +# +++your code here+++ +# Define print_words(filename) and print_top(filename) functions. +# You could write a helper utility function that reads a file +# and builds and returns a word/count dict for it. +# Then print_words() and print_top() can just call the utility function. + +#### LAB(begin solution) + +def word_count_dict(filename): + """Returns a word/count dict for this filename.""" + # Utility used by count() and Topcount(). + word_count = {} # Map each word to its count + input_file = open(filename, 'r') + for line in input_file: + words = line.split() + for word in words: + word = word.lower() + # Special case if we're seeing this word for the first time. + if not word in word_count: + word_count[word] = 1 + else: + word_count[word] = word_count[word] + 1 + input_file.close() # Not strictly required, but good form. + return word_count + + +def print_words(filename): + """Prints one per line ' ' sorted by word for the given file.""" + word_count = word_count_dict(filename) + words = sorted(word_count.keys()) + for word in words: + print((word, word_count[word])) + + +def get_count(word_count_tuple): + """Returns the count from a dict word/count tuple -- used for custom sort.""" + return word_count_tuple[1] + + +def print_top(filename): + """Prints the top count listing for the given file.""" + word_count = word_count_dict(filename) + + # Each item is a (word, count) tuple. + # Sort them so the big counts are first using key=get_count() to extract count. + items = sorted(list(word_count.items()), key=get_count, reverse=True) + + # Print the first 20 + for item in items[:20]: + print((item[0], item[1])) + +##### LAB(end solution) + + +# This basic command line argument parsing code is provided and +# calls the print_words() and print_top() functions which you must define. +def main(): + if len(sys.argv) != 3: + print('usage: ./wordcount.py {--count | --topcount} file') + sys.exit(1) + + option = sys.argv[1] + filename = sys.argv[2] + if option == '--count': + print_words(filename) + elif option == '--topcount': + print_top(filename) + else: + print(('unknown option: ' + option)) + sys.exit(1) + +if __name__ == '__main__': + main()