diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..3929491ca --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +language: python + +python: + - 2.7 + - 3.3 + +script: + - PYTHON_VER=`python -c 'import sys; print(sys.version_info[0])'` + - cd python$PYTHON_VER + - python _runner_tests.py + - python contemplate_koans.py # Run all the koans +# - python contemplate_koans.py about_asserts about_none # Run a subset of +# # koans +# +# Working through Python Koans in a fork? Want to use Travis CI to show which +# koans you've passed? Then comment out one of the above "contemplate_koans" +# lines above! +# +notifications: + email: true + +# Some other koans (see runner/sensei.py or "ls koans" to see the light): +# +# about_none about_lists about_list_assignments about_dictionaries +# about_strings about_tuples about_methods about_control_statements +# about_true_and_false about_sets ... \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..703ebf1e0 --- /dev/null +++ b/README.rst @@ -0,0 +1,17 @@ +Python Koans Answers +==================== + +.. image:: https://travis-ci.org/gregmalcolm/python_koans.png?branch=answers + :target: http://travis-ci.org/gregmalcolm/python_koans + +*NOTE:* These answers are ANCIENT! + +These answers were completed when Python Koans was still young back in 2010. + +The koans have changed quite a bit since then. That said, if you're really stuck +these might help push you in the right direction if you're stuck. + +I also recommend taking a look at other peoples more modern answers in forked +repos... + +- Greg \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index faa018e0e..000000000 --- a/README.txt +++ /dev/null @@ -1,145 +0,0 @@ -============ -Python Koans -============ - -Python Koans is a port of Edgecase's "Ruby Koans". - -Python Koans is an interactive tutorial for learning Python by making tests pass. - -Most tests are 'fixed' by filling the missing parts of assert functions. Eg: - - self.assertEqual(__, 1+2) - -which can be fixed by replacing the __ part with the appropriate code: - - self.assertEqual(3, 1+2) - -Occasionally you will encounter some failing tests that are already filled out. In these cases you will need to finish implementing some code to progress. For example, there is an exercise for writing some code that will tell you if a triangle is equilateral, isosceles or scalene. - -As well as being a great way to learn some Python, it is also a good way to get a taste of Test Driven Development (TDD). - - -Downloading Python Koans ------------------------- - -Python Koans is available through Murcurial on bitbucket: - - http://bitbucket.org/gregmalcolm/python_koans - -It is also mirrored on github for Git users : - - http://wiki.github.com/gregmalcolm/python_koans - -Either site will allow you to download the source as a zip/gz/bz2. - - -Installing Python Koans ------------------------ - -Aside from downloading or checking out the latest version of Python Koans, all you need to install is Python. - -At this time of writing there are two versions of the koans, one for use with Python 2.6 and one for Python 3.1. You should be able to work with newer Python versions, but older ones will likely give you problems. - -You can download Python from here: - - http://www.python.org/download - -On installing Python make sure the folder containing the python executable is in the system path. In other words, you need to be able to be able to run Python from a command console. With Python 2 it will be called 'python' or 'python.exe' depending on the operating system. For Python 3 it will either be 'python3' or for windows it will be 'python.exe'. - -If you have problems, this may help: - - http://www.python.org/about/gettingstarted - - -Getting Started ---------------- - -From a *nix terminal or windows command prompt go to the python koans\python_VERSION folder and run: - - python contemplate_koans.py - -or - - python3 contemplate_koans.py - -In my case I'm using Python 3 with windows, so I fire up my command shell (cmd.exe) and run this: - - C:\>cd "c:\hg\python_koans\python 3" - C:\hg\python_koans\python 3_1>python contemplate_koans.py - - Thinking AboutAsserts - test_assert_truth has damaged your karma. - - You have not yet reached enlightenment ... - AssertionError: False is not True - - Please meditate on the following code: - File "C:\hg\python_koans\python 3\koans\about_asserts.py", line 12, in test_ - assert_truth - self.assertTrue(False) # This should be true - - - Beautiful is better than ugly. - C:\hg\python_koans\python 3> - -Apparently a test failed: - - AssertionError: False is not True - -It also tells me exactly where the problem in, its an assert on line 12 of .\koans\about_asserts.py. This one is easy, just change False to True to make the test pass. - -Sooner or later you will likely encounter tests where you are not sure what the expected value should be. For example: - - class Dog: - pass - - def test_objects_are_objects(self): - fido = self.Dog() - self.assertEqual(__, isinstance(fido, object)) - -This is where the Python Command Line can come in handy. in this case I can fire up the command line, recreate the scenario and run queries: - - C:\hg\python_koans\python 3>python - Python 3.1.2 (r312:79149, Mar 21 2010, 00:41:52) [MSC v.1500 32 bit (Intel)] on - win32 - Type "help", "copyright", "credits" or "license" for more information. - >>> class Dog: pass - ... - >>> fido = Dog() - >>> isinstance(fido, object) - True - >>> - -Getting the Most From the Koans -------------------------------- - -Quoting the Ruby Koans instructions: - - "In test-driven development the mantra has always been, red, green, refactor. Write a failing test and run it (red), make the test pass (green), then refactor it (that is look at the code and see if you can make it any better. In this case you will need to run the koan and see it fail (red), make the test pass (green), then take a moment and reflect upon the test to see what it is teaching you and improve the code to better communicate its intent (refactor)." - - -Content -------- - -Python is a made up of about 2/3 Ruby Koans ported material and 1/3 Python specific tests. The content ported from Ruby Koans includes all the assignment projects. - -Content for Python 3 is a little different to the Python 2 flavor due to big changes between the 2 different languages. For example in the Python 2 variant the differences between old and new style classes are covered. This loses relevance in in the Python 3 version, but there are some extra tests covering new functionality. - - -Finding More Koan Projects --------------------------- - -Right now there are a lot of spinoff Koan projects out there for a great number of languages and frameworks. Many of them do not have that much content, but contributing to them is a great way to learn. At the moment most of them can be found by searching for 'koans' on github. - -A couple of promising projects include DotNetKoans and TestMongoKoans. - - -Acknowledgments ---------------- - -Thanks go to Jim Weirich and Joe O'Brien for the original Ruby Koans that Python Koans is based on! Also the Ruby Koans in turn borrows from Metakoans so thanks also go to Ara Howard for that! - - -Also thanks to everyone who helped with the Python Koans conversion! In particular I got a great headstart on the project by forking from this Python Koans startup project: - - http://bitbucket.org/mcrute/python_koans/ diff --git a/python 2/koans/about_lists.py b/python 2/koans/about_lists.py deleted file mode 100644 index 94dfef4c3..000000000 --- a/python 2/koans/about_lists.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutArrays in the Ruby Koans -# - -from runner.koan import * - -class AboutLists(Koan): - def test_creating_lists(self): - empty_list = list() - self.assertEqual(list, type(empty_list)) - self.assertEqual(__, len(empty_list)) - - def test_list_literals(self): - nums = list() - self.assertEqual([], nums) - - nums[0:] = [1] - self.assertEqual([1], nums) - - nums[1:] = [2] - self.assertEqual([1, __], nums) - - nums.append(333) - self.assertEqual([1, 2, __], nums) - - def test_accessing_list_elements(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[0]) - self.assertEqual(__, noms[3]) - self.assertEqual(__, noms[-1]) - self.assertEqual(__, noms[-3]) - - def test_slicing_lists(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[0:1]) - self.assertEqual(__, noms[0:2]) - self.assertEqual(__, noms[2:2]) - self.assertEqual(__, noms[2:20]) - self.assertEqual(__, noms[4:0]) - self.assertEqual(__, noms[4:100]) - self.assertEqual(__, noms[5:0]) - - def test_slicing_to_the_edge(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[2:]) - self.assertEqual(__, noms[:2]) - - def test_lists_and_ranges(self): - self.assertEqual(list, type(range(5))) - self.assertEqual(__, range(5)) - self.assertEqual(__, range(5, 9)) - - def test_ranges_with_steps(self): - self.assertEqual(__, range(0, 8, 2)) - self.assertEqual(__, range(1, 8, 3)) - self.assertEqual(__, range(5, -7, -4)) - self.assertEqual(__, range(5, -8, -4)) - - def test_insertions(self): - knight = ['you', 'shall', 'pass'] - knight.insert(2, 'not') - self.assertEqual(__, knight) - - knight.insert(0, 'Arthur') - self.assertEqual(__, knight) - - def test_popping_lists(self): - stack = [10, 20, 30] - stack.append('last') - - self.assertEqual(__, stack) - - popped_value = stack.pop() - self.assertEqual(__, popped_value) - self.assertEqual(__, stack) - - popped_value = stack.pop(1) - self.assertEqual(__, popped_value) - self.assertEqual(__, stack) - - # Notice that there is a "pop" but no "push" in python? - - # Part of the Python philosophy is that there ideally should be one and - # only one way of doing anything. A 'push' is the same as an 'append'. - - # To learn more about this try typing "import this" from the python - # console... ;) - - def test_use_deques_for_making_queues(self): - from collections import deque - - queue = deque([1, 2]) - queue.append('last') - - self.assertEqual(__, list(queue)) - - popped_value = queue.popleft() - self.assertEqual(__, popped_value) - self.assertEqual(__, list(queue)) diff --git a/python 2/koans/about_true_and_false.py b/python 2/koans/about_true_and_false.py deleted file mode 100644 index f94b36ad8..000000000 --- a/python 2/koans/about_true_and_false.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutTrueAndFalse(Koan): - def truth_value(self, condition): - if condition: - return 'true stuff' - else: - return 'false stuff' - - def test_true_is_treated_as_true(self): - self.assertEqual(__, self.truth_value(True)) - - def test_false_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(False)) - - def test_none_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(None)) - - def test_zero_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(0)) - - def test_empty_collections_are_treated_as_false(self): - self.assertEqual(__, self.truth_value([])) - self.assertEqual(__, self.truth_value({})) - self.assertEqual(__, self.truth_value(set())) - - def test_strings_are_treated_as_false(self): - self.assertEqual(__, self.truth_value("")) - - def test_everything_else_is_treated_as_true(self): - self.assertEqual(__, self.truth_value(1)) - self.assertEqual(__, self.truth_value("Python is named after Monty Python")) - self.assertEqual(__, self.truth_value(' ')) diff --git a/python 2/runner/sensei.py b/python 2/runner/sensei.py deleted file mode 100644 index 3d7010d50..000000000 --- a/python 2/runner/sensei.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import re - -import helper -from mockable_test_result import MockableTestResult - -class Sensei(MockableTestResult): - def __init__(self, stream): - unittest.TestResult.__init__(self) - self.stream = stream - self.prevTestClassName = None - self.pass_count = 0 - - def startTest(self, test): - MockableTestResult.startTest(self, test) - - if helper.cls_name(test) != self.prevTestClassName: - self.prevTestClassName = helper.cls_name(test) - if not self.failures: - self.stream.writeln() - self.stream.writeln("Thinking {0}".format(helper.cls_name(test))) - - def addSuccess(self, test): - if self.passesCount(): - MockableTestResult.addSuccess(self, test) - self.stream.writeln(" {0} has expanded your awareness.".format(test._testMethodName)) - self.pass_count += 1 - - def addError(self, test, err): - # Having 1 list for errors and 1 list for failures would mess with - # the error sequence - self.addFailure(test, err) - - def passesCount(self): - return not (self.failures and helper.cls_name(self.failures[0][0]) != self.prevTestClassName) - - def addFailure(self, test, err): - MockableTestResult.addFailure(self, test, err) - - def sortFailures(self, testClassName): - table = list() - for test, err in self.failures: - if helper.cls_name(test) == testClassName: - m = re.search("(?<= line )\d+" ,err) - if m: - tup = (int(m.group(0)), test, err) - table.append(tup) - - if table: - return sorted(table) - else: - return None - - def firstFailure(self): - if not self.failures: return None - - table = self.sortFailures(helper.cls_name(self.failures[0][0])) - - if table: - return (table[0][1], table[0][2]) - else: - return None - - def learn(self): - self.errorReport() - - self.stream.writeln("") - self.stream.writeln("") - self.stream.writeln(self.say_something_zenlike()) - - if self.failures: return - self.stream.writeln("\n**************************************************") - self.stream.writeln("That was the last one, well done!") - self.stream.writeln("\nIf you want more, take a look at about_extra_credit_task.py") - - def errorReport(self): - problem = self.firstFailure() - if not problem: return - test, err = problem - self.stream.writeln(" {0} has damaged your karma.".format(test._testMethodName)) - - self.stream.writeln("") - self.stream.writeln("You have not yet reached enlightenment ...") - self.stream.writeln(" {0}".format(self.scrapeAssertionError(err))) - self.stream.writeln("") - self.stream.writeln("Please meditate on the following code:") - self.stream.writeln(self.scrapeInterestingStackDump(err)) - - def scrapeAssertionError(self, err): - if not err: return "" - - error_text = "" - count = 0 - for line in err.splitlines(): - m = re.search("^[^^ ].*$",line) - if m and m.group(0): - count+=1 - - if count>1: - error_text += (" " + line.strip()).rstrip() + '\n' - return error_text.strip('\n') - - def scrapeInterestingStackDump(self, err): - if not err: - return "" - - lines = err.splitlines() - - sep = '@@@@@SEP@@@@@' - - scrape = "" - for line in lines: - m = re.search("^ File .*$",line) - if m and m.group(0): - scrape += '\n' + line - - m = re.search("^ \w(\w)+.*$",line) - if m and m.group(0): - scrape += sep + line - - lines = scrape.splitlines() - - scrape = "" - for line in lines: - m = re.search("^.*[/\\\\]koans[/\\\\].*$",line) - if m and m.group(0): - scrape += line + '\n' - return scrape.replace(sep, '\n').strip('\n') - - # Hat's tip to Tim Peters for the zen statements from The Zen - # of Python (http://www.python.org/dev/peps/pep-0020/) - # - # Also a hat's tip to Ara T. Howard for the zen statements from his - # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html) and - # Edgecase's later permatation in the Ruby Koans - def say_something_zenlike(self): - if self.failures: - turn = self.pass_count % 37 - - if turn == 0: - return "Beautiful is better than ugly." - elif turn == 1 or turn == 2: - return "Explicit is better than implicit." - elif turn == 3 or turn == 4: - return "Simple is better than complex." - elif turn == 5 or turn == 6: - return "Complex is better than complicated." - elif turn == 7 or turn == 8: - return "Flat is better than nested." - elif turn == 9 or turn == 10: - return "Sparse is better than dense." - elif turn == 11 or turn == 12: - return "Readability counts." - elif turn == 13 or turn == 14: - return "Special cases aren't special enough to break the rules." - elif turn == 15 or turn == 16: - return "Although practicality beats purity." - elif turn == 17 or turn == 18: - return "Errors should never pass silently." - elif turn == 19 or turn == 20: - return "Unless explicitly silenced." - elif turn == 21 or turn == 22: - return "In the face of ambiguity, refuse the temptation to guess." - elif turn == 23 or turn == 24: - return "There should be one-- and preferably only one --obvious way to do it." - elif turn == 25 or turn == 26: - return "Although that way may not be obvious at first unless you're Dutch." - elif turn == 27 or turn == 28: - return "Now is better than never." - elif turn == 29 or turn == 30: - return "Although never is often better than right now." - elif turn == 31 or turn == 32: - return "If the implementation is hard to explain, it's a bad idea." - elif turn == 33 or turn == 34: - return "If the implementation is easy to explain, it may be a good idea." - else: - return "Namespaces are one honking great idea -- let's do more of those!" - - else: - return "Nobody ever expects the Spanish Inquisition." - - # Hopefully this will never ever happen! - return "The temple in collapsing! Run!!!" - \ No newline at end of file diff --git a/python 3/koans/a_normal_folder/a_module.py b/python 3/koans/a_normal_folder/a_module.py deleted file mode 100644 index f8b4231a4..000000000 --- a/python 3/koans/a_normal_folder/a_module.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Duck: - @property - def name(self): - return "Howard" \ No newline at end of file diff --git a/python 3/koans/about_lists.py b/python 3/koans/about_lists.py deleted file mode 100644 index 474ecddc1..000000000 --- a/python 3/koans/about_lists.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutArrays in the Ruby Koans -# - -from runner.koan import * - -class AboutLists(Koan): - def test_creating_lists(self): - empty_list = list() - self.assertEqual(list, type(empty_list)) - self.assertEqual(__, len(empty_list)) - - def test_list_literals(self): - nums = list() - self.assertEqual([], nums) - - nums[0:] = [1] - self.assertEqual([1], nums) - - nums[1:] = [2] - self.assertListEqual([1, __], nums) - - nums.append(333) - self.assertListEqual([1, 2, __], nums) - - def test_accessing_list_elements(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[0]) - self.assertEqual(__, noms[3]) - self.assertEqual(__, noms[-1]) - self.assertEqual(__, noms[-3]) - - def test_slicing_lists(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[0:1]) - self.assertEqual(__, noms[0:2]) - self.assertEqual(__, noms[2:2]) - self.assertEqual(__, noms[2:20]) - self.assertEqual(__, noms[4:0]) - self.assertEqual(__, noms[4:100]) - self.assertEqual(__, noms[5:0]) - - def test_slicing_to_the_edge(self): - noms = ['peanut', 'butter', 'and', 'jelly'] - - self.assertEqual(__, noms[2:]) - self.assertEqual(__, noms[:2]) - - def test_lists_and_ranges(self): - self.assertEqual(range, type(range(5))) - self.assertNotEqual([1, 2, 3, 4, 5], range(1,6)) - self.assertEqual(__, list(range(5))) - self.assertEqual(__, list(range(5, 9))) - - def test_ranges_with_steps(self): - self.assertEqual(__, list(range(0, 8, 2))) - self.assertEqual(__, list(range(1, 8, 3))) - self.assertEqual(__, list(range(5, -7, -4))) - self.assertEqual(__, list(range(5, -8, -4))) - - def test_insertions(self): - knight = ['you', 'shall', 'pass'] - knight.insert(2, 'not') - self.assertEqual(__, knight) - - knight.insert(0, 'Arthur') - self.assertEqual(__, knight) - - def test_popping_lists(self): - stack = [10, 20, 30] - stack.append('last') - - self.assertEqual(__, stack) - - popped_value = stack.pop() - self.assertEqual(__, popped_value) - self.assertEqual(__, stack) - - popped_value = stack.pop(1) - self.assertEqual(__, popped_value) - self.assertEqual(__, stack) - - # Notice that there is a "pop" but no "push" in python? - - # Part of the Python philosophy is that there ideally should be one and - # only one way of doing anything. A 'push' is the same as an 'append'. - - # To learn more about this try typing "import this" from the python - # console... ;) - - def test_making_queues(self): - queue = [1, 2] - queue.append('last') - - self.assertEqual(__, queue) - - popped_value = queue.pop(0) - self.assertEqual(__, popped_value) - self.assertEqual(__, queue) - - # Note, for Python 2 popping from the left hand side of a list is - # inefficient. Use collections.deque instead. - - # This is not as issue for Python 3 through - diff --git a/python 3/koans/about_sets.py b/python 3/koans/about_sets.py deleted file mode 100644 index 2fd2410ca..000000000 --- a/python 3/koans/about_sets.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutSets(Koan): - def test_sets_make_keep_lists_unique(self): - highlanders = ['MacLeod', 'Ramirez', 'MacLeod', 'Matunas', 'MacLeod', 'Malcolm', 'MacLeod'] - - there_can_only_be_only_one = set(highlanders) - - self.assertEqual(__, there_can_only_be_only_one) - - def test_empty_sets_have_different_syntax_to_populated_sets(self): - self.assertEqual(__, {1, 2, 3}) - self.assertEqual(__, set()) - - def test_dictionaries_and_sets_use_same_curly_braces(self): - # Note: Sets have only started using braces since Python 3 - - self.assertEqual(__, type({1, 2, 3}).__name__) - self.assertEqual(__, type({'one': 1, 'two': 2}).__name__) - - self.assertEqual(__, type({}).__name__) - - def test_creating_sets_using_strings(self): - self.assertEqual(__, {'12345'}) - self.assertEqual(__, set('12345')) - - def test_convert_the_set_into_a_list_to_sort_it(self): - self.assertEqual(__, sorted(set('12345'))) - - # ------------------------------------------------------------------ - - def chars_in(self, a_set): - return ''.join(sorted(a_set)) - - def test_set_have_arithmetic_operators(self): - good_guy = set('macleod') - bad_guy = set('mutunas') - - self.assertEqual(__, self.chars_in( good_guy - bad_guy) ) - self.assertEqual(__, self.chars_in( good_guy | bad_guy )) - self.assertEqual(__, self.chars_in( good_guy & bad_guy )) - self.assertEqual(__, self.chars_in( good_guy ^ bad_guy )) - - # ------------------------------------------------------------------ - - def test_we_can_query_set_membership(self): - self.assertEqual(__, 127 in {127, 0, 0, 1} ) - self.assertEqual(__, 'cow' not in set('apocalypse now') ) - - def test_we_can_compare_subsets(self): - self.assertEqual(__, set('cake') <= set('cherry cake')) - self.assertEqual(__, set('cake').issubset(set('cherry cake')) ) - - self.assertEqual(__, set('cake') > set('pie')) diff --git a/python 3/koans/about_true_and_false.py b/python 3/koans/about_true_and_false.py deleted file mode 100644 index f94b36ad8..000000000 --- a/python 3/koans/about_true_and_false.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutTrueAndFalse(Koan): - def truth_value(self, condition): - if condition: - return 'true stuff' - else: - return 'false stuff' - - def test_true_is_treated_as_true(self): - self.assertEqual(__, self.truth_value(True)) - - def test_false_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(False)) - - def test_none_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(None)) - - def test_zero_is_treated_as_false(self): - self.assertEqual(__, self.truth_value(0)) - - def test_empty_collections_are_treated_as_false(self): - self.assertEqual(__, self.truth_value([])) - self.assertEqual(__, self.truth_value({})) - self.assertEqual(__, self.truth_value(set())) - - def test_strings_are_treated_as_false(self): - self.assertEqual(__, self.truth_value("")) - - def test_everything_else_is_treated_as_true(self): - self.assertEqual(__, self.truth_value(1)) - self.assertEqual(__, self.truth_value("Python is named after Monty Python")) - self.assertEqual(__, self.truth_value(' ')) diff --git a/python 3/runner/sensei.py b/python 3/runner/sensei.py deleted file mode 100644 index d43873610..000000000 --- a/python 3/runner/sensei.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import re - -from . import helper -from .mockable_test_result import MockableTestResult - -class Sensei(MockableTestResult): - def __init__(self, stream): - unittest.TestResult.__init__(self) - self.stream = stream - self.prevTestClassName = None - self.pass_count = 0 - - def startTest(self, test): - MockableTestResult.startTest(self, test) - - if helper.cls_name(test) != self.prevTestClassName: - self.prevTestClassName = helper.cls_name(test) - if not self.failures: - self.stream.writeln() - self.stream.writeln("Thinking {0}".format(helper.cls_name(test))) - - def addSuccess(self, test): - if self.passesCount(): - MockableTestResult.addSuccess(self, test) - self.stream.writeln(" {0} has expanded your awareness.".format(test._testMethodName)) - self.pass_count += 1 - - def addError(self, test, err): - # Having 1 list for errors and 1 list for failures would mess with - # the error sequence - self.addFailure(test, err) - - def passesCount(self): - return not (self.failures and helper.cls_name(self.failures[0][0]) != self.prevTestClassName) - - def addFailure(self, test, err): - MockableTestResult.addFailure(self, test, err) - - def sortFailures(self, testClassName): - table = list() - for test, err in self.failures: - if helper.cls_name(test) == testClassName: - m = re.search("(?<= line )\d+" ,err) - if m: - tup = (int(m.group(0)), test, err) - table.append(tup) - - if table: - return sorted(table) - else: - return None - - def firstFailure(self): - if not self.failures: return None - - table = self.sortFailures(helper.cls_name(self.failures[0][0])) - - if table: - return (table[0][1], table[0][2]) - else: - return None - - def learn(self): - self.errorReport() - - self.stream.writeln("") - self.stream.writeln("") - self.stream.writeln(self.say_something_zenlike()) - - if self.failures: return - self.stream.writeln("\n**************************************************") - self.stream.writeln("That was the last one, well done!") - self.stream.writeln("\nIf you want more, take a look at about_extra_credit_task.py") - - def errorReport(self): - problem = self.firstFailure() - if not problem: return - test, err = problem - self.stream.writeln(" {0} has damaged your karma.".format(test._testMethodName)) - - self.stream.writeln("") - self.stream.writeln("You have not yet reached enlightenment ...") - self.stream.writeln(self.scrapeAssertionError(err)) - self.stream.writeln("") - self.stream.writeln("Please meditate on the following code:") - self.stream.writeln(self.scrapeInterestingStackDump(err)) - - def scrapeAssertionError(self, err): - if not err: return "" - - error_text = "" - count = 0 - for line in err.splitlines(): - m = re.search("^[^^ ].*$",line) - if m and m.group(0): - count+=1 - - if count>1: - error_text += (" " + line.strip()).rstrip() + '\n' - return error_text.strip('\n') - - def scrapeInterestingStackDump(self, err): - if not err: - return "" - - lines = err.splitlines() - - sep = '@@@@@SEP@@@@@' - - scrape = "" - for line in lines: - m = re.search("^ File .*$",line) - if m and m.group(0): - scrape += '\n' + line - - m = re.search("^ \w(\w)+.*$",line) - if m and m.group(0): - scrape += sep + line - - lines = scrape.splitlines() - - scrape = "" - for line in lines: - m = re.search("^.*[/\\\\]koans[/\\\\].*$",line) - if m and m.group(0): - scrape += line + '\n' - return scrape.replace(sep, '\n').strip('\n') - - # Hat's tip to Tim Peters for the zen statements from The Zen - # of Python (http://www.python.org/dev/peps/pep-0020/) - # - # Also a hat's tip to Ara T. Howard for the zen statements from his - # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html) and - # Edgecase's later permatation in the Ruby Koans - def say_something_zenlike(self): - if self.failures: - turn = self.pass_count % 37 - - if turn == 0: - return "Beautiful is better than ugly." - elif turn == 1 or turn == 2: - return "Explicit is better than implicit." - elif turn == 3 or turn == 4: - return "Simple is better than complex." - elif turn == 5 or turn == 6: - return "Complex is better than complicated." - elif turn == 7 or turn == 8: - return "Flat is better than nested." - elif turn == 9 or turn == 10: - return "Sparse is better than dense." - elif turn == 11 or turn == 12: - return "Readability counts." - elif turn == 13 or turn == 14: - return "Special cases aren't special enough to break the rules." - elif turn == 15 or turn == 16: - return "Although practicality beats purity." - elif turn == 17 or turn == 18: - return "Errors should never pass silently." - elif turn == 19 or turn == 20: - return "Unless explicitly silenced." - elif turn == 21 or turn == 22: - return "In the face of ambiguity, refuse the temptation to guess." - elif turn == 23 or turn == 24: - return "There should be one-- and preferably only one --obvious way to do it." - elif turn == 25 or turn == 26: - return "Although that way may not be obvious at first unless you're Dutch." - elif turn == 27 or turn == 28: - return "Now is better than never." - elif turn == 29 or turn == 30: - return "Although never is often better than right now." - elif turn == 31 or turn == 32: - return "If the implementation is hard to explain, it's a bad idea." - elif turn == 33 or turn == 34: - return "If the implementation is easy to explain, it may be a good idea." - else: - return "Namespaces are one honking great idea -- let's do more of those!" - - else: - return "Nobody ever expects the Spanish Inquisition." - - # Hopefully this will never ever happen! - return "The temple in collapsing! Run!!!" - \ No newline at end of file diff --git a/python 2/_runner_tests.py b/python2/_runner_tests.py similarity index 100% rename from python 2/_runner_tests.py rename to python2/_runner_tests.py diff --git a/python 2/contemplate_koans.py b/python2/contemplate_koans.py similarity index 100% rename from python 2/contemplate_koans.py rename to python2/contemplate_koans.py diff --git a/python 2/example_file.txt b/python2/example_file.txt similarity index 100% rename from python 2/example_file.txt rename to python2/example_file.txt diff --git a/python 2/koans/GREEDS_RULES.txt b/python2/koans/GREEDS_RULES.txt similarity index 100% rename from python 2/koans/GREEDS_RULES.txt rename to python2/koans/GREEDS_RULES.txt diff --git a/python 2/koans/__init__.py b/python2/koans/__init__.py similarity index 100% rename from python 2/koans/__init__.py rename to python2/koans/__init__.py diff --git a/python 2/koans/a_normal_folder/a_module.py b/python2/koans/a_normal_folder/a_module.py similarity index 100% rename from python 2/koans/a_normal_folder/a_module.py rename to python2/koans/a_normal_folder/a_module.py diff --git a/python 2/koans/a_package_folder/__init__.py b/python2/koans/a_package_folder/__init__.py similarity index 100% rename from python 2/koans/a_package_folder/__init__.py rename to python2/koans/a_package_folder/__init__.py diff --git a/python 2/koans/a_package_folder/a_module.py b/python2/koans/a_package_folder/a_module.py similarity index 100% rename from python 2/koans/a_package_folder/a_module.py rename to python2/koans/a_package_folder/a_module.py diff --git a/python 3/koans/about_asserts.py b/python2/koans/about_asserts.py similarity index 83% rename from python 3/koans/about_asserts.py rename to python2/koans/about_asserts.py index 3905498d3..f36f88af2 100644 --- a/python 3/koans/about_asserts.py +++ b/python2/koans/about_asserts.py @@ -9,25 +9,25 @@ def test_assert_truth(self): """ We shall contemplate truth by testing reality, via asserts. """ - self.assertTrue(False) # This should be true + self.assertTrue(True) # This should be true def test_assert_with_message(self): """ Enlightenment may be more easily achieved with appropriate messages. """ - self.assertTrue(False, "This should be true -- Please fix this") + self.assertTrue(True, "This should be true -- Please fix this") def test_fill_in_values(self): """ Sometimes we will ask you to fill in the values """ - self.assertEqual(__, 1 + 1) + self.assertEqual(2, 1 + 1) def test_assert_equality(self): """ To understand reality, we must compare our expectations against reality. """ - expected_value = __ + expected_value = 2 actual_value = 1 + 1 self.assertTrue(expected_value == actual_value) @@ -35,7 +35,7 @@ def test_a_better_way_of_asserting_equality(self): """ Some ways of asserting equality are better than others. """ - expected_value = __ + expected_value = 2 actual_value = 1 + 1 self.assertEqual(expected_value, actual_value) @@ -46,5 +46,5 @@ def test_that_unittest_asserts_work_the_same_way_as_python_asserts(self): """ # This throws an AssertionError exception - assert False + assert True diff --git a/python 2/koans/about_attribute_access.py b/python2/koans/about_attribute_access.py similarity index 82% rename from python 2/koans/about_attribute_access.py rename to python2/koans/about_attribute_access.py index bcb293255..f44580e13 100644 --- a/python 2/koans/about_attribute_access.py +++ b/python2/koans/about_attribute_access.py @@ -18,8 +18,8 @@ def test_calling_undefined_functions_normally_results_in_errors(self): try: typical.foobar() except Exception as exception: - self.assertEqual(__, type(exception).__name__) - self.assertMatch(__, exception[0]) + self.assertEqual('AttributeError', type(exception).__name__) + self.assertMatch("'TypicalObject' object has no attribute 'foobar'", exception[0]) def test_calling_getattribute_causes_an_attribute_error(self): typical = self.TypicalObject() @@ -27,7 +27,7 @@ def test_calling_getattribute_causes_an_attribute_error(self): try: typical.__getattribute__('foobar') except AttributeError as exception: - self.assertMatch(__, exception[0]) + self.assertMatch("'TypicalObject' object has no attribute 'foobar'", exception[0]) # THINK ABOUT IT: # @@ -43,17 +43,17 @@ def __getattribute__(self, attr_name): def test_all_attribute_reads_are_caught(self): catcher = self.CatchAllAttributeReads() - self.assertMatch(__, catcher.foobar) + self.assertMatch("Someone called 'foobar' and it could not be found", catcher.foobar) def test_intercepting_return_values_can_disrupt_the_call_chain(self): catcher = self.CatchAllAttributeReads() - self.assertMatch(__, catcher.foobaz) # This is fine + self.assertMatch("Someone called 'foobaz' and it could not be found", catcher.foobaz) # This is fine try: catcher.foobaz(1) except TypeError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'str' object is not callable", ex[0]) # foobaz returns a string. What happens to the '(1)' part? # Try entering this into a python console to reproduce the issue: @@ -64,7 +64,7 @@ def test_intercepting_return_values_can_disrupt_the_call_chain(self): def test_changes_to_the_getattribute_implementation_affects_getattr_function(self): catcher = self.CatchAllAttributeReads() - self.assertMatch(__, getattr(catcher, 'any_attribute')) + self.assertMatch("Someone called 'any_attribute' and it could not be found", getattr(catcher, 'any_attribute')) # ------------------------------------------------------------------ @@ -79,8 +79,8 @@ def __getattribute__(self, attr_name): def test_foo_attributes_are_caught(self): catcher = self.WellBehavedFooCatcher() - self.assertEqual(__, catcher.foo_bar) - self.assertEqual(__, catcher.foo_baz) + self.assertEqual('Foo to you too', catcher.foo_bar) + self.assertEqual('Foo to you too', catcher.foo_baz) def test_non_foo_messages_are_treated_normally(self): catcher = self.WellBehavedFooCatcher() @@ -88,7 +88,7 @@ def test_non_foo_messages_are_treated_normally(self): try: catcher.normal_undefined_attribute except AttributeError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'WellBehavedFooCatcher' object has no attribute 'normal_undefined_attribute'", ex[0]) # ------------------------------------------------------------------ @@ -125,7 +125,7 @@ def test_getattribute_is_a_bit_overzealous_sometimes(self): catcher = self.RecursiveCatcher() catcher.my_method() global stack_depth - self.assertEqual(__, stack_depth) + self.assertEqual(11, stack_depth) # ------------------------------------------------------------------ @@ -146,17 +146,17 @@ def test_getattr_ignores_known_attributes(self): catcher = self.MinimalCatcher() catcher.my_method() - self.assertEqual(__, catcher.no_of_getattr_calls) + self.assertEqual(0, catcher.no_of_getattr_calls) def test_getattr_only_catches_unknown_attributes(self): catcher = self.MinimalCatcher() catcher.purple_flamingos() catcher.free_pie() - self.assertEqual(__, + self.assertEqual('DuffObject', type(catcher.give_me_duff_or_give_me_death()).__name__) - self.assertEqual(__, catcher.no_of_getattr_calls) + self.assertEqual(3, catcher.no_of_getattr_calls) # ------------------------------------------------------------------ @@ -177,9 +177,9 @@ def test_setattr_intercepts_attribute_assignments(self): fanboy.comic = 'The Laminator, issue #1' fanboy.pie = 'blueberry' - self.assertEqual(__, fanboy.a_pie) + self.assertEqual('blueberry', fanboy.a_pie) - prefix = '__' + prefix = 'my' self.assertEqual("The Laminator, issue #1", getattr(fanboy, prefix + '_comic')) # ------------------------------------------------------------------ @@ -201,7 +201,7 @@ def test_it_modifies_external_attribute_as_expected(self): setter = self.ScarySetter() setter.e = "mc hammer" - self.assertEqual(__, setter.altered_e) + self.assertEqual("mc hammer", setter.altered_e) def test_it_mangles_some_internal_attributes(self): setter = self.ScarySetter() @@ -209,9 +209,9 @@ def test_it_mangles_some_internal_attributes(self): try: coconuts = setter.num_of_coconuts except AttributeError: - self.assertEqual(__, setter.altered_num_of_coconuts) + self.assertEqual(9, setter.altered_num_of_coconuts) def test_in_this_case_private_attributes_remain_unmangled(self): setter = self.ScarySetter() - self.assertEqual(__, setter._num_of_private_coconuts) + self.assertEqual(2, setter._num_of_private_coconuts) diff --git a/python 2/koans/about_class_attributes.py b/python2/koans/about_class_attributes.py similarity index 74% rename from python 2/koans/about_class_attributes.py rename to python2/koans/about_class_attributes.py index dac8f59f4..8c207829f 100644 --- a/python 2/koans/about_class_attributes.py +++ b/python2/koans/about_class_attributes.py @@ -16,36 +16,36 @@ def test_new_style_class_objects_are_objects(self): # phased out it Python 3. fido = self.Dog() - self.assertEqual(__, isinstance(fido, object)) + self.assertEqual(True, isinstance(fido, object)) def test_classes_are_types(self): - self.assertEqual(__, self.Dog.__class__ == type) + self.assertEqual(True, self.Dog.__class__ == type) def test_classes_are_objects_too(self): - self.assertEqual(__, issubclass(self.Dog, object)) + self.assertEqual(True, issubclass(self.Dog, object)) def test_objects_have_methods(self): fido = self.Dog() - self.assertEqual(__, len(dir(fido))) + self.assertEqual(18, len(dir(fido))) def test_classes_have_methods(self): - self.assertEqual(__, len(dir(self.Dog))) + self.assertEqual(18, len(dir(self.Dog))) def test_creating_objects_without_defining_a_class(self): singularity = object() - self.assertEqual(__, len(dir(singularity))) + self.assertEqual(15, len(dir(singularity))) def test_defining_attributes_on_individual_objects(self): fido = self.Dog() fido.legs = 4 - self.assertEqual(__, fido.legs) + self.assertEqual(4, fido.legs) def test_defining_functions_on_individual_objects(self): fido = self.Dog() fido.wag = lambda : 'fidos wag' - self.assertEqual(__, fido.wag()) + self.assertEqual('fidos wag', fido.wag()) def test_other_objects_are_not_affected_by_these_singleton_functions(self): fido = self.Dog() @@ -58,7 +58,7 @@ def wag(): try: rover.wag() except Exception as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'Dog' object has no attribute 'wag'", ex[0]) # ------------------------------------------------------------------ @@ -81,19 +81,19 @@ def growl(cls): return "classmethod growl, arg: cls=" + cls.__name__ def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_too(self): - self.assertMatch(__, self.Dog2.growl()) + self.assertMatch('classmethod growl, arg: cls=Dog2', self.Dog2.growl()) def test_classmethods_are_not_independent_of_instance_methods(self): fido = self.Dog2() - self.assertMatch(__, fido.growl()) - self.assertMatch(__, self.Dog2.growl()) + self.assertMatch('classmethod growl, arg: cls=Dog2', fido.growl()) + self.assertMatch('classmethod growl, arg: cls=Dog2', self.Dog2.growl()) def test_staticmethods_are_unbound_functions_housed_in_a_class(self): - self.assertMatch(__, self.Dog2.bark()) + self.assertMatch('staticmethod bark, arg: None', self.Dog2.bark()) def test_staticmethods_also_overshadow_instance_methods(self): fido = self.Dog2() - self.assertMatch(__, fido.bark()) + self.assertMatch('staticmethod bark, arg: None', fido.bark()) # ------------------------------------------------------------------ @@ -123,20 +123,20 @@ def test_classmethods_can_not_be_used_as_properties(self): try: fido.name = "Fido" except Exception as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'classmethod' object is not callable", ex[0]) def test_classes_and_instances_do_not_share_instance_attributes(self): fido = self.Dog3() fido.set_name_from_instance("Fido") fido.set_name("Rover") - self.assertEqual(__, fido.get_name_from_instance()) - self.assertEqual(__, self.Dog3.get_name()) + self.assertEqual('Fido', fido.get_name_from_instance()) + self.assertEqual('Rover', self.Dog3.get_name()) def test_classes_and_instances_do_share_class_attributes(self): fido = self.Dog3() fido.set_name("Fido") - self.assertEqual(__, fido.get_name()) - self.assertEqual(__, self.Dog3.get_name()) + self.assertEqual('Fido', fido.get_name()) + self.assertEqual('Fido', self.Dog3.get_name()) # ------------------------------------------------------------------ @@ -151,13 +151,13 @@ def a_static_method(): a_static_method = staticmethod(a_static_method) def test_you_can_define_class_methods_without_using_a_decorator(self): - self.assertEqual(__, self.Dog4.a_class_method()) + self.assertEqual('dogs class method', self.Dog4.a_class_method()) def test_you_can_define_static_methods_without_using_a_decorator(self): - self.assertEqual(__, self.Dog4.a_static_method()) + self.assertEqual('dogs static method', self.Dog4.a_static_method()) # ------------------------------------------------------------------ def test_heres_an_easy_way_to_explicitly_call_class_methods_from_instance_methods(self): fido = self.Dog4() - self.assertEqual(__, fido.__class__.a_class_method()) + self.assertEqual('dogs class method', fido.__class__.a_class_method()) diff --git a/python 2/koans/about_classes.py b/python2/koans/about_classes.py similarity index 77% rename from python 2/koans/about_classes.py rename to python2/koans/about_classes.py index 329f44b42..55fa8abfb 100644 --- a/python 2/koans/about_classes.py +++ b/python2/koans/about_classes.py @@ -9,10 +9,10 @@ class Dog(object): def test_instances_of_classes_can_be_created_adding_parenthesis(self): fido = self.Dog() - self.assertEqual(__, type(fido).__name__) + self.assertEqual('Dog', type(fido).__name__) def test_classes_have_docstrings(self): - self.assertMatch(__, self.Dog.__doc__) + self.assertMatch('Dogs need regular', self.Dog.__doc__) # ------------------------------------------------------------------ @@ -25,12 +25,12 @@ def set_name(self, a_name): def test_init_method_is_the_constructor(self): dog = self.Dog2() - self.assertEqual(__, dog._name) + self.assertEqual('Paul', dog._name) def test_private_attributes_are_not_really_private(self): dog = self.Dog2() dog.set_name("Fido") - self.assertEqual(__, dog._name) + self.assertEqual('Fido', dog._name) # The _ prefix in _name implies private ownership, but nothing is truly # private in Python. @@ -38,11 +38,11 @@ def test_you_can_also_access_the_value_out_using_getattr_and_dict(self): fido = self.Dog2() fido.set_name("Fido") - self.assertEqual(__, getattr(fido, "_name")) + self.assertEqual('Fido', getattr(fido, "_name")) # getattr(), setattr() and delattr() are a way of accessing attributes # by method rather than through assignment operators - self.assertEqual(__, fido.__dict__["_name"]) + self.assertEqual('Fido', fido.__dict__["_name"]) # Yes, this works here, but don't rely on the __dict__ object! Some # class implementations use optimization which result in __dict__ not # showing everything. @@ -66,8 +66,8 @@ def test_that_name_can_be_read_as_a_property(self): fido = self.Dog3() fido.set_name("Fido") - self.assertEqual(__, fido.get_name()) # access as method - self.assertEqual(__, fido.name) # access as property + self.assertEqual('Fido', fido.get_name()) # access as method + self.assertEqual('Fido', fido.name) # access as property # ------------------------------------------------------------------ @@ -87,7 +87,7 @@ def test_creating_properties_with_decorators_is_slightly_easier(self): fido = self.Dog4() fido.name = "Fido" - self.assertEqual(__, fido.name) + self.assertEqual('Fido', fido.name) # ------------------------------------------------------------------ @@ -101,10 +101,10 @@ def name(self): def test_init_provides_initial_values_for_instance_variables(self): fido = self.Dog5("Fido") - self.assertEqual(__, fido.name) + self.assertEqual('Fido', fido.name) def test_args_must_match_init(self): - self.assertRaises(___, self.Dog5) # Evaluates self.Dog5() + self.assertRaises(TypeError, self.Dog5) # Evaluates self.Dog5() # THINK ABOUT IT: # Why is this so? @@ -113,7 +113,7 @@ def test_different_objects_have_difference_instance_variables(self): fido = self.Dog5("Fido") rover = self.Dog5("Rover") - self.assertEqual(____, rover.name == fido.name) + self.assertEqual(False, rover.name == fido.name) # ------------------------------------------------------------------ @@ -125,7 +125,7 @@ def get_self(self): return self def __str__(self): - return __ + return self._name def __repr__(self): return "" @@ -134,26 +134,26 @@ def __repr__(self): def test_inside_a_method_self_refers_to_the_containing_object(self): fido = self.Dog6("Fido") - self.assertEqual(__, fido.get_self()) # Not a string! + self.assertEqual(fido, fido.get_self()) # Not a string! def test_str_provides_a_string_version_of_the_object(self): fido = self.Dog6("Fido") - self.assertEqual("Bob", str(fido)) + self.assertEqual("Fido", str(fido)) def test_str_is_used_explicitly_in_string_interpolation(self): fido = self.Dog6("Fido") - self.assertEqual(__, "My dog is " + str(fido)) + self.assertEqual('My dog is Fido', "My dog is " + str(fido)) def test_repr_provides_a_more_complete_string_version(self): fido = self.Dog6("Fido") - self.assertEqual(__, repr(fido)) + self.assertEqual("", repr(fido)) def test_all_objects_support_str_and_repr(self): seq = [1,2,3] - self.assertEqual(__, str(seq)) - self.assertEqual(__, repr(seq)) + self.assertEqual('[1, 2, 3]', str(seq)) + self.assertEqual('[1, 2, 3]', repr(seq)) - self.assertEqual(__, str("STRING")) - self.assertEqual(__, repr("STRING")) + self.assertEqual('STRING', str("STRING")) + self.assertEqual("'STRING'", repr("STRING")) \ No newline at end of file diff --git a/python 2/koans/about_control_statements.py b/python2/koans/about_control_statements.py similarity index 84% rename from python 2/koans/about_control_statements.py rename to python2/koans/about_control_statements.py index 7b3d30c9a..2f16b548e 100644 --- a/python 2/koans/about_control_statements.py +++ b/python2/koans/about_control_statements.py @@ -10,13 +10,13 @@ def test_if_then_else_statements(self): result = 'true value' else: result = 'false value' - self.assertEqual(__, result) + self.assertEqual('true value', result) def test_if_then_statements(self): result = 'default value' if True: result = 'true value' - self.assertEqual(__, result) + self.assertEqual('true value', result) def test_while_statement(self): i = 1 @@ -24,7 +24,7 @@ def test_while_statement(self): while i <= 10: result = result * i i += 1 - self.assertEqual(__, result) + self.assertEqual(3628800, result) def test_break_statement(self): i = 1 @@ -33,7 +33,7 @@ def test_break_statement(self): if i > 10: break result = result * i i += 1 - self.assertEqual(__, result) + self.assertEqual(3628800, result) def test_continue_statement(self): i = 0 @@ -42,14 +42,14 @@ def test_continue_statement(self): i += 1 if (i % 2) == 0: continue result.append(i) - self.assertEqual(__, result) + self.assertEqual([1,3,5,7,9], result) def test_for_statement(self): phrase = ["fish", "and", "chips"] result = [] for item in phrase: result.append(item.upper()) - self.assertEqual([__, __, __], result) + self.assertEqual(['FISH', 'AND', 'CHIPS'], result) def test_for_statement_with_tuples(self): round_table = [ @@ -62,7 +62,7 @@ def test_for_statement_with_tuples(self): for knight, answer in round_table: result.append("Contestant: '" + knight + "' Answer: '" + answer + "'") - text = __ + text = 'Green' self.assertMatch(text, result[2]) diff --git a/python 2/koans/about_decorating_with_classes.py b/python2/koans/about_decorating_with_classes.py similarity index 78% rename from python 2/koans/about_decorating_with_classes.py rename to python2/koans/about_decorating_with_classes.py index 9617cf01e..accdaa703 100644 --- a/python 2/koans/about_decorating_with_classes.py +++ b/python2/koans/about_decorating_with_classes.py @@ -14,26 +14,26 @@ def maximum(self, a, b): def test_partial_that_wrappers_no_args(self): """ - Before we can understand this type of decorator we need to consider + Before we can understand Class Decorators we need to consider the partial. """ max = functools.partial(self.maximum) - self.assertEqual(__, max(7,23)) - self.assertEqual(__, max(10,-10)) + self.assertEqual(23, max(7,23)) + self.assertEqual(10, max(10,-10)) def test_partial_that_wrappers_first_arg(self): max0 = functools.partial(self.maximum, 0) - self.assertEqual(__, max0(-4)) - self.assertEqual(__, max0(5)) + self.assertEqual(0, max0(-4)) + self.assertEqual(5, max0(5)) def test_partial_that_wrappers_all_args(self): always99 = functools.partial(self.maximum, 99, 20) always20 = functools.partial(self.maximum, 9, 20) - self.assertEqual(__, always99()) - self.assertEqual(__, always20()) + self.assertEqual(99, always99()) + self.assertEqual(20, always20()) # ------------------------------------------------------------------ @@ -64,8 +64,8 @@ def test_decorator_with_no_arguments(self): # To clarify: the decorator above the function has no arguments, even # if the decorated function does - self.assertEqual(__, self.foo()) - self.assertEqual(__, self.parrot('pieces of eight')) + self.assertEqual("foo, foo", self.foo()) + self.assertEqual('PIECES OF EIGHT, PIECES OF EIGHT', self.parrot('pieces of eight')) # ------------------------------------------------------------------ @@ -77,7 +77,7 @@ def test_what_a_decorator_is_doing_to_a_function(self): #wrap the function with the decorator self.sound_check = self.doubleit(self.sound_check) - self.assertEqual(__, self.sound_check()) + self.assertEqual('Testing..., Testing...', self.sound_check()) # ------------------------------------------------------------------ @@ -108,11 +108,11 @@ def idler(self, num): pass def test_decorator_with_an_argument(self): - self.assertEqual(__, self.count_badly(2)) - self.assertEqual(__, self.count_badly.__doc__) + self.assertEqual(5, self.count_badly(2)) + self.assertEqual('Increments a value by one. Kind of.', self.count_badly.__doc__) def test_documentor_which_already_has_a_docstring(self): - self.assertEqual(__, self.idler.__doc__) + self.assertEqual('Idler: Does nothing', self.idler.__doc__) # ------------------------------------------------------------------ @@ -123,6 +123,6 @@ def homer(self): return "D'oh" def test_we_can_chain_decorators(self): - self.assertEqual(__, self.homer()) - self.assertEqual(__, self.homer.__doc__) + self.assertEqual("D'oh, D'oh, D'oh, D'oh", self.homer()) + self.assertEqual("DOH!", self.homer.__doc__) \ No newline at end of file diff --git a/python 2/koans/about_decorating_with_functions.py b/python2/koans/about_decorating_with_functions.py similarity index 76% rename from python 2/koans/about_decorating_with_functions.py rename to python2/koans/about_decorating_with_functions.py index 69cd02d93..6952bc33a 100644 --- a/python 2/koans/about_decorating_with_functions.py +++ b/python2/koans/about_decorating_with_functions.py @@ -14,8 +14,8 @@ def mediocre_song(self): return "o/~ We all live in a broken submarine o/~" def test_decorators_can_modify_a_function(self): - self.assertMatch(__, self.mediocre_song()) - self.assertEqual(__, self.mediocre_song.wow_factor) + self.assertMatch("o/~ We all live in a broken submarine o/~", self.mediocre_song()) + self.assertEqual('COWBELL BABY!', self.mediocre_song.wow_factor) # ------------------------------------------------------------------ @@ -29,5 +29,5 @@ def render_tag(self, name): return name def test_decorators_can_change_a_function_output(self): - self.assertEqual(__, self.render_tag('llama')) + self.assertEqual('', self.render_tag('llama')) diff --git a/python 2/koans/about_deleting_objects.py b/python2/koans/about_deleting_objects.py similarity index 87% rename from python 2/koans/about_deleting_objects.py rename to python2/koans/about_deleting_objects.py index c47583051..b3404b749 100644 --- a/python 2/koans/about_deleting_objects.py +++ b/python2/koans/about_deleting_objects.py @@ -9,7 +9,7 @@ def test_del_can_remove_slices(self): del lottery_nums[1] del lottery_nums[2:4] - self.assertEqual(__, lottery_nums) + self.assertEqual([4, 15, 42], lottery_nums) def test_del_can_remove_entire_lists(self): lottery_nums = [4, 8, 15, 16, 23, 42] @@ -18,7 +18,7 @@ def test_del_can_remove_entire_lists(self): win = lottery_nums except Exception as e: pass - self.assertMatch(__, e[0]) + self.assertMatch("local variable 'lottery_nums' referenced before assignment", e[0]) # ==================================================================== @@ -51,8 +51,8 @@ def test_del_can_remove_attributes(self): except AttributeError as e: err_msg2 = e.args[0] - self.assertMatch(__, err_msg1) - self.assertMatch(__, err_msg2) + self.assertMatch("'ClosingSale' object has no attribute 'toilet_brushes'", err_msg1) + self.assertMatch("'ClosingSale' object has no attribute 'hamsters'", err_msg2) # ==================================================================== @@ -81,7 +81,7 @@ def test_del_works_with_properties(self): self.assertEqual('Senor Ninguno', cowboy.name) del cowboy.name - self.assertEqual(__, cowboy.name) + self.assertEqual('The man with no name', cowboy.name) # ==================================================================== @@ -108,7 +108,7 @@ def test_another_way_to_make_a_deletable_property(self): self.assertEqual('Patrick', citizen.name) del citizen.name - self.assertEqual(__, citizen.name) + self.assertEqual('Number Six', citizen.name) # ==================================================================== @@ -124,4 +124,4 @@ def tests_del_can_be_overriden(self): sale = self.MoreOrganisedClosingSale() self.assertEqual(5, sale.jellies()) del sale.jellies - self.assertEqual(__, sale.last_deletion) + self.assertEqual('jellies', sale.last_deletion) diff --git a/python 2/koans/about_dice_project.py b/python2/koans/about_dice_project.py similarity index 93% rename from python 2/koans/about_dice_project.py rename to python2/koans/about_dice_project.py index f16deed7b..0180b592d 100644 --- a/python 2/koans/about_dice_project.py +++ b/python2/koans/about_dice_project.py @@ -14,9 +14,7 @@ def values(self): return self._values def roll(self, n): - # Needs implementing! - # Tip: random.randint(min, max) can be used to generate random numbers - pass + self._values = map(lambda n: random.randint(1, 6), list(range(n))) class AboutDiceProject(Koan): def test_can_create_a_dice_set(self): diff --git a/python 2/koans/about_dictionaries.py b/python2/koans/about_dictionaries.py similarity index 66% rename from python 2/koans/about_dictionaries.py rename to python2/koans/about_dictionaries.py index 3c47178b3..955549dcf 100644 --- a/python 2/koans/about_dictionaries.py +++ b/python2/koans/about_dictionaries.py @@ -12,22 +12,22 @@ def test_creating_dictionaries(self): empty_dict = dict() self.assertEqual(dict, type(empty_dict)) self.assertEqual(dict(), empty_dict) - self.assertEqual(__, len(empty_dict)) + self.assertEqual(0, len(empty_dict)) def test_dictionary_literals(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish)) + self.assertEqual(2, len(babel_fish)) def test_accessing_dictionaries(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, babel_fish['one']) - self.assertEqual(__, babel_fish['two']) + self.assertEqual('uno', babel_fish['one']) + self.assertEqual('dos', babel_fish['two']) def test_changing_dictionaries(self): babel_fish = { 'one': "uno", 'two': "dos" } babel_fish['one'] = "eins" - expected = { 'two': "dos", 'one': __ } + expected = { 'two': "dos", 'one': 'eins' } self.assertEqual(expected, babel_fish) # Bonus Question: Why was "expected" broken out into a variable @@ -37,26 +37,26 @@ def test_dictionary_is_unordered(self): dict1 = { 'one': "uno", 'two': "dos" } dict2 = { 'two': "dos", 'one': "uno" } - self.assertEqual(____, dict1 == dict2) + self.assertEqual(True, dict1 == dict2) def test_dictionary_keys(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish.keys())) - self.assertEqual(__, 'one' in babel_fish) - self.assertEqual(__, 'two' in babel_fish) + self.assertEqual(2, len(babel_fish.keys())) + self.assertEqual(True, 'one' in babel_fish) + self.assertEqual(True, 'two' in babel_fish) self.assertEqual(list, babel_fish.keys().__class__) def test_dictionary_values(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish.values())) - self.assertEqual(__, 'uno' in babel_fish.values()) - self.assertEqual(__, 'dos' in babel_fish.values()) + self.assertEqual(2, len(babel_fish.values())) + self.assertEqual(True, 'uno' in babel_fish.values()) + self.assertEqual(True, 'dos' in babel_fish.values()) self.assertEqual(list, babel_fish.values().__class__) def test_making_a_dictionary_from_a_sequence_of_keys(self): cards = {}.fromkeys(("red warrior", "green elf", "blue valkyrie", "yellow dwarf", "confused looking zebra"), 42) - self.assertEqual(__, len(cards)) - self.assertEqual(__, cards["green elf"]) - self.assertEqual(__, cards["yellow dwarf"]) + self.assertEqual(5, len(cards)) + self.assertEqual(42, cards["green elf"]) + self.assertEqual(42, cards["yellow dwarf"]) diff --git a/python 2/koans/about_exceptions.py b/python2/koans/about_exceptions.py similarity index 66% rename from python 2/koans/about_exceptions.py rename to python2/koans/about_exceptions.py index 22d94a7c6..b24d7deee 100644 --- a/python 2/koans/about_exceptions.py +++ b/python2/koans/about_exceptions.py @@ -10,10 +10,10 @@ class MySpecialError(RuntimeError): def test_exceptions_inherit_from_exception(self): mro = self.MySpecialError.__mro__ - self.assertEqual(__, mro[1].__name__) - self.assertEqual(__, mro[2].__name__) - self.assertEqual(__, mro[3].__name__) - self.assertEqual(__, mro[4].__name__) + self.assertEqual('RuntimeError', mro[1].__name__) + self.assertEqual('StandardError', mro[2].__name__) + self.assertEqual('Exception', mro[3].__name__) + self.assertEqual('BaseException', mro[4].__name__) def test_try_clause(self): result = None @@ -22,15 +22,15 @@ def test_try_clause(self): except StandardError as ex: result = 'exception handled' - self.assertEqual(__, result) + self.assertEqual('exception handled', result) - self.assertEqual(____, isinstance(ex, StandardError)) - self.assertEqual(____, isinstance(ex, RuntimeError)) + self.assertEqual(True, isinstance(ex, StandardError)) + self.assertEqual(False, isinstance(ex, RuntimeError)) self.assertTrue(issubclass(RuntimeError, StandardError), \ "RuntimeError is a subclass of StandardError") - self.assertEqual(__, ex[0]) + self.assertEqual('Oops', ex[0]) def test_raising_a_specific_error(self): result = None @@ -39,8 +39,8 @@ def test_raising_a_specific_error(self): except self.MySpecialError as ex: result = 'exception handled' - self.assertEqual(__, result) - self.assertEqual(__, ex[0]) + self.assertEqual('exception handled', result) + self.assertEqual('My Message', ex[0]) def test_else_clause(self): result = None @@ -52,7 +52,7 @@ def test_else_clause(self): else: result = 'no damage done' - self.assertEqual(__, result) + self.assertEqual('no damage done', result) def test_finally_clause(self): result = None @@ -64,4 +64,4 @@ def test_finally_clause(self): finally: result = 'always run' - self.assertEqual(__, result) + self.assertEqual('always run', result) diff --git a/python 2/koans/about_extra_credit.py b/python2/koans/about_extra_credit.py similarity index 100% rename from python 2/koans/about_extra_credit.py rename to python2/koans/about_extra_credit.py diff --git a/python 3/koans/about_generators.py b/python2/koans/about_generators.py similarity index 81% rename from python 3/koans/about_generators.py rename to python2/koans/about_generators.py index 5e649fab7..cc4b3b86a 100644 --- a/python 3/koans/about_generators.py +++ b/python2/koans/about_generators.py @@ -15,11 +15,9 @@ class AboutGenerators(Koan): def test_generating_values_on_the_fly(self): result = list() bacon_generator = (n + ' bacon' for n in ['crunchy','veggie','danish']) - for bacon in bacon_generator: result.append(bacon) - - self.assertEqual(__, result) + self.assertEqual(['crunchy bacon', 'veggie bacon', 'danish bacon'], result) def test_generators_are_different_to_list_comprehensions(self): num_list = [x*2 for x in range(1,3)] @@ -28,9 +26,8 @@ def test_generators_are_different_to_list_comprehensions(self): self.assertEqual(2, num_list[0]) # A generator has to be iterated through. - with self.assertRaises(___): num = num_generator[0] - - self.assertEqual(__, list(num_generator)[0]) # This works though + self.assertRaises(TypeError, num_generator, [0]) # Evaluates num_generator[0] + self.assertEqual(2, list(num_generator)[0]) # This works though # Both list comprehensions and generators can be iterated though. However, a generator # function is only called on the first iteration. The values are generated on the fly @@ -44,8 +41,8 @@ def test_generator_expressions_are_a_one_shot_deal(self): attempt1 = list(dynamite) attempt2 = list(dynamite) - self.assertEqual(__, list(attempt1)) - self.assertEqual(__, list(attempt2)) + self.assertEqual(['Boom!', 'Boom!', 'Boom!'], list(attempt1)) + self.assertEqual([], list(attempt2)) # ------------------------------------------------------------------ @@ -59,12 +56,12 @@ def test_generator_method_will_yield_values_during_iteration(self): result = list() for item in self.simple_generator_method(): result.append(item) - self.assertEqual(__, result) + self.assertEqual(['peanut', 'butter', 'and', 'jelly'], result) def test_coroutines_can_take_arguments(self): result = self.simple_generator_method() - self.assertEqual(__, next(result)) - self.assertEqual(__, next(result)) + self.assertEqual('peanut', next(result)) + self.assertEqual('butter', next(result)) result.close() # ------------------------------------------------------------------ @@ -75,7 +72,7 @@ def cube_me(self, seq): def test_generator_method_with_parameter(self): result = self.cube_me(range(2,5)) - self.assertEqual(__, list(result)) + self.assertEqual([4,9,16], list(result)) # ------------------------------------------------------------------ @@ -88,7 +85,7 @@ def sum_it(self, seq): def test_generator_keeps_track_of_local_variables(self): result = self.sum_it(range(2,5)) - self.assertEqual(__, list(result)) + self.assertEqual([9], list(result)) # ------------------------------------------------------------------ @@ -99,7 +96,7 @@ def generator_with_coroutine(self): def test_generators_can_take_coroutines(self): generator = self.generator_with_coroutine() next(generator) - self.assertEqual(__, generator.send(1 + 2)) + self.assertEqual(3, generator.send(1 + 2)) # ------------------------------------------------------------------ @@ -117,6 +114,6 @@ def test_generators_can_see_if_they_have_been_called_with_a_value(self): generator2 = self.yield_tester() next(generator2) - self.assertEqual(__, next(generator2)) + self.assertEqual('no value', next(generator2)) \ No newline at end of file diff --git a/python 2/koans/about_inheritance.py b/python2/koans/about_inheritance.py similarity index 79% rename from python 2/koans/about_inheritance.py rename to python2/koans/about_inheritance.py index bebb0f93f..3f8fe7451 100644 --- a/python 2/koans/about_inheritance.py +++ b/python2/koans/about_inheritance.py @@ -23,31 +23,31 @@ def bark(self): return "yip" def test_subclasses_have_the_parent_as_an_ancestor(self): - self.assertEqual(____, issubclass(self.Chihuahua, self.Dog)) + self.assertEqual(True, issubclass(self.Chihuahua, self.Dog)) def test_this_subclass_ultimately_inherits_from_object_class(self): - self.assertEqual(____, issubclass(self.Chihuahua, object)) + self.assertEqual(True, issubclass(self.Chihuahua, object)) def test_instances_inherit_behavior_from_parent_class(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.name) + self.assertEqual('Chico', chico.name) def test_subclasses_add_new_behavior(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.wag()) + self.assertEqual('happy', chico.wag()) try: fido = self.Dog("Fido") fido.wag() except StandardError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'Dog' object has no attribute 'wag'", ex[0]) def test_subclasses_can_modify_existing_behavior(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.bark()) + self.assertEqual('yip', chico.bark()) fido = self.Dog("Fido") - self.assertEqual(__, fido.bark()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ @@ -57,7 +57,7 @@ def bark(self): def test_subclasses_can_invoke_parent_behavior_via_super(self): ralph = self.BullDog("Ralph") - self.assertEqual(__, ralph.bark()) + self.assertEqual('WOOF, GRR', ralph.bark()) # ------------------------------------------------------------------ @@ -67,7 +67,7 @@ def growl(self): def test_super_works_across_methods(self): george = self.GreatDane("George") - self.assertEqual(__, george.growl()) + self.assertEqual('WOOF, GROWL', george.growl()) # --------------------------------------------------------- @@ -84,8 +84,8 @@ def test_base_init_does_not_get_called_automatically(self): try: name = snoopy.name except Exception as ex: - self.assertMatch(__, ex[0]) + self.assertMatch('name', ex[0]) def test_base_init_has_to_be_called_explicitly(self): boxer = self.Greyhound("Boxer") - self.assertEqual(__, boxer.name) \ No newline at end of file + self.assertEqual('Boxer', boxer.name) \ No newline at end of file diff --git a/python 2/koans/about_iteration.py b/python2/koans/about_iteration.py similarity index 79% rename from python 2/koans/about_iteration.py rename to python2/koans/about_iteration.py index 52ed2385a..d72fddeac 100644 --- a/python 2/koans/about_iteration.py +++ b/python2/koans/about_iteration.py @@ -13,20 +13,20 @@ def test_iterators_are_a_type(self): for num in it: fib += num - self.assertEqual(__ , fib) + self.assertEqual(15 , fib) def test_iterating_with_next(self): stages = iter(['alpha','beta','gamma']) try: - self.assertEqual(__, next(stages)) + self.assertEqual('alpha', next(stages)) next(stages) - self.assertEqual(__, next(stages)) + self.assertEqual('gamma', next(stages)) next(stages) except StopIteration as ex: err_msg = 'Ran out of iterations' - self.assertMatch(__, err_msg) + self.assertMatch('Ran out of iterations', err_msg) # ------------------------------------------------------------------ @@ -37,7 +37,7 @@ def test_map_transforms_elements_of_a_list(self): seq = [1, 2, 3] mapped_seq = map(self.add_ten, seq) - self.assertEqual(__, mapped_seq) + self.assertEqual([11,12,13], mapped_seq) def test_filter_selects_certain_items_from_a_list(self): def is_even(item): return (item % 2) == 0 @@ -45,7 +45,7 @@ def is_even(item): return (item % 2) == 0 seq = [1, 2, 3, 4, 5, 6] even_numbers = filter(is_even, seq) - self.assertEqual(__, even_numbers) + self.assertEqual([2,4,6], even_numbers) def test_just_return_first_item_found(self): def is_big_name(item): return len(item) > 4 @@ -54,12 +54,12 @@ def is_big_name(item): return len(item) > 4 # NOTE This still iterates through the whole names, so not particularly # efficient - self.assertEqual([__], filter(is_big_name, names)[:1]) + self.assertEqual(['Clarence'], filter(is_big_name, names)[:1]) # Boring but effective for item in names: if is_big_name(item): - self.assertEqual(__, item) + self.assertEqual('Clarence', item) break # ------------------------------------------------------------------ @@ -72,10 +72,10 @@ def multiply(self,sum,item): def test_reduce_will_blow_your_mind(self): result = reduce(self.add, [2, 3, 4]) - self.assertEqual(__, result) + self.assertEqual(9, result) result2 = reduce(self.multiply, [2, 3, 4], 1) - self.assertEqual(__, result2) + self.assertEqual(24, result2) # Extra Credit: # Describe in your own words what reduce does. @@ -87,21 +87,21 @@ def test_creating_lists_with_list_comprehensions(self): comprehension = [delicacy.capitalize() for delicacy in feast] - self.assertEqual(__, comprehension[0]) - self.assertEqual(__, comprehension[2]) + self.assertEqual('Lambs', comprehension[0]) + self.assertEqual('Orangutans', comprehension[2]) def test_use_pass_for_iterations_with_no_body(self): for num in range(1,5): pass - self.assertEqual(__, num) + self.assertEqual(4, num) # ------------------------------------------------------------------ def test_all_iteration_methods_work_on_any_sequence_not_just_lists(self): # Ranges are an iteratable sequence result = map(self.add_ten, range(1,4)) - self.assertEqual(__, list(result)) + self.assertEqual([11,12,13], list(result)) try: # Files act like a collection of lines @@ -109,7 +109,7 @@ def test_all_iteration_methods_work_on_any_sequence_not_just_lists(self): def make_upcase(line) : return line.strip().upper() upcase_lines = map(make_upcase, file.readlines()) - self.assertEqual(__, list(upcase_lines)) + self.assertEqual(['THIS', 'IS', 'A', 'TEST'], list(upcase_lines)) # NOTE: You can create your own collections that work with each, # map, select, etc. diff --git a/python 3/koans/about_lambdas.py b/python2/koans/about_lambdas.py similarity index 76% rename from python 3/koans/about_lambdas.py rename to python2/koans/about_lambdas.py index 2268e6c65..e848c05cf 100644 --- a/python 3/koans/about_lambdas.py +++ b/python2/koans/about_lambdas.py @@ -10,7 +10,7 @@ class AboutLambdas(Koan): def test_lambdas_can_be_assigned_to_variables_and_called_explicitly(self): add_one = lambda n: n + 1 - self.assertEqual(__, add_one(10)) + self.assertEqual(11, add_one(10)) # ------------------------------------------------------------------ @@ -21,8 +21,8 @@ def test_accessing_lambda_via_assignment(self): sausages = self.make_order('sausage') eggs = self.make_order('egg') - self.assertEqual(__, sausages(3)) - self.assertEqual(__, eggs(2)) + self.assertEqual('3 sausages', sausages(3)) + self.assertEqual('2 eggs', eggs(2)) def test_accessing_lambda_without_assignment(self): - self.assertEqual(__, self.make_order('spam')(39823)) + self.assertEqual('39823 spams', self.make_order('spam')(39823)) diff --git a/python 2/koans/about_list_assignments.py b/python2/koans/about_list_assignments.py similarity index 66% rename from python 2/koans/about_list_assignments.py rename to python2/koans/about_list_assignments.py index d7e2e8998..e8a0f9e7f 100644 --- a/python 2/koans/about_list_assignments.py +++ b/python2/koans/about_list_assignments.py @@ -10,21 +10,21 @@ class AboutListAssignments(Koan): def test_non_parallel_assignment(self): names = ["John", "Smith"] - self.assertEqual(__, names) + self.assertEqual(["John", "Smith"], names) def test_parallel_assignments(self): first_name, last_name = ["John", "Smith"] - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual("John", first_name) + self.assertEqual("Smith", last_name) def test_parallel_assignments_with_sublists(self): first_name, last_name = [["Willie", "Rae"], "Johnson"] - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual(["Willie", "Rae"], first_name) + self.assertEqual('Johnson', last_name) def test_swapping_with_parallel_assignment(self): first_name = "Roy" last_name = "Rob" first_name, last_name = last_name, first_name - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual("Rob", first_name) + self.assertEqual('Roy', last_name) diff --git a/python2/koans/about_lists.py b/python2/koans/about_lists.py new file mode 100644 index 000000000..ade7bce89 --- /dev/null +++ b/python2/koans/about_lists.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Based on AboutArrays in the Ruby Koans +# + +from runner.koan import * + +class AboutLists(Koan): + #def test_creating_lists(self): + # empty_list = list() + # self.assertEqual(list, type(empty_list)) + # self.assertEqual(__, len(empty_list)) + # + #def test_list_literals(self): + # nums = list() + # self.assertEqual([], nums) + # + # nums[0:] = [1] + # self.assertEqual([1], nums) + # + # nums[1:] = [2] + # self.assertEqual([1, __], nums) + # + # nums.append(333) + # self.assertEqual([1, 2, __], nums) + # + #def test_accessing_list_elements(self): + # noms = ['peanut', 'butter', 'and', 'jelly'] + # + # self.assertEqual(__, noms[0]) + # self.assertEqual(__, noms[3]) + # self.assertEqual(__, noms[-1]) + # self.assertEqual(__, noms[-3]) + # + #def test_slicing_lists(self): + # noms = ['peanut', 'butter', 'and', 'jelly'] + # + # self.assertEqual(__, noms[0:1]) + # self.assertEqual(__, noms[0:2]) + # self.assertEqual(__, noms[2:2]) + # self.assertEqual(__, noms[2:20]) + # self.assertEqual(__, noms[4:0]) + # self.assertEqual(__, noms[4:100]) + # self.assertEqual(__, noms[5:0]) + # + #def test_slicing_to_the_edge(self): + # noms = ['peanut', 'butter', 'and', 'jelly'] + # + # self.assertEqual(__, noms[2:]) + # self.assertEqual(__, noms[:2]) + # + #def test_lists_and_ranges(self): + # self.assertEqual(list, type(range(5))) + # self.assertEqual(__, range(5)) + # self.assertEqual(__, range(5, 9)) + # + #def test_ranges_with_steps(self): + # self.assertEqual(__, range(0, 8, 2)) + # self.assertEqual(__, range(1, 8, 3)) + # self.assertEqual(__, range(5, -7, -4)) + # self.assertEqual(__, range(5, -8, -4)) + + def test_insertions(self): + knight = ['you', 'shall', 'pass'] + knight.insert(2, 'not') + self.assertEqual(['you', 'shall', 'not', 'pass'], knight) + + knight.insert(0, 'Arthur') + self.assertEqual(['Arthur', 'you', 'shall', 'not', 'pass'], knight) + + def test_popping_lists(self): + stack = [10, 20, 30] + stack.append('last') + + self.assertEqual([10, 20, 30, 'last'], stack) + + popped_value = stack.pop() + self.assertEqual('last', popped_value) + self.assertEqual([10, 20, 30], stack) + + popped_value = stack.pop(1) + self.assertEqual(20, popped_value) + self.assertEqual([10, 30], stack) + + # Notice that there is a "pop" but no "push" in python? + + # Part of the Python philosophy is that there ideally should be one and + # only one way of doing anything. A 'push' is the same as an 'append'. + + # To learn more about this try typing "import this" from the python + # console... ;) + + def test_use_deques_for_making_queues(self): + from collections import deque + + queue = deque([1, 2]) + queue.append('last') + + self.assertEqual([1,2,'last'], list(queue)) + + popped_value = queue.popleft() + self.assertEqual(1, popped_value) + self.assertEqual([2, 'last'], list(queue)) diff --git a/python 2/koans/about_method_bindings.py b/python2/koans/about_method_bindings.py similarity index 71% rename from python 2/koans/about_method_bindings.py rename to python2/koans/about_method_bindings.py index 8762ff1ee..b41a7b288 100644 --- a/python 2/koans/about_method_bindings.py +++ b/python2/koans/about_method_bindings.py @@ -16,47 +16,47 @@ def method(self): class AboutMethodBindings(Koan): def test_methods_are_bound_to_an_object(self): obj = Class() - self.assertEqual(__, obj.method.im_self == obj) + self.assertEqual(True, obj.method.im_self == obj) def test_methods_are_also_bound_to_a_function(self): obj = Class() - self.assertEqual(__, obj.method()) - self.assertEqual(__, obj.method.im_func(obj)) + self.assertEqual('parrot', obj.method()) + self.assertEqual('parrot', obj.method.im_func(obj)) def test_functions_have_attributes(self): - self.assertEqual(__, len(dir(function))) - self.assertEqual(__, dir(function) == dir(Class.method.im_func)) + self.assertEqual(31, len(dir(function))) + self.assertEqual(True, dir(function) == dir(Class.method.im_func)) def test_bound_methods_have_different_attributes(self): obj = Class() - self.assertEqual(__, len(dir(obj.method))) + self.assertEqual(23, len(dir(obj.method))) def test_setting_attributes_on_an_unbound_function(self): function.cherries = 3 - self.assertEqual(__, function.cherries) + self.assertEqual(3, function.cherries) def test_setting_attributes_on_a_bound_method_directly(self): obj = Class() try: obj.method.cherries = 3 except AttributeError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'instancemethod' object has no attribute 'cherries'", ex[0]) def test_setting_attributes_on_methods_by_accessing_the_inner_function(self): obj = Class() obj.method.im_func.cherries = 3 - self.assertEqual(__, obj.method.cherries) + self.assertEqual(3, obj.method.cherries) def test_functions_can_have_inner_functions(self): function2.get_fruit = function - self.assertEqual(__, function2.get_fruit()) + self.assertEqual('pineapple', function2.get_fruit()) def test_inner_functions_are_unbound(self): function2.get_fruit = function try: cls = function2.get_fruit.im_self except AttributeError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("'function' object has no attribute 'im_self'", ex[0]) # ------------------------------------------------------------------ @@ -73,8 +73,8 @@ def test_get_descriptor_resolves_attribute_binding(self): # binding_owner = obj # owner_type = cls - self.assertEqual(__, type(bound_obj).__name__) - self.assertEqual(__, type(binding_owner).__name__) + self.assertEqual('BoundClass', type(bound_obj).__name__) + self.assertEqual('AboutMethodBindings', type(binding_owner).__name__) self.assertEqual(AboutMethodBindings, owner_type) # ------------------------------------------------------------------ @@ -91,5 +91,5 @@ def __set__(self, obj, val): def test_set_descriptor_changes_behavior_of_attribute_assignment_changes(self): self.assertEqual(None, self.color.choice) self.color = 'purple' - self.assertEqual(__, self.color.choice) + self.assertEqual('purple', self.color.choice) diff --git a/python 2/koans/about_methods.py b/python2/koans/about_methods.py similarity index 77% rename from python 2/koans/about_methods.py rename to python2/koans/about_methods.py index 1ae60e799..3ea700504 100644 --- a/python 2/koans/about_methods.py +++ b/python2/koans/about_methods.py @@ -12,7 +12,7 @@ def my_global_function(a,b): class AboutMethods(Koan): def test_calling_an_global_function(self): - self.assertEqual(__, my_global_function(2,3)) + self.assertEqual(5, my_global_function(2,3)) # NOTE: Wrong number of arguments is not a SYNTAX error, but a # runtime error. @@ -20,7 +20,7 @@ def test_calling_functions_with_wrong_number_of_arguments(self): try: my_global_function() except Exception as exception: - self.assertEqual(__, type(exception).__name__) + self.assertEqual('TypeError', type(exception).__name__) self.assertMatch( r'my_global_function\(\) takes exactly 2 arguments \(0 given\)' , exception[0]) @@ -30,7 +30,7 @@ def test_calling_functions_with_wrong_number_of_arguments(self): except Exception as e: # Note, watch out for parenthesis. They need slashes in front! - self.assertMatch(__, e[0]) + self.assertMatch('my_global_function', e[0]) # ------------------------------------------------------------------ @@ -38,7 +38,7 @@ def pointless_method(self, a, b): sum = a + b def test_which_does_not_return_anything(self): - self.assertEqual(__, self.pointless_method(1, 2)) + self.assertEqual(None, self.pointless_method(1, 2)) # Notice that methods accessed from class scope do not require # you to pass the first "self" argument? @@ -48,8 +48,8 @@ def method_with_defaults(self, a, b='default_value'): return [a, b] def test_calling_with_default_values(self): - self.assertEqual(__, self.method_with_defaults(1)) - self.assertEqual(__, self.method_with_defaults(1, 2)) + self.assertEqual([1, 'default_value'], self.method_with_defaults(1)) + self.assertEqual([1,2], self.method_with_defaults(1, 2)) # ------------------------------------------------------------------ @@ -57,9 +57,9 @@ def method_with_var_args(self, *args): return args def test_calling_with_variable_arguments(self): - self.assertEqual(__, self.method_with_var_args()) + self.assertEqual((), self.method_with_var_args()) self.assertEqual(('one',), self.method_with_var_args('one')) - self.assertEqual(__, self.method_with_var_args('one', 'two')) + self.assertEqual(('one','two'), self.method_with_var_args('one', 'two')) # ------------------------------------------------------------------ @@ -70,13 +70,13 @@ def test_functions_without_self_arg_are_global_functions(self): def function_with_the_same_name(a, b): return a * b - self.assertEqual(__, function_with_the_same_name(3,4)) + self.assertEqual(12, function_with_the_same_name(3,4)) def test_calling_methods_in_same_class_with_explicit_receiver(self): def function_with_the_same_name(a, b): return a * b - self.assertEqual(__, self.function_with_the_same_name(3,4)) + self.assertEqual(7, self.function_with_the_same_name(3,4)) # ------------------------------------------------------------------ @@ -89,10 +89,10 @@ def another_method_with_the_same_name(self): return 42 def test_that_old_methods_are_hidden_by_redefinitions(self): - self.assertEqual(__, self.another_method_with_the_same_name()) + self.assertEqual(42, self.another_method_with_the_same_name()) def test_that_overlapped_method_is_still_there(self): - self.assertEqual(__, self.link_to_overlapped_method()) + self.assertEqual(10, self.link_to_overlapped_method()) # ------------------------------------------------------------------ @@ -100,21 +100,21 @@ def empty_method(self): pass def test_methods_that_do_nothing_need_to_use_pass_as_a_filler(self): - self.assertEqual(__, self.empty_method()) + self.assertEqual(None, self.empty_method()) def test_pass_does_nothing_at_all(self): "You" "shall" "not" pass - self.assertEqual(____, "Still got to this line" != None) + self.assertEqual(True, "Still got to this line" != None) # ------------------------------------------------------------------ def one_line_method(self): return 'Madagascar' def test_no_indentation_required_for_one_line_statement_bodies(self): - self.assertEqual(__, self.one_line_method()) + self.assertEqual('Madagascar', self.one_line_method()) # ------------------------------------------------------------------ @@ -123,7 +123,7 @@ def method_with_documentation(self): return "ok" def test_the_documentation_can_be_viewed_with_the_doc_method(self): - self.assertMatch(__, self.method_with_documentation.__doc__) + self.assertMatch('A string', self.method_with_documentation.__doc__) # ------------------------------------------------------------------ @@ -140,13 +140,13 @@ def __password(self): def test_calling_methods_in_other_objects(self): rover = self.Dog() - self.assertEqual(__, rover.name()) + self.assertEqual('Fido', rover.name()) def test_private_access_is_implied_but_not_enforced(self): rover = self.Dog() # This is a little rude, but legal - self.assertEqual(__, rover._tail()) + self.assertEqual('wagging', rover._tail()) def test_attributes_with_double_underscore_prefixes_are_subject_to_name_mangling(self): rover = self.Dog() @@ -154,10 +154,10 @@ def test_attributes_with_double_underscore_prefixes_are_subject_to_name_mangling #This may not be possible... password = __password() except Exception as ex: - self.assertEqual(__, type(ex).__name__) + self.assertEqual('NameError', type(ex).__name__) # But this still is! - self.assertEqual(__, rover._Dog__password()) + self.assertEqual('password', rover._Dog__password()) # Name mangling exists to avoid name clash issues when subclassing. # It is not for providing effective access protection diff --git a/python 2/koans/about_modules.py b/python2/koans/about_modules.py similarity index 77% rename from python 2/koans/about_modules.py rename to python2/koans/about_modules.py index 37aad480a..e689b72bd 100644 --- a/python 2/koans/about_modules.py +++ b/python2/koans/about_modules.py @@ -16,21 +16,21 @@ def test_importing_other_python_scripts_as_modules(self): import local_module # local_module.py duck = local_module.Duck() - self.assertEqual(__, duck.name) + self.assertEqual('Daffy', duck.name) def test_importing_attributes_from_classes_using_from_keyword(self): from local_module import Duck duck = Duck() # no module qualifier needed this time - self.assertEqual(__, duck.name) + self.assertEqual('Daffy', duck.name) def test_we_can_import_multiple_items_at_once(self): import jims, joes jims_dog = jims.Dog() joes_dog = joes.Dog() - self.assertEqual(__, jims_dog.identify()) - self.assertEqual(__, joes_dog.identify()) + self.assertEqual('jims dog', jims_dog.identify()) + self.assertEqual('joes dog', joes_dog.identify()) def test_importing_all_module_attributes_at_once(self): # NOTE Using this module level import declared at the top of this script: @@ -41,20 +41,20 @@ def test_importing_all_module_attributes_at_once(self): goose = Goose() hamster = Hamster() - self.assertEqual(__, goose.name) - self.assertEqual(__, hamster.name) + self.assertEqual('Mr Stabby', goose.name) + self.assertEqual('Phil', hamster.name) def test_modules_hide_attributes_prefixed_by_underscores(self): try: private_squirrel = _SecretSquirrel() except NameError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("global name '_SecretSquirrel' is not defined", ex[0]) def test_private_attributes_are_still_accessible_in_modules(self): from local_module import Duck # local_module.py duck = Duck() - self.assertEqual(__, duck._password) + self.assertEqual('password', duck._password) # module level attribute hiding doesn't affect class attributes # (unless the class itself is hidden). @@ -64,14 +64,14 @@ def test_a_module_can_choose_which_attributes_are_available_to_wildcards(self): # 'Goat' is on the __ALL__ list goat = Goat() - self.assertEqual(__, goat.name) + self.assertEqual('George', goat.name) # How about velociraptors? lizard = _Velociraptor() - self.assertEqual(__, lizard.name) + self.assertEqual('Cuddles', lizard.name) # SecretDuck? Never heard of her! try: duck = SecretDuck() except NameError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch('global name .* is not defined', ex[0]) diff --git a/python 2/koans/about_monkey_patching.py b/python2/koans/about_monkey_patching.py similarity index 77% rename from python 2/koans/about_monkey_patching.py rename to python2/koans/about_monkey_patching.py index c88f85bf4..7e805e78c 100644 --- a/python 2/koans/about_monkey_patching.py +++ b/python2/koans/about_monkey_patching.py @@ -14,7 +14,7 @@ def bark(self): def test_as_defined_dogs_do_bark(self): fido = self.Dog() - self.assertEqual(__, fido.bark()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ @@ -24,8 +24,8 @@ def wag(self): return "HAPPY" self.Dog.wag = wag fido = self.Dog() - self.assertEqual(__, fido.wag()) - self.assertEqual(__, fido.bark()) + self.assertEqual('HAPPY', fido.wag()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ @@ -33,7 +33,7 @@ def test_most_built_in_classes_cannot_be_monkey_patched(self): try: int.is_even = lambda self: (self % 2) == 0 except StandardError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("can't set attributes of built-in", ex[0]) # ------------------------------------------------------------------ @@ -42,7 +42,7 @@ class MyInt(int): pass def test_subclasses_of_built_in_classes_can_be_be_monkey_patched(self): self.MyInt.is_even = lambda self: (self % 2) == 0 - self.assertEqual(____, self.MyInt(1).is_even()) - self.assertEqual(____, self.MyInt(2).is_even()) + self.assertEqual(False, self.MyInt(1).is_even()) + self.assertEqual(True, self.MyInt(2).is_even()) \ No newline at end of file diff --git a/python 2/koans/about_multiple_inheritance.py b/python2/koans/about_multiple_inheritance.py similarity index 86% rename from python 2/koans/about_multiple_inheritance.py rename to python2/koans/about_multiple_inheritance.py index 297b5f549..e6f0179be 100644 --- a/python 2/koans/about_multiple_inheritance.py +++ b/python2/koans/about_multiple_inheritance.py @@ -87,7 +87,7 @@ def here(self): def test_normal_methods_are_available_in_the_object(self): jeff = self.Spiderpig() - self.assertMatch(__, jeff.speak()) + self.assertMatch('This looks like a job for Spiderpig!', jeff.speak()) def test_base_class_methods_are_also_available_in_the_object(self): jeff = self.Spiderpig() @@ -95,22 +95,22 @@ def test_base_class_methods_are_also_available_in_the_object(self): jeff.set_name("Rover") except: self.fail("This should not happen") - self.assertEqual(____, jeff.can_climb_walls()) + self.assertEqual(True, jeff.can_climb_walls()) def test_base_class_methods_can_affect_instance_variables_in_the_object(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.name) + self.assertEqual('Jeff', jeff.name) jeff.set_name("Rover") - self.assertEqual(__, jeff.name) + self.assertEqual('Rover', jeff.name) def test_left_hand_side_inheritance_tends_to_be_higher_priority(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.color()) + self.assertEqual('pink', jeff.color()) def test_super_class_methods_are_higher_priority_than_super_super_classes(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.legs()) + self.assertEqual(8, jeff.legs()) def test_we_can_inspect_the_method_resolution_order(self): # @@ -119,10 +119,10 @@ def test_we_can_inspect_the_method_resolution_order(self): mro = type(self.Spiderpig()).__mro__ self.assertEqual('Spiderpig', mro[0].__name__) self.assertEqual('Pig', mro[1].__name__) - self.assertEqual(__, mro[2].__name__) - self.assertEqual(__, mro[3].__name__) - self.assertEqual(__, mro[4].__name__) - self.assertEqual(__, mro[5].__name__) + self.assertEqual('Spider', mro[2].__name__) + self.assertEqual('Animal', mro[3].__name__) + self.assertEqual('Nameable', mro[4].__name__) + self.assertEqual('object', mro[5].__name__) def test_confirm_the_mro_controls_the_calling_order(self): jeff = self.Spiderpig() @@ -132,7 +132,7 @@ def test_confirm_the_mro_controls_the_calling_order(self): self.assertMatch('Pig', next.here()) next = super(AboutMultipleInheritance.Pig, jeff) - self.assertMatch(__, next.here()) + self.assertMatch('Spider', next.here()) # Hang on a minute?!? That last class name might be a super class of # the 'jeff' object, but its hardly a superclass of Pig, is it? diff --git a/python 2/koans/about_new_style_classes.py b/python2/koans/about_new_style_classes.py similarity index 58% rename from python 2/koans/about_new_style_classes.py rename to python2/koans/about_new_style_classes.py index 9ecd47b97..3a4c3b4c1 100644 --- a/python 2/koans/about_new_style_classes.py +++ b/python2/koans/about_new_style_classes.py @@ -17,43 +17,43 @@ class NewStyleClass(object): pass def test_new_style_classes_inherit_from_object_base_class(self): - self.assertEqual(____, issubclass(self.NewStyleClass, object)) - self.assertEqual(____, issubclass(self.OldStyleClass, object)) + self.assertEqual(True, issubclass(self.NewStyleClass, object)) + self.assertEqual(False, issubclass(self.OldStyleClass, object)) def test_new_style_classes_have_more_attributes(self): - self.assertEqual(__, len(dir(self.OldStyleClass))) - self.assertEqual(__, self.OldStyleClass.__doc__) - self.assertEqual(__, self.OldStyleClass.__module__) + self.assertEqual(2, len(dir(self.OldStyleClass))) + self.assertEqual('An old style class', self.OldStyleClass.__doc__) + self.assertEqual('koans.about_new_style_classes', self.OldStyleClass.__module__) - self.assertEqual(__, len(dir(self.NewStyleClass))) + self.assertEqual(18, len(dir(self.NewStyleClass))) # To examine the available attributes, run 'dir()' # from a python console # ------------------------------------------------------------------ def test_old_style_classes_have_type_but_no_class_attribute(self): - self.assertEqual(__, type(self.OldStyleClass).__name__) + self.assertEqual('classobj', type(self.OldStyleClass).__name__) try: cls = self.OldStyleClass.__class__ except Exception as ex: pass - self.assertMatch(__, ex[0]) + self.assertMatch("class OldStyleClass has no attribute '__class__'", ex[0]) def test_new_style_classes_have_same_class_as_type(self): new_style = self.NewStyleClass() - self.assertEqual(__, type(self.NewStyleClass).__name__) - self.assertEqual(__, type(self.NewStyleClass) == self.NewStyleClass.__class__) + self.assertEqual('type', type(self.NewStyleClass).__name__) + self.assertEqual(True, type(self.NewStyleClass) == self.NewStyleClass.__class__) # ------------------------------------------------------------------ def test_in_old_style_instances_class_is_different_to_type(self): old_style = self.OldStyleClass() - self.assertEqual(__, type(old_style).__name__) - self.assertEqual(__, old_style.__class__.__name__) + self.assertEqual('instance', type(old_style).__name__) + self.assertEqual('OldStyleClass', old_style.__class__.__name__) def test_new_style_instances_have_same_class_as_type(self): new_style = self.NewStyleClass() - self.assertEqual(__, type(new_style).__name__) - self.assertEqual(__, type(new_style) == new_style.__class__) + self.assertEqual('NewStyleClass', type(new_style).__name__) + self.assertEqual(True, type(new_style) == new_style.__class__) diff --git a/python 2/koans/about_none.py b/python2/koans/about_none.py similarity index 77% rename from python 2/koans/about_none.py rename to python2/koans/about_none.py index 0bc9f7de0..01aac1c33 100644 --- a/python 2/koans/about_none.py +++ b/python2/koans/about_none.py @@ -11,11 +11,11 @@ class AboutNone(Koan): def test_none_is_an_object(self): "Unlike NULL in a lot of languages" - self.assertEqual(__, isinstance(None, object)) + self.assertEqual(True, isinstance(None, object)) def test_none_is_universal(self): "There is only one None" - self.assertEqual(__, None is None) + self.assertEqual(True, None is None) def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self): """ @@ -30,17 +30,17 @@ def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self None.some_method_none_does_not_know_about() except Exception as ex: # What exception has been caught? - self.assertEqual(__, ex.__class__.__name__) + self.assertEqual('AttributeError', ex.__class__.__name__) # What message was attached to the exception? # (HINT: replace __ with part of the error message.) - self.assertMatch(__, ex.args[0]) + self.assertMatch('NoneType', ex.args[0]) def test_none_is_distinct(self): """ None is distinct from other things which are False. """ - self.assertEqual(____, None is not 0) - self.assertEqual(____, None is not False) + self.assertEqual(True, None is not 0) + self.assertEqual(True, None is not False) diff --git a/python 2/koans/about_packages.py b/python2/koans/about_packages.py similarity index 86% rename from python 2/koans/about_packages.py rename to python2/koans/about_packages.py index 245656dfa..1dddd1f81 100644 --- a/python 2/koans/about_packages.py +++ b/python2/koans/about_packages.py @@ -30,20 +30,20 @@ def test_subfolders_can_form_part_of_a_module_package(self): from a_package_folder.a_module import Duck duck = Duck() - self.assertEqual(__, duck.name) + self.assertEqual('Donald', duck.name) def test_subfolders_become_modules_if_they_have_an_init_module(self): # Import ./a_package_folder/__init__.py from a_package_folder import an_attribute - self.assertEqual(__, an_attribute) + self.assertEqual(1984, an_attribute) def test_subfolders_without_an_init_module_are_not_part_of_the_package(self): # Import ./a_normal_folder/ try: import a_normal_folder except ImportError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch('No module named a_normal_folder', ex[0]) # ------------------------------------------------------------------ @@ -51,7 +51,7 @@ def test_use_absolute_imports_to_import_upper_level_modules(self): # Import /contemplate_koans.py import contemplate_koans - self.assertEqual(__, contemplate_koans.__name__) + self.assertEqual('contemplate_koans', contemplate_koans.__name__) # contemplate_koans.py is the root module in this package because its # the first python module called in koans. @@ -65,4 +65,4 @@ def test_import_a_module_in_a_subfolder_folder_using_an_absolute_path(self): # Import contemplate_koans.py/koans/a_package_folder/a_module.py from koans.a_package_folder.a_module import Duck - self.assertEqual(__, Duck.__module__) + self.assertEqual('koans.a_package_folder.a_module', Duck.__module__) diff --git a/python 2/koans/about_proxy_object_project.py b/python2/koans/about_proxy_object_project.py similarity index 79% rename from python 2/koans/about_proxy_object_project.py rename to python2/koans/about_proxy_object_project.py index 7a8ca80ae..51b9bd1f8 100644 --- a/python 2/koans/about_proxy_object_project.py +++ b/python2/koans/about_proxy_object_project.py @@ -20,12 +20,39 @@ class Proxy(object): def __init__(self, target_object): - # WRITE CODE HERE + # Write your attribute initialisations here + self._messages = list() - #initialize '_obj' attribute last. Trust me on this! + #initialize attribute this last. Trust me! self._obj = target_object - # WRITE CODE HERE + # WRITE CODE HERE + def __getattr__(self, name): + #print 'Debug GET ' + type(self).__name__ + "." + name + " dict=" + str(self.__dict__) + + attr = getattr(self._obj, name) + + self._messages.append(name) + return attr + + def __setattr__(self, name, value): + #print 'Debug SET ' + type(self).__name__ + "." + name + "=" + str(value) + " __dict__=" + str(self.__dict__) + + if '_' == name[0]: + return object.__setattr__(self, name, value) + + setattr(self._obj, name, value) + + self._messages.append(name + '=') + + def messages(self): + return self._messages + + def was_called(self, attr): + return self.number_of_times_called(attr) > 0 + + def number_of_times_called(self, attr): + return len(filter(lambda msg: msg==attr , self._messages)) # The proxy object should pass the following Koan: # diff --git a/python 2/koans/about_scope.py b/python2/koans/about_scope.py similarity index 76% rename from python 2/koans/about_scope.py rename to python2/koans/about_scope.py index 9fd7501e3..b07ded0ee 100644 --- a/python 2/koans/about_scope.py +++ b/python2/koans/about_scope.py @@ -19,18 +19,18 @@ def test_dog_is_not_available_in_the_current_scope(self): try: fido = Dog() except Exception as ex: - self.assertMatch(__, ex[0]) + self.assertMatch("global name 'Dog' is not defined", ex[0]) def test_you_can_reference_nested_classes_using_the_scope_operator(self): fido = jims.Dog() # name 'jims' module name is taken from jim.py filename rover = joes.Dog() - self.assertEqual(__, fido.identify()) - self.assertEqual(__, rover.identify()) + self.assertEqual('jims dog', fido.identify()) + self.assertEqual('joes dog', rover.identify()) - self.assertEqual(____, type(fido) == type(rover)) - self.assertEqual(____, jims.Dog == joes.Dog) + self.assertEqual(False, type(fido) == type(rover)) + self.assertEqual(False, jims.Dog == joes.Dog) # ------------------------------------------------------------------ @@ -38,26 +38,26 @@ class str(object): pass def test_bare_bones_class_names_do_not_assume_the_current_scope(self): - self.assertEqual(____, AboutScope.str == str) + self.assertEqual(False, AboutScope.str == str) def test_nested_string_is_not_the_same_as_the_system_string(self): - self.assertEqual(____, self.str == type("HI")) + self.assertEqual(False, self.str == type("HI")) def test_str_without_self_prefix_stays_in_the_global_scope(self): - self.assertEqual(____, str == type("HI")) + self.assertEqual(True, str == type("HI")) # ------------------------------------------------------------------ PI = 3.1416 def test_constants_are_defined_with_an_initial_uppercase_letter(self): - self.assertAlmostEqual(_____, self.PI) + self.assertAlmostEqual(3.1416, self.PI) # Note, floating point numbers in python are not precise. # assertAlmostEqual will check that it is 'close enough' def test_constants_are_assumed_by_convention_only(self): self.PI = "rhubarb" - self.assertEqual(_____, self.PI) + self.assertEqual("rhubarb", self.PI) # There aren't any real constants in python. Its up to the developer # to keep to the convention and not modify them. @@ -74,13 +74,13 @@ def test_incrementing_with_local_counter(self): global counter start = counter self.increment_using_local_counter(start) - self.assertEqual(____, counter == start + 1) + self.assertEqual(False, counter == start + 1) def test_incrementing_with_global_counter(self): global counter start = counter self.increment_using_global_counter() - self.assertEqual(____, counter == start + 1) + self.assertEqual(True, counter == start + 1) # ------------------------------------------------------------------ @@ -88,5 +88,5 @@ def test_incrementing_with_global_counter(self): deadly_bingo = [4, 8, 15, 16, 23, 42] def test_global_attributes_can_be_created_in_the_middle_of_a_class(self): - self.assertEqual(__, deadly_bingo[5]) + self.assertEqual(42, deadly_bingo[5]) \ No newline at end of file diff --git a/python 3/koans/about_scoring_project.py b/python2/koans/about_scoring_project.py similarity index 80% rename from python 3/koans/about_scoring_project.py rename to python2/koans/about_scoring_project.py index 938028cb9..5bcc445ec 100644 --- a/python 3/koans/about_scoring_project.py +++ b/python2/koans/about_scoring_project.py @@ -33,9 +33,29 @@ # Your goal is to write the score method. def score(dice): - # You need to write this method - pass + score = 0 + tallies = {}.fromkeys(set(dice), 0) + for die in dice: + tallies[die] += 1 + + if 1 in tallies and tallies[1] >= 3: + tallies[1] -= 3 + score += 1000 + + for k in tallies: + if tallies[k] >= 3: + tallies[k] -= 3 + score += k * 100 + + for k in tallies: + if k == 1: + score += 100 * tallies[k] + elif k== 5: + score += 50 * tallies[k] + + return score + class AboutScoringProject(Koan): def test_score_of_an_empty_list_is_zero(self): self.assertEqual(0, score([])) diff --git a/python 2/koans/about_sets.py b/python2/koans/about_sets.py similarity index 50% rename from python 2/koans/about_sets.py rename to python2/koans/about_sets.py index 63a2bfc71..c118dbeff 100644 --- a/python 2/koans/about_sets.py +++ b/python2/koans/about_sets.py @@ -9,13 +9,13 @@ def test_sets_make_keep_lists_unique(self): there_can_only_be_only_one = set(highlanders) - self.assertEqual(__, there_can_only_be_only_one) + self.assertEqual(set(['Malcolm', 'Matunas', 'MacLeod', 'Ramirez']), there_can_only_be_only_one) def test_sets_are_unordered(self): - self.assertEqual(set([__, __, __, __, __]), set('12345')) + self.assertEqual(set(['1', '3', '2', '5', '4']), set('12345')) def test_convert_the_set_into_a_list_to_sort_it(self): - self.assertEqual(__, sorted(set('12345'))) + self.assertEqual(['1','2','3','4','5'], sorted(set('12345'))) # ------------------------------------------------------------------ @@ -27,19 +27,19 @@ def test_set_have_arithmetic_operators(self): good_guy = set('macleod') bad_guy = set('mutunas') - self.assertEqual(__, self.chars_in( good_guy - bad_guy) ) - self.assertEqual(__, self.chars_in( good_guy | bad_guy )) - self.assertEqual(__, self.chars_in( good_guy & bad_guy )) - self.assertEqual(__, self.chars_in( good_guy ^ bad_guy )) + self.assertEqual('cdelo', self.chars_in( good_guy - bad_guy) ) + self.assertEqual('acdelmnostu', self.chars_in( good_guy | bad_guy )) + self.assertEqual('am', self.chars_in( good_guy & bad_guy )) + self.assertEqual('cdelnostu', self.chars_in( good_guy ^ bad_guy )) # ------------------------------------------------------------------ def test_we_can_query_set_membership(self): - self.assertEqual(__, 127 in set([127, 0, 0, 1]) ) - self.assertEqual(__, 'cow' not in set('apocalypse now') ) + self.assertEqual(True, 127 in set([127, 0, 0, 1]) ) + self.assertEqual(True, 'cow' not in set('apocalypse now') ) def test_we_can_compare_subsets(self): - self.assertEqual(__, set('cake') <= set('cherry cake')) - self.assertEqual(__, set('cake').issubset(set('cherry cake')) ) + self.assertEqual(True, set('cake') <= set('cherry cake')) + self.assertEqual(True, set('cake').issubset(set('cherry cake')) ) - self.assertEqual(__, set('cake') > set('pie')) + self.assertEqual(False, set('cake') > set('pie')) diff --git a/python 2/koans/about_strings.py b/python2/koans/about_strings.py similarity index 72% rename from python 2/koans/about_strings.py rename to python2/koans/about_strings.py index bdf1991e6..29422f694 100644 --- a/python 2/koans/about_strings.py +++ b/python2/koans/about_strings.py @@ -7,94 +7,94 @@ class AboutStrings(Koan): def test_double_quoted_strings_are_strings(self): string = "Hello, world." - self.assertEqual(__, isinstance(string, basestring)) + self.assertEqual(True, isinstance(string, basestring)) def test_single_quoted_strings_are_also_strings(self): string = 'Goodbye, world.' - self.assertEqual(__, isinstance(string, basestring)) + self.assertEqual(True, isinstance(string, basestring)) def test_triple_quote_strings_are_also_strings(self): string = """Howdy, world!""" - self.assertEqual(__, isinstance(string, basestring)) + self.assertEqual(True, isinstance(string, basestring)) def test_triple_single_quotes_work_too(self): string = '''Bonjour tout le monde!''' - self.assertEqual(__, isinstance(string, basestring)) + self.assertEqual(True, isinstance(string, basestring)) def test_use_single_quotes_to_create_string_with_double_quotes(self): string = 'He said, "Go Away."' - self.assertEqual(__, string) + self.assertEqual("He said, \"Go Away.\"", string) def test_use_double_quotes_to_create_strings_with_single_quotes(self): string = "Don't" - self.assertEqual(__, string) + self.assertEqual("Don't", string) def test_use_backslash_for_escaping_quotes_in_strings(self): a = "He said, \"Don't\"" b = 'He said, "Don\'t"' - self.assertEqual(__, (a == b)) + self.assertEqual(True, (a == b)) def test_use_backslash_at_the_end_of_a_line_to_continue_onto_the_next_line(self): string = "It was the best of times,\n\ It was the worst of times." - self.assertEqual(__, len(string)) + self.assertEqual(52, len(string)) def test_triple_quoted_strings_can_span_lines(self): string = """ Howdy, world! """ - self.assertEqual(__, len(string)) + self.assertEqual(15, len(string)) def test_triple_quoted_strings_need_less_escaping(self): a = "Hello \"world\"." b = """Hello "world".""" - self.assertEqual(__, (a == b)) + self.assertEqual(True, (a == b)) def but_you_still_have_to_be_careful_at_the_end_of_a_triple_quoted_string(self): string = """Hello "world\"""" def test_plus_concatenates_strings(self): string = "Hello, " + "world" - self.assertEqual(__, string) + self.assertEqual('Hello, world', string) def test_plus_will_not_modify_original_strings(self): hi = "Hello, " there = "world" string = hi + there - self.assertEqual(__, hi) - self.assertEqual(__, there) + self.assertEqual('Hello, ', hi) + self.assertEqual('world', there) def test_plus_equals_will_append_to_end_of_string(self): hi = "Hello, " there = "world" hi += there - self.assertEqual(__, hi) + self.assertEqual('Hello, world', hi) def test_plus_equals_also_leaves_original_string_unmodified(self): original = "Hello, " hi = original there = "world" hi += there - self.assertEqual(__, original) + self.assertEqual('Hello, ', original) def test_most_strings_interpret_escape_characters(self): string = "\n" self.assertEqual('\n', string) self.assertEqual("""\n""", string) - self.assertEqual(__, len(string)) + self.assertEqual(1, len(string)) def test_use_format_to_interpolate_variables(self): value1 = 'one' value2 = 2 string = "The values are {0} and {1}".format(value1, value2) - self.assertEqual(__, string) + self.assertEqual('The values are one and 2', string) def test_formatted_values_con_be_shown_in_any_order_or_be_repeated(self): value1 = 'doh' value2 = 'DOH' string = "The values are {1}, {0}, {0} and {1}!".format(value1, value2) - self.assertEqual(__, string) + self.assertEqual('The values are DOH, doh, doh and DOH!', string) def test_any_python_expression_may_be_interpolated(self): import math # import a standard python module with math functions @@ -102,24 +102,24 @@ def test_any_python_expression_may_be_interpolated(self): decimal_places = 4 string = "The square root of 5 is {0:.{1}f}".format(math.sqrt(5), \ decimal_places) - self.assertEqual(__, string) + self.assertEqual('The square root of 5 is 2.2361', string) def test_you_can_get_a_substring_from_a_string(self): string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[7:10]) + self.assertEqual('let', string[7:10]) def test_you_can_get_a_single_character_from_a_string(self): string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[1]) + self.assertEqual('a', string[1]) def test_single_characters_can_be_represented_by_integers(self): - self.assertEqual(__, ord('a')) - self.assertEqual(__, ord('b') == (ord('a') + 1)) + self.assertEqual(97, ord('a')) + self.assertEqual(True, ord('b') == (ord('a') + 1)) def test_strings_can_be_split(self): string = "Sausage Egg Cheese" words = string.split() - self.assertEqual([__, __, __], words) + self.assertEqual(['Sausage', 'Egg', 'Cheese'], words) def test_strings_can_be_split_with_different_patterns(self): import re #import python regular expression library @@ -129,18 +129,18 @@ def test_strings_can_be_split_with_different_patterns(self): words = pattern.split(string) - self.assertEqual([__, __, __, __], words) + self.assertEqual(['the', 'rain', 'in', 'spain'], words) # Pattern is a Python regular expression pattern which matches ',' or ';' def test_regular_expression_strings_do_not_interpret_escape_characters(self): string = r'\n' self.assertNotEqual('\n', string) - self.assertEqual(__, string) - self.assertEqual(__, len(string)) + self.assertEqual('\\n', string) + self.assertEqual(2, len(string)) # This is to make patterns easier to read def test_strings_can_be_joined(self): words = ["Now", "is", "the", "time"] - self.assertEqual(__, ' '.join(words)) \ No newline at end of file + self.assertEqual('Now is the time', ' '.join(words)) \ No newline at end of file diff --git a/python 2/koans/about_triangle_project.py b/python2/koans/about_triangle_project.py similarity index 100% rename from python 2/koans/about_triangle_project.py rename to python2/koans/about_triangle_project.py diff --git a/python 2/koans/about_triangle_project2.py b/python2/koans/about_triangle_project2.py similarity index 100% rename from python 2/koans/about_triangle_project2.py rename to python2/koans/about_triangle_project2.py diff --git a/python2/koans/about_true_and_false.py b/python2/koans/about_true_and_false.py new file mode 100644 index 000000000..9a0947af3 --- /dev/null +++ b/python2/koans/about_true_and_false.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + +class AboutTrueAndFalse(Koan): + def truth_value(self, condition): + if condition: + return 'true stuff' + else: + return 'false stuff' + + def test_true_is_treated_as_true(self): + self.assertEqual('true stuff', self.truth_value(True)) + + def test_false_is_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(False)) + + def test_none_is_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(None)) + + def test_empty_collections_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(0)) + + def test_empty_collections_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value([])) + self.assertEqual('false stuff', self.truth_value({})) + self.assertEqual('false stuff', self.truth_value(set())) + + def test_strings_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value("")) + + def test_everything_else_is_treated_as_true(self): + self.assertEqual('true stuff', self.truth_value(1)) + self.assertEqual('true stuff', self.truth_value("Python is named after Monty Python")) + self.assertEqual('true stuff', self.truth_value(' ')) diff --git a/python 2/koans/about_tuples.py b/python2/koans/about_tuples.py similarity index 70% rename from python 2/koans/about_tuples.py rename to python2/koans/about_tuples.py index fbfc35ba5..4628150b4 100644 --- a/python 2/koans/about_tuples.py +++ b/python2/koans/about_tuples.py @@ -6,14 +6,14 @@ class AboutTuples(Koan): def test_creating_a_tuple(self): count_of_three = (1, 2, 5) - self.assertEqual(__, count_of_three[2]) + self.assertEqual(5, count_of_three[2]) def test_tuples_are_immutable_so_item_assignment_is_not_possible(self): count_of_three = (1, 2, 5) try: count_of_three[2] = "three" except TypeError as ex: - self.assertMatch(__, ex[0]) + self.assertMatch('tuple', ex[0]) def test_tuples_are_immutable_so_appending_is_not_possible(self): count_of_three = (1, 2, 5) @@ -24,7 +24,7 @@ def test_tuples_are_immutable_so_appending_is_not_possible(self): # Note, assertMatch() uses regular expression pattern matching, # so you don't have to copy the whole message. - self.assertMatch(__, ex[0]) + self.assertMatch('tuple', ex[0]) # Tuples are less flexible than lists, but faster. @@ -35,23 +35,23 @@ def test_tuples_can_only_be_changed_through_replacement(self): list_count.append("boom") count_of_three = tuple(list_count) - self.assertEqual(__, count_of_three) + self.assertEqual((1, 2, 5, "boom"), count_of_three) def test_tuples_of_one_are_peculiar(self): - self.assertEqual(__, (1)) - self.assertEqual(__, ("Hello comma!", )) - self.assertEqual(__, (1,)) - self.assertEqual(__, tuple("Surprise!")) + self.assertEqual(1, (1)) + self.assertEqual(("Hello comma!", ), ("Hello comma!", )) + self.assertEqual((1,), (1,)) + self.assertEqual(('S', 'u', 'r', 'p', 'r', 'i', 's', 'e', '!'), tuple("Surprise!")) def test_creating_empty_tuples(self): - self.assertEqual(__ , ()) - self.assertEqual(__ , tuple()) #Sometimes less confusing + self.assertEqual(() , ()) + self.assertEqual(() , tuple()) #Sometimes less confusing def test_tuples_can_be_embedded(self): lat = (37, 14, 6, 'N') lon = (115, 48, 40, 'W') place = ('Area 51', lat, lon) - self.assertEqual(__, place) + self.assertEqual(('Area 51', (37, 14, 6, 'N'), (115, 48, 40, 'W')), place) def test_tuples_are_good_for_representing_records(self): locations = [ @@ -61,8 +61,8 @@ def test_tuples_are_good_for_representing_records(self): locations.append( ("Cthulu", (26, 40, 1, 'N'), (70, 45, 7, 'W')) ) - self.assertEqual(__, locations[2][0]) - self.assertEqual(__, locations[0][1][2]) + self.assertEqual('Cthulu', locations[2][0]) + self.assertEqual(15.56, locations[0][1][2]) \ No newline at end of file diff --git a/python 2/koans/about_with_statements.py b/python2/koans/about_with_statements.py similarity index 85% rename from python 2/koans/about_with_statements.py rename to python2/koans/about_with_statements.py index bb4db2ddf..e723897a5 100644 --- a/python 2/koans/about_with_statements.py +++ b/python2/koans/about_with_statements.py @@ -21,7 +21,7 @@ def count_lines(self, file_name): if file: file.close() def test_counting_lines(self): - self.assertEqual(__, self.count_lines("example_file.txt")) + self.assertEqual(4, self.count_lines("example_file.txt")) # ------------------------------------------------------------------ @@ -35,7 +35,7 @@ def find_line(self, file_name): if file: file.close() def test_finding_lines(self): - self.assertEqual(__, self.find_line("example_file.txt")) + self.assertEqual('test\n', self.find_line("example_file.txt")) ## ------------------------------------------------------------------ ## THINK ABOUT IT: @@ -82,16 +82,18 @@ def count_lines2(self, file_name): return count def test_counting_lines2(self): - self.assertEqual(__, self.count_lines2("example_file.txt")) + self.assertEqual(4, self.count_lines2("example_file.txt")) # ------------------------------------------------------------------ def find_line2(self, file_name): - # Rewrite find_line using the Context Manager. - pass + with self.FileContextManager(file_name) as file: + for line in file.readlines(): + match = re.search('e', line) + if match: return line def test_finding_lines2(self): - self.assertEqual(__, self.find_line2("example_file.txt")) + self.assertEqual('test\n', self.find_line2("example_file.txt")) self.assertNotEqual(None, self.find_line2("example_file.txt")) # ------------------------------------------------------------------ @@ -104,4 +106,4 @@ def count_lines3(self, file_name): return count def test_open_already_has_its_own_built_in_context_manager(self): - self.assertEqual(__, self.count_lines3("example_file.txt")) + self.assertEqual(4, self.count_lines3("example_file.txt")) diff --git a/python 2/koans/another_local_module.py b/python2/koans/another_local_module.py similarity index 100% rename from python 2/koans/another_local_module.py rename to python2/koans/another_local_module.py diff --git a/python 2/koans/jims.py b/python2/koans/jims.py similarity index 100% rename from python 2/koans/jims.py rename to python2/koans/jims.py diff --git a/python 2/koans/joes.py b/python2/koans/joes.py similarity index 100% rename from python 2/koans/joes.py rename to python2/koans/joes.py diff --git a/python 2/koans/local_module.py b/python2/koans/local_module.py similarity index 100% rename from python 2/koans/local_module.py rename to python2/koans/local_module.py diff --git a/python 2/koans/local_module_with_all_defined.py b/python2/koans/local_module_with_all_defined.py similarity index 100% rename from python 2/koans/local_module_with_all_defined.py rename to python2/koans/local_module_with_all_defined.py diff --git a/python 2/koans/triangle.py b/python2/koans/triangle.py similarity index 56% rename from python 2/koans/triangle.py rename to python2/koans/triangle.py index b71c2dd77..87c7370d9 100644 --- a/python 2/koans/triangle.py +++ b/python2/koans/triangle.py @@ -17,8 +17,18 @@ # about_triangle_project_2.py # def triangle(a, b, c): - # DELETE 'PASS' AND WRITE THIS CODE - pass + sides = sorted([a, b, c]) + unique_sides = sorted(set(sides)) + + if unique_sides[0] <= 0 : raise TriangleError('Sides cannot be 0 or less') + if sides[2] >= sides[0] + sides[1] : raise TriangleError('sum of 2 sides cannot be greater than remaining side') + + if len(unique_sides) == 1: + return 'equilateral' + elif len(unique_sides) == 2: + return 'isosceles' + elif len(unique_sides) == 3: + return 'scalene' # Error class used in part 2. No need to change this code. class TriangleError(StandardError): diff --git a/python 2/libs/__init__.py b/python2/libs/__init__.py similarity index 100% rename from python 2/libs/__init__.py rename to python2/libs/__init__.py diff --git a/python2/libs/colorama/LICENSE-colorama b/python2/libs/colorama/LICENSE-colorama new file mode 100644 index 000000000..b7464472e --- /dev/null +++ b/python2/libs/colorama/LICENSE-colorama @@ -0,0 +1,33 @@ +Copyright (c) 2010 Jonathan Hartley + +Released under the New BSD license (reproduced below), or alternatively you may +use this software under any OSI approved open source license such as those at +http://opensource.org/licenses/alphabetical + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name(s) of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/python2/libs/colorama/__init__.py b/python2/libs/colorama/__init__.py new file mode 100644 index 000000000..331174e57 --- /dev/null +++ b/python2/libs/colorama/__init__.py @@ -0,0 +1,6 @@ +from .initialise import init +from .ansi import Fore, Back, Style +from .ansitowin32 import AnsiToWin32 + +VERSION = '0.1.18' + diff --git a/python2/libs/colorama/ansi.py b/python2/libs/colorama/ansi.py new file mode 100644 index 000000000..7b818e19e --- /dev/null +++ b/python2/libs/colorama/ansi.py @@ -0,0 +1,49 @@ +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' + +def code_to_chars(code): + return CSI + str(code) + 'm' + +class AnsiCodes(object): + def __init__(self, codes): + for name in dir(codes): + if not name.startswith('_'): + value = getattr(codes, name) + setattr(self, name, code_to_chars(value)) + +class AnsiFore: + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + +class AnsiBack: + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + +class AnsiStyle: + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiCodes( AnsiFore ) +Back = AnsiCodes( AnsiBack ) +Style = AnsiCodes( AnsiStyle ) + diff --git a/python2/libs/colorama/ansitowin32.py b/python2/libs/colorama/ansitowin32.py new file mode 100644 index 000000000..5c39a480e --- /dev/null +++ b/python2/libs/colorama/ansitowin32.py @@ -0,0 +1,176 @@ + +import re +import sys + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll + + +if windll is not None: + winterm = WinTerm() + + +def is_a_tty(stream): + return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + self.__convertor.write(text) + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = sys.platform.startswith('win') + + # should we strip ANSI sequences from our output? + if strip is None: + strip = on_windows + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = on_windows and is_a_tty(wrapped) + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + } + + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + else: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + for match in self.ANSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(paramstring) + self.call_win32(command, params) + + + def extract_params(self, paramstring): + def split(paramstring): + for p in paramstring.split(';'): + if p != '': + yield int(p) + return tuple(split(paramstring)) + + + def call_win32(self, command, params): + if params == []: + params = [0] + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + diff --git a/python2/libs/colorama/initialise.py b/python2/libs/colorama/initialise.py new file mode 100644 index 000000000..eca2bea40 --- /dev/null +++ b/python2/libs/colorama/initialise.py @@ -0,0 +1,38 @@ +import atexit +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + +atexit_done = False + + +def reset_all(): + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if wrap==False and (autoreset==True or convert==True or strip==True): + raise ValueError('wrap=False conflicts with any other arg=True') + + sys.stdout = wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + sys.stderr = wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + diff --git a/python2/libs/colorama/win32.py b/python2/libs/colorama/win32.py new file mode 100644 index 000000000..5f49e6cba --- /dev/null +++ b/python2/libs/colorama/win32.py @@ -0,0 +1,95 @@ + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + from ctypes import windll +except ImportError: + windll = None + SetConsoleTextAttribute = lambda *_: None +else: + from ctypes import ( + byref, Structure, c_char, c_short, c_uint32, c_ushort + ) + + handles = { + STDOUT: windll.kernel32.GetStdHandle(STDOUT), + STDERR: windll.kernel32.GetStdHandle(STDERR), + } + + SHORT = c_short + WORD = c_ushort + DWORD = c_uint32 + TCHAR = c_char + + class COORD(Structure): + """struct in wincon.h""" + _fields_ = [ + ('X', SHORT), + ('Y', SHORT), + ] + + class SMALL_RECT(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("Left", SHORT), + ("Top", SHORT), + ("Right", SHORT), + ("Bottom", SHORT), + ] + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", WORD), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + + def GetConsoleScreenBufferInfo(stream_id): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = windll.kernel32.GetConsoleScreenBufferInfo( + handle, byref(csbi)) + # This fails when imported via setup.py when installing using 'pip' + # presumably the fix is that running setup.py should not trigger all + # this activity. + # assert success + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + success = windll.kernel32.SetConsoleTextAttribute(handle, attrs) + assert success + + def SetConsoleCursorPosition(stream_id, position): + handle = handles[stream_id] + position = COORD(*position) + success = windll.kernel32.SetConsoleCursorPosition(handle, position) + assert success + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = TCHAR(char) + length = DWORD(length) + start = COORD(*start) + num_written = DWORD(0) + # AttributeError: function 'FillConsoleOutputCharacter' not found + # could it just be that my types are wrong? + success = windll.kernel32.FillConsoleOutputCharacter( + handle, char, length, start, byref(num_written)) + assert success + return num_written.value + + +if __name__=='__main__': + x = GetConsoleScreenBufferInfo(STDOUT) + print(x.dwSize) + print(x.dwCursorPosition) + print(x.wAttributes) + print(x.srWindow) + print(x.dwMaximumWindowSize) + diff --git a/python2/libs/colorama/winterm.py b/python2/libs/colorama/winterm.py new file mode 100644 index 000000000..63f4469cc --- /dev/null +++ b/python2/libs/colorama/winterm.py @@ -0,0 +1,69 @@ + +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + + +class WinTerm(object): + + def __init__(self): + self._default = \ + win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + + def get_attrs(self): + return self._fore + self._back * 16 + self._style + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & WinStyle.BRIGHT + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + diff --git a/python 2/libs/mock.py b/python2/libs/mock.py similarity index 100% rename from python 2/libs/mock.py rename to python2/libs/mock.py diff --git a/python 2/run.bat b/python2/run.bat similarity index 100% rename from python 2/run.bat rename to python2/run.bat diff --git a/python 2/run.sh b/python2/run.sh similarity index 100% rename from python 2/run.sh rename to python2/run.sh diff --git a/python 2/runner/__init__.py b/python2/runner/__init__.py similarity index 100% rename from python 2/runner/__init__.py rename to python2/runner/__init__.py diff --git a/python 2/runner/helper.py b/python2/runner/helper.py similarity index 100% rename from python 2/runner/helper.py rename to python2/runner/helper.py diff --git a/python 2/runner/koan.py b/python2/runner/koan.py similarity index 100% rename from python 2/runner/koan.py rename to python2/runner/koan.py diff --git a/python 2/runner/mockable_test_result.py b/python2/runner/mockable_test_result.py similarity index 100% rename from python 2/runner/mockable_test_result.py rename to python2/runner/mockable_test_result.py diff --git a/python 2/runner/mountain.py b/python2/runner/mountain.py similarity index 85% rename from python 2/runner/mountain.py rename to python2/runner/mountain.py index 4ad479a9c..b988c1291 100644 --- a/python 2/runner/mountain.py +++ b/python2/runner/mountain.py @@ -13,13 +13,14 @@ def __init__(self): self.stream = WritelnDecorator(sys.stdout) self.tests = path_to_enlightenment.koans() self.lesson = Sensei(self.stream) - + def walk_the_path(self, args=None): "Run the koans tests with a custom runner output." - - if args and len(args) >=2: - self.tests = unittest.TestLoader().loadTestsFromName("koans." + args[1]) + if args and len(args) >=2: + args.pop(0) + test_names = ["koans." + test_name for test_name in args] + self.tests = unittest.TestLoader().loadTestsFromNames(test_names) self.tests(self.lesson) self.lesson.learn() return self.lesson diff --git a/python 2/runner/path_to_enlightenment.py b/python2/runner/path_to_enlightenment.py similarity index 100% rename from python 2/runner/path_to_enlightenment.py rename to python2/runner/path_to_enlightenment.py diff --git a/python 2/runner/runner_tests/__init__.py b/python2/runner/runner_tests/__init__.py similarity index 100% rename from python 2/runner/runner_tests/__init__.py rename to python2/runner/runner_tests/__init__.py diff --git a/python 2/runner/runner_tests/test_helper.py b/python2/runner/runner_tests/test_helper.py similarity index 97% rename from python 2/runner/runner_tests/test_helper.py rename to python2/runner/runner_tests/test_helper.py index ea3ee00c3..fef9b6026 100644 --- a/python 2/runner/runner_tests/test_helper.py +++ b/python2/runner/runner_tests/test_helper.py @@ -6,10 +6,10 @@ from runner import helper class TestHelper(unittest.TestCase): - + def test_that_get_class_name_works_with_a_string_instance(self): self.assertEqual("str", helper.cls_name(str())) - + def test_that_get_class_name_works_with_a_4(self): self.assertEquals("int", helper.cls_name(4)) diff --git a/python 2/runner/runner_tests/test_mountain.py b/python2/runner/runner_tests/test_mountain.py similarity index 75% rename from python 2/runner/runner_tests/test_mountain.py rename to python2/runner/runner_tests/test_mountain.py index efcfc1839..0f8d08ccc 100644 --- a/python 2/runner/runner_tests/test_mountain.py +++ b/python2/runner/runner_tests/test_mountain.py @@ -13,13 +13,9 @@ def setUp(self): path_to_enlightenment.koans = Mock() self.mountain = Mountain() self.mountain.stream.writeln = Mock() - - def test_it_retrieves_some_koans_tests(self): - self.mountain.walk_the_path() - self.assertTrue(self.mountain.tests, "No test suite") - + def test_it_gets_test_results(self): self.mountain.lesson.learn = Mock() self.mountain.walk_the_path() self.assertTrue(self.mountain.lesson.learn.called) - \ No newline at end of file + diff --git a/python 3/runner/runner_tests/test_sensei.py b/python2/runner/runner_tests/test_sensei.py similarity index 66% rename from python 3/runner/runner_tests/test_sensei.py rename to python2/runner/runner_tests/test_sensei.py index 555e4c9e7..8149a7fac 100644 --- a/python 3/runner/runner_tests/test_sensei.py +++ b/python2/runner/runner_tests/test_sensei.py @@ -10,6 +10,7 @@ from runner.sensei import Sensei from runner.writeln_decorator import WritelnDecorator from runner.mockable_test_result import MockableTestResult +from runner import path_to_enlightenment class AboutParrots: pass @@ -80,45 +81,14 @@ class AboutFreemasons: """ - class TestSensei(unittest.TestCase): def setUp(self): self.sensei = Sensei(WritelnDecorator(sys.stdout)) self.sensei.stream.writeln = Mock() - - def test_that_it_delegates_testing_to_test_cases(self): - MockableTestResult.startTest = Mock() - self.sensei.startTest(Mock()) - self.assertTrue(MockableTestResult.startTest.called) - - def test_that_user_is_notified_if_test_involves_a_different_test_class_to_last_time(self): - MockableTestResult.startTest = Mock() - - self.sensei.prevTestClassName == 'AboutLumberjacks' - nextTest = AboutParrots() - - self.sensei.startTest(nextTest) - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_user_is_not_notified_about_test_class_repeats(self): - MockableTestResult.startTest = Mock() - - self.sensei.prevTestClassName == 'AboutParrots' - nextTest = AboutParrots() - - self.sensei.startTest(nextTest) - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_cached_classname_updates_after_the_test(self): - self.assertEqual(None, self.sensei.prevTestClassName) - self.sensei.startTest(Mock()) - self.assertNotEqual(None, self.sensei.prevTestClassName) - - def test_that_errors_are_diverted_to_the_failures_list(self): - MockableTestResult.addFailure = Mock() - self.sensei.addError(Mock(), Mock()) - self.assertTrue(MockableTestResult.addFailure.called) + path_to_enlightenment.koans = Mock() + self.tests = Mock() + self.tests.countTestCases = Mock() def test_that_failures_are_handled_in_the_base_class(self): MockableTestResult.addFailure = Mock() @@ -130,46 +100,26 @@ def test_that_it_successes_only_count_if_passes_are_currently_allowed(self): MockableTestResult.addSuccess = Mock() self.sensei.addSuccess(Mock()) self.assertTrue(self.sensei.passesCount.called) - - def test_that_if_there_are_failures_and_the_prev_class_is_different_successes_are_not_allowed(self): - self.sensei.failures = [(AboutLumberjacks(), Mock())] - self.sensei.prevTestClassName = "AboutTheMinitry" - self.assertFalse(self.sensei.passesCount()) - - def test_that_if_there_are_failures_and_the_prev_class_is_the_same_successes_are_allowed(self): - self.sensei.failures = [(AboutLumberjacks(), Mock())] - self.sensei.prevTestClassName = "AboutLumberjacks" - self.assertTrue(self.sensei.passesCount()) - - def test_that_if_there_are_no_failures_successes_are_allowed(self): - self.sensei.failures = None - self.sensei.prevTestClassName = "AboutLumberjacks" - self.assertTrue(self.sensei.passesCount()) def test_that_it_passes_on_add_successes_message(self): MockableTestResult.addSuccess = Mock() self.sensei.addSuccess(Mock()) self.assertTrue(MockableTestResult.addSuccess.called) - + def test_that_it_increases_the_passes_on_every_success(self): pass_count = self.sensei.pass_count MockableTestResult.addSuccess = Mock() self.sensei.addSuccess(Mock()) self.assertEqual(pass_count + 1, self.sensei.pass_count) - def test_that_it_displays_each_success(self): - MockableTestResult.addSuccess = Mock() - self.sensei.addSuccess(Mock()) - self.assertTrue(self.sensei.stream.writeln.called) - def test_that_nothing_is_returned_as_a_first_result_if_there_are_no_failures(self): self.sensei.failures = [] self.assertEqual(None, self.sensei.firstFailure()) - + def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_failures(self): self.sensei.failures = [] self.assertEqual(None, self.sensei.sortFailures("AboutLife")) - + def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_relevent_failures(self): self.sensei.failures = [ (AboutTheKnightsWhoSayNi(),"File 'about_the_knights_whn_say_ni.py', line 24"), @@ -177,7 +127,7 @@ def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_relevent_fail (AboutMessiahs(),"File 'about_messiahs.py', line 844") ] self.assertEqual(None, self.sensei.sortFailures("AboutLife")) - + def test_that_nothing_is_returned_as_sorted_result_if_there_are_3_shuffled_results(self): self.sensei.failures = [ (AboutTennis(),"File 'about_tennis.py', line 299"), @@ -188,26 +138,26 @@ def test_that_nothing_is_returned_as_sorted_result_if_there_are_3_shuffled_resul (AboutMrGumby(),"File 'about_mr_gumby.py', line odd"), (AboutMessiahs(),"File 'about_messiahs.py', line 844") ] - + expected = [ (AboutTennis(),"File 'about_tennis.py', line 2"), (AboutTennis(),"File 'about_tennis.py', line 30"), (AboutTennis(),"File 'about_tennis.py', line 299") ] - + results = self.sensei.sortFailures("AboutTennis") self.assertEqual(3, len(results)) self.assertEqual(2, results[0][0]) self.assertEqual(30, results[1][0]) self.assertEqual(299, results[2][0]) - + def test_that_it_will_choose_not_find_anything_with_non_standard_error_trace_string(self): self.sensei.failures = [ (AboutMrGumby(),"File 'about_mr_gumby.py', line MISSING"), ] self.assertEqual(None, self.sensei.sortFailures("AboutMrGumby")) - - + + def test_that_it_will_choose_correct_first_result_with_lines_9_and_27(self): self.sensei.failures = [ (AboutTrebuchets(),"File 'about_trebuchets.py', line 27"), @@ -215,7 +165,7 @@ def test_that_it_will_choose_correct_first_result_with_lines_9_and_27(self): (AboutTrebuchets(),"File 'about_trebuchets.py', line 73v") ] self.assertEqual("File 'about_trebuchets.py', line 9", self.sensei.firstFailure()[1]) - + def test_that_it_will_choose_correct_first_result_with_multiline_test_classes(self): self.sensei.failures = [ (AboutGiantFeet(),"File 'about_giant_feet.py', line 999"), @@ -224,33 +174,7 @@ def test_that_it_will_choose_correct_first_result_with_multiline_test_classes(se (AboutFreemasons(),"File 'about_freemasons.py', line 11") ] self.assertEqual("File 'about_giant_feet.py', line 44", self.sensei.firstFailure()[1]) - - def test_that_end_report_displays_something(self): - self.sensei.learn() - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_end_report_shows_the_failure_report(self): - self.sensei.errorReport = Mock() - self.sensei.learn() - self.assertTrue(self.sensei.errorReport.called) - - def test_that_end_report_should_have_something_zenlike_on_it(self): - self.sensei.say_something_zenlike = Mock() - self.sensei.learn() - self.assertTrue(self.sensei.say_something_zenlike.called) - - def test_that_error_report_shows_something_if_there_is_a_failure(self): - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = (Mock(), "FAILED Parrot is breathing, Line 42") - self.sensei.errorReport() - self.assertTrue(self.sensei.stream.writeln.called) - def test_that_error_report_does_not_show_anything_if_there_is_no_failure(self): - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = None - self.sensei.errorReport() - self.assertFalse(self.sensei.stream.writeln.called) - def test_that_error_report_features_the_assertion_error(self): self.sensei.scrapeAssertionError = Mock() self.sensei.firstFailure = Mock() @@ -267,19 +191,19 @@ def test_that_error_report_features_a_stack_dump(self): def test_that_scraping_the_assertion_error_with_nothing_gives_you_a_blank_back(self): self.assertEqual("", self.sensei.scrapeAssertionError(None)) - + def test_that_scraping_the_assertion_error_with_messaged_assert(self): self.assertEqual(" AssertionError: Another fine mess you've got me into Stanley...", self.sensei.scrapeAssertionError(error_assertion_with_message)) - + def test_that_scraping_the_assertion_error_with_assert_equals(self): self.assertEqual(" AssertionError: 4 != 99", self.sensei.scrapeAssertionError(error_assertion_equals)) - + def test_that_scraping_the_assertion_error_with_assert_true(self): self.assertEqual(" AssertionError", self.sensei.scrapeAssertionError(error_assertion_true)) - + def test_that_scraping_the_assertion_error_with_syntax_error(self): self.assertEqual(" SyntaxError: invalid syntax", self.sensei.scrapeAssertionError(error_mess)) @@ -300,51 +224,27 @@ def test_that_scraping_the_assertion_error_with_list_error(self): def test_that_scraping_a_non_existent_stack_dump_gives_you_nothing(self): self.assertEqual("", self.sensei.scrapeInterestingStackDump(None)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_messaged_assert(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 43, in test_durability - self.assertEqual("Steel","Lard", "Another fine mess you've got me into Stanley...")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_with_message)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_assert_equals(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 49, in test_math - self.assertEqual(4,99)""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_equals)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_assert_true(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_armories.py", line 25, in test_weoponary - self.assertTrue("Pen" > "Sword")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_true)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_syntax_error(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_asserts.py", line 20 - self.assertTrue(eoe"Pen" > "Sword", "nhnth")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_mess)) def test_that_if_there_are_no_failures_say_the_final_zenlike_remark(self): self.sensei.failures = None words = self.sensei.say_something_zenlike() - + m = re.search("Spanish Inquisition", words) self.assertTrue(m and m.group(0)) - + def test_that_if_there_are_0_successes_it_will_say_the_first_zen_of_python_koans(self): self.sensei.pass_count = 0 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Beautiful is better than ugly", words) self.assertTrue(m and m.group(0)) - + def test_that_if_there_is_1_successes_it_will_say_the_second_zen_of_python_koans(self): self.sensei.pass_count = 1 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Explicit is better than implicit", words) self.assertTrue(m and m.group(0)) @@ -352,7 +252,7 @@ def test_that_if_there_is_10_successes_it_will_say_the_sixth_zen_of_python_koans self.sensei.pass_count = 10 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Sparse is better than dense", words) self.assertTrue(m and m.group(0)) @@ -360,7 +260,7 @@ def test_that_if_there_is_36_successes_it_will_say_the_final_zen_of_python_koans self.sensei.pass_count = 36 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Namespaces are one honking great idea", words) self.assertTrue(m and m.group(0)) @@ -368,6 +268,29 @@ def test_that_if_there_is_37_successes_it_will_say_the_first_zen_of_python_koans self.sensei.pass_count = 37 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Beautiful is better than ugly", words) self.assertTrue(m and m.group(0)) + + def test_that_total_lessons_return_7_if_there_are_7_lessons(self): + self.sensei.filter_all_lessons = Mock() + self.sensei.filter_all_lessons.return_value = [1,2,3,4,5,6,7] + + self.assertEqual(7, self.sensei.total_lessons()) + + def test_that_total_lessons_return_0_if_all_lessons_is_none(self): + self.sensei.filter_all_lessons = Mock() + self.sensei.filter_all_lessons.return_value = None + + self.assertEqual(0, self.sensei.total_lessons()) + + def test_total_koans_return_43_if_there_are_43_test_cases(self): + self.sensei.tests.countTestCases = Mock() + self.sensei.tests.countTestCases.return_value = 43 + + self.assertEqual(43, self.sensei.total_koans()) + + def test_filter_all_lessons_will_discover_test_classes_if_none_have_been_discovered_yet(self): + self.sensei.all_lessons = 0 + self.assertTrue(len(self.sensei.filter_all_lessons()) > 10) + self.assertTrue(len(self.sensei.all_lessons) > 10) diff --git a/python2/runner/sensei.py b/python2/runner/sensei.py new file mode 100644 index 000000000..4e8165334 --- /dev/null +++ b/python2/runner/sensei.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import re +import sys +import os +import glob + +import helper +from mockable_test_result import MockableTestResult +from runner import path_to_enlightenment + +from libs.colorama import init, Fore, Style +init() # init colorama + +class Sensei(MockableTestResult): + def __init__(self, stream): + unittest.TestResult.__init__(self) + self.stream = stream + self.prevTestClassName = None + self.tests = path_to_enlightenment.koans() + self.pass_count = 0 + self.lesson_pass_count = 0 + self.all_lessons = None + + def startTest(self, test): + MockableTestResult.startTest(self, test) + + if helper.cls_name(test) != self.prevTestClassName: + self.prevTestClassName = helper.cls_name(test) + if not self.failures: + self.stream.writeln() + self.stream.writeln("{0}{1}Thinking {2}".format( + Fore.RESET, Style.NORMAL, helper.cls_name(test))) + if helper.cls_name(test) != 'AboutAsserts': + self.lesson_pass_count += 1 + + def addSuccess(self, test): + if self.passesCount(): + MockableTestResult.addSuccess(self, test) + self.stream.writeln( \ + " {0}{1}{2} has expanded your awareness.{3}{4}" \ + .format(Fore.GREEN, Style.BRIGHT, test._testMethodName, \ + Fore.RESET, Style.NORMAL)) + self.pass_count += 1 + + def addError(self, test, err): + # Having 1 list for errors and 1 list for failures would mess with + # the error sequence + self.addFailure(test, err) + + def passesCount(self): + return not (self.failures and helper.cls_name(self.failures[0][0]) != + self.prevTestClassName) + + def addFailure(self, test, err): + MockableTestResult.addFailure(self, test, err) + + def sortFailures(self, testClassName): + table = list() + for test, err in self.failures: + if helper.cls_name(test) == testClassName: + m = re.search("(?<= line )\d+" ,err) + if m: + tup = (int(m.group(0)), test, err) + table.append(tup) + + if table: + return sorted(table) + else: + return None + + def firstFailure(self): + if not self.failures: return None + + table = self.sortFailures(helper.cls_name(self.failures[0][0])) + + if table: + return (table[0][1], table[0][2]) + else: + return None + + def learn(self): + self.errorReport() + + self.stream.writeln("") + self.stream.writeln("") + self.stream.writeln(self.report_progress()) + if self.failures: + self.stream.writeln(self.report_remaining()) + self.stream.writeln("") + self.stream.writeln(self.say_something_zenlike()) + + if self.failures: sys.exit(-1) + self.stream.writeln( + "\n{0}**************************************************" \ + .format(Fore.RESET)) + self.stream.writeln("\n{0}That was the last one, well done!" \ + .format(Fore.MAGENTA)) + self.stream.writeln( + "\nIf you want more, take a look at about_extra_credit_task.py") + + def errorReport(self): + problem = self.firstFailure() + if not problem: return + test, err = problem + self.stream.writeln(" {0}{1}{2} has damaged your " + "karma.".format(Fore.RED, Style.BRIGHT, test._testMethodName)) + + self.stream.writeln("\n{0}{1}You have not yet reached enlightenment ..." \ + .format(Fore.RESET, Style.NORMAL)) + self.stream.writeln("{0}{1}{2}".format(Fore.RED, \ + Style.BRIGHT, self.scrapeAssertionError(err))) + self.stream.writeln("") + self.stream.writeln("{0}{1}Please meditate on the following code:" \ + .format(Fore.RESET, Style.NORMAL)) + self.stream.writeln("{0}{1}{2}{3}{4}".format(Fore.YELLOW, Style.BRIGHT, \ + self.scrapeInterestingStackDump(err), Fore.RESET, Style.NORMAL)) + + def scrapeAssertionError(self, err): + if not err: return "" + + error_text = "" + count = 0 + for line in err.splitlines(): + m = re.search("^[^^ ].*$",line) + if m and m.group(0): + count+=1 + + if count>1: + error_text += (" " + line.strip()).rstrip() + '\n' + return error_text.strip('\n') + + def scrapeInterestingStackDump(self, err): + if not err: + return "" + + lines = err.splitlines() + + sep = '@@@@@SEP@@@@@' + + stack_text = "" + for line in lines: + m = re.search("^ File .*$",line) + if m and m.group(0): + stack_text += '\n' + line + + m = re.search("^ \w(\w)+.*$",line) + if m and m.group(0): + stack_text += sep + line + + lines = stack_text.splitlines() + + stack_text = "" + for line in lines: + m = re.search("^.*[/\\\\]koans[/\\\\].*$",line) + if m and m.group(0): + stack_text += line + '\n' + + + stack_text = stack_text.replace(sep, '\n').strip('\n') + stack_text = re.sub(r'(about_\w+.py)', + r"{0}\1{1}".format(Fore.BLUE, Fore.YELLOW), stack_text) + stack_text = re.sub(r'(line \d+)', + r"{0}\1{1}".format(Fore.BLUE, Fore.YELLOW), stack_text) + return stack_text + + def report_progress(self): + return "You have completed {0} koans and " \ + "{1} lessons.".format( + self.pass_count, + self.lesson_pass_count) + + def report_remaining(self): + koans_remaining = self.total_koans() - self.pass_count + lessons_remaining = self.total_lessons() - self.pass_count + + return "You are now {0} koans and {1} lessons away from " \ + "reaching enlightenment.".format( + koans_remaining, + lessons_remaining) + + # Hat's tip to Tim Peters for the zen statements from The 'Zen + # of Python' (http://www.python.org/dev/peps/pep-0020/) + # + # Also a hat's tip to Ara T. Howard for the zen statements from his + # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html) and + # Edgecase's later permutation in the Ruby Koans + def say_something_zenlike(self): + if self.failures: + turn = self.pass_count % 37 + + zenness = ""; + if turn == 0: + zenness = "Beautiful is better than ugly." + elif turn == 1 or turn == 2: + zenness = "Explicit is better than implicit." + elif turn == 3 or turn == 4: + zenness = "Simple is better than complex." + elif turn == 5 or turn == 6: + zenness = "Complex is better than complicated." + elif turn == 7 or turn == 8: + zenness = "Flat is better than nested." + elif turn == 9 or turn == 10: + zenness = "Sparse is better than dense." + elif turn == 11 or turn == 12: + zenness = "Readability counts." + elif turn == 13 or turn == 14: + zenness = "Special cases aren't special enough to " \ + "break the rules." + elif turn == 15 or turn == 16: + zenness = "Although practicality beats purity." + elif turn == 17 or turn == 18: + zenness = "Errors should never pass silently." + elif turn == 19 or turn == 20: + zenness = "Unless explicitly silenced." + elif turn == 21 or turn == 22: + zenness = "In the face of ambiguity, refuse the " \ + "temptation to guess." + elif turn == 23 or turn == 24: + zenness = "There should be one-- and preferably only " \ + "one --obvious way to do it." + elif turn == 25 or turn == 26: + zenness = "Although that way may not be obvious at " \ + "first unless you're Dutch." + elif turn == 27 or turn == 28: + zenness = "Now is better than never." + elif turn == 29 or turn == 30: + zenness = "Although never is often better than right " \ + "now." + elif turn == 31 or turn == 32: + zenness = "If the implementation is hard to explain, " \ + "it's a bad idea." + elif turn == 33 or turn == 34: + zenness = "If the implementation is easy to explain, " \ + "it may be a good idea." + else: + zenness = "Namespaces are one honking great idea -- " \ + "let's do more of those!" + return "{0}{1}{2}{3}".format(Fore.CYAN, zenness, Fore.RESET, Style.NORMAL); + else: + return "{0}Nobody ever expects the Spanish Inquisition." \ + .format(Fore.CYAN) + + # Hopefully this will never ever happen! + return "The temple is collapsing! Run!!!" + + def total_lessons(self): + all_lessons = self.filter_all_lessons() + + if all_lessons: + return len(all_lessons) + else: + return 0 + + def total_koans(self): + return self.tests.countTestCases() + + def filter_all_lessons(self): + cur_dir = os.path.split(os.path.realpath(__file__))[0] + if not self.all_lessons: + self.all_lessons = glob.glob('{0}/../koans/about*.py'.format(cur_dir)) + self.all_lessons = filter(lambda filename: + "about_extra_credit" not in filename, + self.all_lessons) + return self.all_lessons diff --git a/python 2/runner/writeln_decorator.py b/python2/runner/writeln_decorator.py similarity index 100% rename from python 2/runner/writeln_decorator.py rename to python2/runner/writeln_decorator.py diff --git a/python 3/_runner_tests.py b/python3/_runner_tests.py similarity index 100% rename from python 3/_runner_tests.py rename to python3/_runner_tests.py diff --git a/python 3/contemplate_koans.py b/python3/contemplate_koans.py similarity index 100% rename from python 3/contemplate_koans.py rename to python3/contemplate_koans.py diff --git a/python 3/example_file.txt b/python3/example_file.txt similarity index 100% rename from python 3/example_file.txt rename to python3/example_file.txt diff --git a/python 3/koans/GREEDS_RULES.txt b/python3/koans/GREEDS_RULES.txt similarity index 100% rename from python 3/koans/GREEDS_RULES.txt rename to python3/koans/GREEDS_RULES.txt diff --git a/python 3/koans/__init__.py b/python3/koans/__init__.py similarity index 100% rename from python 3/koans/__init__.py rename to python3/koans/__init__.py diff --git a/python 3/koans/a_package_folder/__init__.py b/python3/koans/a_package_folder/__init__.py similarity index 100% rename from python 3/koans/a_package_folder/__init__.py rename to python3/koans/a_package_folder/__init__.py diff --git a/python 3/koans/a_package_folder/a_module.py b/python3/koans/a_package_folder/a_module.py similarity index 100% rename from python 3/koans/a_package_folder/a_module.py rename to python3/koans/a_package_folder/a_module.py diff --git a/python 2/koans/about_asserts.py b/python3/koans/about_asserts.py similarity index 71% rename from python 2/koans/about_asserts.py rename to python3/koans/about_asserts.py index 3905498d3..79c07e096 100644 --- a/python 2/koans/about_asserts.py +++ b/python3/koans/about_asserts.py @@ -9,25 +9,28 @@ def test_assert_truth(self): """ We shall contemplate truth by testing reality, via asserts. """ - self.assertTrue(False) # This should be true + #### self.assertTrue(False) # This should be true + self.assertTrue(True) # This should be true def test_assert_with_message(self): """ Enlightenment may be more easily achieved with appropriate messages. """ - self.assertTrue(False, "This should be true -- Please fix this") + #### self.assertTrue(False, "This should be true -- Please fix this") + self.assertTrue(True, "This should be true -- Please fix this") def test_fill_in_values(self): """ Sometimes we will ask you to fill in the values """ - self.assertEqual(__, 1 + 1) + self.assertEqual(2, 1 + 1) def test_assert_equality(self): """ To understand reality, we must compare our expectations against reality. """ - expected_value = __ + #### expected_value = __ + expected_value = 2 actual_value = 1 + 1 self.assertTrue(expected_value == actual_value) @@ -35,7 +38,8 @@ def test_a_better_way_of_asserting_equality(self): """ Some ways of asserting equality are better than others. """ - expected_value = __ + #### expected_value = __ + expected_value = 2 actual_value = 1 + 1 self.assertEqual(expected_value, actual_value) @@ -46,5 +50,6 @@ def test_that_unittest_asserts_work_the_same_way_as_python_asserts(self): """ # This throws an AssertionError exception - assert False + #### assert False + assert True diff --git a/python 3/koans/about_attribute_access.py b/python3/koans/about_attribute_access.py similarity index 81% rename from python 3/koans/about_attribute_access.py rename to python3/koans/about_attribute_access.py index 4eabb76f9..86817409a 100644 --- a/python 3/koans/about_attribute_access.py +++ b/python3/koans/about_attribute_access.py @@ -15,12 +15,12 @@ class TypicalObject: def test_calling_undefined_functions_normally_results_in_errors(self): typical = self.TypicalObject() - with self.assertRaises(___): typical.foobar() + with self.assertRaises(AttributeError): typical.foobar() def test_calling_getattribute_causes_an_attribute_error(self): typical = self.TypicalObject() - with self.assertRaises(___): typical.__getattribute__('foobar') + with self.assertRaises(AttributeError): typical.__getattribute__('foobar') # THINK ABOUT IT: # @@ -36,21 +36,21 @@ def __getattribute__(self, attr_name): def test_all_attribute_reads_are_caught(self): catcher = self.CatchAllAttributeReads() - self.assertRegexpMatches(catcher.foobar, __) + self.assertRegexpMatches(catcher.foobar, "Someone called 'foobar' and it could not be found") def test_intercepting_return_values_can_disrupt_the_call_chain(self): catcher = self.CatchAllAttributeReads() - self.assertRegexpMatches(catcher.foobaz, __) # This is fine + self.assertRegexpMatches(catcher.foobaz, "Someone called 'foobaz' and it could not be found") # This is fine try: catcher.foobaz(1) except TypeError as ex: err_msg = ex.args[0] - self.assertRegexpMatches(err_msg, __) + self.assertRegexpMatches(err_msg, "'str' object is not callable") - # foobaz returns a string. What happens to the '(1)' part? + # foobaz is returns a string. What happens to the '(1)' part? # Try entering this into a python console to reproduce the issue: # # "foobaz"(1) @@ -59,7 +59,7 @@ def test_intercepting_return_values_can_disrupt_the_call_chain(self): def test_changes_to_the_getattribute_implementation_affects_getattr_function(self): catcher = self.CatchAllAttributeReads() - self.assertRegexpMatches(getattr(catcher, 'any_attribute'), __) + self.assertRegexpMatches(getattr(catcher, 'any_attribute'), "Someone called 'any_attribute' and it could not be found") # ------------------------------------------------------------------ @@ -73,13 +73,13 @@ def __getattribute__(self, attr_name): def test_foo_attributes_are_caught(self): catcher = self.WellBehavedFooCatcher() - self.assertEqual(__, catcher.foo_bar) - self.assertEqual(__, catcher.foo_baz) + self.assertEqual('Foo to you too', catcher.foo_bar) + self.assertEqual('Foo to you too', catcher.foo_baz) def test_non_foo_messages_are_treated_normally(self): catcher = self.WellBehavedFooCatcher() - with self.assertRaises(___): catcher.normal_undefined_attribute + with self.assertRaises(AttributeError): catcher.normal_undefined_attribute # ------------------------------------------------------------------ @@ -113,7 +113,7 @@ def test_getattribute_is_a_bit_overzealous_sometimes(self): catcher = self.RecursiveCatcher() catcher.my_method() global stack_depth - self.assertEqual(__, stack_depth) + self.assertEqual(11, stack_depth) # ------------------------------------------------------------------ @@ -134,17 +134,17 @@ def test_getattr_ignores_known_attributes(self): catcher = self.MinimalCatcher() catcher.my_method() - self.assertEqual(__, catcher.no_of_getattr_calls) + self.assertEqual(0, catcher.no_of_getattr_calls) def test_getattr_only_catches_unknown_attributes(self): catcher = self.MinimalCatcher() catcher.purple_flamingos() catcher.free_pie() - self.assertEqual(__, + self.assertEqual('DuffObject', type(catcher.give_me_duff_or_give_me_death()).__name__) - self.assertEqual(__, catcher.no_of_getattr_calls) + self.assertEqual(3, catcher.no_of_getattr_calls) # ------------------------------------------------------------------ @@ -165,9 +165,10 @@ def test_setattr_intercepts_attribute_assignments(self): fanboy.comic = 'The Laminator, issue #1' fanboy.pie = 'blueberry' - self.assertEqual(__, fanboy.a_pie) + self.assertEqual('blueberry', fanboy.a_pie) - prefix = '__' + ##### prefix = '__' + prefix = 'my' self.assertEqual("The Laminator, issue #1", getattr(fanboy, prefix + '_comic')) # ------------------------------------------------------------------ @@ -185,11 +186,11 @@ def __setattr__(self, attr_name, value): object.__setattr__(self, new_attr_name, value) - def test_it_modifies_external_attribute_as_expected(self): + def test_it_modifies_external_attribute_setting_as_expected(self): setter = self.ScarySetter() setter.e = "mc hammer" - self.assertEqual(__, setter.altered_e) + self.assertEqual('mc hammer', setter.altered_e) def test_it_mangles_some_internal_attributes(self): setter = self.ScarySetter() @@ -197,9 +198,9 @@ def test_it_mangles_some_internal_attributes(self): try: coconuts = setter.num_of_coconuts except AttributeError: - self.assertEqual(__, setter.altered_num_of_coconuts) + self.assertEqual(9, setter.altered_num_of_coconuts) def test_in_this_case_private_attributes_remain_unmangled(self): setter = self.ScarySetter() - self.assertEqual(__, setter._num_of_private_coconuts) + self.assertEqual(2, setter._num_of_private_coconuts) diff --git a/python 3/koans/about_class_attributes.py b/python3/koans/about_class_attributes.py similarity index 68% rename from python 3/koans/about_class_attributes.py rename to python3/koans/about_class_attributes.py index bdf0ea70a..a746f7641 100644 --- a/python 3/koans/about_class_attributes.py +++ b/python3/koans/about_class_attributes.py @@ -10,52 +10,52 @@ class AboutClassAttributes(Koan): class Dog: pass - + def test_objects_are_objects(self): fido = self.Dog() - self.assertEqual(__, isinstance(fido, object)) + self.assertEqual(True, isinstance(fido, object)) def test_classes_are_types(self): - self.assertEqual(__, self.Dog.__class__ == type) - + self.assertEqual(True, self.Dog.__class__ == type) + def test_classes_are_objects_too(self): - self.assertEqual(__, issubclass(self.Dog, object)) - + self.assertEqual(True, issubclass(self.Dog, object)) + def test_objects_have_methods(self): fido = self.Dog() - self.assertEqual(__, len(dir(fido))) - + self.assertEqual(25, len(dir(fido))) + def test_classes_have_methods(self): - self.assertEqual(__, len(dir(self.Dog))) + self.assertEqual(25, len(dir(self.Dog))) def test_creating_objects_without_defining_a_class(self): - singularity = object() - self.assertEqual(__, len(dir(singularity))) + singularity = object() + self.assertEqual(22, len(dir(singularity))) def test_defining_attributes_on_individual_objects(self): fido = self.Dog() fido.legs = 4 - - self.assertEqual(__, fido.legs) - + + self.assertEqual(4, fido.legs) + def test_defining_functions_on_individual_objects(self): fido = self.Dog() fido.wag = lambda : 'fidos wag' - - self.assertEqual(__, fido.wag()) + + self.assertEqual('fidos wag', fido.wag()) def test_other_objects_are_not_affected_by_these_singleton_functions(self): fido = self.Dog() rover = self.Dog() def wag(): - return 'fidos wag' + return 'fidos wag' fido.wag = wag - - with self.assertRaises(___): rover.wag() - + + with self.assertRaises(AttributeError): rover.wag() + # ------------------------------------------------------------------ - + class Dog2: def wag(self): return 'instance wag' @@ -69,28 +69,28 @@ def growl(self): @staticmethod def bark(): return "staticmethod bark, arg: None" - + @classmethod def growl(cls): - return "classmethod growl, arg: cls=" + cls.__name__ - + return "classmethod growl, arg: cls=" + cls.__name__ + def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_too(self): - self.assertRegexpMatches(self.Dog2.growl(), __) - + self.assertRegexpMatches(self.Dog2.growl(), 'classmethod growl, arg: cls=Dog2') + def test_classmethods_are_not_independent_of_instance_methods(self): fido = self.Dog2() - self.assertRegexpMatches(fido.growl(), __) - self.assertRegexpMatches(self.Dog2.growl(), __) + self.assertRegexpMatches(fido.growl(), 'classmethod growl, arg: cls=Dog2') + self.assertRegexpMatches(self.Dog2.growl(), 'classmethod growl, arg: cls=Dog2') def test_staticmethods_are_unbound_functions_housed_in_a_class(self): - self.assertRegexpMatches(self.Dog2.bark(), __) + self.assertRegexpMatches(self.Dog2.bark(), 'staticmethod bark, arg: None') def test_staticmethods_also_overshadow_instance_methods(self): fido = self.Dog2() - self.assertRegexpMatches(fido.bark(), __) - + self.assertRegexpMatches(fido.bark(), 'staticmethod bark, arg: None') + # ------------------------------------------------------------------ - + class Dog3: def __init__(self): self._name = None @@ -100,37 +100,37 @@ def get_name_from_instance(self): def set_name_from_instance(self, name): self._name = name - - @classmethod + + @classmethod def get_name(cls): return cls._name - @classmethod + @classmethod def set_name(cls, name): cls._name = name - + name = property(get_name, set_name) name_from_instance = property(get_name_from_instance, set_name_from_instance) - + def test_classmethods_can_not_be_used_as_properties(self): fido = self.Dog3() - with self.assertRaises(___): fido.name = "Fido" - + with self.assertRaises(TypeError): fido.name = "Fido" + def test_classes_and_instances_do_not_share_instance_attributes(self): fido = self.Dog3() fido.set_name_from_instance("Fido") fido.set_name("Rover") - self.assertEqual(__, fido.get_name_from_instance()) - self.assertEqual(__, self.Dog3.get_name()) - + self.assertEqual('Fido', fido.get_name_from_instance()) + self.assertEqual('Rover', self.Dog3.get_name()) + def test_classes_and_instances_do_share_class_attributes(self): fido = self.Dog3() fido.set_name("Fido") - self.assertEqual(__, fido.get_name()) - self.assertEqual(__, self.Dog3.get_name()) - + self.assertEqual('Fido', fido.get_name()) + self.assertEqual('Fido', self.Dog3.get_name()) + # ------------------------------------------------------------------ - + class Dog4: def a_class_method(cls): return 'dogs class method' @@ -140,15 +140,15 @@ def a_static_method(): a_class_method = classmethod(a_class_method) a_static_method = staticmethod(a_static_method) - + def test_you_can_define_class_methods_without_using_a_decorator(self): - self.assertEqual(__, self.Dog4.a_class_method()) + self.assertEqual('dogs class method', self.Dog4.a_class_method()) def test_you_can_define_static_methods_without_using_a_decorator(self): - self.assertEqual(__, self.Dog4.a_static_method()) - + self.assertEqual('dogs static method', self.Dog4.a_static_method()) + # ------------------------------------------------------------------ - + def test_heres_an_easy_way_to_explicitly_call_class_methods_from_instance_methods(self): fido = self.Dog4() - self.assertEqual(__, fido.__class__.a_class_method()) + self.assertEqual('dogs class method', fido.__class__.a_class_method()) diff --git a/python 3/koans/about_classes.py b/python3/koans/about_classes.py similarity index 77% rename from python 3/koans/about_classes.py rename to python3/koans/about_classes.py index eee20e74d..c565c310d 100644 --- a/python 3/koans/about_classes.py +++ b/python3/koans/about_classes.py @@ -9,10 +9,10 @@ class Dog: def test_instances_of_classes_can_be_created_adding_parenthesis(self): fido = self.Dog() - self.assertEqual(__, type(fido).__name__) + self.assertEqual('Dog', type(fido).__name__) def test_classes_have_docstrings(self): - self.assertRegexpMatches(self.Dog.__doc__, __) + self.assertRegexpMatches(self.Dog.__doc__, 'Dogs need regular walkies') # ------------------------------------------------------------------ @@ -25,12 +25,12 @@ def set_name(self, a_name): def test_init_method_is_the_constructor(self): dog = self.Dog2() - self.assertEqual(__, dog._name) + self.assertEqual('Paul', dog._name) def test_private_attributes_are_not_really_private(self): dog = self.Dog2() dog.set_name("Fido") - self.assertEqual(__, dog._name) + self.assertEqual('Fido', dog._name) # The _ prefix in _name implies private ownership, but nothing is truly # private in Python. @@ -38,11 +38,11 @@ def test_you_can_also_access_the_value_out_using_getattr_and_dict(self): fido = self.Dog2() fido.set_name("Fido") - self.assertEqual(__, getattr(fido, "_name")) + self.assertEqual('Fido', getattr(fido, "_name")) # getattr(), setattr() and delattr() are a way of accessing attributes # by method rather than through assignment operators - self.assertEqual(__, fido.__dict__["_name"]) + self.assertEqual('Fido', fido.__dict__["_name"]) # Yes, this works here, but don't rely on the __dict__ object! Some # class implementations use optimization which result in __dict__ not # showing everything. @@ -67,10 +67,10 @@ def test_that_name_can_be_read_as_a_property(self): fido.set_name("Fido") # access as method - self.assertEqual(__, fido.get_name()) + self.assertEqual('Fido', fido.get_name()) # access as property - self.assertEqual(__, fido.name) + self.assertEqual('Fido', fido.name) # ------------------------------------------------------------------ @@ -90,7 +90,7 @@ def test_creating_properties_with_decorators_is_slightly_easier(self): fido = self.Dog4() fido.name = "Fido" - self.assertEqual(__, fido.name) + self.assertEqual("Fido", fido.name) # ------------------------------------------------------------------ @@ -104,10 +104,10 @@ def name(self): def test_init_provides_initial_values_for_instance_variables(self): fido = self.Dog5("Fido") - self.assertEqual(__, fido.name) + self.assertEqual("Fido", fido.name) def test_args_must_match_init(self): - with self.assertRaises(___): self.Dog5() + with self.assertRaises(TypeError): self.Dog5() # THINK ABOUT IT: # Why is this so? @@ -116,7 +116,7 @@ def test_different_objects_have_difference_instance_variables(self): fido = self.Dog5("Fido") rover = self.Dog5("Rover") - self.assertEqual(__, rover.name == fido.name) + self.assertEqual(False, rover.name == fido.name) # ------------------------------------------------------------------ @@ -128,36 +128,36 @@ def get_self(self): return self def __str__(self): - return __ + return self._name +##### return __ def __repr__(self): return "" + def test_inside_a_method_self_refers_to_the_containing_object(self): fido = self.Dog6("Fido") - self.assertEqual(__, fido.get_self()) # Not a string! + self.assertEqual(fido, fido.get_self()) # Not a string! def test_str_provides_a_string_version_of_the_object(self): fido = self.Dog6("Fido") - - self.assertEqual("Bob", str(fido)) + self.assertEqual("Fido", str(fido)) def test_str_is_used_explicitly_in_string_interpolation(self): fido = self.Dog6("Fido") - - self.assertEqual(__, "My dog is " + str(fido)) + self.assertEqual('My dog is Fido', "My dog is " + str(fido)) def test_repr_provides_a_more_complete_string_version(self): fido = self.Dog6("Fido") - self.assertEqual(__, repr(fido)) + self.assertEqual("", repr(fido)) def test_all_objects_support_str_and_repr(self): seq = [1,2,3] - self.assertEqual(__, str(seq)) - self.assertEqual(__, repr(seq)) + self.assertEqual("[1, 2, 3]", str(seq)) + self.assertEqual('[1, 2, 3]', repr(seq)) - self.assertEqual(__, str("STRING")) - self.assertEqual(__, repr("STRING")) + self.assertEqual('STRING', str("STRING")) + self.assertEqual("'STRING'", repr("STRING")) \ No newline at end of file diff --git a/python 3/koans/about_control_statements.py b/python3/koans/about_control_statements.py similarity index 76% rename from python 3/koans/about_control_statements.py rename to python3/koans/about_control_statements.py index f1cb5c0bd..de15073eb 100644 --- a/python 3/koans/about_control_statements.py +++ b/python3/koans/about_control_statements.py @@ -10,13 +10,13 @@ def test_if_then_else_statements(self): result = 'true value' else: result = 'false value' - self.assertEqual(__, result) + self.assertEqual('true value', result) def test_if_then_statements(self): result = 'default value' if True: result = 'true value' - self.assertEqual(__, result) + self.assertEqual('true value', result) def test_while_statement(self): i = 1 @@ -24,7 +24,7 @@ def test_while_statement(self): while i <= 10: result = result * i i += 1 - self.assertEqual(__, result) + self.assertEqual(3628800, result) def test_break_statement(self): i = 1 @@ -33,7 +33,7 @@ def test_break_statement(self): if i > 10: break result = result * i i += 1 - self.assertEqual(__, result) + self.assertEqual(3628800, result) def test_continue_statement(self): i = 0 @@ -42,27 +42,29 @@ def test_continue_statement(self): i += 1 if (i % 2) == 0: continue result.append(i) - self.assertEqual(__, result) + self.assertEqual([1, 3, 5, 7, 9], result) def test_for_statement(self): phrase = ["fish", "and", "chips"] result = [] for item in phrase: result.append(item.upper()) - self.assertEqual([__, __, __], result) + #####self.assertEqual([__, __, __], result) + self.assertEqual(['FISH', 'AND', 'CHIPS'], result) def test_for_statement_with_tuples(self): round_table = [ ("Lancelot", "Blue"), - ("Galahad", "I don't know!"), + ("Gallahad", "I don't know!"), ("Robin", "Blue! I mean Green!"), - ("Arthur", "Is that an African Swallow or Amazonian Swallow?") + ("Arthur", "Is that an Africon Swallow or Amazonian Swallow?") ] result = [] for knight, answer in round_table: result.append("Contestant: '" + knight + "' Answer: '" + answer + "'") - text = __ + #####text = __ + text = r'Green' self.assertRegexpMatches(result[2], text) diff --git a/python 3/koans/about_decorating_with_classes.py b/python3/koans/about_decorating_with_classes.py similarity index 76% rename from python 3/koans/about_decorating_with_classes.py rename to python3/koans/about_decorating_with_classes.py index 358488c4c..2080f40e5 100644 --- a/python 3/koans/about_decorating_with_classes.py +++ b/python3/koans/about_decorating_with_classes.py @@ -14,26 +14,26 @@ def maximum(self, a, b): def test_partial_that_wrappers_no_args(self): """ - Before we can understand this type of decorator we need to consider + Before we can understand Class Decorators we need to consider the partial. """ max = functools.partial(self.maximum) - self.assertEqual(__, max(7,23)) - self.assertEqual(__, max(10,-10)) + self.assertEqual(23, max(7,23)) + self.assertEqual(10, max(10,-10)) def test_partial_that_wrappers_first_arg(self): max0 = functools.partial(self.maximum, 0) - self.assertEqual(__, max0(-4)) - self.assertEqual(__, max0(5)) + self.assertEqual(0, max0(-4)) + self.assertEqual(5, max0(5)) def test_partial_that_wrappers_all_args(self): always99 = functools.partial(self.maximum, 99, 20) always20 = functools.partial(self.maximum, 9, 20) - self.assertEqual(__, always99()) - self.assertEqual(__, always20()) + self.assertEqual(99, always99()) + self.assertEqual(20, always20()) # ------------------------------------------------------------------ @@ -64,8 +64,8 @@ def test_decorator_with_no_arguments(self): # To clarify: the decorator above the function has no arguments, even # if the decorated function does - self.assertEqual(__, self.foo()) - self.assertEqual(__, self.parrot('pieces of eight')) + self.assertEqual("foo, foo", self.foo()) + self.assertEqual('PIECES OF EIGHT, PIECES OF EIGHT', self.parrot('pieces of eight')) # ------------------------------------------------------------------ @@ -77,7 +77,7 @@ def test_what_a_decorator_is_doing_to_a_function(self): #wrap the function with the decorator self.sound_check = self.doubleit(self.sound_check) - self.assertEqual(__, self.sound_check()) + self.assertEqual('Testing..., Testing...', self.sound_check()) # ------------------------------------------------------------------ @@ -95,7 +95,7 @@ def decorated_function(*args): decorated_function.__doc__ = self.fn_doc return decorated_function - @documenter("Increments a value by one. Kind of.") + @documenter("Increments a value by one.") def count_badly(self, num): num += 1 if num==3: @@ -108,11 +108,11 @@ def idler(self, num): pass def test_decorator_with_an_argument(self): - self.assertEqual(__, self.count_badly(2)) - self.assertEqual(__, self.count_badly.__doc__) + self.assertEqual(5, self.count_badly(2)) + self.assertEqual('Increments a value by one.', self.count_badly.__doc__) def test_documentor_which_already_has_a_docstring(self): - self.assertEqual(__, self.idler.__doc__) + self.assertEqual('Idler: Does nothing', self.idler.__doc__) # ------------------------------------------------------------------ @@ -123,6 +123,6 @@ def homer(self): return "D'oh" def test_we_can_chain_decorators(self): - self.assertEqual(__, self.homer()) - self.assertEqual(__, self.homer.__doc__) + self.assertEqual("D'oh, D'oh, D'oh, D'oh", self.homer()) + self.assertEqual("DOH!", self.homer.__doc__) \ No newline at end of file diff --git a/python 3/koans/about_decorating_with_functions.py b/python3/koans/about_decorating_with_functions.py similarity index 75% rename from python 3/koans/about_decorating_with_functions.py rename to python3/koans/about_decorating_with_functions.py index def65098c..70eeb4271 100644 --- a/python 3/koans/about_decorating_with_functions.py +++ b/python3/koans/about_decorating_with_functions.py @@ -14,8 +14,8 @@ def mediocre_song(self): return "o/~ We all live in a broken submarine o/~" def test_decorators_can_modify_a_function(self): - self.assertRegexpMatches(self.mediocre_song(), __) - self.assertEqual(__, self.mediocre_song.wow_factor) + self.assertRegexpMatches(self.mediocre_song(), "o/~ We all live in a broken submarine o/~") + self.assertEqual('COWBELL BABY!', self.mediocre_song.wow_factor) # ------------------------------------------------------------------ @@ -29,5 +29,5 @@ def render_tag(self, name): return name def test_decorators_can_change_a_function_output(self): - self.assertEqual(__, self.render_tag('llama')) + self.assertEqual("", self.render_tag('llama')) diff --git a/python 3/koans/about_deleting_objects.py b/python3/koans/about_deleting_objects.py similarity index 82% rename from python 3/koans/about_deleting_objects.py rename to python3/koans/about_deleting_objects.py index 87eea9a23..f8a14a786 100644 --- a/python 3/koans/about_deleting_objects.py +++ b/python3/koans/about_deleting_objects.py @@ -9,13 +9,13 @@ def test_del_can_remove_slices(self): del lottery_nums[1] del lottery_nums[2:4] - self.assertEqual(___, lottery_nums) + self.assertEqual([4, 15, 42], lottery_nums) def test_del_can_remove_entire_lists(self): lottery_nums = [4, 8, 15, 16, 23, 42] del lottery_nums - with self.assertRaises(___): win = lottery_nums + with self.assertRaises(UnboundLocalError): win = lottery_nums # ==================================================================== @@ -48,8 +48,8 @@ def test_del_can_remove_attributes(self): except AttributeError as e: err_msg2 = e.args[0] - self.assertRegexpMatches(err_msg1, __) - self.assertRegexpMatches(err_msg2, __) + self.assertRegexpMatches(err_msg1, "'ClosingSale' object has no attribute 'toilet_brushes'") + self.assertRegexpMatches(err_msg2, "'ClosingSale' object has no attribute 'hamsters'") # ==================================================================== @@ -75,10 +75,10 @@ def del_name(self): def test_del_works_with_properties(self): cowboy = self.ClintEastwood() cowboy.name = 'Senor Ninguno' - self.assertEqual('Senor Ninguno', cowboy.name) + self.assertEqual('Senor Ninguno', cowboy.name) ##### del cowboy.name - self.assertEqual(__, cowboy.name) + self.assertEqual('The man with no name', cowboy.name) # ==================================================================== @@ -102,10 +102,10 @@ def name(self): def test_another_way_to_make_a_deletable_property(self): citizen = self.Prisoner() citizen.name = "Patrick" - self.assertEqual('Patrick', citizen.name) + self.assertEqual('Patrick', citizen.name) ##### del citizen.name - self.assertEqual(__, citizen.name) + self.assertEqual('Number Six', citizen.name) # ==================================================================== @@ -119,6 +119,6 @@ def __delattr__(self, attr_name): def tests_del_can_be_overriden(self): sale = self.MoreOrganisedClosingSale() - self.assertEqual(__, sale.jellies()) + self.assertEqual(5, sale.jellies()) del sale.jellies - self.assertEqual(__, sale.last_deletion) + self.assertEqual('jellies', sale.last_deletion) diff --git a/python 3/koans/about_dice_project.py b/python3/koans/about_dice_project.py similarity index 86% rename from python 3/koans/about_dice_project.py rename to python3/koans/about_dice_project.py index c9bdc44fe..38c95e1ef 100644 --- a/python 3/koans/about_dice_project.py +++ b/python3/koans/about_dice_project.py @@ -14,9 +14,11 @@ def values(self): return self._values def roll(self, n): - # Needs implementing! - # Tip: random.randint(min, max) can be used to generate random numbers - pass + self._values = list(map(lambda n: random.randint(1, 6), list(range(n)))) + + ##### # Needs implementing! + ##### # Tip: random.randint(min, max) can be used to generate random numbers + ##### pass class AboutDiceProject(Koan): def test_can_create_a_dice_set(self): @@ -55,7 +57,7 @@ def test_dice_values_should_change_between_rolls(self): # # If the rolls are random, then it is possible (although not # likely) that two consecutive rolls are equal. What would be a - # better way to test this? + # better way to test this. def test_you_can_roll_different_numbers_of_dice(self): dice = DiceSet() diff --git a/python 3/koans/about_dictionaries.py b/python3/koans/about_dictionaries.py similarity index 58% rename from python 3/koans/about_dictionaries.py rename to python3/koans/about_dictionaries.py index 63c56d7ce..ee187a561 100644 --- a/python 3/koans/about_dictionaries.py +++ b/python3/koans/about_dictionaries.py @@ -10,24 +10,25 @@ class AboutDictionaries(Koan): def test_creating_dictionaries(self): empty_dict = dict() - self.assertEqual(dict, type(empty_dict)) - self.assertDictEqual({}, empty_dict) - self.assertEqual(__, len(empty_dict)) + self.assertEqual(dict, type(empty_dict)) ##### + self.assertDictEqual({}, empty_dict) ##### + self.assertEqual(0, len(empty_dict)) - def test_dictionary_literals(self): + def test_dictianary_literals(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish)) + self.assertEqual(2, len(babel_fish)) def test_accessing_dictionaries(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, babel_fish['one']) - self.assertEqual(__, babel_fish['two']) + self.assertEqual('uno', babel_fish['one']) + self.assertEqual('dos', babel_fish['two']) def test_changing_dictionaries(self): babel_fish = { 'one': "uno", 'two': "dos" } babel_fish['one'] = "eins" - expected = { 'two': "dos", 'one': __ } + #####expected = { 'two': "dos", 'one': __ } + expected = { 'two': "dos", 'one': 'eins' } self.assertDictEqual(expected, babel_fish) # Bonus Question: Why was "expected" broken out into a variable @@ -37,26 +38,26 @@ def test_dictionary_is_unordered(self): dict1 = { 'one': "uno", 'two': "dos" } dict2 = { 'two': "dos", 'one': "uno" } - self.assertEqual(__, dict1 == dict2) + self.assertEqual(True, dict1 == dict2) def test_dictionary_keys(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish.keys())) - self.assertEqual(__, 'one' in babel_fish) - self.assertEqual(__, 'two' in babel_fish) - self.assertEqual('dict_keys', babel_fish.keys().__class__.__name__) + self.assertEqual(2, len(babel_fish.keys())) + self.assertEqual(True, 'one' in babel_fish) + self.assertEqual(True, 'two' in babel_fish) + self.assertEqual('dict_keys', babel_fish.keys().__class__.__name__) ##### def test_dictionary_values(self): babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish.values())) - self.assertEqual(__, 'uno' in babel_fish.values()) - self.assertEqual(__, 'dos' in babel_fish.values()) - self.assertEqual('dict_values', babel_fish.values().__class__.__name__) + self.assertEqual(2, len(babel_fish.values())) + self.assertEqual(True, 'uno' in babel_fish.values()) + self.assertEqual(True, 'dos' in babel_fish.values()) + self.assertEqual('dict_values', babel_fish.values().__class__.__name__) ##### def test_making_a_dictionary_from_a_sequence_of_keys(self): cards = {}.fromkeys(("red warrior", "green elf", "blue valkyrie", "yellow dwarf", "confused looking zebra"), 42) - self.assertEqual(__, len(cards)) - self.assertEqual(__, cards["green elf"]) - self.assertEqual(__, cards["yellow dwarf"]) + self.assertEqual(5, len(cards)) + self.assertEqual(42, cards["green elf"]) + self.assertEqual(42, cards["yellow dwarf"]) diff --git a/python 3/koans/about_exceptions.py b/python3/koans/about_exceptions.py similarity index 64% rename from python 3/koans/about_exceptions.py rename to python3/koans/about_exceptions.py index 956f45f24..a77055c25 100644 --- a/python 3/koans/about_exceptions.py +++ b/python3/koans/about_exceptions.py @@ -10,10 +10,10 @@ class MySpecialError(RuntimeError): def test_exceptions_inherit_from_exception(self): mro = self.MySpecialError.mro() - self.assertEqual(__, mro[1].__name__) - self.assertEqual(__, mro[2].__name__) - self.assertEqual(__, mro[3].__name__) - self.assertEqual(__, mro[4].__name__) + self.assertEqual('RuntimeError', mro[1].__name__) + self.assertEqual('Exception', mro[2].__name__) + self.assertEqual('BaseException', mro[3].__name__) + self.assertEqual('object', mro[4].__name__) def test_try_clause(self): result = None @@ -24,15 +24,15 @@ def test_try_clause(self): ex2 = ex - self.assertEqual(__, result) + self.assertEqual('exception handled', result) - self.assertEqual(__, isinstance(ex2, Exception)) - self.assertEqual(__, isinstance(ex2, RuntimeError)) + self.assertEqual(True, isinstance(ex2, Exception)) + self.assertEqual(False, isinstance(ex2, RuntimeError)) self.assertTrue(issubclass(RuntimeError, Exception), \ - "RuntimeError is a subclass of Exception") + "RuntimeError is a subclass of StandardError") ##### - self.assertEqual(__, ex2.args[0]) + self.assertEqual('Oops', ex2.args[0]) def test_raising_a_specific_error(self): result = None @@ -42,8 +42,8 @@ def test_raising_a_specific_error(self): result = 'exception handled' msg = ex.args[0] - self.assertEqual(__, result) - self.assertEqual(__, msg) + self.assertEqual('exception handled', result) + self.assertEqual("My Message", msg) def test_else_clause(self): result = None @@ -55,7 +55,7 @@ def test_else_clause(self): else: result = 'no damage done' - self.assertEqual(__, result) + self.assertEqual('no damage done', result) def test_finally_clause(self): @@ -68,4 +68,4 @@ def test_finally_clause(self): finally: result = 'always run' - self.assertEqual(__, result) + self.assertEqual('always run', result) diff --git a/python 3/koans/about_extra_credit.py b/python3/koans/about_extra_credit.py similarity index 100% rename from python 3/koans/about_extra_credit.py rename to python3/koans/about_extra_credit.py diff --git a/python 2/koans/about_generators.py b/python3/koans/about_generators.py similarity index 79% rename from python 2/koans/about_generators.py rename to python3/koans/about_generators.py index 13f798bd8..e0ac81517 100644 --- a/python 2/koans/about_generators.py +++ b/python3/koans/about_generators.py @@ -15,9 +15,11 @@ class AboutGenerators(Koan): def test_generating_values_on_the_fly(self): result = list() bacon_generator = (n + ' bacon' for n in ['crunchy','veggie','danish']) + for bacon in bacon_generator: result.append(bacon) - self.assertEqual(__, result) + + self.assertEqual(['crunchy bacon', 'veggie bacon', 'danish bacon'], result) def test_generators_are_different_to_list_comprehensions(self): num_list = [x*2 for x in range(1,3)] @@ -26,8 +28,9 @@ def test_generators_are_different_to_list_comprehensions(self): self.assertEqual(2, num_list[0]) # A generator has to be iterated through. - self.assertRaises(___, num_generator, [0]) # Evaluates num_generator[0] - self.assertEqual(__, list(num_generator)[0]) # This works though + with self.assertRaises(TypeError): num = num_generator[0] + + self.assertEqual(2, list(num_generator)[0]) # This works though # Both list comprehensions and generators can be iterated though. However, a generator # function is only called on the first iteration. The values are generated on the fly @@ -41,8 +44,8 @@ def test_generator_expressions_are_a_one_shot_deal(self): attempt1 = list(dynamite) attempt2 = list(dynamite) - self.assertEqual(__, list(attempt1)) - self.assertEqual(__, list(attempt2)) + self.assertEqual(['Boom!', 'Boom!', 'Boom!'], list(attempt1)) + self.assertEqual([], list(attempt2)) # ------------------------------------------------------------------ @@ -56,12 +59,12 @@ def test_generator_method_will_yield_values_during_iteration(self): result = list() for item in self.simple_generator_method(): result.append(item) - self.assertEqual(__, result) + self.assertEqual(['peanut', 'butter', 'and', 'jelly'], result) def test_coroutines_can_take_arguments(self): result = self.simple_generator_method() - self.assertEqual(__, next(result)) - self.assertEqual(__, next(result)) + self.assertEqual('peanut', next(result)) + self.assertEqual('butter', next(result)) result.close() # ------------------------------------------------------------------ @@ -72,7 +75,7 @@ def cube_me(self, seq): def test_generator_method_with_parameter(self): result = self.cube_me(range(2,5)) - self.assertEqual(__, list(result)) + self.assertEqual([4, 9, 16], list(result)) # ------------------------------------------------------------------ @@ -85,7 +88,7 @@ def sum_it(self, seq): def test_generator_keeps_track_of_local_variables(self): result = self.sum_it(range(2,5)) - self.assertEqual(__, list(result)) + self.assertEqual([9], list(result)) # ------------------------------------------------------------------ @@ -94,9 +97,9 @@ def generator_with_coroutine(self): yield result def test_generators_can_take_coroutines(self): - generator = self.generator_with_coroutine() - next(generator) - self.assertEqual(__, generator.send(1 + 2)) + generater = self.generator_with_coroutine() + next(generater) + self.assertEqual(3, generater.send(1 + 2)) # ------------------------------------------------------------------ @@ -110,10 +113,10 @@ def yield_tester(self): def test_generators_can_see_if_they_have_been_called_with_a_value(self): generator = self.yield_tester() next(generator) - self.assertEqual('with value', generator.send('with value')) + self.assertEqual('with value', generator.send('with value')) ##### generator2 = self.yield_tester() next(generator2) - self.assertEqual(__, next(generator2)) + self.assertEqual('no value', next(generator2)) \ No newline at end of file diff --git a/python 3/koans/about_inheritance.py b/python3/koans/about_inheritance.py similarity index 69% rename from python 3/koans/about_inheritance.py rename to python3/koans/about_inheritance.py index ac9cf0f62..aa04049fa 100644 --- a/python 3/koans/about_inheritance.py +++ b/python3/koans/about_inheritance.py @@ -23,42 +23,42 @@ def bark(self): return "yip" def test_subclasses_have_the_parent_as_an_ancestor(self): - self.assertEqual(__, issubclass(self.Chihuahua, self.Dog)) + self.assertEqual(True, issubclass(self.Chihuahua, self.Dog)) - def test_this_all_classes_in_python_3_ultimately_inherit_from_object_class(self): - self.assertEqual(__, issubclass(self.Chihuahua, object)) + def test_this_all_classes_in_python_3_ultimately_inherits_from_object_class(self): + self.assertEqual(True, issubclass(self.Chihuahua, object)) # Note: This isn't the case in Python 2. In that version you have - # to inherit from a built in class or object explicitly + # to inherit from a built in class, or object explictly def test_instances_inherit_behavior_from_parent_class(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.name) + self.assertEqual('Chico', chico.name) def test_subclasses_add_new_behavior(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.wag()) + self.assertEqual('happy', chico.wag()) fido = self.Dog("Fido") - with self.assertRaises(___): fido.wag() + with self.assertRaises(AttributeError): fido.wag() def test_subclasses_can_modify_existing_behavior(self): chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.bark()) + self.assertEqual('yip', chico.bark()) fido = self.Dog("Fido") - self.assertEqual(__, fido.bark()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ class BullDog(Dog): def bark(self): return super().bark() + ", GRR" - # Note, super() is much simpler to use in Python 3! + # Note, super() is much simpler to use it Python 3! def test_subclasses_can_invoke_parent_behavior_via_super(self): ralph = self.BullDog("Ralph") - self.assertEqual(__, ralph.bark()) + self.assertEqual('WOOF, GRR', ralph.bark()) # ------------------------------------------------------------------ @@ -66,9 +66,9 @@ class GreatDane(Dog): def growl(self): return super().bark() + ", GROWL" - def test_super_works_across_methods(self): + def test_super_does_work_across_methods(self): george = self.GreatDane("George") - self.assertEqual(__, george.growl()) + self.assertEqual('WOOF, GROWL', george.growl()) # --------------------------------------------------------- @@ -82,8 +82,8 @@ def __init__(self, name): def test_base_init_does_not_get_called_automatically(self): snoopy = self.Pug("Snoopy") - with self.assertRaises(___): name = snoopy.name + with self.assertRaises(AttributeError): name = snoopy.name def test_base_init_has_to_be_called_explicitly(self): boxer = self.Greyhound("Boxer") - self.assertEqual(__, boxer.name) \ No newline at end of file + self.assertEqual('Boxer', boxer.name) \ No newline at end of file diff --git a/python 3/koans/about_iteration.py b/python3/koans/about_iteration.py similarity index 77% rename from python 3/koans/about_iteration.py rename to python3/koans/about_iteration.py index aa7b2e988..775269ab5 100644 --- a/python 3/koans/about_iteration.py +++ b/python3/koans/about_iteration.py @@ -13,20 +13,20 @@ def test_iterators_are_a_type(self): for num in it: fib += num - self.assertEqual(__ , fib) + self.assertEqual(15 , fib) def test_iterating_with_next(self): stages = iter(['alpha','beta','gamma']) try: - self.assertEqual(__, next(stages)) + self.assertEqual('alpha', next(stages)) next(stages) - self.assertEqual(__, next(stages)) + self.assertEqual('gamma', next(stages)) next(stages) except StopIteration as ex: err_msg = 'Ran out of iterations' - self.assertRegexpMatches(err_msg, __) + self.assertRegexpMatches(err_msg, 'Ran out of iterations') # ------------------------------------------------------------------ @@ -39,15 +39,15 @@ def test_map_transforms_elements_of_a_list(self): mapping = map(self.add_ten, seq) - self.assertNotEqual(list, type(mapping).__name__) - self.assertEqual(__, type(mapping).__name__) - # In Python 3 built in iterator funcs return iteratable view objects + self.assertNotEqual(list, type(mapping).__name__) ##### + self.assertEqual('map', type(mapping).__name__) + # In Python 3 built in iterator funcs return iterable view objects # instead of lists for item in mapping: mapped_seq.append(item) - self.assertEqual(__, mapped_seq) + self.assertEqual([11, 12, 13], mapped_seq) # None, iterator methods actually return objects of iter type in # python 3. In python 2 map() would give you a list. @@ -61,7 +61,7 @@ def is_even(item): return (item % 2) == 0 for item in filter(is_even, seq): even_numbers.append(item) - self.assertEqual(__, even_numbers) + self.assertEqual([2, 4, 6], even_numbers) def test_just_return_first_item_found(self): def is_big_name(item): return len(item) > 4 @@ -75,7 +75,7 @@ def is_big_name(item): return len(item) > 4 except StopIteration: msg = 'Ran out of big names' - self.assertEqual(__, name) + self.assertEqual('Clarence', name) # ------------------------------------------------------------------ @@ -92,13 +92,13 @@ def test_reduce_will_blow_your_mind(self): # to the functools module. result = functools.reduce(self.add, [2, 3, 4]) - self.assertEqual(__, type(result).__name__) + self.assertEqual('int', type(result).__name__) # Reduce() syntax is same as Python 2 - self.assertEqual(__, result) + self.assertEqual(9, result) result2 = functools.reduce(self.multiply, [2, 3, 4], 1) - self.assertEqual(__, result2) + self.assertEqual(24, result2) # Extra Credit: # Describe in your own words what reduce does. @@ -110,21 +110,21 @@ def test_creating_lists_with_list_comprehensions(self): comprehension = [delicacy.capitalize() for delicacy in feast] - self.assertEqual(__, comprehension[0]) - self.assertEqual(__, comprehension[2]) + self.assertEqual('Lambs', comprehension[0]) + self.assertEqual('Orangutans', comprehension[2]) def test_use_pass_for_iterations_with_no_body(self): for num in range(1,5): pass - self.assertEqual(__, num) + self.assertEqual(4, num) # ------------------------------------------------------------------ def test_all_iteration_methods_work_on_any_sequence_not_just_lists(self): - # Ranges are an iteratable sequence + # Ranges are an iterable sequence result = map(self.add_ten, range(1,4)) - self.assertEqual(__, list(result)) + self.assertEqual([11, 12, 13], list(result)) try: # Files act like a collection of lines @@ -132,7 +132,7 @@ def test_all_iteration_methods_work_on_any_sequence_not_just_lists(self): def make_upcase(line) : return line.strip().upper() upcase_lines = map(make_upcase, file.readlines()) - self.assertEqual(__, list(upcase_lines)) + self.assertEqual(['THIS', 'IS', 'A', 'TEST'], list(upcase_lines)) # NOTE: You can create your own collections that work with each, # map, select, etc. diff --git a/python 2/koans/about_lambdas.py b/python3/koans/about_lambdas.py similarity index 76% rename from python 2/koans/about_lambdas.py rename to python3/koans/about_lambdas.py index 2268e6c65..e848c05cf 100644 --- a/python 2/koans/about_lambdas.py +++ b/python3/koans/about_lambdas.py @@ -10,7 +10,7 @@ class AboutLambdas(Koan): def test_lambdas_can_be_assigned_to_variables_and_called_explicitly(self): add_one = lambda n: n + 1 - self.assertEqual(__, add_one(10)) + self.assertEqual(11, add_one(10)) # ------------------------------------------------------------------ @@ -21,8 +21,8 @@ def test_accessing_lambda_via_assignment(self): sausages = self.make_order('sausage') eggs = self.make_order('egg') - self.assertEqual(__, sausages(3)) - self.assertEqual(__, eggs(2)) + self.assertEqual('3 sausages', sausages(3)) + self.assertEqual('2 eggs', eggs(2)) def test_accessing_lambda_without_assignment(self): - self.assertEqual(__, self.make_order('spam')(39823)) + self.assertEqual('39823 spams', self.make_order('spam')(39823)) diff --git a/python 3/koans/about_list_assignments.py b/python3/koans/about_list_assignments.py similarity index 62% rename from python 3/koans/about_list_assignments.py rename to python3/koans/about_list_assignments.py index 2c8e8639c..642f04c4e 100644 --- a/python 3/koans/about_list_assignments.py +++ b/python3/koans/about_list_assignments.py @@ -10,28 +10,28 @@ class AboutListAssignments(Koan): def test_non_parallel_assignment(self): names = ["John", "Smith"] - self.assertEqual(__, names) + self.assertEqual(["John", "Smith"], names) def test_parallel_assignments(self): first_name, last_name = ["John", "Smith"] - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual("John", first_name) + self.assertEqual("Smith", last_name) def test_parallel_assignments_with_extra_values(self): title, *first_names, last_name = ["Sir", "Ricky", "Bobby", "Worthington"] - self.assertEqual(__, title) - self.assertEqual(__, first_names) - self.assertEqual(__, last_name) + self.assertEqual("Sir", title) + self.assertEqual(["Ricky", "Bobby"], first_names) + self.assertEqual("Worthington", last_name) def test_parallel_assignments_with_sublists(self): first_name, last_name = [["Willie", "Rae"], "Johnson"] - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual(["Willie", "Rae"], first_name) + self.assertEqual("Johnson", last_name) def test_swapping_with_parallel_assignment(self): first_name = "Roy" last_name = "Rob" first_name, last_name = last_name, first_name - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) + self.assertEqual("Rob", first_name) + self.assertEqual("Roy", last_name) diff --git a/python3/koans/about_lists.py b/python3/koans/about_lists.py new file mode 100644 index 000000000..e2c8d2df5 --- /dev/null +++ b/python3/koans/about_lists.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Based on AboutArrays in the Ruby Koans +# + +from runner.koan import * + +class AboutLists(Koan): + def test_creating_lists(self): + empty_list = list() + self.assertEqual(list, type(empty_list)) ##### + self.assertEqual(0, len(empty_list)) + + def test_list_literals(self): + nums = list() + self.assertEqual([], nums) + + nums[0:] = [1] + self.assertEqual([1], nums) + + nums[1:] = [2] + #####self.assertListEqual([1, __], nums) + self.assertListEqual([1, 2], nums) + + nums.append(333) + #self.assertListEqual([1, 2, __], nums) + self.assertListEqual([1, 2, 333], nums) + + def test_accessing_list_elements(self): + noms = ['peanut', 'butter', 'and', 'jelly'] + + self.assertEqual('peanut', noms[0]) + self.assertEqual('jelly', noms[3]) + self.assertEqual('jelly', noms[-1]) + self.assertEqual('butter', noms[-3]) + + def test_slicing_lists(self): + noms = ['peanut', 'butter', 'and', 'jelly'] + + self.assertEqual(['peanut'], noms[0:1]) + self.assertEqual(['peanut', 'butter'], noms[0:2]) + self.assertEqual([], noms[2:2]) + self.assertEqual(['and', 'jelly'], noms[2:20]) + self.assertEqual([], noms[4:0]) + self.assertEqual([], noms[4:100]) + self.assertEqual([], noms[5:0]) + + def test_slicing_to_the_edge(self): + noms = ['peanut', 'butter', 'and', 'jelly'] + + self.assertEqual(['and', 'jelly'], noms[2:]) + self.assertEqual(['peanut', 'butter'], noms[:2]) + + def test_lists_and_ranges(self): + self.assertEqual(range, type(range(5))) ##### + self.assertNotEqual([1, 2, 3, 4, 5], range(1,6)) ##### + self.assertEqual([0, 1, 2, 3, 4], list(range(5))) + self.assertEqual([5, 6, 7, 8], list(range(5, 9))) + + def test_ranges_with_steps(self): + self.assertEqual([0, 2, 4, 6], list(range(0, 8, 2))) + self.assertEqual([1, 4, 7], list(range(1, 8, 3))) + self.assertEqual([5, 1, -3], list(range(5, -7, -4))) + self.assertEqual([5, 1, -3, -7], list(range(5, -8, -4))) + + def test_insertions(self): + knight = ['you', 'shall', 'pass'] + knight.insert(2, 'not') + self.assertEqual(['you', 'shall', 'not', 'pass'], knight) + + knight.insert(0, 'Arthur') + self.assertEqual(['Arthur', 'you', 'shall', 'not', 'pass'], knight) + + def test_popping_lists(self): + stack = [10, 20, 30] + stack.append('last') + + self.assertEqual([10, 20, 30, 'last'], stack) + + popped_value = stack.pop() + self.assertEqual('last', popped_value) + self.assertEqual([10, 20, 30], stack) + + popped_value = stack.pop(1) + self.assertEqual(20, popped_value) + self.assertEqual([10, 30], stack) + + # Notice that there is a "pop" but no "push" in python? + + # Part of the Python philosophy is that there ideally should be one and + # only one way of doing anything. A 'push' is the same as an 'append'. + + # To learn more about this try typing "import this" from the python + # console... ;) + + def test_making_queues(self): + queue = [1, 2] + queue.append('last') + + self.assertEqual([1, 2, 'last'], queue) + + popped_value = queue.pop(0) + self.assertEqual(1, popped_value) + self.assertEqual([2, 'last'], queue) + + # Note, for Python 2 popping from the left hand side of a list is + # inefficient. Use collections.deque instead. + + # This is not as issue for Python 3 through + diff --git a/python 3/koans/about_method_bindings.py b/python3/koans/about_method_bindings.py similarity index 62% rename from python 3/koans/about_method_bindings.py rename to python3/koans/about_method_bindings.py index 45f09177b..bad31f809 100644 --- a/python 3/koans/about_method_bindings.py +++ b/python3/koans/about_method_bindings.py @@ -11,47 +11,47 @@ def function2(): class Class: def method(self): - return "parrot" + return "parrot" class AboutMethodBindings(Koan): - def test_methods_are_bound_to_an_object(self): + def test_methods_are_bound_to_an_object(self): obj = Class() - self.assertEqual(__, obj.method.__self__ == obj) + self.assertEqual(True, obj.method.__self__ == obj) def test_methods_are_also_bound_to_a_function(self): obj = Class() - self.assertEqual(__, obj.method()) - self.assertEqual(__, obj.method.__func__(obj)) - + self.assertEqual('parrot', obj.method()) + self.assertEqual('parrot', obj.method.__func__(obj)) + def test_functions_have_attributes(self): obj = Class() - self.assertEqual(__, len(dir(function))) - self.assertEqual(__, dir(function) == dir(obj.method.__func__)) - + self.assertEqual(34, len(dir(function))) + self.assertEqual(True, dir(function) == dir(obj.method.__func__)) + def test_methods_have_different_attributes(self): obj = Class() - self.assertEqual(__, len(dir(obj.method))) + self.assertEqual(26, len(dir(obj.method))) def test_setting_attributes_on_an_unbound_function(self): function.cherries = 3 - self.assertEqual(__, function.cherries) + self.assertEqual(3, function.cherries) def test_setting_attributes_on_a_bound_method_directly(self): obj = Class() - with self.assertRaises(___): obj.method.cherries = 3 - + with self.assertRaises(AttributeError): obj.method.cherries = 3 + def test_setting_attributes_on_methods_by_accessing_the_inner_function(self): obj = Class() obj.method.__func__.cherries = 3 - self.assertEqual(__, obj.method.cherries) - + self.assertEqual(3, obj.method.cherries) + def test_functions_can_have_inner_functions(self): function2.get_fruit = function - self.assertEqual(__, function2.get_fruit()) + self.assertEqual('pineapple', function2.get_fruit()) def test_inner_functions_are_unbound(self): function2.get_fruit = function - with self.assertRaises(___): cls = function2.get_fruit.__self__ + with self.assertRaises(AttributeError): cls = function2.get_fruit.__self__ # ------------------------------------------------------------------ @@ -60,7 +60,7 @@ def __get__(self, obj, cls): return (self, obj, cls) binding = BoundClass() - + def test_get_descriptor_resolves_attribute_binding(self): bound_obj, binding_owner, owner_type = self.binding # Look at BoundClass.__get__(): @@ -68,9 +68,9 @@ def test_get_descriptor_resolves_attribute_binding(self): # binding_owner = obj # owner_type = cls - self.assertEqual(__, type(bound_obj).__name__) - self.assertEqual(__, type(binding_owner).__name__) - self.assertEqual(AboutMethodBindings, owner_type) + self.assertEqual('BoundClass', type(bound_obj).__name__) + self.assertEqual('AboutMethodBindings', type(binding_owner).__name__) + self.assertEqual(AboutMethodBindings, owner_type) ##### # ------------------------------------------------------------------ @@ -82,9 +82,9 @@ def __set__(self, obj, val): self.choice = val color = SuperColor() - + def test_set_descriptor_changes_behavior_of_attribute_assignment_changes(self): - self.assertEqual(None, self.color.choice) + self.assertEqual(None, self.color.choice) ##### self.color = 'purple' - self.assertEqual(__, self.color.choice) - + self.assertEqual('purple', self.color.choice) + diff --git a/python 3/koans/about_methods.py b/python3/koans/about_methods.py similarity index 70% rename from python 3/koans/about_methods.py rename to python3/koans/about_methods.py index 0bd6ed238..8f8ef8934 100644 --- a/python 3/koans/about_methods.py +++ b/python3/koans/about_methods.py @@ -12,8 +12,8 @@ def my_global_function(a,b): class AboutMethods(Koan): def test_calling_a_global_function(self): - self.assertEqual(__, my_global_function(2,3)) - + self.assertEqual(5, my_global_function(2,3)) + # NOTE: Wrong number of arguments is not a SYNTAX error, but a # runtime error. def test_calling_functions_with_wrong_number_of_arguments(self): @@ -22,36 +22,34 @@ def test_calling_functions_with_wrong_number_of_arguments(self): except TypeError as exception: msg = exception.args[0] - self.assertRegexpMatches(msg, - r'my_global_function\(\) takes exactly 2 positional ' + - r'arguments \(0 given\)') - + self.assertRegexpMatches(msg, r'my_global_function\(\) missing 2 required positional arguments') ##### + try: my_global_function(1, 2, 3) except Exception as e: msg = e.args[0] - + # Note, watch out for parenthesis. They need slashes in front! - self.assertRegexpMatches(msg, __) - + self.assertRegexpMatches(msg, r'my_global_function\(\) takes 2 positional arguments') + # ------------------------------------------------------------------ - + def pointless_method(self, a, b): sum = a + b def test_which_does_not_return_anything(self): - self.assertEqual(__, self.pointless_method(1, 2)) + self.assertEqual(None, self.pointless_method(1, 2)) # Notice that methods accessed from class scope do not require # you to pass the first "self" argument? - + # ------------------------------------------------------------------ def method_with_defaults(self, a, b='default_value'): return [a, b] def test_calling_with_default_values(self): - self.assertEqual(__, self.method_with_defaults(1)) - self.assertEqual(__, self.method_with_defaults(1, 2)) + self.assertEqual([1, 'default_value'], self.method_with_defaults(1)) + self.assertEqual([1, 2], self.method_with_defaults(1, 2)) # ------------------------------------------------------------------ @@ -59,42 +57,42 @@ def method_with_var_args(self, *args): return args def test_calling_with_variable_arguments(self): - self.assertEqual(__, self.method_with_var_args()) + self.assertEqual((), self.method_with_var_args()) self.assertEqual(('one',), self.method_with_var_args('one')) - self.assertEqual(__, self.method_with_var_args('one', 'two')) + self.assertEqual(('one', 'two'), self.method_with_var_args('one', 'two')) # ------------------------------------------------------------------ def function_with_the_same_name(self, a, b): return a + b - + def test_functions_without_self_arg_are_global_functions(self): def function_with_the_same_name(a, b): return a * b - self.assertEqual(__, function_with_the_same_name(3,4)) - + self.assertEqual(12, function_with_the_same_name(3,4)) + def test_calling_methods_in_same_class_with_explicit_receiver(self): def function_with_the_same_name(a, b): return a * b - self.assertEqual(__, self.function_with_the_same_name(3,4)) + self.assertEqual(7, self.function_with_the_same_name(3,4)) # ------------------------------------------------------------------ def another_method_with_the_same_name(self): return 10 - + link_to_overlapped_method = another_method_with_the_same_name - + def another_method_with_the_same_name(self): return 42 def test_that_old_methods_are_hidden_by_redefinitions(self): - self.assertEqual(__, self.another_method_with_the_same_name()) + self.assertEqual(42, self.another_method_with_the_same_name()) def test_that_overlapped_method_is_still_there(self): - self.assertEqual(__, self.link_to_overlapped_method()) + self.assertEqual(10, self.link_to_overlapped_method()) # ------------------------------------------------------------------ @@ -102,37 +100,38 @@ def empty_method(self): pass def test_methods_that_do_nothing_need_to_use_pass_as_a_filler(self): - self.assertEqual(__, self.empty_method()) + self.assertEqual(None, self.empty_method()) def test_pass_does_nothing_at_all(self): "You" "shall" "not" pass - self.assertEqual(____, "Still got to this line" != None) + #####self.assertEqual(____, "Still got to this line" != None) + self.assertEqual(True, "Still got to this line" != None) # ------------------------------------------------------------------ def one_line_method(self): return 'Madagascar' - + def test_no_indentation_required_for_one_line_statement_bodies(self): - self.assertEqual(__, self.one_line_method()) + self.assertEqual('Madagascar', self.one_line_method()) # ------------------------------------------------------------------ def method_with_documentation(self): - "A string placed at the beginning of a function is used for documentation" + "A string placed at the beginning of a funtion is used for documentation" return "ok" - + def test_the_documentation_can_be_viewed_with_the_doc_method(self): - self.assertRegexpMatches(self.method_with_documentation.__doc__, __) + self.assertRegexpMatches(self.method_with_documentation.__doc__, 'A string placed at the beginning of a funtion is used for documentation') # ------------------------------------------------------------------ - class Dog: + class Dog: def name(self): return "Fido" - + def _tail(self): # Prefixing a method with an underscore implies private scope return "wagging" @@ -140,23 +139,23 @@ def _tail(self): def __password(self): return 'password' # Genius! - def test_calling_methods_in_other_objects(self): + def test_calling_methods_in_other_objects_require_explicit_receiver(self): rover = self.Dog() - self.assertEqual(__, rover.name()) - + self.assertEqual('Fido', rover.name()) + def test_private_access_is_implied_but_not_enforced(self): rover = self.Dog() # This is a little rude, but legal - self.assertEqual(__, rover._tail()) + self.assertEqual('wagging', rover._tail()) def test_attributes_with_double_underscore_prefixes_are_subject_to_name_mangling(self): rover = self.Dog() - with self.assertRaises(___): password = __password() - + with self.assertRaises(NameError): password = __password() + # But this still is! - self.assertEqual(__, rover._Dog__password()) - + self.assertEqual('password', rover._Dog__password()) + # Name mangling exists to avoid name clash issues when subclassing. # It is not for providing effective access protection diff --git a/python 3/koans/about_modules.py b/python3/koans/about_modules.py similarity index 76% rename from python 3/koans/about_modules.py rename to python3/koans/about_modules.py index 4974fa245..f0a864993 100644 --- a/python 3/koans/about_modules.py +++ b/python3/koans/about_modules.py @@ -16,21 +16,21 @@ def test_importing_other_python_scripts_as_modules(self): from . import local_module # local_module.py duck = local_module.Duck() - self.assertEqual(__, duck.name) + self.assertEqual('Daffy', duck.name) def test_importing_attributes_from_classes_using_from_keyword(self): from .local_module import Duck duck = Duck() # no module qualifier needed this time - self.assertEqual(__, duck.name) + self.assertEqual('Daffy', duck.name) def test_we_can_import_multiple_items_at_once(self): from . import jims, joes jims_dog = jims.Dog() joes_dog = joes.Dog() - self.assertEqual(__, jims_dog.identify()) - self.assertEqual(__, joes_dog.identify()) + self.assertEqual('jims dog', jims_dog.identify()) + self.assertEqual('joes dog', joes_dog.identify()) def test_importing_all_module_attributes_at_once(self): # NOTE Using this module level import declared at the top of this script: @@ -41,17 +41,17 @@ def test_importing_all_module_attributes_at_once(self): goose = Goose() hamster = Hamster() - self.assertEqual(__, goose.name) - self.assertEqual(__, hamster.name) + self.assertEqual('Mr Stabby', goose.name) + self.assertEqual('Phil', hamster.name) def test_modules_hide_attributes_prefixed_by_underscores(self): - with self.assertRaises(___): private_squirrel = _SecretSquirrel() + with self.assertRaises(NameError): private_squirrel = _SecretSquirrel() def test_private_attributes_are_still_accessible_in_modules(self): from .local_module import Duck # local_module.py duck = Duck() - self.assertEqual(__, duck._password) + self.assertEqual('password', duck._password) # module level attribute hiding doesn't affect class attributes # (unless the class itself is hidden). @@ -61,11 +61,11 @@ def test_a_module_can_choose_which_attributes_are_available_to_wildcards(self): # 'Goat' is on the __ALL__ list goat = Goat() - self.assertEqual(__, goat.name) + self.assertEqual('George', goat.name) # How about velociraptors? lizard = _Velociraptor() - self.assertEqual(__, lizard.name) + self.assertEqual('Cuddles', lizard.name) # SecretDuck? Never heard of her! - with self.assertRaises(___): duck = SecretDuck() + with self.assertRaises(NameError): duck = SecretDuck() diff --git a/python 3/koans/about_monkey_patching.py b/python3/koans/about_monkey_patching.py similarity index 77% rename from python 3/koans/about_monkey_patching.py rename to python3/koans/about_monkey_patching.py index 40dd5598c..774801981 100644 --- a/python 3/koans/about_monkey_patching.py +++ b/python3/koans/about_monkey_patching.py @@ -14,7 +14,7 @@ def bark(self): def test_as_defined_dogs_do_bark(self): fido = self.Dog() - self.assertEqual(__, fido.bark()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ @@ -24,8 +24,8 @@ def wag(self): return "HAPPY" self.Dog.wag = wag fido = self.Dog() - self.assertEqual(__, fido.wag()) - self.assertEqual(__, fido.bark()) + self.assertEqual('HAPPY', fido.wag()) + self.assertEqual('WOOF', fido.bark()) # ------------------------------------------------------------------ @@ -35,7 +35,7 @@ def test_most_built_in_classes_cannot_be_monkey_patched(self): except Exception as ex: err_msg = ex.args[0] - self.assertRegexpMatches(err_msg, __) + self.assertRegexpMatches(err_msg, "can't set attributes of built-in") # ------------------------------------------------------------------ @@ -44,7 +44,7 @@ class MyInt(int): pass def test_subclasses_of_built_in_classes_can_be_be_monkey_patched(self): self.MyInt.is_even = lambda self: (self % 2) == 0 - self.assertEqual(__, self.MyInt(1).is_even()) - self.assertEqual(__, self.MyInt(2).is_even()) + self.assertEqual(False, self.MyInt(1).is_even()) + self.assertEqual(True, self.MyInt(2).is_even()) \ No newline at end of file diff --git a/python 3/koans/about_multiple_inheritance.py b/python3/koans/about_multiple_inheritance.py similarity index 78% rename from python 3/koans/about_multiple_inheritance.py rename to python3/koans/about_multiple_inheritance.py index 25e8021ff..299a40ec0 100644 --- a/python 3/koans/about_multiple_inheritance.py +++ b/python3/koans/about_multiple_inheritance.py @@ -87,7 +87,7 @@ def here(self): def test_normal_methods_are_available_in_the_object(self): jeff = self.Spiderpig() - self.assertRegexpMatches(jeff.speak(), __) + self.assertRegexpMatches(jeff.speak(), 'This looks like a job for Spiderpig!') def test_base_class_methods_are_also_available_in_the_object(self): jeff = self.Spiderpig() @@ -95,44 +95,44 @@ def test_base_class_methods_are_also_available_in_the_object(self): jeff.set_name("Rover") except: self.fail("This should not happen") - self.assertEqual(__, jeff.can_climb_walls()) + self.assertEqual(True, jeff.can_climb_walls()) def test_base_class_methods_can_affect_instance_variables_in_the_object(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.name) + self.assertEqual('Jeff', jeff.name) jeff.set_name("Rover") - self.assertEqual(__, jeff.name) + self.assertEqual('Rover', jeff.name) - def test_left_hand_side_inheritance_tends_to_be_higher_priority(self): + def test_left_side_inheritence_methods_are_higher_priority_than_right_hand_side_siblings(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.color()) + self.assertEqual('pink', jeff.color()) def test_super_class_methods_are_higher_priority_than_super_super_classes(self): jeff = self.Spiderpig() - self.assertEqual(__, jeff.legs()) + self.assertEqual(8, jeff.legs()) def test_we_can_inspect_the_method_resolution_order(self): # # MRO = Method Resolution Order # mro = type(self.Spiderpig()).mro() - self.assertEqual('Spiderpig', mro[0].__name__) - self.assertEqual('Pig', mro[1].__name__) - self.assertEqual(__, mro[2].__name__) - self.assertEqual(__, mro[3].__name__) - self.assertEqual(__, mro[4].__name__) - self.assertEqual(__, mro[5].__name__) + self.assertEqual('Spiderpig', mro[0].__name__) ##### + self.assertEqual('Pig', mro[1].__name__) ##### + self.assertEqual('Spider', mro[2].__name__) + self.assertEqual('Animal', mro[3].__name__) + self.assertEqual('Nameable', mro[4].__name__) + self.assertEqual('object', mro[5].__name__) def test_confirm_the_mro_controls_the_calling_order(self): jeff = self.Spiderpig() - self.assertRegexpMatches(jeff.here(), 'Spiderpig') + self.assertRegexpMatches(jeff.here(), 'Spiderpig') ## next = super(AboutMultipleInheritance.Spiderpig, jeff) - self.assertRegexpMatches(next.here(), 'Pig') + self.assertRegexpMatches(next.here(), 'Pig') ## next = super(AboutMultipleInheritance.Pig, jeff) - self.assertRegexpMatches(next.here(), __) + self.assertRegexpMatches(next.here(), 'Spider') # Hang on a minute?!? That last class name might be a super class of # the 'jeff' object, but its hardly a superclass of Pig, is it? diff --git a/python 3/koans/about_none.py b/python3/koans/about_none.py similarity index 70% rename from python 3/koans/about_none.py rename to python3/koans/about_none.py index 138876fa5..043c996be 100644 --- a/python 3/koans/about_none.py +++ b/python3/koans/about_none.py @@ -11,11 +11,11 @@ class AboutNone(Koan): def test_none_is_an_object(self): "Unlike NULL in a lot of languages" - self.assertEqual(__, isinstance(None, object)) + self.assertEqual(True, isinstance(None, object)) def test_none_is_universal(self): "There is only one None" - self.assertEqual(____, None is None) + self.assertEqual(True, None is None) def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self): """ @@ -27,22 +27,22 @@ def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self Don't worry about what 'try' and 'catch' do, we'll talk about this later """ try: - None.some_method_none_does_not_know_about() + None.some_method_none_doesnt_know_about() except Exception as ex: ex2 = ex # What exception has been caught? - self.assertEqual(__, ex2.__class__.__name__) + self.assertEqual('AttributeError', ex2.__class__.__name__) # What message was attached to the exception? # (HINT: replace __ with part of the error message.) - self.assertRegexpMatches(ex2.args[0], __) + self.assertRegexpMatches(ex2.args[0], "'NoneType' object has no attribute 'some_method_none_doesnt_know_about'") def test_none_is_distinct(self): """ None is distinct from other things which are False. """ - self.assertEqual(__, None is not 0) - self.assertEqual(__, None is not False) + self.assertEqual(True, None is not 0) + self.assertEqual(True, None is not False) diff --git a/python 3/koans/about_packages.py b/python3/koans/about_packages.py similarity index 80% rename from python 3/koans/about_packages.py rename to python3/koans/about_packages.py index 61f5c5fb5..72b3caf2c 100644 --- a/python 3/koans/about_packages.py +++ b/python3/koans/about_packages.py @@ -19,7 +19,7 @@ # about_attribute_access.py # about_class_attributes.py # about_classes.py -# ... +# ... # a_package_folder/ # __init__.py # a_module.py @@ -28,19 +28,15 @@ class AboutPackages(Koan): def test_subfolders_can_form_part_of_a_module_package(self): # Import ./a_package_folder/a_module.py from .a_package_folder.a_module import Duck - + duck = Duck() - self.assertEqual(__, duck.name) - + self.assertEqual('Donald', duck.name) + def test_subfolders_become_modules_if_they_have_an_init_module(self): # Import ./a_package_folder/__init__.py from .a_package_folder import an_attribute - - self.assertEqual(__, an_attribute) - - def test_subfolders_without_an_init_module_are_not_part_of_the_package(self): - # Import ./a_normal_folder/ - with self.assertRaises(___): from . import a_normal_folder + + self.assertEqual(1984, an_attribute) # ------------------------------------------------------------------ @@ -48,8 +44,8 @@ def test_use_absolute_imports_to_import_upper_level_modules(self): # Import /contemplate_koans.py import contemplate_koans - self.assertEqual(__, contemplate_koans.__name__) - + self.assertEqual('contemplate_koans', contemplate_koans.__name__) + # contemplate_koans.py is the root module in this package because its # the first python module called in koans. # @@ -62,4 +58,4 @@ def test_import_a_module_in_a_subfolder_folder_using_an_absolute_path(self): # Import contemplate_koans.py/koans/a_package_folder/a_module.py from koans.a_package_folder.a_module import Duck - self.assertEqual(__, Duck.__module__) + self.assertEqual('koans.a_package_folder.a_module', Duck.__module__) diff --git a/python 3/koans/about_proxy_object_project.py b/python3/koans/about_proxy_object_project.py similarity index 72% rename from python 3/koans/about_proxy_object_project.py rename to python3/koans/about_proxy_object_project.py index e1dd6f024..312787146 100644 --- a/python 3/koans/about_proxy_object_project.py +++ b/python3/koans/about_proxy_object_project.py @@ -18,14 +18,49 @@ from runner.koan import * -class Proxy: +class Proxy(object): def __init__(self, target_object): - # WRITE CODE HERE + # Write your attribute initialisations here + self._messages = list() - #initialize '_obj' attribute last. Trust me on this! + #initialize attribute this last. Trust me! self._obj = target_object + + # WRITE CODE HERE + def __getattr__(self, name): + #print 'Debug GET ' + type(self).__name__ + "." + name + " dict=" + str(self.__dict__) - # WRITE CODE HERE + attr = getattr(self._obj, name) + + self._messages.append(name) + return attr + + def __setattr__(self, name, value): + #print 'Debug SET ' + type(self).__name__ + "." + name + "=" + str(value) + " __dict__=" + str(self.__dict__) + + if '_' == name[0]: + return object.__setattr__(self, name, value) + + setattr(self._obj, name, value) + + self._messages.append(name + '=') + + def messages(self): + return self._messages + + def was_called(self, attr): + return self.number_of_times_called(attr) > 0 + + def number_of_times_called(self, attr): + return len(list(filter(lambda msg: msg==attr , self._messages))) +#class Proxy: +# def __init__(self, target_object): +# # WRITE CODE HERE +# +# #initialize '_obj' attribute last. Trust me on this! +# self._obj = target_object +# +# # WRITE CODE HERE # The proxy object should pass the following Koan: # @@ -57,7 +92,7 @@ def test_proxy_handles_invalid_messages(self): tv = Proxy(Television()) ex = None - with self.assertRaises(AttributeError): + with self.assertRaises(AttributeError): ##### tv.no_such_method() @@ -82,15 +117,15 @@ def test_proxy_counts_method_calls(self): self.assertEqual(0, tv.number_of_times_called('is_on')) def test_proxy_can_record_more_than_just_tv_objects(self): - proxy = Proxy("Py Ohio 2010") + proxy = Proxy("Code Mash 2009") result = proxy.upper() - self.assertEqual("PY OHIO 2010", result) + self.assertEqual("CODE MASH 2009", result) result = proxy.split() - self.assertEqual(["Py", "Ohio", "2010"], result) + self.assertEqual(["Code", "Mash", "2009"], result) self.assertEqual(['upper', 'split'], proxy.messages()) # ==================================================================== diff --git a/python 3/koans/about_scope.py b/python3/koans/about_scope.py similarity index 73% rename from python 3/koans/about_scope.py rename to python3/koans/about_scope.py index b2d0d6614..fe9d74f68 100644 --- a/python 3/koans/about_scope.py +++ b/python3/koans/about_scope.py @@ -16,18 +16,18 @@ class AboutScope(Koan): # def test_dog_is_not_available_in_the_current_scope(self): - with self.assertRaises(___): fido = Dog() + with self.assertRaises(NameError): fido = Dog() def test_you_can_reference_nested_classes_using_the_scope_operator(self): fido = jims.Dog() # name 'jims' module name is taken from jim.py filename rover = joes.Dog() - self.assertEqual(__, fido.identify()) - self.assertEqual(__, rover.identify()) + self.assertEqual('jims dog', fido.identify()) + self.assertEqual('joes dog', rover.identify()) - self.assertEqual(__, type(fido) == type(rover)) - self.assertEqual(__, jims.Dog == joes.Dog) + self.assertEqual(False, type(fido) == type(rover)) + self.assertEqual(False, jims.Dog == joes.Dog) # ------------------------------------------------------------------ @@ -35,27 +35,29 @@ class str: pass def test_bare_bones_class_names_do_not_assume_the_current_scope(self): - self.assertEqual(__, AboutScope.str == str) + self.assertEqual(False, AboutScope.str == str) def test_nested_string_is_not_the_same_as_the_system_string(self): - self.assertEqual(__, self.str == type("HI")) + self.assertEqual(False, self.str == type("HI")) def test_str_without_self_prefix_stays_in_the_global_scope(self): - self.assertEqual(__, str == type("HI")) + self.assertEqual(True, str == type("HI")) # ------------------------------------------------------------------ PI = 3.1416 def test_constants_are_defined_with_an_initial_uppercase_letter(self): - self.assertAlmostEqual(_____, self.PI) + #####self.assertAlmostEqual(_____, self.PI) + self.assertAlmostEqual(3.1416, self.PI) # Note, floating point numbers in python are not precise. # assertAlmostEqual will check that it is 'close enough' def test_constants_are_assumed_by_convention_only(self): self.PI = "rhubarb" - self.assertEqual(_____, self.PI) - # There aren't any real constants in python. Its up to the developer + #####self.assertEqual(_____, self.PI) + self.assertEqual("rhubarb", self.PI) + # There aren't any real contants in python. Its up to the developer # to keep to the convention and not modify them. # ------------------------------------------------------------------ @@ -71,13 +73,13 @@ def test_incrementing_with_local_counter(self): global counter start = counter self.increment_using_local_counter(start) - self.assertEqual(__, counter == start + 1) + self.assertEqual(False, counter == start + 1) def test_incrementing_with_global_counter(self): global counter start = counter self.increment_using_global_counter() - self.assertEqual(__, counter == start + 1) + self.assertEqual(True, counter == start + 1) # ------------------------------------------------------------------ @@ -96,10 +98,10 @@ def from_the_boosh(): return from_the_boosh() def test_getting_something_locally(self): - self.assertEqual(__, self.local_access()) + self.assertEqual('this is a local shop for local people', self.local_access()) def test_getting_something_nonlocally(self): - self.assertEqual(__, self.nonlocal_access()) + self.assertEqual('eels', self.nonlocal_access()) # ------------------------------------------------------------------ @@ -107,5 +109,5 @@ def test_getting_something_nonlocally(self): deadly_bingo = [4, 8, 15, 16, 23, 42] def test_global_attributes_can_be_created_in_the_middle_of_a_class(self): - self.assertEqual(__, deadly_bingo[5]) + self.assertEqual(42, deadly_bingo[5]) \ No newline at end of file diff --git a/python 2/koans/about_scoring_project.py b/python3/koans/about_scoring_project.py similarity index 79% rename from python 2/koans/about_scoring_project.py rename to python3/koans/about_scoring_project.py index 938028cb9..55a0663ed 100644 --- a/python 2/koans/about_scoring_project.py +++ b/python3/koans/about_scoring_project.py @@ -33,8 +33,31 @@ # Your goal is to write the score method. def score(dice): - # You need to write this method - pass + score = 0 + + tallies = {}.fromkeys(set(dice), 0) + for die in dice: + tallies[die] += 1 + + if 1 in tallies and tallies[1] >= 3: + tallies[1] -= 3 + score += 1000 + + for k in tallies: + if tallies[k] >= 3: + tallies[k] -= 3 + score += k * 100 + + for k in tallies: + if k == 1: + score += 100 * tallies[k] + elif k== 5: + score += 50 * tallies[k] + + return score + +##### # You need to write this method +##### pass class AboutScoringProject(Koan): def test_score_of_an_empty_list_is_zero(self): diff --git a/python3/koans/about_sets.py b/python3/koans/about_sets.py new file mode 100644 index 000000000..81cdaddab --- /dev/null +++ b/python3/koans/about_sets.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + +class AboutSets(Koan): + def test_sets_make_keep_lists_unique(self): + highlanders = ['MacLeod', 'Ramirez', 'MacLeod', 'Matunas', 'MacLeod', 'Malcolm', 'MacLeod'] + + there_can_only_be_only_one = set(highlanders) + + self.assertEqual({'Malcolm', 'Matunas', 'MacLeod', 'Ramirez'}, there_can_only_be_only_one) + + def test_empty_sets_have_different_syntax_to_populated_sets(self): + self.assertEqual({1, 2, 3}, {1, 2, 3}) + self.assertEqual(set(), set()) + + def test_dictionaries_and_sets_use_same_curly_braces(self): + # Note: Sets have only started using braces since Python 3 + + self.assertEqual('set', type({1, 2, 3}).__name__) + self.assertEqual('dict', type({'one': 1, 'two': 2}).__name__) + + self.assertEqual('dict', type({}).__name__) + + def test_creating_sets_using_strings(self): + self.assertEqual({'12345'}, {'12345'}) + self.assertEqual({'1','3','2','5','4'}, set('12345')) + + def test_convert_the_set_into_a_list_to_sort_it(self): + self.assertEqual(['1','2','3','4','5'], sorted(set('12345'))) + + # ------------------------------------------------------------------ + + def chars_in(self, a_set): + return ''.join(sorted(a_set)) + + def test_set_have_arithmetic_operators(self): + good_guy = set('macleod') + bad_guy = set('mutunas') + + self.assertEqual('cdelo', self.chars_in( good_guy - bad_guy) ) + self.assertEqual('acdelmnostu', self.chars_in( good_guy | bad_guy )) + self.assertEqual('am', self.chars_in( good_guy & bad_guy )) + self.assertEqual('cdelnostu', self.chars_in( good_guy ^ bad_guy )) + + # ------------------------------------------------------------------ + + def test_we_can_query_set_membership(self): + self.assertEqual(True, 127 in {127, 0, 0, 1} ) + self.assertEqual(True, 'cow' not in set('apocalypse now') ) + + def test_we_can_compare_subsets(self): + self.assertEqual(True, set('cake') <= set('cherry cake')) + self.assertEqual(True, set('cake').issubset(set('cherry cake')) ) + + self.assertEqual(False, set('cake') > set('pie')) diff --git a/python 3/koans/about_strings.py b/python3/koans/about_strings.py similarity index 68% rename from python 3/koans/about_strings.py rename to python3/koans/about_strings.py index bfa0f7388..d8d92721a 100644 --- a/python 3/koans/about_strings.py +++ b/python3/koans/about_strings.py @@ -7,94 +7,94 @@ class AboutStrings(Koan): def test_double_quoted_strings_are_strings(self): string = "Hello, world." - self.assertEqual(__, isinstance(string, str)) + self.assertEqual(True, isinstance(string, str)) def test_single_quoted_strings_are_also_strings(self): string = 'Goodbye, world.' - self.assertEqual(__, isinstance(string, str)) + self.assertEqual(True, isinstance(string, str)) def test_triple_quote_strings_are_also_strings(self): string = """Howdy, world!""" - self.assertEqual(__, isinstance(string, str)) + self.assertEqual(True, isinstance(string, str)) def test_triple_single_quotes_work_too(self): string = '''Bonjour tout le monde!''' - self.assertEqual(__, isinstance(string, str)) + self.assertEqual(True, isinstance(string, str)) def test_use_single_quotes_to_create_string_with_double_quotes(self): string = 'He said, "Go Away."' - self.assertEqual(__, string) + self.assertEqual("He said, \"Go Away.\"", string) def test_use_double_quotes_to_create_strings_with_single_quotes(self): string = "Don't" - self.assertEqual(__, string) + self.assertEqual('Don\'t', string) def test_use_backslash_for_escaping_quotes_in_strings(self): a = "He said, \"Don't\"" b = 'He said, "Don\'t"' - self.assertEqual(__, (a == b)) + self.assertEqual(True, (a == b)) def test_use_backslash_at_the_end_of_a_line_to_continue_onto_the_next_line(self): string = "It was the best of times,\n\ It was the worst of times." - self.assertEqual(__, len(string)) + self.assertEqual(52, len(string)) def test_triple_quoted_strings_can_span_lines(self): string = """ Howdy, world! """ - self.assertEqual(__, len(string)) + self.assertEqual(15, len(string)) def test_triple_quoted_strings_need_less_escaping(self): a = "Hello \"world\"." b = """Hello "world".""" - self.assertEqual(__, (a == b)) + self.assertEqual(True, (a == b)) def but_you_still_have_to_be_careful_at_the_end_of_a_triple_quoted_string(self): string = """Hello "world\"""" def test_plus_concatenates_strings(self): string = "Hello, " + "world" - self.assertEqual(__, string) + self.assertEqual('Hello, world', string) def test_plus_will_not_modify_original_strings(self): hi = "Hello, " there = "world" string = hi + there - self.assertEqual(__, hi) - self.assertEqual(__, there) + self.assertEqual('Hello, ', hi) + self.assertEqual('world', there) def test_plus_equals_will_append_to_end_of_string(self): hi = "Hello, " there = "world" hi += there - self.assertEqual(__, hi) + self.assertEqual('Hello, world', hi) def test_plus_equals_also_leaves_original_string_unmodified(self): original = "Hello, " hi = original there = "world" hi += there - self.assertEqual(__, original) + self.assertEqual("Hello, ", original) def test_most_strings_interpret_escape_characters(self): string = "\n" - self.assertEqual('\n', string) - self.assertEqual("""\n""", string) - self.assertEqual(__, len(string)) + self.assertEqual('\n', string) ##### + self.assertEqual("""\n""", string) ##### + self.assertEqual(1, len(string)) def test_use_format_to_interpolate_variables(self): value1 = 'one' value2 = 2 string = "The values are {0} and {1}".format(value1, value2) - self.assertEqual(__, string) + self.assertEqual('The values are one and 2', string) def test_formatted_values_con_be_shown_in_any_order_or_be_repeated(self): value1 = 'doh' value2 = 'DOH' string = "The values are {1}, {0}, {0} and {1}!".format(value1, value2) - self.assertEqual(__, string) + self.assertEqual('The values are DOH, doh, doh and DOH!', string) def test_any_python_expression_may_be_interpolated(self): import math # import a standard python module with math functions @@ -102,24 +102,25 @@ def test_any_python_expression_may_be_interpolated(self): decimal_places = 4 string = "The square root of 5 is {0:.{1}f}".format(math.sqrt(5), \ decimal_places) - self.assertEqual(__, string) + self.assertEqual('The square root of 5 is 2.2361', string) def test_you_can_get_a_substring_from_a_string(self): string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[7:10]) + self.assertEqual('let', string[7:10]) def test_you_can_get_a_single_character_from_a_string(self): string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[1]) + self.assertEqual('a', string[1]) def test_single_characters_can_be_represented_by_integers(self): - self.assertEqual(__, ord('a')) - self.assertEqual(__, ord('b') == (ord('a') + 1)) + self.assertEqual(97, ord('a')) + self.assertEqual(True, ord('b') == (ord('a') + 1)) def test_strings_can_be_split(self): string = "Sausage Egg Cheese" words = string.split() - self.assertListEqual([__, __, __], words) + ####self.assertListEqual([__, __, __], words) + self.assertListEqual(['Sausage', 'Egg', 'Cheese'], words) def test_strings_can_be_split_with_different_patterns(self): import re #import python regular expression library @@ -129,18 +130,19 @@ def test_strings_can_be_split_with_different_patterns(self): words = pattern.split(string) - self.assertListEqual([__, __, __, __], words) + ##### self.assertListEqual([__, __, __, __], words) + self.assertListEqual(['the', 'rain', 'in', 'spain'], words) # Pattern is a Python regular expression pattern which matches ',' or ';' def test_regular_expression_strings_do_not_interpret_escape_characters(self): string = r'\n' - self.assertNotEqual('\n', string) - self.assertEqual(__, string) - self.assertEqual(__, len(string)) + self.assertNotEqual('\n', string) ##### + self.assertEqual('\\n', string) + self.assertEqual(2, len(string)) # This is to make patterns easier to read def test_strings_can_be_joined(self): words = ["Now", "is", "the", "time"] - self.assertEqual(__, ' '.join(words)) + self.assertEqual('Now is the time', ' '.join(words)) diff --git a/python 3/koans/about_triangle_project.py b/python3/koans/about_triangle_project.py similarity index 100% rename from python 3/koans/about_triangle_project.py rename to python3/koans/about_triangle_project.py diff --git a/python 3/koans/about_triangle_project2.py b/python3/koans/about_triangle_project2.py similarity index 100% rename from python 3/koans/about_triangle_project2.py rename to python3/koans/about_triangle_project2.py diff --git a/python3/koans/about_true_and_false.py b/python3/koans/about_true_and_false.py new file mode 100644 index 000000000..a11c7b2b3 --- /dev/null +++ b/python3/koans/about_true_and_false.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + +class AboutTrueAndFalse(Koan): + def truth_value(self, condition): + if condition: + return 'true stuff' + else: + return 'false stuff' + + def test_true_is_treated_as_true(self): + self.assertEqual('true stuff', self.truth_value(True)) + + def test_false_is_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(False)) + + def test_none_is_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(None)) + + def test_empty_collections_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value(0)) + + def test_empty_collections_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value([])) + self.assertEqual('false stuff', self.truth_value({})) + self.assertEqual('false stuff', self.truth_value(set())) + + def test_strings_are_treated_as_false(self): + self.assertEqual('false stuff', self.truth_value("")) + + def test_everthing_else_is_treated_as_true(self): + self.assertEqual('true stuff', self.truth_value(1)) + self.assertEqual('true stuff', self.truth_value("Python is named after Monty Python")) + self.assertEqual('true stuff', self.truth_value(' ')) diff --git a/python 3/koans/about_tuples.py b/python3/koans/about_tuples.py similarity index 55% rename from python 3/koans/about_tuples.py rename to python3/koans/about_tuples.py index cfb1ec1e2..27987a6d2 100644 --- a/python 3/koans/about_tuples.py +++ b/python3/koans/about_tuples.py @@ -6,9 +6,9 @@ class AboutTuples(Koan): def test_creating_a_tuple(self): count_of_three = (1, 2, 5) - self.assertEqual(5, __) + self.assertEqual(5, count_of_three[2]) - def test_tuples_are_immutable_so_item_assignment_is_not_possible(self): + def test_tuples_are_immutable_so_no_item_assignment_is_not_possible(self): count_of_three = (1, 2, 5) try: count_of_three[2] = "three" @@ -18,38 +18,38 @@ def test_tuples_are_immutable_so_item_assignment_is_not_possible(self): # Note, assertRegexpMatches() uses regular expression pattern matching, # so you don't have to copy the whole message. - self.assertRegexpMatches(msg, __) + self.assertRegexpMatches(msg, "'tuple' object does not support item assignment") - def test_tuples_are_immutable_so_appending_is_not_possible(self): + def test_tuples_are_immutable_so_no_appending_is_not_possible(self): count_of_three = (1, 2, 5) - with self.assertRaises(___): count_of_three.append("boom") + with self.assertRaises(AttributeError): count_of_three.append("boom") - # Tuples are less flexible than lists, but faster. + # Tuples are less flexible thon lists, but faster. - def test_tuples_can_only_be_changed_through_replacement(self): + def test_tuples_can_anly_be_changed_be_replacement(self): count_of_three = (1, 2, 5) list_count = list(count_of_three) list_count.append("boom") count_of_three = tuple(list_count) - self.assertEqual(__, count_of_three) + self.assertEqual((1, 2, 5, "boom"), count_of_three) def test_tuples_of_one_are_peculiar(self): - self.assertEqual(__, (1)) - self.assertEqual(__, ("Hello comma!", )) - self.assertEqual(__, (1,)) - self.assertEqual(__, tuple("Surprise!")) + self.assertEqual(1, (1)) + self.assertEqual(('Hello comma!',), ("Hello comma!", )) + self.assertEqual((1,), (1,)) + self.assertEqual(('S', 'u', 'r', 'p', 'r', 'i', 's', 'e', '!'), tuple("Surprise!")) def test_creating_empty_tuples(self): - self.assertEqual(__ , ()) - self.assertEqual(__ , tuple()) #Sometimes less confusing + self.assertEqual(() , ()) + self.assertEqual(() , tuple()) #Sometimes less confusing def test_tuples_can_be_embedded(self): lat = (37, 14, 6, 'N') lon = (115, 48, 40, 'W') place = ('Area 51', lat, lon) - self.assertEqual(__, place) + self.assertEqual(('Area 51', (37, 14, 6, 'N'), (115, 48, 40, 'W')), place) def test_tuples_are_good_for_representing_records(self): locations = [ @@ -59,8 +59,8 @@ def test_tuples_are_good_for_representing_records(self): locations.append( ("Cthulu", (26, 40, 1, 'N'), (70, 45, 7, 'W')) ) - self.assertEqual(__, locations[2][0]) - self.assertEqual(__, locations[0][1][2]) + self.assertEqual("Cthulu", locations[2][0]) + self.assertEqual(15.56, locations[0][1][2]) \ No newline at end of file diff --git a/python 3/koans/about_with_statements.py b/python3/koans/about_with_statements.py similarity index 81% rename from python 3/koans/about_with_statements.py rename to python3/koans/about_with_statements.py index 66f0a912e..2d7e6f265 100644 --- a/python 3/koans/about_with_statements.py +++ b/python3/koans/about_with_statements.py @@ -21,7 +21,7 @@ def count_lines(self, file_name): if file: file.close() def test_counting_lines(self): - self.assertEqual(__, self.count_lines("example_file.txt")) + self.assertEqual(4, self.count_lines("example_file.txt")) # ------------------------------------------------------------------ @@ -35,7 +35,7 @@ def find_line(self, file_name): if file: file.close() def test_finding_lines(self): - self.assertEqual(__, self.find_line("example_file.txt")) + self.assertEqual('test\n', self.find_line("example_file.txt")) ## ------------------------------------------------------------------ ## THINK ABOUT IT: @@ -82,17 +82,23 @@ def count_lines2(self, file_name): return count def test_counting_lines2(self): - self.assertEqual(__, self.count_lines2("example_file.txt")) + self.assertEqual(4, self.count_lines2("example_file.txt")) # ------------------------------------------------------------------ def find_line2(self, file_name): - # Rewrite find_line using the Context Manager. - pass + ##### # Rewrite find_line using the Context Manager. + + with self.FileContextManager(file_name) as file: + for line in file.readlines(): + match = re.search('e', line) + if match: return line + + ##### pass def test_finding_lines2(self): - self.assertEqual(__, self.find_line2("example_file.txt")) - self.assertNotEqual(__, self.find_line2("example_file.txt")) + self.assertEqual('test\n', self.find_line2("example_file.txt")) + self.assertNotEqual(None, self.find_line2("example_file.txt")) # ------------------------------------------------------------------ @@ -104,4 +110,4 @@ def count_lines3(self, file_name): return count def test_open_already_has_its_own_built_in_context_manager(self): - self.assertEqual(__, self.count_lines3("example_file.txt")) + self.assertEqual(4, self.count_lines3("example_file.txt")) diff --git a/python 3/koans/another_local_module.py b/python3/koans/another_local_module.py similarity index 100% rename from python 3/koans/another_local_module.py rename to python3/koans/another_local_module.py diff --git a/python 3/koans/jims.py b/python3/koans/jims.py similarity index 100% rename from python 3/koans/jims.py rename to python3/koans/jims.py diff --git a/python 3/koans/joes.py b/python3/koans/joes.py similarity index 100% rename from python 3/koans/joes.py rename to python3/koans/joes.py diff --git a/python 3/koans/local_module.py b/python3/koans/local_module.py similarity index 100% rename from python 3/koans/local_module.py rename to python3/koans/local_module.py diff --git a/python 3/koans/local_module_with_all_defined.py b/python3/koans/local_module_with_all_defined.py similarity index 100% rename from python 3/koans/local_module_with_all_defined.py rename to python3/koans/local_module_with_all_defined.py diff --git a/python 3/koans/triangle.py b/python3/koans/triangle.py similarity index 53% rename from python 3/koans/triangle.py rename to python3/koans/triangle.py index 4d8d66f42..bf7e979f5 100644 --- a/python 3/koans/triangle.py +++ b/python3/koans/triangle.py @@ -17,8 +17,20 @@ # about_triangle_project_2.py # def triangle(a, b, c): - # DELETE 'PASS' AND WRITE THIS CODE - pass + sides = sorted([a, b, c]) + unique_sides = sorted(set(sides)) + + if unique_sides[0] <= 0 : raise TriangleError('Sides cannot be 0 or less') + if sides[2] >= sides[0] + sides[1] : raise TriangleError('sum of 2 sides cannot be greater than remaining side') + + if len(unique_sides) == 1: + return 'equilateral' + elif len(unique_sides) == 2: + return 'isosceles' + elif len(unique_sides) == 3: + return 'scalene' + ##### # DELETE 'PASS' AND WRITE THIS CODE + ##### pass # Error class used in part 2. No need to change this code. class TriangleError(Exception): diff --git a/python 3/libs/__init__.py b/python3/libs/__init__.py similarity index 100% rename from python 3/libs/__init__.py rename to python3/libs/__init__.py diff --git a/python3/libs/colorama/LICENSE-colorama b/python3/libs/colorama/LICENSE-colorama new file mode 100644 index 000000000..b7464472e --- /dev/null +++ b/python3/libs/colorama/LICENSE-colorama @@ -0,0 +1,33 @@ +Copyright (c) 2010 Jonathan Hartley + +Released under the New BSD license (reproduced below), or alternatively you may +use this software under any OSI approved open source license such as those at +http://opensource.org/licenses/alphabetical + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name(s) of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/python3/libs/colorama/__init__.py b/python3/libs/colorama/__init__.py new file mode 100644 index 000000000..331174e57 --- /dev/null +++ b/python3/libs/colorama/__init__.py @@ -0,0 +1,6 @@ +from .initialise import init +from .ansi import Fore, Back, Style +from .ansitowin32 import AnsiToWin32 + +VERSION = '0.1.18' + diff --git a/python3/libs/colorama/ansi.py b/python3/libs/colorama/ansi.py new file mode 100644 index 000000000..7b818e19e --- /dev/null +++ b/python3/libs/colorama/ansi.py @@ -0,0 +1,49 @@ +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' + +def code_to_chars(code): + return CSI + str(code) + 'm' + +class AnsiCodes(object): + def __init__(self, codes): + for name in dir(codes): + if not name.startswith('_'): + value = getattr(codes, name) + setattr(self, name, code_to_chars(value)) + +class AnsiFore: + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + +class AnsiBack: + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + +class AnsiStyle: + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiCodes( AnsiFore ) +Back = AnsiCodes( AnsiBack ) +Style = AnsiCodes( AnsiStyle ) + diff --git a/python3/libs/colorama/ansitowin32.py b/python3/libs/colorama/ansitowin32.py new file mode 100644 index 000000000..5c39a480e --- /dev/null +++ b/python3/libs/colorama/ansitowin32.py @@ -0,0 +1,176 @@ + +import re +import sys + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll + + +if windll is not None: + winterm = WinTerm() + + +def is_a_tty(stream): + return hasattr(stream, 'isatty') and stream.isatty() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def write(self, text): + self.__convertor.write(text) + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = sys.platform.startswith('win') + + # should we strip ANSI sequences from our output? + if strip is None: + strip = on_windows + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = on_windows and is_a_tty(wrapped) + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + } + + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + else: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + for match in self.ANSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(paramstring) + self.call_win32(command, params) + + + def extract_params(self, paramstring): + def split(paramstring): + for p in paramstring.split(';'): + if p != '': + yield int(p) + return tuple(split(paramstring)) + + + def call_win32(self, command, params): + if params == []: + params = [0] + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + diff --git a/python3/libs/colorama/initialise.py b/python3/libs/colorama/initialise.py new file mode 100644 index 000000000..eca2bea40 --- /dev/null +++ b/python3/libs/colorama/initialise.py @@ -0,0 +1,38 @@ +import atexit +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + +atexit_done = False + + +def reset_all(): + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if wrap==False and (autoreset==True or convert==True or strip==True): + raise ValueError('wrap=False conflicts with any other arg=True') + + sys.stdout = wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + sys.stderr = wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + diff --git a/python3/libs/colorama/win32.py b/python3/libs/colorama/win32.py new file mode 100644 index 000000000..5f49e6cba --- /dev/null +++ b/python3/libs/colorama/win32.py @@ -0,0 +1,95 @@ + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + from ctypes import windll +except ImportError: + windll = None + SetConsoleTextAttribute = lambda *_: None +else: + from ctypes import ( + byref, Structure, c_char, c_short, c_uint32, c_ushort + ) + + handles = { + STDOUT: windll.kernel32.GetStdHandle(STDOUT), + STDERR: windll.kernel32.GetStdHandle(STDERR), + } + + SHORT = c_short + WORD = c_ushort + DWORD = c_uint32 + TCHAR = c_char + + class COORD(Structure): + """struct in wincon.h""" + _fields_ = [ + ('X', SHORT), + ('Y', SHORT), + ] + + class SMALL_RECT(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("Left", SHORT), + ("Top", SHORT), + ("Right", SHORT), + ("Bottom", SHORT), + ] + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", WORD), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + + def GetConsoleScreenBufferInfo(stream_id): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = windll.kernel32.GetConsoleScreenBufferInfo( + handle, byref(csbi)) + # This fails when imported via setup.py when installing using 'pip' + # presumably the fix is that running setup.py should not trigger all + # this activity. + # assert success + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + success = windll.kernel32.SetConsoleTextAttribute(handle, attrs) + assert success + + def SetConsoleCursorPosition(stream_id, position): + handle = handles[stream_id] + position = COORD(*position) + success = windll.kernel32.SetConsoleCursorPosition(handle, position) + assert success + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = TCHAR(char) + length = DWORD(length) + start = COORD(*start) + num_written = DWORD(0) + # AttributeError: function 'FillConsoleOutputCharacter' not found + # could it just be that my types are wrong? + success = windll.kernel32.FillConsoleOutputCharacter( + handle, char, length, start, byref(num_written)) + assert success + return num_written.value + + +if __name__=='__main__': + x = GetConsoleScreenBufferInfo(STDOUT) + print(x.dwSize) + print(x.dwCursorPosition) + print(x.wAttributes) + print(x.srWindow) + print(x.dwMaximumWindowSize) + diff --git a/python3/libs/colorama/winterm.py b/python3/libs/colorama/winterm.py new file mode 100644 index 000000000..63f4469cc --- /dev/null +++ b/python3/libs/colorama/winterm.py @@ -0,0 +1,69 @@ + +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + + +class WinTerm(object): + + def __init__(self): + self._default = \ + win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + + def get_attrs(self): + return self._fore + self._back * 16 + self._style + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & WinStyle.BRIGHT + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + + def fore(self, fore=None, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + diff --git a/python 3/libs/mock.py b/python3/libs/mock.py similarity index 100% rename from python 3/libs/mock.py rename to python3/libs/mock.py diff --git a/python 3/run.bat b/python3/run.bat similarity index 100% rename from python 3/run.bat rename to python3/run.bat diff --git a/python 3/run.sh b/python3/run.sh similarity index 100% rename from python 3/run.sh rename to python3/run.sh diff --git a/python 3/runner/__init__.py b/python3/runner/__init__.py similarity index 100% rename from python 3/runner/__init__.py rename to python3/runner/__init__.py diff --git a/python 3/runner/helper.py b/python3/runner/helper.py similarity index 100% rename from python 3/runner/helper.py rename to python3/runner/helper.py diff --git a/python 3/runner/koan.py b/python3/runner/koan.py similarity index 100% rename from python 3/runner/koan.py rename to python3/runner/koan.py diff --git a/python 3/runner/mockable_test_result.py b/python3/runner/mockable_test_result.py similarity index 100% rename from python 3/runner/mockable_test_result.py rename to python3/runner/mockable_test_result.py diff --git a/python 3/runner/mountain.py b/python3/runner/mountain.py similarity index 97% rename from python 3/runner/mountain.py rename to python3/runner/mountain.py index eabb55862..dab3b415f 100644 --- a/python 3/runner/mountain.py +++ b/python3/runner/mountain.py @@ -13,10 +13,10 @@ def __init__(self): self.stream = WritelnDecorator(sys.stdout) self.tests = path_to_enlightenment.koans() self.lesson = Sensei(self.stream) - + def walk_the_path(self, args=None): "Run the koans tests with a custom runner output." - + if args and len(args) >=2: self.tests = unittest.TestLoader().loadTestsFromName("koans." + args[1]) diff --git a/python 3/runner/path_to_enlightenment.py b/python3/runner/path_to_enlightenment.py similarity index 100% rename from python 3/runner/path_to_enlightenment.py rename to python3/runner/path_to_enlightenment.py diff --git a/python 3/runner/runner_tests/__init__.py b/python3/runner/runner_tests/__init__.py similarity index 100% rename from python 3/runner/runner_tests/__init__.py rename to python3/runner/runner_tests/__init__.py diff --git a/python 3/runner/runner_tests/test_helper.py b/python3/runner/runner_tests/test_helper.py similarity index 97% rename from python 3/runner/runner_tests/test_helper.py rename to python3/runner/runner_tests/test_helper.py index ea3ee00c3..fef9b6026 100644 --- a/python 3/runner/runner_tests/test_helper.py +++ b/python3/runner/runner_tests/test_helper.py @@ -6,10 +6,10 @@ from runner import helper class TestHelper(unittest.TestCase): - + def test_that_get_class_name_works_with_a_string_instance(self): self.assertEqual("str", helper.cls_name(str())) - + def test_that_get_class_name_works_with_a_4(self): self.assertEquals("int", helper.cls_name(4)) diff --git a/python 3/runner/runner_tests/test_mountain.py b/python3/runner/runner_tests/test_mountain.py similarity index 75% rename from python 3/runner/runner_tests/test_mountain.py rename to python3/runner/runner_tests/test_mountain.py index efcfc1839..0f8d08ccc 100644 --- a/python 3/runner/runner_tests/test_mountain.py +++ b/python3/runner/runner_tests/test_mountain.py @@ -13,13 +13,9 @@ def setUp(self): path_to_enlightenment.koans = Mock() self.mountain = Mountain() self.mountain.stream.writeln = Mock() - - def test_it_retrieves_some_koans_tests(self): - self.mountain.walk_the_path() - self.assertTrue(self.mountain.tests, "No test suite") - + def test_it_gets_test_results(self): self.mountain.lesson.learn = Mock() self.mountain.walk_the_path() self.assertTrue(self.mountain.lesson.learn.called) - \ No newline at end of file + diff --git a/python 2/runner/runner_tests/test_sensei.py b/python3/runner/runner_tests/test_sensei.py similarity index 60% rename from python 2/runner/runner_tests/test_sensei.py rename to python3/runner/runner_tests/test_sensei.py index 97360da97..416c7ce2f 100644 --- a/python 2/runner/runner_tests/test_sensei.py +++ b/python3/runner/runner_tests/test_sensei.py @@ -10,6 +10,7 @@ from runner.sensei import Sensei from runner.writeln_decorator import WritelnDecorator from runner.mockable_test_result import MockableTestResult +from runner import path_to_enlightenment class AboutParrots: pass @@ -31,7 +32,7 @@ class AboutFreemasons: pass error_assertion_with_message = """Traceback (most recent call last): - File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 43, in test_durability + File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py ", line 43, in test_durability self.assertEqual("Steel","Lard", "Another fine mess you've got me into Stanley...") AssertionError: Another fine mess you've got me into Stanley...""" @@ -80,95 +81,32 @@ class AboutFreemasons: """ + class TestSensei(unittest.TestCase): def setUp(self): self.sensei = Sensei(WritelnDecorator(sys.stdout)) self.sensei.stream.writeln = Mock() - - def test_that_it_delegates_testing_to_test_cases(self): - MockableTestResult.startTest = Mock() - self.sensei.startTest(Mock()) - self.assertTrue(MockableTestResult.startTest.called) - - def test_that_user_is_notified_if_test_involves_a_different_test_class_to_last_time(self): - MockableTestResult.startTest = Mock() - - self.sensei.prevTestClassName == 'AboutLumberjacks' - nextTest = AboutParrots() - - self.sensei.startTest(nextTest) - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_user_is_not_notified_about_test_class_repeats(self): - MockableTestResult.startTest = Mock() - - self.sensei.prevTestClassName == 'AboutParrots' - nextTest = AboutParrots() - - self.sensei.startTest(nextTest) - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_cached_classname_updates_after_the_test(self): - self.assertEqual(None, self.sensei.prevTestClassName) - self.sensei.startTest(Mock()) - self.assertNotEqual(None, self.sensei.prevTestClassName) - - def test_that_errors_are_diverted_to_the_failures_list(self): - MockableTestResult.addFailure = Mock() - self.sensei.addError(Mock(), Mock()) - self.assertTrue(MockableTestResult.addFailure.called) - - def test_that_failures_are_handled_in_the_base_class(self): - MockableTestResult.addFailure = Mock() - self.sensei.addFailure(Mock(), Mock()) - self.assertTrue(MockableTestResult.addFailure.called) + path_to_enlightenment.koans = Mock() + self.tests = Mock() + self.tests.countTestCases = Mock() def test_that_it_successes_only_count_if_passes_are_currently_allowed(self): self.sensei.passesCount = Mock() MockableTestResult.addSuccess = Mock() self.sensei.addSuccess(Mock()) self.assertTrue(self.sensei.passesCount.called) - - def test_that_if_there_are_failures_and_the_prev_class_is_different_successes_are_not_allowed(self): - self.sensei.failures = [(AboutLumberjacks(), Mock())] - self.sensei.prevTestClassName = "AboutTheMinitry" - self.assertFalse(self.sensei.passesCount()) - - def test_that_if_there_are_failures_and_the_prev_class_is_the_same_successes_are_allowed(self): - self.sensei.failures = [(AboutLumberjacks(), Mock())] - self.sensei.prevTestClassName = "AboutLumberjacks" - self.assertTrue(self.sensei.passesCount()) - - def test_that_if_there_are_no_failures_successes_are_allowed(self): - self.sensei.failures = None - self.sensei.prevTestClassName = "AboutLumberjacks" - self.assertTrue(self.sensei.passesCount()) - def test_that_it_passes_on_add_successes_message(self): - MockableTestResult.addSuccess = Mock() - self.sensei.addSuccess(Mock()) - self.assertTrue(MockableTestResult.addSuccess.called) - def test_that_it_increases_the_passes_on_every_success(self): pass_count = self.sensei.pass_count MockableTestResult.addSuccess = Mock() self.sensei.addSuccess(Mock()) self.assertEqual(pass_count + 1, self.sensei.pass_count) - def test_that_it_displays_each_success(self): - MockableTestResult.addSuccess = Mock() - self.sensei.addSuccess(Mock()) - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_nothing_is_returned_as_a_first_result_if_there_are_no_failures(self): - self.sensei.failures = [] - self.assertEqual(None, self.sensei.firstFailure()) - def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_failures(self): self.sensei.failures = [] self.assertEqual(None, self.sensei.sortFailures("AboutLife")) - + def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_relevent_failures(self): self.sensei.failures = [ (AboutTheKnightsWhoSayNi(),"File 'about_the_knights_whn_say_ni.py', line 24"), @@ -176,7 +114,7 @@ def test_that_nothing_is_returned_as_sorted_result_if_there_are_no_relevent_fail (AboutMessiahs(),"File 'about_messiahs.py', line 844") ] self.assertEqual(None, self.sensei.sortFailures("AboutLife")) - + def test_that_nothing_is_returned_as_sorted_result_if_there_are_3_shuffled_results(self): self.sensei.failures = [ (AboutTennis(),"File 'about_tennis.py', line 299"), @@ -187,26 +125,26 @@ def test_that_nothing_is_returned_as_sorted_result_if_there_are_3_shuffled_resul (AboutMrGumby(),"File 'about_mr_gumby.py', line odd"), (AboutMessiahs(),"File 'about_messiahs.py', line 844") ] - + expected = [ (AboutTennis(),"File 'about_tennis.py', line 2"), (AboutTennis(),"File 'about_tennis.py', line 30"), (AboutTennis(),"File 'about_tennis.py', line 299") ] - + results = self.sensei.sortFailures("AboutTennis") self.assertEqual(3, len(results)) self.assertEqual(2, results[0][0]) self.assertEqual(30, results[1][0]) self.assertEqual(299, results[2][0]) - + def test_that_it_will_choose_not_find_anything_with_non_standard_error_trace_string(self): self.sensei.failures = [ (AboutMrGumby(),"File 'about_mr_gumby.py', line MISSING"), ] self.assertEqual(None, self.sensei.sortFailures("AboutMrGumby")) - - + + def test_that_it_will_choose_correct_first_result_with_lines_9_and_27(self): self.sensei.failures = [ (AboutTrebuchets(),"File 'about_trebuchets.py', line 27"), @@ -214,7 +152,7 @@ def test_that_it_will_choose_correct_first_result_with_lines_9_and_27(self): (AboutTrebuchets(),"File 'about_trebuchets.py', line 73v") ] self.assertEqual("File 'about_trebuchets.py', line 9", self.sensei.firstFailure()[1]) - + def test_that_it_will_choose_correct_first_result_with_multiline_test_classes(self): self.sensei.failures = [ (AboutGiantFeet(),"File 'about_giant_feet.py', line 999"), @@ -223,39 +161,6 @@ def test_that_it_will_choose_correct_first_result_with_multiline_test_classes(se (AboutFreemasons(),"File 'about_freemasons.py', line 11") ] self.assertEqual("File 'about_giant_feet.py', line 44", self.sensei.firstFailure()[1]) - - def test_that_end_report_displays_something(self): - self.sensei.learn() - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_end_report_shows_the_failure_report(self): - self.sensei.errorReport = Mock() - self.sensei.learn() - self.assertTrue(self.sensei.errorReport.called) - - def test_that_end_report_should_have_something_zenlike_on_it(self): - self.sensei.say_something_zenlike = Mock() - self.sensei.learn() - self.assertTrue(self.sensei.say_something_zenlike.called) - - def test_that_error_report_shows_something_if_there_is_a_failure(self): - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = (Mock(), "FAILED Parrot is breathing, Line 42") - self.sensei.errorReport() - self.assertTrue(self.sensei.stream.writeln.called) - - def test_that_error_report_does_not_show_anything_if_there_is_no_failure(self): - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = None - self.sensei.errorReport() - self.assertFalse(self.sensei.stream.writeln.called) - - def test_that_error_report_features_the_assertion_error(self): - self.sensei.scrapeAssertionError = Mock() - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = (Mock(), "FAILED") - self.sensei.errorReport() - self.assertTrue(self.sensei.scrapeAssertionError.called) def test_that_error_report_features_a_stack_dump(self): self.sensei.scrapeInterestingStackDump = Mock() @@ -266,19 +171,19 @@ def test_that_error_report_features_a_stack_dump(self): def test_that_scraping_the_assertion_error_with_nothing_gives_you_a_blank_back(self): self.assertEqual("", self.sensei.scrapeAssertionError(None)) - + def test_that_scraping_the_assertion_error_with_messaged_assert(self): self.assertEqual(" AssertionError: Another fine mess you've got me into Stanley...", self.sensei.scrapeAssertionError(error_assertion_with_message)) - + def test_that_scraping_the_assertion_error_with_assert_equals(self): self.assertEqual(" AssertionError: 4 != 99", self.sensei.scrapeAssertionError(error_assertion_equals)) - + def test_that_scraping_the_assertion_error_with_assert_true(self): self.assertEqual(" AssertionError", self.sensei.scrapeAssertionError(error_assertion_true)) - + def test_that_scraping_the_assertion_error_with_syntax_error(self): self.assertEqual(" SyntaxError: invalid syntax", self.sensei.scrapeAssertionError(error_mess)) @@ -299,51 +204,27 @@ def test_that_scraping_the_assertion_error_with_list_error(self): def test_that_scraping_a_non_existent_stack_dump_gives_you_nothing(self): self.assertEqual("", self.sensei.scrapeInterestingStackDump(None)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_messaged_assert(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 43, in test_durability - self.assertEqual("Steel","Lard", "Another fine mess you've got me into Stanley...")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_with_message)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_assert_equals(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 49, in test_math - self.assertEqual(4,99)""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_equals)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_assert_true(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_armories.py", line 25, in test_weoponary - self.assertTrue("Pen" > "Sword")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_assertion_true)) - - def test_that_scraping_the_stack_dump_only_shows_interesting_lines_for_syntax_error(self): - expected = """ File "/Users/Greg/hg/python_koans/koans/about_asserts.py", line 20 - self.assertTrue(eoe"Pen" > "Sword", "nhnth")""" - self.assertEqual(expected, - self.sensei.scrapeInterestingStackDump(error_mess)) - + def test_that_if_there_are_no_failures_say_the_final_zenlike_remark(self): self.sensei.failures = None words = self.sensei.say_something_zenlike() - + m = re.search("Spanish Inquisition", words) self.assertTrue(m and m.group(0)) - + def test_that_if_there_are_0_successes_it_will_say_the_first_zen_of_python_koans(self): self.sensei.pass_count = 0 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Beautiful is better than ugly", words) self.assertTrue(m and m.group(0)) - + def test_that_if_there_is_1_successes_it_will_say_the_second_zen_of_python_koans(self): self.sensei.pass_count = 1 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Explicit is better than implicit", words) self.assertTrue(m and m.group(0)) @@ -351,7 +232,7 @@ def test_that_if_there_is_10_successes_it_will_say_the_sixth_zen_of_python_koans self.sensei.pass_count = 10 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Sparse is better than dense", words) self.assertTrue(m and m.group(0)) @@ -359,7 +240,7 @@ def test_that_if_there_is_36_successes_it_will_say_the_final_zen_of_python_koans self.sensei.pass_count = 36 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Namespaces are one honking great idea", words) self.assertTrue(m and m.group(0)) @@ -367,6 +248,29 @@ def test_that_if_there_is_37_successes_it_will_say_the_first_zen_of_python_koans self.sensei.pass_count = 37 self.sensei.failures = Mock() words = self.sensei.say_something_zenlike() - + m = re.search("Beautiful is better than ugly", words) self.assertTrue(m and m.group(0)) + + def test_that_total_lessons_return_7_if_there_are_7_lessons(self): + self.sensei.filter_all_lessons = Mock() + self.sensei.filter_all_lessons.return_value = [1,2,3,4,5,6,7] + + self.assertEqual(7, self.sensei.total_lessons()) + + def test_that_total_lessons_return_0_if_all_lessons_is_none(self): + self.sensei.filter_all_lessons = Mock() + self.sensei.filter_all_lessons.return_value = None + + self.assertEqual(0, self.sensei.total_lessons()) + + def test_total_koans_return_43_if_there_are_43_test_cases(self): + self.sensei.tests.countTestCases = Mock() + self.sensei.tests.countTestCases.return_value = 43 + + self.assertEqual(43, self.sensei.total_koans()) + + def test_filter_all_lessons_will_discover_test_classes_if_none_have_been_discovered_yet(self): + self.sensei.all_lessons = 0 + self.assertTrue(len(self.sensei.filter_all_lessons()) > 10) + self.assertTrue(len(self.sensei.all_lessons) > 10) diff --git a/python3/runner/sensei.py b/python3/runner/sensei.py new file mode 100644 index 000000000..434e4e9a0 --- /dev/null +++ b/python3/runner/sensei.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import re +import sys +import os +import glob + +from . import helper +from .mockable_test_result import MockableTestResult +from runner import path_to_enlightenment + +from libs.colorama import init, Fore, Style +init() # init colorama + +class Sensei(MockableTestResult): + def __init__(self, stream): + unittest.TestResult.__init__(self) + self.stream = stream + self.prevTestClassName = None + self.tests = path_to_enlightenment.koans() + self.pass_count = 0 + self.lesson_pass_count = 0 + self.all_lessons = None + + def startTest(self, test): + MockableTestResult.startTest(self, test) + + if helper.cls_name(test) != self.prevTestClassName: + self.prevTestClassName = helper.cls_name(test) + if not self.failures: + self.stream.writeln() + self.stream.writeln("{0}{1}Thinking {2}".format( + Fore.RESET, Style.NORMAL, helper.cls_name(test))) + if helper.cls_name(test) != 'AboutAsserts': + self.lesson_pass_count += 1 + + def addSuccess(self, test): + if self.passesCount(): + MockableTestResult.addSuccess(self, test) + self.stream.writeln( \ + " {0}{1}{2} has expanded your awareness.{3}{4}" \ + .format(Fore.GREEN, Style.BRIGHT, test._testMethodName, \ + Fore.RESET, Style.NORMAL)) + self.pass_count += 1 + + def addError(self, test, err): + # Having 1 list for errors and 1 list for failures would mess with + # the error sequence + self.addFailure(test, err) + + def passesCount(self): + return not (self.failures and helper.cls_name(self.failures[0][0]) != self.prevTestClassName) + + def addFailure(self, test, err): + MockableTestResult.addFailure(self, test, err) + + def sortFailures(self, testClassName): + table = list() + for test, err in self.failures: + if helper.cls_name(test) == testClassName: + m = re.search("(?<= line )\d+" ,err) + if m: + tup = (int(m.group(0)), test, err) + table.append(tup) + + if table: + return sorted(table) + else: + return None + + def firstFailure(self): + if not self.failures: return None + + table = self.sortFailures(helper.cls_name(self.failures[0][0])) + + if table: + return (table[0][1], table[0][2]) + else: + return None + + def learn(self): + self.errorReport() + + self.stream.writeln("") + self.stream.writeln("") + self.stream.writeln(self.report_progress()) + if self.failures: + self.stream.writeln(self.report_remaining()) + self.stream.writeln("") + self.stream.writeln(self.say_something_zenlike()) + + if self.failures: sys.exit(-1) + self.stream.writeln( + "\n{0}**************************************************" \ + .format(Fore.RESET)) + self.stream.writeln("\n{0}That was the last one, well done!" \ + .format(Fore.MAGENTA)) + self.stream.writeln( + "\nIf you want more, take a look at about_extra_credit_task.py{0}{1}" \ + .format(Fore.RESET, Style.NORMAL)) + + def errorReport(self): + problem = self.firstFailure() + if not problem: return + test, err = problem + self.stream.writeln(" {0}{1}{2} has damaged your " + "karma.".format(Fore.RED, Style.BRIGHT, test._testMethodName)) + + self.stream.writeln("\n{0}{1}You have not yet reached enlightenment ..." \ + .format(Fore.RESET, Style.NORMAL)) + self.stream.writeln("{0}{1}{2}".format(Fore.RED, \ + Style.BRIGHT, self.scrapeAssertionError(err))) + self.stream.writeln("") + self.stream.writeln("{0}{1}Please meditate on the following code:" \ + .format(Fore.RESET, Style.NORMAL)) + self.stream.writeln("{0}{1}{2}{3}{4}".format(Fore.YELLOW, Style.BRIGHT, \ + self.scrapeInterestingStackDump(err), Fore.RESET, Style.NORMAL)) + + def scrapeAssertionError(self, err): + if not err: return "" + + error_text = "" + count = 0 + for line in err.splitlines(): + m = re.search("^[^^ ].*$",line) + if m and m.group(0): + count+=1 + + if count>1: + error_text += (" " + line.strip()).rstrip() + '\n' + return error_text.strip('\n') + + def scrapeInterestingStackDump(self, err): + if not err: + return "" + + lines = err.splitlines() + + sep = '@@@@@SEP@@@@@' + + stack_text = "" + for line in lines: + m = re.search("^ File .*$",line) + if m and m.group(0): + stack_text += '\n' + line + + m = re.search("^ \w(\w)+.*$",line) + if m and m.group(0): + stack_text += sep + line + + lines = stack_text.splitlines() + + stack_text = "" + for line in lines: + m = re.search("^.*[/\\\\]koans[/\\\\].*$",line) + if m and m.group(0): + stack_text += line + '\n' + + + stack_text = stack_text.replace(sep, '\n').strip('\n') + stack_text = re.sub(r'(about_\w+.py)', + r"{0}\1{1}".format(Fore.BLUE, Fore.YELLOW), stack_text) + stack_text = re.sub(r'(line \d+)', + r"{0}\1{1}".format(Fore.BLUE, Fore.YELLOW), stack_text) + return stack_text + + def report_progress(self): + return "You have completed {0} koans and " \ + "{1} lessons.".format( + self.pass_count, + self.lesson_pass_count) + + def report_remaining(self): + koans_remaining = self.total_koans() - self.pass_count + lessons_remaining = self.total_lessons() - self.pass_count + + return "You are now {0} koans and {1} lessons away from " \ + "reaching enlightenment.".format( + koans_remaining, + lessons_remaining) + + # Hat's tip to Tim Peters for the zen statements from The 'Zen + # of Python' (http://www.python.org/dev/peps/pep-0020/) + # + # Also a hat's tip to Ara T. Howard for the zen statements from his + # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html) and + # Edgecase's later permutation in the Ruby Koans + def say_something_zenlike(self): + if self.failures: + turn = self.pass_count % 37 + + zenness = ""; + if turn == 0: + zenness = "Beautiful is better than ugly." + elif turn == 1 or turn == 2: + zenness = "Explicit is better than implicit." + elif turn == 3 or turn == 4: + zenness = "Simple is better than complex." + elif turn == 5 or turn == 6: + zenness = "Complex is better than complicated." + elif turn == 7 or turn == 8: + zenness = "Flat is better than nested." + elif turn == 9 or turn == 10: + zenness = "Sparse is better than dense." + elif turn == 11 or turn == 12: + zenness = "Readability counts." + elif turn == 13 or turn == 14: + zenness = "Special cases aren't special enough to " \ + "break the rules." + elif turn == 15 or turn == 16: + zenness = "Although practicality beats purity." + elif turn == 17 or turn == 18: + zenness = "Errors should never pass silently." + elif turn == 19 or turn == 20: + zenness = "Unless explicitly silenced." + elif turn == 21 or turn == 22: + zenness = "In the face of ambiguity, refuse the " \ + "temptation to guess." + elif turn == 23 or turn == 24: + zenness = "There should be one-- and preferably only " \ + "one --obvious way to do it." + elif turn == 25 or turn == 26: + zenness = "Although that way may not be obvious at " \ + "first unless you're Dutch." + elif turn == 27 or turn == 28: + zenness = "Now is better than never." + elif turn == 29 or turn == 30: + zenness = "Although never is often better than right " \ + "now." + elif turn == 31 or turn == 32: + zenness = "If the implementation is hard to explain, " \ + "it's a bad idea." + elif turn == 33 or turn == 34: + zenness = "If the implementation is easy to explain, " \ + "it may be a good idea." + else: + zenness = "Namespaces are one honking great idea -- " \ + "let's do more of those!" + return "{0}{1}{2}{3}".format(Fore.CYAN, zenness, Fore.RESET, Style.NORMAL); + else: + return "{0}Nobody ever expects the Spanish Inquisition." \ + .format(Fore.CYAN) + + # Hopefully this will never ever happen! + return "The temple is collapsing! Run!!!" + + def total_lessons(self): + all_lessons = self.filter_all_lessons() + if all_lessons: + return len(all_lessons) + else: + return 0 + + def total_koans(self): + return self.tests.countTestCases() + + def filter_all_lessons(self): + cur_dir = os.path.split(os.path.realpath(__file__))[0] + if not self.all_lessons: + self.all_lessons = glob.glob('{0}/../koans/about*.py'.format(cur_dir)) + self.all_lessons = list(filter(lambda filename: + "about_extra_credit" not in filename, + self.all_lessons)) + + return self.all_lessons diff --git a/python 3/runner/writeln_decorator.py b/python3/runner/writeln_decorator.py similarity index 100% rename from python 3/runner/writeln_decorator.py rename to python3/runner/writeln_decorator.py