diff --git a/.gitignore b/.gitignore index 27b56e02d..776f6d90d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .DS_Store answers .hg +.idea \ No newline at end of file diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 000000000..d6122b7f2 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,11 @@ +# Install custom tools, runtimes, etc. +# For example "bastet", a command-line tetris clone: +# RUN brew install bastet +# +# More information: https://www.gitpod.io/docs/config-docker/ + +FROM gitpod/workspace-full:latest + +USER gitpod + +RUN pip3 install pytest==4.4.2 pytest-testdox mock \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..94253242b --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,14 @@ +image: + file: .gitpod.Dockerfile + +tasks: + - command: python contemplate_koans.py + +github: + prebuilds: + # enable for the master/default branch (defaults to true) + master: true + # enable for pull requests coming from this repo (defaults to true) + pullRequests: false + # add a "Review in Gitpod" button as a comment to pull requests (defaults to true) + addComment: false diff --git a/.hgignore b/.hgignore index cf9029f6d..f1035d222 100644 --- a/.hgignore +++ b/.hgignore @@ -4,3 +4,4 @@ syntax: glob .DS_Store answers .git +.idea \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..e8f7d64ac --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: python + +python: + - 3.9 + +script: + - 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 ... diff --git a/Contributor Notes.txt b/Contributor Notes.txt index c3cd6b256..c14bb947a 100644 --- a/Contributor Notes.txt +++ b/Contributor Notes.txt @@ -1,15 +1,12 @@ - Testing a specific koan =================================== -This will help when adding/modifing koans +This will help when adding/modifying koans Running a whole test case: - $ python contemplate_koans.py about_strings - or $ python3 contemplate_koans.py about_strings - + Running a single test: - $ python contemplate_koans.py about_strings.AboutStrings.test_triple_quoted_strings_need_less_escaping + $ python3 contemplate_koans.py about_strings.AboutStrings.test_triple_quoted_strings_need_less_escaping diff --git a/MIT-LICENSE b/MIT-LICENSE index 128401ac9..5e57f89fd 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,22 +1,19 @@ -Copyright (c) 2010 Greg Malcolm and The Status Is Not Quo +Copyright 2021 Greg Malcolm and The Status Is Not Quo -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..e2f303fe9 --- /dev/null +++ b/README.rst @@ -0,0 +1,242 @@ +============ +Python Koans +============ + +.. image:: https://travis-ci.org/gregmalcolm/python_koans.png?branch=master + :target: http://travis-ci.org/gregmalcolm/python_koans + +.. image:: https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod + :target: https://gitpod.io/#https://github.com/gregmalcolm/python_koans + +.. image:: https://www.eclipse.org/che/contribute.svg + :target: https://workspaces.openshift.com/f?url=https://gitpod.io/#https://github.com/gregmalcolm/python_koans + +One click installation: +----------------------- + +.. image:: https://www.eclipse.org/che/contribute.svg + :target: https://workspaces.openshift.com/f?url=https://gitpod.io/#https://github.com/gregmalcolm/python_koans +| or +.. image:: https://gitpod.io/button/open-in-gitpod.svg + :target: https://gitpod.io/#https://gitpod.io/#https://github.com/gregmalcolm/python_koans + +| + +Python Koans is a port of Edgecase's "Ruby Koans" which can be found +at http://rubykoans.com/. + +.. image:: https://user-images.githubusercontent.com/2614930/28401740-ec6214b2-6cd0-11e7-8afd-30ed3102bfd6.png + +Python Koans is an interactive tutorial for learning the Python programming +language by making tests pass. + +Most tests are *fixed* by filling the missing parts of assert functions. Eg: + +.. code-block:: python + + self.assertEqual(__, 1+2) + +which can be fixed by replacing the __ part with the appropriate code: + +.. code-block:: python + + 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 on GitHub: + +* https://github.com/gregmalcolm/python_koans + +You can clone with Git or download the source as a zip/gz/bz2. + + +Installing Python Koans +----------------------- + +Aside from downloading or checking out the latest version of Python Koans, you +need to install the Python interpreter. + +At this time of writing, we support Python 3. The policy is to try to keep +current with the latest production version. + +You should be able to work with newer Python versions, but older ones will +likely give you problems. + +You can download Python from here: + +* https://www.python.org/downloads/ + +After installing Python make sure the folder containing the python executable +is in the system path. In other words, you need to be able to run Python from a +command console. It will either be ``python3`` or for Windows it will be ``python.exe``. + +If you have problems, this may help: + +* https://www.python.org/about/gettingstarted/ + +Windows users may also want to update the line in the batch file ``run.bat`` to +set the python path:: + + SET PYTHON_PATH=C:\Python39 + + +Getting Started +--------------- + +Jake Hebbert has created a couple of screencasts available here: + +https://www.youtube.com/watch?v=e2WXgXEjbHY&list=PL5Up_u-XkWgNcunP_UrTJG_3EXgbK2BQJ&index=1 + +Or if you prefer to read: + +From a \*nix terminal or Windows command prompt run:: + +.. code-block:: sh + + python contemplate_koans.py + +or: + +.. code-block:: sh + + 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: + +.. image:: https://user-images.githubusercontent.com/2614930/28401747-f723ff00-6cd0-11e7-9b9a-a6993b753cf6.png + +Apparently a test failed:: + + AssertionError: False is not True + +It also tells me exactly where the problem is, it's 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: + +.. code-block:: python + + 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: + +.. image:: https://user-images.githubusercontent.com/2614930/28401750-f9dcb296-6cd0-11e7-98eb-c20318eada33.png + +Sniffer Support +--------------- + +Sniffer allows you to run the tests continuously. If you modify any files files +in the koans directory, it will rerun the tests. + +To set this up, you need to install sniffer: + +.. code-block:: sh + + python3 -m pip install sniffer + +You should also run one of these libraries depending on your system. This will +automatically trigger sniffer when a file changes, otherwise sniffer will have +to poll to see if the files have changed. + +On Linux: + +.. code-block:: sh + + python3 -m pip install pyinotify + +On Windows: + +.. code-block:: sh + + python3 -m pip install pywin32 + + Also available here: + + https://github.com/mhammond/pywin32/releases + +On macOS: + +.. code-block:: sh + + python3 -m pip install MacFSEvents + +Once it is set up, you just run: + +.. code-block:: sh + + sniffer + +Just modify one of the koans files and you'll see that the tests are triggered +automatically. Sniffer is controlled by ``scent.py``. + +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)." + + + +Finding More Koan Projects +-------------------------- + +There are number of other great Koan projects out there for various languages +and frameworks. Most of them can be found in GitHub. Also there is a little +koans activity on Bitbucket. + +* GitHub koan projects: + https://github.com/search?q=koans&ref=cmdform + +* Bitbucket koan projects: + https://bitbucket.org/repo/all?name=koans + +Translations +------------ + +Translations are always welcome! Feel free to add one to this README +if you happen to work on one: + +https://github.com/mswell/python_koans_br + +Acknowledgments +--------------- + +Thanks go to Jim Weirich and Joe O'Brien for the original Ruby Koans that the +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 has contributed to Python Koans! I got a great +headstart by taking over a code base initiated by the combined Mikes of +FPIP. So here's a little plug for their very cool Python podcast: + +* https://www.frompythonimportpodcast.com/ + +A big thanks also to Mike Pirnat @pirnat and Kevin Chase @kjc have pitched in +as co-maintainers at various times diff --git a/README.txt b/README.txt deleted file mode 100644 index cac80571f..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 Mercurial 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/_runner_tests.py b/_runner_tests.py similarity index 57% rename from python 2/_runner_tests.py rename to _runner_tests.py index 55ef5a12a..26fec382d 100644 --- a/python 2/_runner_tests.py +++ b/_runner_tests.py @@ -7,14 +7,20 @@ from runner.runner_tests.test_mountain import TestMountain from runner.runner_tests.test_sensei import TestSensei from runner.runner_tests.test_helper import TestHelper +from runner.runner_tests.test_path_to_enlightenment import TestFilterKoanNames +from runner.runner_tests.test_path_to_enlightenment import TestKoansSuite + def suite(): suite = unittest.TestSuite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMountain)) suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestSensei)) suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestHelper)) + suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFilterKoanNames)) + suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestKoansSuite)) return suite + if __name__ == '__main__': - unittest.TextTestRunner(verbosity=2).run(suite()) - \ No newline at end of file + res = unittest.TextTestRunner(verbosity=2).run(suite()) + sys.exit(not res.wasSuccessful()) diff --git a/contemplate_koans.py b/contemplate_koans.py new file mode 100644 index 000000000..ddf992388 --- /dev/null +++ b/contemplate_koans.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# +# Acknowledgment: +# +# Python Koans is a port of Ruby Koans originally written by Jim Weirich +# and Joe O'brien of Edgecase. There are some differences and tweaks specific +# to the Python language, but a great deal of it has been copied wholesale. +# So thanks guys! +# + +import sys + +if __name__ == '__main__': + if sys.version_info < (3, 0): + print("\nThis is the Python 3 version of Python Koans, but you are " + + "running it with Python 2!\n\n" + "Did you accidentally use the wrong Python script? \nTry:\n\n" + + " python3 contemplate_koans.py\n") + else: + if sys.version_info < (3, 7): + print("\n" + + "********************************************************\n" + + "WARNING:\n" + + "This version of Python Koans was designed for " + + "Python 3.7 or greater.\n" + + "Your version of Python is older, so you may run into " + + "problems!\n\n" + + "But let's see how far we get...\n" + + "********************************************************\n") + + from runner.mountain import Mountain + + Mountain().walk_the_path(sys.argv) diff --git a/python 2/example_file.txt b/example_file.txt similarity index 100% rename from python 2/example_file.txt rename to example_file.txt diff --git a/koans.txt b/koans.txt new file mode 100644 index 000000000..8e2788028 --- /dev/null +++ b/koans.txt @@ -0,0 +1,40 @@ +# Lines starting with # are ignored. +koans.about_asserts.AboutAsserts +koans.about_strings.AboutStrings +koans.about_none.AboutNone +koans.about_lists.AboutLists +koans.about_list_assignments.AboutListAssignments +koans.about_dictionaries.AboutDictionaries +koans.about_string_manipulation.AboutStringManipulation +koans.about_tuples.AboutTuples +koans.about_methods.AboutMethods +koans.about_control_statements.AboutControlStatements +koans.about_true_and_false.AboutTrueAndFalse +koans.about_sets.AboutSets +koans.about_triangle_project.AboutTriangleProject +koans.about_exceptions.AboutExceptions +koans.about_triangle_project2.AboutTriangleProject2 +koans.about_iteration.AboutIteration +koans.about_comprehension.AboutComprehension +koans.about_generators.AboutGenerators +koans.about_lambdas.AboutLambdas +koans.about_scoring_project.AboutScoringProject +koans.about_classes.AboutClasses +koans.about_with_statements.AboutWithStatements +koans.about_monkey_patching.AboutMonkeyPatching +koans.about_dice_project.AboutDiceProject +koans.about_method_bindings.AboutMethodBindings +koans.about_decorating_with_functions.AboutDecoratingWithFunctions +koans.about_decorating_with_classes.AboutDecoratingWithClasses +koans.about_inheritance.AboutInheritance +koans.about_multiple_inheritance.AboutMultipleInheritance +koans.about_scope.AboutScope +koans.about_modules.AboutModules +koans.about_packages.AboutPackages +koans.about_class_attributes.AboutClassAttributes +koans.about_attribute_access.AboutAttributeAccess +koans.about_deleting_objects.AboutDeletingObjects +koans.about_proxy_object_project.AboutProxyObjectProject +koans.about_proxy_object_project.TelevisionTest +koans.about_extra_credit.AboutExtraCredit +koans.about_regex.AboutRegex diff --git a/python 2/koans/GREEDS_RULES.txt b/koans/GREEDS_RULES.txt similarity index 100% rename from python 2/koans/GREEDS_RULES.txt rename to koans/GREEDS_RULES.txt diff --git a/python 2/koans/__init__.py b/koans/__init__.py similarity index 100% rename from python 2/koans/__init__.py rename to koans/__init__.py diff --git a/python 2/koans/a_package_folder/__init__.py b/koans/a_package_folder/__init__.py similarity index 100% rename from python 2/koans/a_package_folder/__init__.py rename to koans/a_package_folder/__init__.py diff --git a/python 3/koans/a_package_folder/a_module.py b/koans/a_package_folder/a_module.py similarity index 100% rename from python 3/koans/a_package_folder/a_module.py rename to koans/a_package_folder/a_module.py diff --git a/python 2/koans/about_asserts.py b/koans/about_asserts.py similarity index 52% rename from python 2/koans/about_asserts.py rename to koans/about_asserts.py index 3905498d3..d17ed0cdf 100644 --- a/python 2/koans/about_asserts.py +++ b/koans/about_asserts.py @@ -9,13 +9,18 @@ def test_assert_truth(self): """ We shall contemplate truth by testing reality, via asserts. """ - self.assertTrue(False) # This should be true - + + # Confused? This video should help: + # + # http://bit.ly/about_asserts + + self.assertTrue(False) # 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") def test_fill_in_values(self): """ @@ -37,14 +42,37 @@ def test_a_better_way_of_asserting_equality(self): """ expected_value = __ actual_value = 1 + 1 - + self.assertEqual(expected_value, actual_value) - + def test_that_unittest_asserts_work_the_same_way_as_python_asserts(self): """ - Knowing how things really work is half the battle + Understand what lies within. """ - + # This throws an AssertionError exception assert False - + + def test_that_sometimes_we_need_to_know_the_class_type(self): + """ + What is in a class name? + """ + + # Sometimes we will ask you what the class type of an object is. + # + # For example, contemplate the text string "navel". What is its class type? + # The koans runner will include this feedback for this koan: + # + # AssertionError: '-=> FILL ME IN! <=-' != + # + # So "navel".__class__ is equal to ? No not quite. This + # is just what it displays. The answer is simply str. + # + # See for yourself: + + self.assertEqual(__, "navel".__class__) # It's str, not + + # Need an illustration? More reading can be found here: + # + # https://github.com/gregmalcolm/python_koans/wiki/Class-Attribute + diff --git a/python 3/koans/about_attribute_access.py b/koans/about_attribute_access.py similarity index 83% rename from python 3/koans/about_attribute_access.py rename to koans/about_attribute_access.py index 4eabb76f9..f12f61143 100644 --- a/python 3/koans/about_attribute_access.py +++ b/koans/about_attribute_access.py @@ -8,113 +8,114 @@ from runner.koan import * class AboutAttributeAccess(Koan): - + class TypicalObject: pass - + def test_calling_undefined_functions_normally_results_in_errors(self): typical = self.TypicalObject() - + with self.assertRaises(___): typical.foobar() - + def test_calling_getattribute_causes_an_attribute_error(self): typical = self.TypicalObject() with self.assertRaises(___): typical.__getattribute__('foobar') - + # THINK ABOUT IT: # # If the method __getattribute__() causes the AttributeError, then # what would happen if we redefine __getattribute__()? - + # ------------------------------------------------------------------ - + class CatchAllAttributeReads: def __getattribute__(self, attr_name): return "Someone called '" + attr_name + "' and it could not be found" - + def test_all_attribute_reads_are_caught(self): catcher = self.CatchAllAttributeReads() - - self.assertRegexpMatches(catcher.foobar, __) + + self.assertRegex(catcher.foobar, __) def test_intercepting_return_values_can_disrupt_the_call_chain(self): catcher = self.CatchAllAttributeReads() - self.assertRegexpMatches(catcher.foobaz, __) # This is fine - + self.assertRegex(catcher.foobaz, __) # This is fine + try: catcher.foobaz(1) except TypeError as ex: err_msg = ex.args[0] - - self.assertRegexpMatches(err_msg, __) - + + self.assertRegex(err_msg, __) + # foobaz returns a string. What happens to the '(1)' part? # Try entering this into a python console to reproduce the issue: # # "foobaz"(1) # - + def test_changes_to_the_getattribute_implementation_affects_getattr_function(self): catcher = self.CatchAllAttributeReads() - - self.assertRegexpMatches(getattr(catcher, 'any_attribute'), __) - + + self.assertRegex(getattr(catcher, 'any_attribute'), __) + # ------------------------------------------------------------------ - + class WellBehavedFooCatcher: def __getattribute__(self, attr_name): if attr_name[:3] == "foo": return "Foo to you too" else: return super().__getattribute__(attr_name) - + def test_foo_attributes_are_caught(self): catcher = self.WellBehavedFooCatcher() - + self.assertEqual(__, catcher.foo_bar) self.assertEqual(__, catcher.foo_baz) - + def test_non_foo_messages_are_treated_normally(self): catcher = self.WellBehavedFooCatcher() - + with self.assertRaises(___): catcher.normal_undefined_attribute # ------------------------------------------------------------------ - + global stack_depth - stack_depth = 0 + stack_depth = 0 class RecursiveCatcher: def __init__(self): global stack_depth - stack_depth = 0 + stack_depth = 0 self.no_of_getattribute_calls = 0 - - def __getattribute__(self, attr_name): - global stack_depth # We need something that is outside the scope of this class + + def __getattribute__(self, attr_name): + # We need something that is outside the scope of this class: + global stack_depth stack_depth += 1 if stack_depth<=10: # to prevent a stack overflow self.no_of_getattribute_calls += 1 # Oops! We just accessed an attribute (no_of_getattribute_calls) # Guess what happens when self.no_of_getattribute_calls is - # accessed? + # accessed? # Using 'object' directly because using super() here will also # trigger a __getattribute__() call. - return object.__getattribute__(self, attr_name) - + return object.__getattribute__(self, attr_name) + def my_method(self): pass - + def test_getattribute_is_a_bit_overzealous_sometimes(self): catcher = self.RecursiveCatcher() catcher.my_method() global stack_depth self.assertEqual(__, stack_depth) - + # ------------------------------------------------------------------ class MinimalCatcher: @@ -126,7 +127,7 @@ def __init__(self): def __getattr__(self, attr_name): self.no_of_getattr_calls += 1 return self.DuffObject - + def my_method(self): pass @@ -135,62 +136,66 @@ def test_getattr_ignores_known_attributes(self): catcher.my_method() self.assertEqual(__, catcher.no_of_getattr_calls) - + def test_getattr_only_catches_unknown_attributes(self): catcher = self.MinimalCatcher() catcher.purple_flamingos() catcher.free_pie() - + self.assertEqual(__, type(catcher.give_me_duff_or_give_me_death()).__name__) - + self.assertEqual(__, catcher.no_of_getattr_calls) - + # ------------------------------------------------------------------ class PossessiveSetter(object): def __setattr__(self, attr_name, value): new_attr_name = attr_name - + if attr_name[-5:] == 'comic': new_attr_name = "my_" + new_attr_name elif attr_name[-3:] == 'pie': - new_attr_name = "a_" + new_attr_name + new_attr_name = "a_" + new_attr_name - object.__setattr__(self, new_attr_name, value) + object.__setattr__(self, new_attr_name, value) def test_setattr_intercepts_attribute_assignments(self): fanboy = self.PossessiveSetter() - + fanboy.comic = 'The Laminator, issue #1' fanboy.pie = 'blueberry' - - self.assertEqual(__, fanboy.a_pie) + + self.assertEqual(__, fanboy.a_pie) + + # + # NOTE: Change the prefix to make this next assert pass + # prefix = '__' self.assertEqual("The Laminator, issue #1", getattr(fanboy, prefix + '_comic')) # ------------------------------------------------------------------ - class ScarySetter: + class ScarySetter: def __init__(self): self.num_of_coconuts = 9 self._num_of_private_coconuts = 2 - + def __setattr__(self, attr_name, value): new_attr_name = attr_name - + if attr_name[0] != '_': new_attr_name = "altered_" + new_attr_name - + object.__setattr__(self, new_attr_name, value) - + def test_it_modifies_external_attribute_as_expected(self): setter = self.ScarySetter() setter.e = "mc hammer" - + self.assertEqual(__, setter.altered_e) - + def test_it_mangles_some_internal_attributes(self): setter = self.ScarySetter() diff --git a/python 3/koans/about_class_attributes.py b/koans/about_class_attributes.py similarity index 88% rename from python 3/koans/about_class_attributes.py rename to koans/about_class_attributes.py index bdf0ea70a..7a5be1df4 100644 --- a/python 3/koans/about_class_attributes.py +++ b/koans/about_class_attributes.py @@ -10,38 +10,38 @@ class AboutClassAttributes(Koan): class Dog: pass - + def test_objects_are_objects(self): fido = self.Dog() self.assertEqual(__, isinstance(fido, object)) def test_classes_are_types(self): self.assertEqual(__, self.Dog.__class__ == type) - + def test_classes_are_objects_too(self): self.assertEqual(__, issubclass(self.Dog, object)) - + def test_objects_have_methods(self): fido = self.Dog() self.assertEqual(__, len(dir(fido))) - + def test_classes_have_methods(self): self.assertEqual(__, len(dir(self.Dog))) def test_creating_objects_without_defining_a_class(self): - singularity = object() + singularity = object() self.assertEqual(__, len(dir(singularity))) def test_defining_attributes_on_individual_objects(self): fido = self.Dog() fido.legs = 4 - + self.assertEqual(__, fido.legs) - + def test_defining_functions_on_individual_objects(self): fido = self.Dog() fido.wag = lambda : 'fidos wag' - + self.assertEqual(__, fido.wag()) def test_other_objects_are_not_affected_by_these_singleton_functions(self): @@ -49,13 +49,13 @@ def test_other_objects_are_not_affected_by_these_singleton_functions(self): rover = self.Dog() def wag(): - return 'fidos wag' + return 'fidos wag' fido.wag = wag - + with self.assertRaises(___): 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.assertRegex(self.Dog2.growl(), __) + def test_classmethods_are_not_independent_of_instance_methods(self): fido = self.Dog2() - self.assertRegexpMatches(fido.growl(), __) - self.assertRegexpMatches(self.Dog2.growl(), __) + self.assertRegex(fido.growl(), __) + self.assertRegex(self.Dog2.growl(), __) def test_staticmethods_are_unbound_functions_housed_in_a_class(self): - self.assertRegexpMatches(self.Dog2.bark(), __) + self.assertRegex(self.Dog2.bark(), __) def test_staticmethods_also_overshadow_instance_methods(self): fido = self.Dog2() - self.assertRegexpMatches(fido.bark(), __) - + self.assertRegex(fido.bark(), __) + # ------------------------------------------------------------------ - + 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" - + 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()) - + 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()) - + # ------------------------------------------------------------------ - + 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()) def test_you_can_define_static_methods_without_using_a_decorator(self): self.assertEqual(__, 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()) diff --git a/python 3/koans/about_classes.py b/koans/about_classes.py similarity index 81% rename from python 3/koans/about_classes.py rename to koans/about_classes.py index fdc67e17a..22dc4e7f0 100644 --- a/python 3/koans/about_classes.py +++ b/koans/about_classes.py @@ -3,79 +3,81 @@ from runner.koan import * + class AboutClasses(Koan): class Dog: "Dogs need regular walkies. Never, ever let them drive." - - def test_instances_of_classes_can_be_created_adding_parenthesis(self): + + def test_instances_of_classes_can_be_created_adding_parentheses(self): + # NOTE: The .__name__ attribute will convert the class + # into a string value. fido = self.Dog() - self.assertEqual(__, type(fido).__name__) - + self.assertEqual(__, fido.__class__.__name__) + def test_classes_have_docstrings(self): - self.assertRegexpMatches(self.Dog.__doc__, __) - + self.assertRegex(self.Dog.__doc__, __) + # ------------------------------------------------------------------ - - class Dog2: - def __init__(self): + + class Dog2: + def __init__(self): self._name = 'Paul' - + def set_name(self, a_name): self._name = a_name - + def test_init_method_is_the_constructor(self): dog = self.Dog2() self.assertEqual(__, dog._name) - - def test_private_attributes_are_not_really_private(self): + + def test_private_attributes_are_not_really_private(self): dog = self.Dog2() dog.set_name("Fido") self.assertEqual(__, dog._name) # The _ prefix in _name implies private ownership, but nothing is truly # private in Python. - + 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")) # getattr(), setattr() and delattr() are a way of accessing attributes # by method rather than through assignment operators - + self.assertEqual(__, 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. - + # ------------------------------------------------------------------ - + class Dog3: - def __init__(self): + def __init__(self): self._name = None def set_name(self, a_name): self._name = a_name - + def get_name(self): return self._name - + name = property(get_name, set_name) - - + def test_that_name_can_be_read_as_a_property(self): fido = self.Dog3() fido.set_name("Fido") - + # access as method self.assertEqual(__, fido.get_name()) - + # access as property self.assertEqual(__, fido.name) - + # ------------------------------------------------------------------ - + class Dog4: - def __init__(self): + def __init__(self): self._name = None @property @@ -85,79 +87,82 @@ def name(self): @name.setter def name(self, a_name): self._name = a_name - + def test_creating_properties_with_decorators_is_slightly_easier(self): fido = self.Dog4() - + fido.name = "Fido" self.assertEqual(__, fido.name) - + # ------------------------------------------------------------------ - + class Dog5: - def __init__(self, initial_name): + def __init__(self, initial_name): self._name = initial_name @property def name(self): return self._name - + def test_init_provides_initial_values_for_instance_variables(self): - fido = self.Dog5("Fido") + fido = self.Dog5("Fido") self.assertEqual(__, fido.name) - + def test_args_must_match_init(self): - with self.assertRaises(___): self.Dog5() - + with self.assertRaises(___): + self.Dog5() + # THINK ABOUT IT: # Why is this so? - - def test_different_objects_have_difference_instance_variables(self): + + def test_different_objects_have_different_instance_variables(self): fido = self.Dog5("Fido") rover = self.Dog5("Rover") - + self.assertEqual(__, rover.name == fido.name) - + # ------------------------------------------------------------------ - + class Dog6: def __init__(self, initial_name): self._name = initial_name - + def get_self(self): return self - + def __str__(self): + # + # Implement this! + # 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.get_self()) # Not a string! + def test_str_provides_a_string_version_of_the_object(self): fido = self.Dog6("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)) - + def test_repr_provides_a_more_complete_string_version(self): fido = self.Dog6("Fido") self.assertEqual(__, repr(fido)) - + def test_all_objects_support_str_and_repr(self): - seq = [1,2,3] - + seq = [1, 2, 3] + self.assertEqual(__, str(seq)) self.assertEqual(__, repr(seq)) - + self.assertEqual(__, str("STRING")) self.assertEqual(__, repr("STRING")) - diff --git a/koans/about_comprehension.py b/koans/about_comprehension.py new file mode 100644 index 000000000..e5add6d9e --- /dev/null +++ b/koans/about_comprehension.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + + +class AboutComprehension(Koan): + + + def test_creating_lists_with_list_comprehensions(self): + feast = ['lambs', 'sloths', 'orangutans', 'breakfast cereals', + 'fruit bats'] + + comprehension = [delicacy.capitalize() for delicacy in feast] + + self.assertEqual(__, comprehension[0]) + self.assertEqual(__, comprehension[2]) + + def test_filtering_lists_with_list_comprehensions(self): + feast = ['spam', 'sloths', 'orangutans', 'breakfast cereals', + 'fruit bats'] + + comprehension = [delicacy for delicacy in feast if len(delicacy) > 6] + + self.assertEqual(__, len(feast)) + self.assertEqual(__, len(comprehension)) + + def test_unpacking_tuples_in_list_comprehensions(self): + list_of_tuples = [(1, 'lumberjack'), (2, 'inquisition'), (4, 'spam')] + comprehension = [ skit * number for number, skit in list_of_tuples ] + + self.assertEqual(__, comprehension[0]) + self.assertEqual(__, comprehension[2]) + + def test_double_list_comprehension(self): + list_of_eggs = ['poached egg', 'fried egg'] + list_of_meats = ['lite spam', 'ham spam', 'fried spam'] + + + comprehension = [ '{0} and {1}'.format(egg, meat) for egg in list_of_eggs for meat in list_of_meats] + + + self.assertEqual(__, comprehension[0]) + self.assertEqual(__, len(comprehension)) + + def test_creating_a_set_with_set_comprehension(self): + comprehension = { x for x in 'aabbbcccc'} + + self.assertEqual(__, comprehension) # remember that set members are unique + + def test_creating_a_dictionary_with_dictionary_comprehension(self): + dict_of_weapons = {'first': 'fear', 'second': 'surprise', + 'third':'ruthless efficiency', 'fourth':'fanatical devotion', + 'fifth': None} + + dict_comprehension = { k.upper(): weapon for k, weapon in dict_of_weapons.items() if weapon} + + self.assertEqual(__, 'first' in dict_comprehension) + self.assertEqual(__, 'FIRST' in dict_comprehension) + self.assertEqual(__, len(dict_of_weapons)) + self.assertEqual(__, len(dict_comprehension)) diff --git a/python 3/koans/about_control_statements.py b/koans/about_control_statements.py similarity index 73% rename from python 3/koans/about_control_statements.py rename to koans/about_control_statements.py index f1cb5c0bd..842c8d91f 100644 --- a/python 3/koans/about_control_statements.py +++ b/koans/about_control_statements.py @@ -17,7 +17,16 @@ def test_if_then_statements(self): if True: result = 'true value' self.assertEqual(__, result) - + + def test_if_then_elif_else_statements(self): + if False: + result = 'first value' + elif True: + result = 'true value' + else: + result = 'default value' + self.assertEqual(__, result) + def test_while_statement(self): i = 1 result = 1 @@ -25,7 +34,7 @@ def test_while_statement(self): result = result * i i += 1 self.assertEqual(__, result) - + def test_break_statement(self): i = 1 result = 1 @@ -34,39 +43,38 @@ def test_break_statement(self): result = result * i i += 1 self.assertEqual(__, result) - + def test_continue_statement(self): i = 0 result = [] while i < 10: i += 1 if (i % 2) == 0: continue - result.append(i) + result.append(i) self.assertEqual(__, result) - + def test_for_statement(self): phrase = ["fish", "and", "chips"] result = [] for item in phrase: result.append(item.upper()) self.assertEqual([__, __, __], result) - + def test_for_statement_with_tuples(self): round_table = [ - ("Lancelot", "Blue"), + ("Lancelot", "Blue"), ("Galahad", "I don't know!"), ("Robin", "Blue! I mean Green!"), - ("Arthur", "Is that an African Swallow or Amazonian Swallow?") + ("Arthur", "Is that an African Swallow or European Swallow?") ] result = [] for knight, answer in round_table: result.append("Contestant: '" + knight + "' Answer: '" + answer + "'") - + text = __ - - self.assertRegexpMatches(result[2], text) - - self.assertNoRegexpMatches(result[0], text) - self.assertNoRegexpMatches(result[1], text) - self.assertNoRegexpMatches(result[3], text) - \ No newline at end of file + + self.assertRegex(result[2], text) + + self.assertNotRegex(result[0], text) + self.assertNotRegex(result[1], text) + self.assertNotRegex(result[3], text) diff --git a/python 3/koans/about_decorating_with_classes.py b/koans/about_decorating_with_classes.py similarity index 91% rename from python 3/koans/about_decorating_with_classes.py rename to koans/about_decorating_with_classes.py index 358488c4c..00bd5fe90 100644 --- a/python 3/koans/about_decorating_with_classes.py +++ b/koans/about_decorating_with_classes.py @@ -4,7 +4,7 @@ from runner.koan import * import functools - + class AboutDecoratingWithClasses(Koan): def maximum(self, a, b): if a>b: @@ -18,17 +18,17 @@ def test_partial_that_wrappers_no_args(self): the partial. """ max = functools.partial(self.maximum) - + self.assertEqual(__, max(7,23)) self.assertEqual(__, 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)) - def test_partial_that_wrappers_all_args(self): + def test_partial_that_wrappers_all_args(self): always99 = functools.partial(self.maximum, 99, 20) always20 = functools.partial(self.maximum, 9, 20) @@ -40,43 +40,43 @@ def test_partial_that_wrappers_all_args(self): class doubleit: def __init__(self, fn): self.fn = fn - + def __call__(self, *args): return self.fn(*args) + ', ' + self.fn(*args) - + def __get__(self, obj, cls=None): if not obj: # Decorating an unbound function return self else: # Decorating a bound method - return functools.partial(self, obj) + return functools.partial(self, obj) @doubleit def foo(self): return "foo" - + @doubleit def parrot(self, text): return text.upper() - + 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')) # ------------------------------------------------------------------ def sound_check(self): - #Note: no decorator + #Note: no decorator return "Testing..." 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()) # ------------------------------------------------------------------ @@ -84,24 +84,24 @@ def test_what_a_decorator_is_doing_to_a_function(self): class documenter: def __init__(self, *args): self.fn_doc = args[0] - + def __call__(self, fn): def decorated_function(*args): return fn(*args) - + if fn.__doc__: decorated_function.__doc__ = fn.__doc__ + ": " + self.fn_doc else: decorated_function.__doc__ = self.fn_doc return decorated_function - + @documenter("Increments a value by one. Kind of.") def count_badly(self, num): num += 1 if num==3: - return 5 + return 5 else: - num + return num @documenter("Does nothing") def idler(self, num): "Idler" @@ -115,7 +115,7 @@ def test_documentor_which_already_has_a_docstring(self): self.assertEqual(__, self.idler.__doc__) # ------------------------------------------------------------------ - + @documenter("DOH!") @doubleit @doubleit @@ -125,4 +125,4 @@ def homer(self): def test_we_can_chain_decorators(self): self.assertEqual(__, self.homer()) self.assertEqual(__, self.homer.__doc__) - \ No newline at end of file + diff --git a/python 2/koans/about_decorating_with_functions.py b/koans/about_decorating_with_functions.py similarity index 79% rename from python 2/koans/about_decorating_with_functions.py rename to koans/about_decorating_with_functions.py index 69cd02d93..a51260213 100644 --- a/python 2/koans/about_decorating_with_functions.py +++ b/koans/about_decorating_with_functions.py @@ -3,31 +3,30 @@ from runner.koan import * - + class AboutDecoratingWithFunctions(Koan): def addcowbell(fn): fn.wow_factor = 'COWBELL BABY!' return fn - + @addcowbell 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.assertRegex(self.mediocre_song(), __) + self.assertEqual(__, self.mediocre_song.wow_factor) + # ------------------------------------------------------------------ def xmltag(fn): def func(*args): return '<' + fn(*args) + '/>' return func - + @xmltag def render_tag(self, name): return name - + def test_decorators_can_change_a_function_output(self): self.assertEqual(__, self.render_tag('llama')) - diff --git a/python 3/koans/about_deleting_objects.py b/koans/about_deleting_objects.py similarity index 90% rename from python 3/koans/about_deleting_objects.py rename to koans/about_deleting_objects.py index 87eea9a23..152265c98 100644 --- a/python 3/koans/about_deleting_objects.py +++ b/koans/about_deleting_objects.py @@ -8,36 +8,36 @@ def test_del_can_remove_slices(self): lottery_nums = [4, 8, 15, 16, 23, 42] del lottery_nums[1] del lottery_nums[2:4] - + self.assertEqual(___, 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 - + # ==================================================================== - + class ClosingSale: def __init__(self): self.hamsters = 7 self.zebras = 84 - + def cameras(self): return 34 - + def toilet_brushes(self): return 48 - + def jellies(self): return 5 - + def test_del_can_remove_attributes(self): crazy_discounts = self.ClosingSale() del self.ClosingSale.toilet_brushes del crazy_discounts.hamsters - + try: still_available = crazy_discounts.toilet_brushes() except AttributeError as e: @@ -47,16 +47,16 @@ def test_del_can_remove_attributes(self): still_available = crazy_discounts.hamsters except AttributeError as e: err_msg2 = e.args[0] - - self.assertRegexpMatches(err_msg1, __) - self.assertRegexpMatches(err_msg2, __) + + self.assertRegex(err_msg1, __) + self.assertRegex(err_msg2, __) # ==================================================================== class ClintEastwood: def __init__(self): self._name = None - + def get_name(self): try: return self._name @@ -65,13 +65,13 @@ def get_name(self): def set_name(self, name): self._name = name - + def del_name(self): del self._name - + name = property(get_name, set_name, del_name, \ "Mr Eastwood's current alias") - + def test_del_works_with_properties(self): cowboy = self.ClintEastwood() cowboy.name = 'Senor Ninguno' @@ -79,14 +79,14 @@ def test_del_works_with_properties(self): del cowboy.name self.assertEqual(__, cowboy.name) - - - # ==================================================================== + + + # ==================================================================== class Prisoner: def __init__(self): self._name = None - + @property def name(self): return self._name @@ -94,11 +94,11 @@ def name(self): @name.setter def name(self, name): self._name = name - + @name.deleter def name(self): self._name = 'Number Six' - + def test_another_way_to_make_a_deletable_property(self): citizen = self.Prisoner() citizen.name = "Patrick" @@ -108,12 +108,12 @@ def test_another_way_to_make_a_deletable_property(self): self.assertEqual(__, citizen.name) # ==================================================================== - + class MoreOrganisedClosingSale(ClosingSale): def __init__(self): self.last_deletion = None super().__init__() - + def __delattr__(self, attr_name): self.last_deletion = attr_name diff --git a/python 3/koans/about_dice_project.py b/koans/about_dice_project.py similarity index 92% rename from python 3/koans/about_dice_project.py rename to koans/about_dice_project.py index c9bdc44fe..27bc54a24 100644 --- a/python 3/koans/about_dice_project.py +++ b/koans/about_dice_project.py @@ -8,15 +8,15 @@ class DiceSet: def __init__(self): self._values = None - - @property + + @property 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 + pass class AboutDiceProject(Koan): def test_can_create_a_dice_set(self): @@ -25,43 +25,43 @@ def test_can_create_a_dice_set(self): def test_rolling_the_dice_returns_a_set_of_integers_between_1_and_6(self): dice = DiceSet() - + dice.roll(5) self.assertTrue(isinstance(dice.values, list), "should be a list") self.assertEqual(5, len(dice.values)) for value in dice.values: self.assertTrue(value >= 1 and value <= 6, "value " + str(value) + " must be between 1 and 6") - + def test_dice_values_do_not_change_unless_explicitly_rolled(self): dice = DiceSet() dice.roll(5) first_time = dice.values second_time = dice.values self.assertEqual(first_time, second_time) - + def test_dice_values_should_change_between_rolls(self): dice = DiceSet() - + dice.roll(5) first_time = dice.values - + dice.roll(5) second_time = dice.values - + self.assertNotEqual(first_time, second_time, \ "Two rolls should not be equal") - + # THINK ABOUT IT: # # 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? - + def test_you_can_roll_different_numbers_of_dice(self): dice = DiceSet() - + dice.roll(3) self.assertEqual(3, len(dice.values)) - + dice.roll(1) self.assertEqual(1, len(dice.values)) diff --git a/koans/about_dictionaries.py b/koans/about_dictionaries.py new file mode 100644 index 000000000..da1ed6bfe --- /dev/null +++ b/koans/about_dictionaries.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Based on AboutHashes in the Ruby Koans +# + +from runner.koan import * + +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)) + + def test_dictionary_literals(self): + empty_dict = {} + self.assertEqual(dict, type(empty_dict)) + babel_fish = { 'one': 'uno', 'two': 'dos' } + self.assertEqual(__, len(babel_fish)) + + def test_accessing_dictionaries(self): + babel_fish = { 'one': 'uno', 'two': 'dos' } + self.assertEqual(__, babel_fish['one']) + self.assertEqual(__, babel_fish['two']) + + def test_changing_dictionaries(self): + babel_fish = { 'one': 'uno', 'two': 'dos' } + babel_fish['one'] = 'eins' + + expected = { 'two': 'dos', 'one': __ } + self.assertDictEqual(expected, babel_fish) + + def test_dictionary_is_unordered(self): + dict1 = { 'one': 'uno', 'two': 'dos' } + dict2 = { 'two': 'dos', 'one': 'uno' } + + self.assertEqual(__, dict1 == dict2) + + + def test_dictionary_keys_and_values(self): + babel_fish = {'one': 'uno', 'two': 'dos'} + self.assertEqual(__, len(babel_fish.keys())) + self.assertEqual(__, len(babel_fish.values())) + self.assertEqual(__, 'one' in babel_fish.keys()) + self.assertEqual(__, 'two' in babel_fish.values()) + self.assertEqual(__, 'uno' in babel_fish.keys()) + self.assertEqual(__, 'dos' in babel_fish.values()) + + 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']) + diff --git a/python 3/koans/about_exceptions.py b/koans/about_exceptions.py similarity index 95% rename from python 3/koans/about_exceptions.py rename to koans/about_exceptions.py index 956f45f24..d321bbc50 100644 --- a/python 3/koans/about_exceptions.py +++ b/koans/about_exceptions.py @@ -7,33 +7,33 @@ class AboutExceptions(Koan): class MySpecialError(RuntimeError): pass - + 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__) - + def test_try_clause(self): result = None try: self.fail("Oops") except Exception as ex: result = 'exception handled' - + ex2 = ex - + self.assertEqual(__, result) - + self.assertEqual(__, isinstance(ex2, Exception)) self.assertEqual(__, isinstance(ex2, RuntimeError)) - + self.assertTrue(issubclass(RuntimeError, Exception), \ "RuntimeError is a subclass of Exception") - + self.assertEqual(__, ex2.args[0]) - + def test_raising_a_specific_error(self): result = None try: @@ -41,10 +41,10 @@ def test_raising_a_specific_error(self): except self.MySpecialError as ex: result = 'exception handled' msg = ex.args[0] - + self.assertEqual(__, result) self.assertEqual(__, msg) - + def test_else_clause(self): result = None try: @@ -54,10 +54,10 @@ def test_else_clause(self): pass else: result = 'no damage done' - + self.assertEqual(__, result) - + def test_finally_clause(self): result = None try: @@ -67,5 +67,5 @@ def test_finally_clause(self): pass finally: result = 'always run' - + self.assertEqual(__, result) diff --git a/python 3/koans/about_extra_credit.py b/koans/about_extra_credit.py similarity index 99% rename from python 3/koans/about_extra_credit.py rename to koans/about_extra_credit.py index 54baef4ea..cb5a67702 100644 --- a/python 3/koans/about_extra_credit.py +++ b/koans/about_extra_credit.py @@ -17,4 +17,3 @@ class AboutExtraCredit(Koan): # test suite in runner/path_to_enlightenment.py def test_extra_credit_task(self): pass - \ No newline at end of file diff --git a/python 3/koans/about_generators.py b/koans/about_generators.py similarity index 82% rename from python 3/koans/about_generators.py rename to koans/about_generators.py index e22b677c7..a81a43ba6 100644 --- a/python 3/koans/about_generators.py +++ b/koans/about_generators.py @@ -11,27 +11,27 @@ from runner.koan import * 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) - + def test_generators_are_different_to_list_comprehensions(self): num_list = [x*2 for x in range(1,3)] num_generator = (x*2 for x in range(1,3)) - + 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.assertEqual(__, list(num_generator)[0]) + # 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 # instead of stored. @@ -43,30 +43,30 @@ 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(__, attempt1) + self.assertEqual(__, attempt2) + # ------------------------------------------------------------------ - + def simple_generator_method(self): yield 'peanut' yield 'butter' yield 'and' yield 'jelly' - + 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) - def test_coroutines_can_take_arguments(self): + def test_generators_can_be_manually_iterated_and_closed(self): result = self.simple_generator_method() self.assertEqual(__, next(result)) self.assertEqual(__, next(result)) result.close() - + # ------------------------------------------------------------------ def square_me(self, seq): @@ -91,13 +91,13 @@ def test_generator_keeps_track_of_local_variables(self): self.assertEqual(__, list(result)) # ------------------------------------------------------------------ - - def generator_with_coroutine(self): + + def coroutine(self): result = yield yield result - - def test_generators_can_take_coroutines(self): - generator = self.generator_with_coroutine() + + def test_generators_can_act_as_coroutines(self): + generator = self.coroutine() # THINK ABOUT IT: # Why is this line necessary? @@ -109,36 +109,34 @@ def test_generators_can_take_coroutines(self): self.assertEqual(__, generator.send(1 + 2)) def test_before_sending_a_value_to_a_generator_next_must_be_called(self): - generator = self.generator_with_coroutine() + generator = self.coroutine() try: - generator.send(1+2) + generator.send(1 + 2) except TypeError as ex: - self.assertMatch(__, ex[0]) - + self.assertRegex(ex.args[0], __) + # ------------------------------------------------------------------ - + def yield_tester(self): value = yield if value: yield value else: yield 'no value' - + 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')) - + generator2 = self.yield_tester() next(generator2) self.assertEqual(__, next(generator2)) - def test_send_none_is_equivelant_to_next(self): + def test_send_none_is_equivalent_to_next(self): generator = self.yield_tester() - + next(generator) - # 'next(generator)' is exactly equivelant to 'generator.send(None)' + # 'next(generator)' is exactly equivalent to 'generator.send(None)' self.assertEqual(__, generator.send(None)) - - diff --git a/python 3/koans/about_inheritance.py b/koans/about_inheritance.py similarity index 87% rename from python 3/koans/about_inheritance.py rename to koans/about_inheritance.py index ac9cf0f62..5d2407dfa 100644 --- a/python 3/koans/about_inheritance.py +++ b/koans/about_inheritance.py @@ -5,85 +5,85 @@ class AboutInheritance(Koan): class Dog: - def __init__(self, name): + def __init__(self, name): self._name = name @property def name(self): return self._name - + def bark(self): return "WOOF" - + class Chihuahua(Dog): def wag(self): return "happy" - + def bark(self): return "yip" - + def test_subclasses_have_the_parent_as_an_ancestor(self): self.assertEqual(__, issubclass(self.Chihuahua, self.Dog)) - - def test_this_all_classes_in_python_3_ultimately_inherit_from_object_class(self): + + def test_all_classes_in_python_3_ultimately_inherit_from_object_class(self): self.assertEqual(__, 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 - + def test_instances_inherit_behavior_from_parent_class(self): chico = self.Chihuahua("Chico") self.assertEqual(__, chico.name) - + def test_subclasses_add_new_behavior(self): chico = self.Chihuahua("Chico") self.assertEqual(__, chico.wag()) - + fido = self.Dog("Fido") with self.assertRaises(___): fido.wag() - + def test_subclasses_can_modify_existing_behavior(self): chico = self.Chihuahua("Chico") self.assertEqual(__, chico.bark()) - + fido = self.Dog("Fido") self.assertEqual(__, fido.bark()) - + # ------------------------------------------------------------------ - + class BullDog(Dog): def bark(self): return super().bark() + ", GRR" # Note, super() is much simpler to use in Python 3! - + def test_subclasses_can_invoke_parent_behavior_via_super(self): ralph = self.BullDog("Ralph") self.assertEqual(__, ralph.bark()) - + # ------------------------------------------------------------------ - + class GreatDane(Dog): def growl(self): return super().bark() + ", GROWL" - + def test_super_works_across_methods(self): george = self.GreatDane("George") self.assertEqual(__, george.growl()) # --------------------------------------------------------- - + class Pug(Dog): - def __init__(self, name): + def __init__(self, name): pass - + class Greyhound(Dog): - def __init__(self, name): + def __init__(self, name): super().__init__(name) def test_base_init_does_not_get_called_automatically(self): snoopy = self.Pug("Snoopy") with self.assertRaises(___): 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.name) diff --git a/python 3/koans/about_iteration.py b/koans/about_iteration.py similarity index 56% rename from python 3/koans/about_iteration.py rename to koans/about_iteration.py index 5d69651f7..1faca8e33 100644 --- a/python 3/koans/about_iteration.py +++ b/koans/about_iteration.py @@ -7,13 +7,13 @@ class AboutIteration(Koan): def test_iterators_are_a_type(self): it = iter(range(1,6)) - - fib = 0 - + + total = 0 + for num in it: - fib += num - - self.assertEqual(__ , fib) + total += num + + self.assertEqual(__ , total) def test_iterating_with_next(self): stages = iter(['alpha','beta','gamma']) @@ -25,8 +25,8 @@ def test_iterating_with_next(self): next(stages) except StopIteration as ex: err_msg = 'Ran out of iterations' - - self.assertRegexpMatches(err_msg, __) + + self.assertRegex(err_msg, __) # ------------------------------------------------------------------ @@ -36,47 +36,51 @@ def add_ten(self, item): def test_map_transforms_elements_of_a_list(self): seq = [1, 2, 3] mapped_seq = list() - + 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, mapping.__class__) + self.assertEqual(__, mapping.__class__) + # 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) - - # None, iterator methods actually return objects of iter type in + + # Note, iterator methods actually return objects of iter type in # python 3. In python 2 map() would give you a list. - + def test_filter_selects_certain_items_from_a_list(self): - def is_even(item): return (item % 2) == 0 + def is_even(item): + return (item % 2) == 0 seq = [1, 2, 3, 4, 5, 6] even_numbers = list() - + for item in filter(is_even, seq): even_numbers.append(item) - + self.assertEqual(__, even_numbers) - - def test_just_return_first_item_found(self): - def is_big_name(item): return len(item) > 4 - - names = ["Jim", "Bill", "Clarence", "Doug", "Eli"] - name = None + def test_filter_returns_all_items_matching_criterion(self): + def is_big_name(item): + return len(item) > 4 + + names = ["Jim", "Bill", "Clarence", "Doug", "Eli", "Elizabeth"] iterator = filter(is_big_name, names) + + self.assertEqual(__, next(iterator)) + self.assertEqual(__, next(iterator)) + try: - name = next(iterator) + next(iterator) + pass except StopIteration: msg = 'Ran out of big names' - self.assertEqual(__, name) - + self.assertEquals(__, msg) # ------------------------------------------------------------------ @@ -85,58 +89,44 @@ def add(self,accum,item): def multiply(self,accum,item): return accum * item - + def test_reduce_will_blow_your_mind(self): import functools # As of Python 3 reduce() has been demoted from a builtin function # to the functools module. - + result = functools.reduce(self.add, [2, 3, 4]) - self.assertEqual(__, type(result).__name__) + self.assertEqual(__, result.__class__) # Reduce() syntax is same as Python 2 - + self.assertEqual(__, result) - - result2 = functools.reduce(self.multiply, [2, 3, 4], 1) + + result2 = functools.reduce(self.multiply, [2, 3, 4], 1) self.assertEqual(__, result2) - + # Extra Credit: # Describe in your own words what reduce does. - + # ------------------------------------------------------------------ - def test_creating_lists_with_list_comprehensions(self): - feast = ['lambs', 'sloths', 'orangutans', 'breakfast cereals', 'fruit bats'] - - comprehension = [delicacy.capitalize() for delicacy in feast] - - self.assertEqual(__, comprehension[0]) - self.assertEqual(__, comprehension[2]) - def test_use_pass_for_iterations_with_no_body(self): for num in range(1,5): pass - + self.assertEqual(__, 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)) - try: - # Files act like a collection of lines - file = open("example_file.txt") - - def make_upcase(line) : return line.strip().upper() - upcase_lines = map(make_upcase, file.readlines()) - self.assertEqual(__, list(upcase_lines)) - - # NOTE: You can create your own collections that work with each, - # map, select, etc. - finally: - # Arg, this is ugly. - # We will figure out how to fix this later. - if file: file.close() + def test_lines_in_a_file_are_iterable_sequences_too(self): + def make_upcase(line): + return line.strip().title() + + file = open("example_file.txt") + upcase_lines = map(make_upcase, file.readlines()) + self.assertEqual(__, list(upcase_lines)) + file.close() diff --git a/python 2/koans/about_lambdas.py b/koans/about_lambdas.py similarity index 96% rename from python 2/koans/about_lambdas.py rename to koans/about_lambdas.py index 2268e6c65..c1e688094 100644 --- a/python 2/koans/about_lambdas.py +++ b/koans/about_lambdas.py @@ -13,14 +13,14 @@ def test_lambdas_can_be_assigned_to_variables_and_called_explicitly(self): self.assertEqual(__, add_one(10)) # ------------------------------------------------------------------ - + def make_order(self, order): return lambda qty: str(qty) + " " + order + "s" - + 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)) diff --git a/python 3/koans/about_list_assignments.py b/koans/about_list_assignments.py similarity index 82% rename from python 3/koans/about_list_assignments.py rename to koans/about_list_assignments.py index 2c8e8639c..8a8d48090 100644 --- a/python 3/koans/about_list_assignments.py +++ b/koans/about_list_assignments.py @@ -11,7 +11,7 @@ class AboutListAssignments(Koan): def test_non_parallel_assignment(self): names = ["John", "Smith"] self.assertEqual(__, names) - + def test_parallel_assignments(self): first_name, last_name = ["John", "Smith"] self.assertEqual(__, first_name) @@ -22,12 +22,18 @@ def test_parallel_assignments_with_extra_values(self): self.assertEqual(__, title) self.assertEqual(__, first_names) self.assertEqual(__, last_name) - + + def test_parallel_assignments_with_fewer_values(self): + title, *first_names, last_name = ["Mr", "Bond"] + self.assertEqual(__, title) + self.assertEqual(__, first_names) + self.assertEqual(__, last_name) + def test_parallel_assignments_with_sublists(self): first_name, last_name = [["Willie", "Rae"], "Johnson"] self.assertEqual(__, first_name) self.assertEqual(__, last_name) - + def test_swapping_with_parallel_assignment(self): first_name = "Roy" last_name = "Rob" diff --git a/python 3/koans/about_lists.py b/koans/about_lists.py similarity index 88% rename from python 3/koans/about_lists.py rename to koans/about_lists.py index 474ecddc1..61cc3bb29 100644 --- a/python 3/koans/about_lists.py +++ b/koans/about_lists.py @@ -16,27 +16,27 @@ def test_creating_lists(self): 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]) @@ -50,7 +50,7 @@ def test_slicing_to_the_edge(self): 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)) @@ -58,6 +58,7 @@ def test_lists_and_ranges(self): self.assertEqual(__, list(range(5, 9))) def test_ranges_with_steps(self): + self.assertEqual(__, list(range(5, 3, -1))) self.assertEqual(__, list(range(0, 8, 2))) self.assertEqual(__, list(range(1, 8, 3))) self.assertEqual(__, list(range(5, -7, -4))) @@ -67,44 +68,42 @@ def test_insertions(self): knight = ['you', 'shall', 'pass'] knight.insert(2, 'not') self.assertEqual(__, knight) - + knight.insert(0, 'Arthur') - self.assertEqual(__, knight) - + self.assertEqual(__, knight) + def test_popping_lists(self): - stack = [10, 20, 30] + stack = [10, 20, 30, 40] 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 + + # Note, 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/koans/about_method_bindings.py similarity index 87% rename from python 3/koans/about_method_bindings.py rename to koans/about_method_bindings.py index 45f09177b..020851bd5 100644 --- a/python 3/koans/about_method_bindings.py +++ b/koans/about_method_bindings.py @@ -11,10 +11,10 @@ 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) @@ -22,12 +22,12 @@ def test_methods_are_also_bound_to_a_function(self): obj = Class() self.assertEqual(__, obj.method()) self.assertEqual(__, 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__)) - + def test_methods_have_different_attributes(self): obj = Class() self.assertEqual(__, len(dir(obj.method))) @@ -38,13 +38,13 @@ def test_setting_attributes_on_an_unbound_function(self): def test_setting_attributes_on_a_bound_method_directly(self): obj = Class() - with self.assertRaises(___): obj.method.cherries = 3 - + with self.assertRaises(___): 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) - + def test_functions_can_have_inner_functions(self): function2.get_fruit = function self.assertEqual(__, function2.get_fruit()) @@ -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,8 +68,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(__, bound_obj.__class__.__name__) + self.assertEqual(__, binding_owner.__class__.__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): + + def test_set_descriptor_changes_behavior_of_attribute_assignment(self): self.assertEqual(None, self.color.choice) self.color = 'purple' self.assertEqual(__, self.color.choice) - + diff --git a/python 3/koans/about_methods.py b/koans/about_methods.py similarity index 92% rename from python 3/koans/about_methods.py rename to koans/about_methods.py index eb4c56cf3..796a07ea5 100644 --- a/python 3/koans/about_methods.py +++ b/koans/about_methods.py @@ -13,7 +13,7 @@ def my_global_function(a,b): class AboutMethods(Koan): def test_calling_a_global_function(self): self.assertEqual(__, 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,20 +22,21 @@ 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\)') - + # Note, the text comparison works for Python 3.2 + # It has changed in the past and may change in the future + self.assertRegex(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.assertRegex(msg, __) + # ------------------------------------------------------------------ - + def pointless_method(self, a, b): sum = a + b @@ -43,7 +44,7 @@ def test_which_does_not_return_anything(self): self.assertEqual(__, 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'): @@ -67,13 +68,13 @@ def test_calling_with_variable_arguments(self): 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)) - + def test_calling_methods_in_same_class_with_explicit_receiver(self): def function_with_the_same_name(a, b): return a * b @@ -84,9 +85,9 @@ def function_with_the_same_name(a, b): 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 @@ -114,7 +115,7 @@ def test_pass_does_nothing_at_all(self): # ------------------------------------------------------------------ def one_line_method(self): return 'Madagascar' - + def test_no_indentation_required_for_one_line_statement_bodies(self): self.assertEqual(__, self.one_line_method()) @@ -123,16 +124,16 @@ def test_no_indentation_required_for_one_line_statement_bodies(self): def method_with_documentation(self): "A string placed at the beginning of a function 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.assertRegex(self.method_with_documentation.__doc__, __) # ------------------------------------------------------------------ - class Dog: + class Dog: def name(self): return "Fido" - + def _tail(self): # Prefixing a method with an underscore implies private scope return "wagging" @@ -143,7 +144,7 @@ def __password(self): def test_calling_methods_in_other_objects(self): rover = self.Dog() self.assertEqual(__, rover.name()) - + def test_private_access_is_implied_but_not_enforced(self): rover = self.Dog() @@ -152,11 +153,10 @@ def test_private_access_is_implied_but_not_enforced(self): def test_attributes_with_double_underscore_prefixes_are_subject_to_name_mangling(self): rover = self.Dog() - with self.assertRaises(___): password = rover.__password() - + with self.assertRaises(___): password = rover.__password() + # But this still is! self.assertEqual(__, 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/koans/about_modules.py similarity index 63% rename from python 3/koans/about_modules.py rename to koans/about_modules.py index 4974fa245..7a266171d 100644 --- a/python 3/koans/about_modules.py +++ b/koans/about_modules.py @@ -11,61 +11,67 @@ from .another_local_module import * from .local_module_with_all_defined import * + class AboutModules(Koan): def test_importing_other_python_scripts_as_modules(self): - from . import local_module # local_module.py - + from . import local_module # local_module.py + duck = local_module.Duck() self.assertEqual(__, 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 + + duck = Duck() # no module qualifier needed this time self.assertEqual(__, 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()) def test_importing_all_module_attributes_at_once(self): - # NOTE Using this module level import declared at the top of this script: - # from .other_local_module import * - # - # Import wildcard cannot be used from within classes or functions - + """ + importing all attributes at once is done like so: + from .another_local_module import * + The import wildcard cannot be used from within classes or functions. + """ + goose = Goose() hamster = Hamster() - + self.assertEqual(__, goose.name) self.assertEqual(__, hamster.name) - + def test_modules_hide_attributes_prefixed_by_underscores(self): - with self.assertRaises(___): private_squirrel = _SecretSquirrel() + with self.assertRaises(___): + private_squirrel = _SecretSquirrel() def test_private_attributes_are_still_accessible_in_modules(self): - from .local_module import Duck # local_module.py - + from .local_module import Duck # local_module.py + duck = Duck() self.assertEqual(__, duck._password) # module level attribute hiding doesn't affect class attributes # (unless the class itself is hidden). - - def test_a_module_can_choose_which_attributes_are_available_to_wildcards(self): - # NOTE Using this module level import declared at the top of this script: - # from .local_module_with_all_defined import * - # 'Goat' is on the __ALL__ list + def test_a_module_can_limit_wildcard_imports(self): + """ + Examine results of: + from .local_module_with_all_defined import * + """ + + # 'Goat' is on the __all__ list goat = Goat() self.assertEqual(__, goat.name) # How about velociraptors? lizard = _Velociraptor() self.assertEqual(__, lizard.name) - + # SecretDuck? Never heard of her! - with self.assertRaises(___): duck = SecretDuck() + with self.assertRaises(___): + duck = SecretDuck() diff --git a/python 3/koans/about_monkey_patching.py b/koans/about_monkey_patching.py similarity index 89% rename from python 3/koans/about_monkey_patching.py rename to koans/about_monkey_patching.py index 40dd5598c..bdea2b8b9 100644 --- a/python 3/koans/about_monkey_patching.py +++ b/koans/about_monkey_patching.py @@ -11,14 +11,14 @@ class AboutMonkeyPatching(Koan): class Dog: def bark(self): return "WOOF" - + def test_as_defined_dogs_do_bark(self): fido = self.Dog() self.assertEqual(__, fido.bark()) # ------------------------------------------------------------------ - - # Add a new method to an existing class. + + # Add a new method to an existing class. def test_after_patching_dogs_can_both_wag_and_bark(self): def wag(self): return "HAPPY" self.Dog.wag = wag @@ -26,25 +26,23 @@ def wag(self): return "HAPPY" fido = self.Dog() self.assertEqual(__, fido.wag()) self.assertEqual(__, fido.bark()) - + # ------------------------------------------------------------------ - + def test_most_built_in_classes_cannot_be_monkey_patched(self): try: int.is_even = lambda self: (self % 2) == 0 except Exception as ex: err_msg = ex.args[0] - - self.assertRegexpMatches(err_msg, __) + + self.assertRegex(err_msg, __) # ------------------------------------------------------------------ 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()) - - \ No newline at end of file diff --git a/python 3/koans/about_multiple_inheritance.py b/koans/about_multiple_inheritance.py similarity index 80% rename from python 3/koans/about_multiple_inheritance.py rename to koans/about_multiple_inheritance.py index 37a68701c..4227f7eb9 100644 --- a/python 3/koans/about_multiple_inheritance.py +++ b/koans/about_multiple_inheritance.py @@ -14,20 +14,20 @@ def __init__(self): def set_name(self, new_name): self._name = new_name - + def here(self): return "In Nameable class" - - class Animal: + + class Animal: def legs(self): return 4 def can_climb_walls(self): return False - + def here(self): return "In Animal class" - + class Pig(Animal): def __init__(self): super().__init__() @@ -36,13 +36,13 @@ def __init__(self): @property def name(self): return self._name - + def speak(self): return "OINK" - + def color(self): - return 'pink' - + return 'pink' + def here(self): return "In Pig class" @@ -68,26 +68,26 @@ def __init__(self): super(AboutMultipleInheritance.Pig, self).__init__() super(AboutMultipleInheritance.Nameable, self).__init__() self._name = "Jeff" - + def speak(self): return "This looks like a job for Spiderpig!" - + def here(self): return "In Spiderpig class" - - # - # Hierarchy: + + # + # Hierarchy: # Animal - # / \ - # Pig Spider Nameable + # / \ + # Pig Spider Nameable # \ | / # Spiderpig - # + # # ------------------------------------------------------------------ - + def test_normal_methods_are_available_in_the_object(self): jeff = self.Spiderpig() - self.assertRegexpMatches(jeff.speak(), __) + self.assertRegex(jeff.speak(), __) def test_base_class_methods_are_also_available_in_the_object(self): jeff = self.Spiderpig() @@ -96,14 +96,14 @@ def test_base_class_methods_are_also_available_in_the_object(self): except: self.fail("This should not happen") self.assertEqual(__, jeff.can_climb_walls()) - + def test_base_class_methods_can_affect_instance_variables_in_the_object(self): jeff = self.Spiderpig() self.assertEqual(__, jeff.name) - + jeff.set_name("Rover") self.assertEqual(__, jeff.name) - + def test_left_hand_side_inheritance_tends_to_be_higher_priority(self): jeff = self.Spiderpig() self.assertEqual(__, jeff.color()) @@ -119,23 +119,22 @@ 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(__, mro[2].__name__) + self.assertEqual(__, mro[3].__name__) + self.assertEqual(__, mro[4].__name__) + self.assertEqual(__, mro[5].__name__) def test_confirm_the_mro_controls_the_calling_order(self): jeff = self.Spiderpig() - self.assertRegexpMatches(jeff.here(), 'Spiderpig') - - next = super(AboutMultipleInheritance.Spiderpig, jeff) - self.assertRegexpMatches(next.here(), 'Pig') + self.assertRegex(jeff.here(), 'Spiderpig') + + next = super(AboutMultipleInheritance.Spiderpig, jeff) + self.assertRegex(next.here(), 'Pig') next = super(AboutMultipleInheritance.Pig, jeff) - self.assertRegexpMatches(next.here(), __) - + self.assertRegex(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? # # To avoid confusion it may help to think of super() as next_mro(). - diff --git a/python 3/koans/about_none.py b/koans/about_none.py similarity index 82% rename from python 3/koans/about_none.py rename to koans/about_none.py index c75e5a2d9..1731f0108 100644 --- a/python 3/koans/about_none.py +++ b/koans/about_none.py @@ -12,37 +12,40 @@ class AboutNone(Koan): def test_none_is_an_object(self): "Unlike NULL in a lot of languages" self.assertEqual(__, isinstance(None, object)) - + def test_none_is_universal(self): "There is only one None" self.assertEqual(____, None is None) - def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self): + def test_what_exception_do_you_get_when_calling_nonexistent_methods(self): """ What is the Exception that is thrown when you call a method that does not exist? - + Hint: launch python command console and try the code in the block below. - + Don't worry about what 'try' and 'except' do, we'll talk about this later """ try: None.some_method_none_does_not_know_about() except Exception as ex: ex2 = ex - + # What exception has been caught? - self.assertEqual(__, ex2.__class__.__name__) - + # + # Need a recap on how to evaluate __class__ attributes? + # + # https://github.com/gregmalcolm/python_koans/wiki/Class-Attribute + + self.assertEqual(__, ex2.__class__) + # What message was attached to the exception? # (HINT: replace __ with part of the error message.) - self.assertRegexpMatches(ex2.args[0], __) - + self.assertRegex(ex2.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) - - diff --git a/python 3/koans/about_packages.py b/koans/about_packages.py similarity index 77% rename from python 3/koans/about_packages.py rename to koans/about_packages.py index 61f5c5fb5..69288a1c8 100644 --- a/python 3/koans/about_packages.py +++ b/koans/about_packages.py @@ -14,12 +14,11 @@ # contemplate_koans.py # koans/ # __init__.py -# __init__.py -# about_asserts.py -# about_attribute_access.py -# about_class_attributes.py -# about_classes.py -# ... +# about_asserts.py +# about_attribute_access.py +# about_class_attributes.py +# about_classes.py +# ... # a_package_folder/ # __init__.py # a_module.py @@ -28,19 +27,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) - + 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 # ------------------------------------------------------------------ @@ -49,11 +44,11 @@ def test_use_absolute_imports_to_import_upper_level_modules(self): import contemplate_koans self.assertEqual(__, contemplate_koans.__name__) - - # contemplate_koans.py is the root module in this package because its + + # contemplate_koans.py is the root module in this package because it's # the first python module called in koans. # - # If contemplate_koan.py was based in a_package_folder that would be + # If contemplate_koans.py was based in a_package_folder that would be # the root folder, which would make reaching the koans folder # almost impossible. So always leave the starting python script in # a folder which can reach everything else. diff --git a/python 3/koans/about_proxy_object_project.py b/koans/about_proxy_object_project.py similarity index 86% rename from python 3/koans/about_proxy_object_project.py rename to koans/about_proxy_object_project.py index e1dd6f024..db70e0c70 100644 --- a/python 3/koans/about_proxy_object_project.py +++ b/koans/about_proxy_object_project.py @@ -13,7 +13,7 @@ # missing handler and any other supporting methods. The specification # of the Proxy class is given in the AboutProxyObjectProject koan. -# Note: This is a bit trickier that it's Ruby Koans counterpart, but you +# Note: This is a bit trickier than its Ruby Koans counterpart, but you # can do it! from runner.koan import * @@ -21,11 +21,11 @@ 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 + + # WRITE CODE HERE # The proxy object should pass the following Koan: # @@ -33,61 +33,60 @@ class AboutProxyObjectProject(Koan): def test_proxy_method_returns_wrapped_object(self): # NOTE: The Television class is defined below tv = Proxy(Television()) - + self.assertTrue(isinstance(tv, Proxy)) - + def test_tv_methods_still_perform_their_function(self): tv = Proxy(Television()) - + tv.channel = 10 tv.power() - + self.assertEqual(10, tv.channel) self.assertTrue(tv.is_on()) - + def test_proxy_records_messages_sent_to_tv(self): tv = Proxy(Television()) - + tv.power() tv.channel = 10 - - self.assertEqual(['power', 'channel='], tv.messages()) - + + self.assertEqual(['power', 'channel'], tv.messages()) + def test_proxy_handles_invalid_messages(self): tv = Proxy(Television()) - - ex = None + with self.assertRaises(AttributeError): tv.no_such_method() - - + + def test_proxy_reports_methods_have_been_called(self): tv = Proxy(Television()) - + tv.power() tv.power() - + self.assertTrue(tv.was_called('power')) self.assertFalse(tv.was_called('channel')) - + def test_proxy_counts_method_calls(self): tv = Proxy(Television()) - + tv.power() tv.channel = 48 tv.power() - + self.assertEqual(2, tv.number_of_times_called('power')) - self.assertEqual(1, tv.number_of_times_called('channel=')) + self.assertEqual(1, tv.number_of_times_called('channel')) 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") - + result = proxy.upper() self.assertEqual("PY OHIO 2010", result) - + result = proxy.split() self.assertEqual(["Py", "Ohio", "2010"], result) @@ -102,21 +101,21 @@ class Television: def __init__(self): self._channel = None self._power = None - + @property def channel(self): return self._channel @channel.setter def channel(self, value): - self._channel = value - + self._channel = value + def power(self): if self._power == 'on': self._power = 'off' else: self._power = 'on' - + def is_on(self): return self._power == 'on' @@ -124,33 +123,33 @@ def is_on(self): class TelevisionTest(Koan): def test_it_turns_on(self): tv = Television() - + tv.power() self.assertTrue(tv.is_on()) - + def test_it_also_turns_off(self): tv = Television() - + tv.power() tv.power() - + self.assertFalse(tv.is_on()) - + def test_edge_case_on_off(self): tv = Television() - + tv.power() tv.power() tv.power() - + self.assertTrue(tv.is_on()) - + tv.power() - + self.assertFalse(tv.is_on()) - + def test_can_set_the_channel(self): tv = Television() - + tv.channel = 11 self.assertEqual(11, tv.channel) diff --git a/koans/about_regex.py b/koans/about_regex.py new file mode 100644 index 000000000..f562594e5 --- /dev/null +++ b/koans/about_regex.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + +import re + + +class AboutRegex(Koan): + """ + These koans are based on Ben's book: Regular Expressions in 10 + minutes. I found this book very useful, so I decided to write + a koan file in order to practice everything it taught me. + http://www.forta.com/books/0672325667/ + """ + + def test_matching_literal_text(self): + """ + Lesson 1 Matching Literal String + """ + string = "Hello, my name is Felix and these koans are based " + \ + "on Ben's book: Regular Expressions in 10 minutes." + m = re.search(__, string) + self.assertTrue( + m and m.group(0) and + m.group(0) == 'Felix', + "I want my name") + + def test_matching_literal_text_how_many(self): + """ + Lesson 1 -- How many matches? + + The default behaviour of most regular expression engines is + to return just the first match. In python you have the + following options: + + match() --> Determine if the RE matches at the + beginning of the string. + search() --> Scan through a string, looking for any + location where this RE matches. + findall() --> Find all substrings where the RE + matches, and return them as a list. + finditer() --> Find all substrings where the RE + matches, and return them as an iterator. + """ + string = ("Hello, my name is Felix and these koans are based " + + "on Ben's book: Regular Expressions in 10 minutes. " + + "Repeat My name is Felix") + m = re.match('Felix', string) # TIP: match may not be the best option + + # I want to know how many times my name appears + self.assertEqual(m, __) + + def test_matching_literal_text_not_case_sensitivity(self): + """ + Lesson 1 -- Matching Literal String non case sensitivity. + Most regex implementations also support matches that are not + case sensitive. In python you can use re.IGNORECASE, in + Javascript you can specify the optional i flag. In Ben's + book you can see more languages. + + """ + string = "Hello, my name is Felix or felix and this koan " + \ + "is based on Ben's book: Regular Expressions in 10 minutes." + + self.assertEqual(re.findall("felix", string), __) + self.assertEqual(re.findall("felix", string, re.IGNORECASE), __) + + def test_matching_any_character(self): + """ + Lesson 1: Matching any character + + `.` matches any character: alphabetic characters, digits, + and punctuation. + """ + string = "pecks.xlx\n" \ + + "orders1.xls\n" \ + + "apec1.xls\n" \ + + "na1.xls\n" \ + + "na2.xls\n" \ + + "sa1.xls" + + # I want to find all uses of myArray + change_this_search_string = 'a..xlx' + self.assertEquals( + len(re.findall(change_this_search_string, string)), + 3) + + def test_matching_set_character(self): + """ + Lesson 2 -- Matching sets of characters + + A set of characters is defined using the metacharacters + `[` and `]`. Everything between them is part of the set, and + any single one of the set members will match. + """ + string = "sales.xlx\n" \ + + "sales1.xls\n" \ + + "orders3.xls\n" \ + + "apac1.xls\n" \ + + "sales2.xls\n" \ + + "na1.xls\n" \ + + "na2.xls\n" \ + + "sa1.xls\n" \ + + "ca1.xls" + # I want to find all files for North America(na) or South + # America(sa), but not (ca) TIP you can use the pattern .a. + # which matches in above test but in this case matches more than + # you want + change_this_search_string = '[nsc]a[2-9].xls' + self.assertEquals( + len(re.findall(change_this_search_string, string)), + 3) + + def test_anything_but_matching(self): + """ + Lesson 2 -- Using character set ranges + Occasionally, you'll have a list of characters that you don't + want to match. Character sets can be negated using the ^ + metacharacter. + + """ + string = "sales.xlx\n" \ + + "sales1.xls\n" \ + + "orders3.xls\n" \ + + "apac1.xls\n" \ + + "sales2.xls\n" \ + + "sales3.xls\n" \ + + "europe2.xls\n" \ + + "sam.xls\n" \ + + "na1.xls\n" \ + + "na2.xls\n" \ + + "sa1.xls\n" \ + + "ca1.xls" + + # I want to find the name 'sam' + change_this_search_string = '[^nc]am' + self.assertEquals( + re.findall(change_this_search_string, string), + ['sam.xls']) diff --git a/python 3/koans/about_scope.py b/koans/about_scope.py similarity index 92% rename from python 3/koans/about_scope.py rename to koans/about_scope.py index b2d0d6614..a9cf2a168 100644 --- a/python 3/koans/about_scope.py +++ b/koans/about_scope.py @@ -3,7 +3,7 @@ from runner.koan import * -from . import jims +from . import jims from . import joes counter = 0 # Global @@ -14,39 +14,39 @@ class AboutScope(Koan): # Look in jims.py and joes.py to see definitions of Dog used # for this set of tests # - + def test_dog_is_not_available_in_the_current_scope(self): with self.assertRaises(___): 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 - + # name 'jims' module name is taken from jims.py filename + rover = joes.Dog() self.assertEqual(__, fido.identify()) self.assertEqual(__, rover.identify()) - + self.assertEqual(__, type(fido) == type(rover)) self.assertEqual(__, jims.Dog == joes.Dog) - + # ------------------------------------------------------------------ - + class str: pass - + def test_bare_bones_class_names_do_not_assume_the_current_scope(self): self.assertEqual(__, AboutScope.str == str) - + def test_nested_string_is_not_the_same_as_the_system_string(self): self.assertEqual(__, self.str == type("HI")) - + def test_str_without_self_prefix_stays_in_the_global_scope(self): self.assertEqual(__, str == type("HI")) # ------------------------------------------------------------------ PI = 3.1416 - + def test_constants_are_defined_with_an_initial_uppercase_letter(self): self.assertAlmostEqual(_____, self.PI) # Note, floating point numbers in python are not precise. @@ -62,11 +62,11 @@ def test_constants_are_assumed_by_convention_only(self): def increment_using_local_counter(self, counter): counter = counter + 1 - + def increment_using_global_counter(self): global counter - counter = counter + 1 - + counter = counter + 1 + def test_incrementing_with_local_counter(self): global counter start = counter @@ -87,25 +87,24 @@ def from_the_league(): stuff = 'this is a local shop for local people' return stuff return from_the_league() - + def nonlocal_access(self): - stuff = 'eels' + stuff = 'eels' def from_the_boosh(): nonlocal stuff return stuff return from_the_boosh() - + def test_getting_something_locally(self): self.assertEqual(__, self.local_access()) - + def test_getting_something_nonlocally(self): self.assertEqual(__, self.nonlocal_access()) - + # ------------------------------------------------------------------ - + global deadly_bingo 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]) - \ No newline at end of file diff --git a/python 2/koans/about_scoring_project.py b/koans/about_scoring_project.py similarity index 92% rename from python 2/koans/about_scoring_project.py rename to koans/about_scoring_project.py index 393af55b5..0fd055de2 100644 --- a/python 2/koans/about_scoring_project.py +++ b/koans/about_scoring_project.py @@ -4,13 +4,13 @@ from runner.koan import * # Greed is a dice game where you roll up to five dice to accumulate -# points. The following "score" function will be used calculate the +# points. The following "score" function will be used to calculate the # score of a single roll of the dice. # # A greed roll is scored as follows: # # * A set of three ones is 1000 points -# +# # * A set of three numbers (other than ones) is worth 100 times the # number. (e.g. three fives is 500 points). # @@ -39,34 +39,34 @@ def score(dice): class AboutScoringProject(Koan): def test_score_of_an_empty_list_is_zero(self): self.assertEqual(0, score([])) - + def test_score_of_a_single_roll_of_5_is_50(self): self.assertEqual(50, score([5])) - + def test_score_of_a_single_roll_of_1_is_100(self): self.assertEqual(100, score([1])) - + def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores(self): self.assertEqual(300, score([1,5,5,1])) - + def test_score_of_single_2s_3s_4s_and_6s_are_zero(self): self.assertEqual(0, score([2,3,4,6])) - + def test_score_of_a_triple_1_is_1000(self): self.assertEqual(1000, score([1,1,1])) - + def test_score_of_other_triples_is_100x(self): self.assertEqual(200, score([2,2,2])) self.assertEqual(300, score([3,3,3])) self.assertEqual(400, score([4,4,4])) self.assertEqual(500, score([5,5,5])) self.assertEqual(600, score([6,6,6])) - + def test_score_of_mixed_is_sum(self): self.assertEqual(250, score([2,5,2,2,3])) self.assertEqual(550, score([5,5,5,5])) self.assertEqual(1150, score([1,1,1,5,1])) - + def test_ones_not_left_out(self): self.assertEqual(300, score([1,2,2,2])) - self.assertEqual(350, score([1,5,2,2,2])) \ No newline at end of file + self.assertEqual(350, score([1,5,2,2,2])) diff --git a/python 3/koans/about_sets.py b/koans/about_sets.py similarity index 66% rename from python 3/koans/about_sets.py rename to koans/about_sets.py index 2fd2410ca..87cf10959 100644 --- a/python 3/koans/about_sets.py +++ b/koans/about_sets.py @@ -6,7 +6,7 @@ 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) @@ -16,12 +16,13 @@ def test_empty_sets_have_different_syntax_to_populated_sets(self): self.assertEqual(__, set()) def test_dictionaries_and_sets_use_same_curly_braces(self): - # Note: Sets have only started using braces since Python 3 + # Note: Literal sets using braces were introduced in python 3. + # They were also backported to python 2.7. + + self.assertEqual(__, {1, 2, 3}.__class__) + self.assertEqual(__, {'one': 1, 'two': 2}.__class__) - self.assertEqual(__, type({1, 2, 3}).__name__) - self.assertEqual(__, type({'one': 1, 'two': 2}).__name__) - - self.assertEqual(__, type({}).__name__) + self.assertEqual(__, {}.__class__) def test_creating_sets_using_strings(self): self.assertEqual(__, {'12345'}) @@ -29,29 +30,26 @@ def test_creating_sets_using_strings(self): 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 )) + scotsmen = {'MacLeod', 'Wallace', 'Willie'} + warriors = {'MacLeod', 'Wallace', 'Leonidas'} + + self.assertEqual(__, scotsmen - warriors) + self.assertEqual(__, scotsmen | warriors) + self.assertEqual(__, scotsmen & warriors) + self.assertEqual(__, scotsmen ^ warriors) # ------------------------------------------------------------------ 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/koans/about_string_manipulation.py b/koans/about_string_manipulation.py new file mode 100644 index 000000000..5204f29ba --- /dev/null +++ b/koans/about_string_manipulation.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from runner.koan import * + +class AboutStringManipulation(Koan): + + 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) + + def test_formatted_values_can_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) + + def test_any_python_expression_may_be_interpolated(self): + import math # import a standard python module with math functions + + decimal_places = 4 + string = "The square root of 5 is {0:.{1}f}".format(math.sqrt(5), + decimal_places) + self.assertEqual(__, string) + + def test_you_can_get_a_substring_from_a_string(self): + string = "Bacon, lettuce and tomato" + self.assertEqual(__, string[7:10]) + + def test_you_can_get_a_single_character_from_a_string(self): + string = "Bacon, lettuce and tomato" + self.assertEqual(__, string[1]) + + def test_single_characters_can_be_represented_by_integers(self): + self.assertEqual(__, ord('a')) + self.assertEqual(__, ord('b') == (ord('a') + 1)) + + def test_strings_can_be_split(self): + string = "Sausage Egg Cheese" + words = string.split() + self.assertListEqual([__, __, __], words) + + def test_strings_can_be_split_with_different_patterns(self): + import re #import python regular expression library + + string = "the,rain;in,spain" + pattern = re.compile(',|;') + + words = pattern.split(string) + + self.assertListEqual([__, __, __, __], words) + + # Pattern is a Python regular expression pattern which matches ',' or ';' + + def test_raw_strings_do_not_interpret_escape_characters(self): + string = r'\n' + self.assertNotEqual('\n', string) + self.assertEqual(__, string) + self.assertEqual(__, len(string)) + + # Useful in regular expressions, file paths, URLs, etc. + + def test_strings_can_be_joined(self): + words = ["Now", "is", "the", "time"] + self.assertEqual(__, ' '.join(words)) + + def test_strings_can_change_case(self): + self.assertEqual(__, 'guido'.capitalize()) + self.assertEqual(__, 'guido'.upper()) + self.assertEqual(__, 'TimBot'.lower()) + self.assertEqual(__, 'guido van rossum'.title()) + self.assertEqual(__, 'ToTaLlY aWeSoMe'.swapcase()) diff --git a/python 3/koans/about_strings.py b/koans/about_strings.py similarity index 51% rename from python 3/koans/about_strings.py rename to koans/about_strings.py index 8e60cf290..25f5f59df 100644 --- a/python 3/koans/about_strings.py +++ b/koans/about_strings.py @@ -4,15 +4,15 @@ from runner.koan import * class AboutStrings(Koan): - + def test_double_quoted_strings_are_strings(self): string = "Hello, world." self.assertEqual(__, isinstance(string, str)) - + def test_single_quoted_strings_are_also_strings(self): string = 'Goodbye, world.' self.assertEqual(__, isinstance(string, str)) - + def test_triple_quote_strings_are_also_strings(self): string = """Howdy, world!""" self.assertEqual(__, isinstance(string, str)) @@ -28,11 +28,11 @@ def test_raw_strings_are_also_strings(self): def test_use_single_quotes_to_create_string_with_double_quotes(self): string = 'He said, "Go Away."' self.assertEqual(__, string) - + def test_use_double_quotes_to_create_strings_with_single_quotes(self): string = "Don't" self.assertEqual(__, string) - + def test_use_backslash_for_escaping_quotes_in_strings(self): a = "He said, \"Don't\"" b = 'He said, "Don\'t"' @@ -42,43 +42,44 @@ 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)) - + def test_triple_quoted_strings_can_span_lines(self): string = """ Howdy, world! """ self.assertEqual(__, len(string)) - + def test_triple_quoted_strings_need_less_escaping(self): a = "Hello \"world\"." b = """Hello "world".""" self.assertEqual(__, (a == b)) - - def but_you_still_have_to_be_careful_at_the_end_of_a_triple_quoted_string(self): + + def test_escaping_quotes_at_the_end_of_triple_quoted_string(self): string = """Hello "world\"""" - + self.assertEqual(__, string) + def test_plus_concatenates_strings(self): string = "Hello, " + "world" self.assertEqual(__, string) def test_adjacent_strings_are_concatenated_automatically(self): - string = "Hello" ", " "World" + string = "Hello" ", " "world" self.assertEqual(__, string) - + def test_plus_will_not_modify_original_strings(self): hi = "Hello, " there = "world" string = hi + there self.assertEqual(__, hi) self.assertEqual(__, there) - + def test_plus_equals_will_append_to_end_of_string(self): hi = "Hello, " there = "world" hi += there self.assertEqual(__, hi) - + def test_plus_equals_also_leaves_original_string_unmodified(self): original = "Hello, " hi = original @@ -91,71 +92,3 @@ def test_most_strings_interpret_escape_characters(self): self.assertEqual('\n', string) self.assertEqual("""\n""", string) self.assertEqual(__, 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) - - 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) - - def test_any_python_expression_may_be_interpolated(self): - import math # import a standard python module with math functions - - decimal_places = 4 - string = "The square root of 5 is {0:.{1}f}".format(math.sqrt(5), \ - decimal_places) - self.assertEqual(__, string) - - def test_you_can_get_a_substring_from_a_string(self): - string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[7:10]) - - def test_you_can_get_a_single_character_from_a_string(self): - string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[1]) - - def test_single_characters_can_be_represented_by_integers(self): - self.assertEqual(__, ord('a')) - self.assertEqual(__, ord('b') == (ord('a') + 1)) - - def test_strings_can_be_split(self): - string = "Sausage Egg Cheese" - words = string.split() - self.assertListEqual([__, __, __], words) - - def test_strings_can_be_split_with_different_patterns(self): - import re #import python regular expression library - - string = "the,rain;in,spain" - pattern = re.compile(',|;') - - words = pattern.split(string) - - self.assertListEqual([__, __, __, __], words) - - # Pattern is a Python regular expression pattern which matches ',' or ';' - - def test_raw_strings_do_not_interpret_escape_characters(self): - string = r'\n' - self.assertNotEqual('\n', string) - self.assertEqual(__, string) - self.assertEqual(__, len(string)) - - # Useful in regular expressions, file paths, URLs, etc. - - def test_strings_can_be_joined(self): - words = ["Now", "is", "the", "time"] - self.assertEqual(__, ' '.join(words)) - - def test_strings_can_change_case(self): - self.assertEqual(__, 'guido'.capitalize()) - self.assertEqual(__, 'guido'.upper()) - self.assertEqual(__, 'TimBot'.lower()) - self.assertEqual(__, 'guido van rossum'.title()) - self.assertEqual(__, 'ToTaLlY aWeSoMe'.swapcase()) diff --git a/python 3/koans/about_triangle_project.py b/koans/about_triangle_project.py similarity index 98% rename from python 3/koans/about_triangle_project.py rename to koans/about_triangle_project.py index 60da2c2ea..ad2485909 100644 --- a/python 3/koans/about_triangle_project.py +++ b/koans/about_triangle_project.py @@ -10,13 +10,13 @@ class AboutTriangleProject(Koan): def test_equilateral_triangles_have_equal_sides(self): self.assertEqual('equilateral', triangle(2, 2, 2)) self.assertEqual('equilateral', triangle(10, 10, 10)) - + def test_isosceles_triangles_have_exactly_two_sides_equal(self): self.assertEqual('isosceles', triangle(3, 4, 4)) self.assertEqual('isosceles', triangle(4, 3, 4)) self.assertEqual('isosceles', triangle(4, 4, 3)) self.assertEqual('isosceles', triangle(10, 10, 2)) - + def test_scalene_triangles_have_no_equal_sides(self): self.assertEqual('scalene', triangle(3, 4, 5)) self.assertEqual('scalene', triangle(10, 11, 12)) diff --git a/python 3/koans/about_triangle_project2.py b/koans/about_triangle_project2.py similarity index 75% rename from python 3/koans/about_triangle_project2.py rename to koans/about_triangle_project2.py index 3e2f2edf7..6113f6180 100644 --- a/python 3/koans/about_triangle_project2.py +++ b/koans/about_triangle_project2.py @@ -10,16 +10,16 @@ class AboutTriangleProject2(Koan): # The first assignment did not talk about how to handle errors. # Let's handle that part now. def test_illegal_triangles_throw_exceptions(self): + # All sides should be greater than 0 with self.assertRaises(TriangleError): triangle(0, 0, 0) - with self.assertRaises(TriangleError): triangle(3, 4, -5) - with self.assertRaises(TriangleError): + # The sum of any two sides should be greater than the third one + with self.assertRaises(TriangleError): triangle(1, 1, 3) - with self.assertRaises(TriangleError): - triangle(2, 4, 2) - + triangle(2, 5, 2) + diff --git a/python 2/koans/about_true_and_false.py b/koans/about_true_and_false.py similarity index 81% rename from python 2/koans/about_true_and_false.py rename to koans/about_true_and_false.py index 9b6639641..5f06a63db 100644 --- a/python 2/koans/about_true_and_false.py +++ b/koans/about_true_and_false.py @@ -3,19 +3,20 @@ 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)) @@ -30,9 +31,13 @@ def test_empty_collections_are_treated_as_false(self): def test_blank_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(1,)) - self.assertEqual(__, self.truth_value("Python is named after Monty Python")) + self.assertEqual(__, self.truth_value([0])) + self.assertEqual(__, self.truth_value((0,))) + self.assertEqual( + __, + self.truth_value("Python is named after Monty Python")) self.assertEqual(__, self.truth_value(' ')) + self.assertEqual(__, self.truth_value('0')) diff --git a/python 3/koans/about_tuples.py b/koans/about_tuples.py similarity index 74% rename from python 3/koans/about_tuples.py rename to koans/about_tuples.py index f206b717b..1b38c8f7f 100644 --- a/python 3/koans/about_tuples.py +++ b/koans/about_tuples.py @@ -6,47 +6,49 @@ class AboutTuples(Koan): def test_creating_a_tuple(self): count_of_three = (1, 2, 5) - self.assertEqual(5, __) - + self.assertEqual(__, count_of_three[2]) + def test_tuples_are_immutable_so_item_assignment_is_not_possible(self): - count_of_three = (1, 2, 5) + + count_of_three = (1, 2, 5) try: count_of_three[2] = "three" except TypeError as ex: msg = ex.args[0] - - # Note, assertRegexpMatches() uses regular expression pattern matching, - # so you don't have to copy the whole message. - self.assertRegexpMatches(msg, __) - + # Note, assertRegex() uses regular expression pattern matching, + # so you don't have to copy the whole message. + + self.assertRegex(msg, __) + def test_tuples_are_immutable_so_appending_is_not_possible(self): count_of_three = (1, 2, 5) with self.assertRaises(___): count_of_three.append("boom") - + # Tuples are less flexible than lists, but faster. def test_tuples_can_only_be_changed_through_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) def test_tuples_of_one_look_peculiar(self): - self.assertEqual(__, (1).__class__.__name__) - self.assertEqual(__, (1,).__class__.__name__) - self.assertEqual(__, ("Hello comma!", )) - + self.assertEqual(__, (1).__class__) + self.assertEqual(__, (1,).__class__) + self.assertEqual(__, ("I'm a tuple",).__class__) + self.assertEqual(__, ("Not a tuple").__class__) + def test_tuple_constructor_can_be_surprising(self): self.assertEqual(__, tuple("Surprise!")) def test_creating_empty_tuples(self): self.assertEqual(__ , ()) - self.assertEqual(__ , tuple()) #Sometimes less confusing - + self.assertEqual(__ , tuple()) # Sometimes less confusing + def test_tuples_can_be_embedded(self): lat = (37, 14, 6, 'N') lon = (115, 48, 40, 'W') @@ -58,11 +60,8 @@ def test_tuples_are_good_for_representing_records(self): ("Illuminati HQ", (38, 52, 15.56, 'N'), (77, 3, 21.46, 'W')), ("Stargate B", (41, 10, 43.92, 'N'), (1, 49, 34.29, 'W')), ] - + locations.append( ("Cthulu", (26, 40, 1, 'N'), (70, 45, 7, 'W')) ) - + self.assertEqual(__, locations[2][0]) self.assertEqual(__, locations[0][1][2]) - - - diff --git a/python 2/koans/about_with_statements.py b/koans/about_with_statements.py similarity index 72% rename from python 2/koans/about_with_statements.py rename to koans/about_with_statements.py index bb4db2ddf..0ae9b5ff8 100644 --- a/python 2/koans/about_with_statements.py +++ b/koans/about_with_statements.py @@ -11,32 +11,38 @@ class AboutWithStatements(Koan): def count_lines(self, file_name): - file = open(file_name) try: - count = 0 - for line in file.readlines(): - count += 1 - return count - finally: - if file: file.close() - + file = open(file_name) + try: + return len(file.readlines()) + finally: + file.close() + except IOError: + # should never happen + self.fail() + def test_counting_lines(self): self.assertEqual(__, self.count_lines("example_file.txt")) - + # ------------------------------------------------------------------ - + def find_line(self, file_name): - file = open(file_name) try: - for line in file.readlines(): - match = re.search('e', line) - if match: return line - finally: - if file: file.close() - + file = open(file_name) + try: + for line in file.readlines(): + match = re.search('e', line) + if match: + return line + finally: + file.close() + except IOError: + # should never happen + self.fail() + def test_finding_lines(self): self.assertEqual(__, self.find_line("example_file.txt")) - + ## ------------------------------------------------------------------ ## THINK ABOUT IT: ## @@ -44,8 +50,8 @@ def test_finding_lines(self): ## They both follow the pattern of "sandwich code". ## ## Sandwich code is code that comes in three parts: (1) the top slice - ## of bread, (2) the meat, and (3) the bottom slice of bread. The - ## the bread part of the sandwich almost always goes together, but + ## of bread, (2) the meat, and (3) the bottom slice of bread. + ## The bread part of the sandwich almost always goes together, but ## the meat part changes all the time. ## ## Because the changing part of the sandwich code is in the middle, @@ -59,49 +65,44 @@ def test_finding_lines(self): ## Python solves the problem using Context Managers. Consider the ## following code: ## - + class FileContextManager(): def __init__(self, file_name): self._file_name = file_name self._file = None - + def __enter__(self): self._file = open(self._file_name) return self._file - + def __exit__(self, cls, value, tb): self._file.close() - + # Now we write: - + def count_lines2(self, file_name): with self.FileContextManager(file_name) as file: - count = 0 - for line in file.readlines(): - count += 1 - return count - + return len(file.readlines()) + def test_counting_lines2(self): self.assertEqual(__, self.count_lines2("example_file.txt")) - + # ------------------------------------------------------------------ - + def find_line2(self, file_name): - # Rewrite find_line using the Context Manager. - pass - + # Using the context manager self.FileContextManager, rewrite this + # function to return the first line containing the letter 'e'. + return None + def test_finding_lines2(self): - self.assertEqual(__, self.find_line2("example_file.txt")) self.assertNotEqual(None, self.find_line2("example_file.txt")) - + self.assertEqual('test\n', self.find_line2("example_file.txt")) + # ------------------------------------------------------------------ - + def count_lines3(self, file_name): with open(file_name) as file: - count = 0 - for line in file.readlines(): - count += 1 - return count - + return len(file.readlines()) + def test_open_already_has_its_own_built_in_context_manager(self): self.assertEqual(__, self.count_lines3("example_file.txt")) diff --git a/python 3/koans/another_local_module.py b/koans/another_local_module.py similarity index 98% rename from python 3/koans/another_local_module.py rename to koans/another_local_module.py index 3b3660cf5..242daaa79 100644 --- a/python 3/koans/another_local_module.py +++ b/koans/another_local_module.py @@ -10,7 +10,7 @@ class Hamster: @property def name(self): return "Phil" - + class _SecretSquirrel: @property def name(self): diff --git a/python 3/koans/jims.py b/koans/jims.py similarity index 100% rename from python 3/koans/jims.py rename to koans/jims.py diff --git a/python 3/koans/joes.py b/koans/joes.py similarity index 100% rename from python 3/koans/joes.py rename to koans/joes.py diff --git a/python 3/koans/local_module.py b/koans/local_module.py similarity index 100% rename from python 3/koans/local_module.py rename to koans/local_module.py diff --git a/python 3/koans/local_module_with_all_defined.py b/koans/local_module_with_all_defined.py similarity index 96% rename from python 3/koans/local_module_with_all_defined.py rename to koans/local_module_with_all_defined.py index 5293f0933..a84ae8307 100644 --- a/python 3/koans/local_module_with_all_defined.py +++ b/koans/local_module_with_all_defined.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- __all__ = ( - 'Goat', + 'Goat', '_Velociraptor' ) diff --git a/python 3/koans/triangle.py b/koans/triangle.py similarity index 100% rename from python 3/koans/triangle.py rename to koans/triangle.py diff --git a/python 2/libs/__init__.py b/libs/__init__.py similarity index 100% rename from python 2/libs/__init__.py rename to libs/__init__.py diff --git a/python 2/libs/colorama/LICENSE-colorama b/libs/colorama/LICENSE-colorama similarity index 100% rename from python 2/libs/colorama/LICENSE-colorama rename to libs/colorama/LICENSE-colorama diff --git a/libs/colorama/__init__.py b/libs/colorama/__init__.py new file mode 100644 index 000000000..2d127fa8e --- /dev/null +++ b/libs/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit +from .ansi import Fore, Back, Style +from .ansitowin32 import AnsiToWin32 + +VERSION = '0.2.7' + diff --git a/python 3/libs/colorama/ansi.py b/libs/colorama/ansi.py similarity index 92% rename from python 3/libs/colorama/ansi.py rename to libs/colorama/ansi.py index 7b818e19e..5dfe374ce 100644 --- a/python 3/libs/colorama/ansi.py +++ b/libs/colorama/ansi.py @@ -1,3 +1,4 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. ''' This module generates ANSI character codes to printing colors to terminals. See: http://en.wikipedia.org/wiki/ANSI_escape_code diff --git a/python 2/libs/colorama/ansitowin32.py b/libs/colorama/ansitowin32.py similarity index 90% rename from python 2/libs/colorama/ansitowin32.py rename to libs/colorama/ansitowin32.py index 9ce77dd6f..ea0a6c15f 100644 --- a/python 2/libs/colorama/ansitowin32.py +++ b/libs/colorama/ansitowin32.py @@ -1,4 +1,4 @@ - +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. import re import sys @@ -118,12 +118,12 @@ def write(self, text): self.wrapped.flush() if self.autoreset: self.reset_all() - + def reset_all(self): if self.convert: self.call_win32('m', (0,)) - else: + elif is_a_tty(self.wrapped): self.wrapped.write(Style.RESET_ALL) @@ -173,4 +173,17 @@ def call_win32(self, command, params): args = func_args[1:] kwargs = dict(on_stderr=self.on_stderr) func(*args, **kwargs) + elif command in ('H', 'f'): # set cursor position + func = winterm.set_cursor_position + func(params, on_stderr=self.on_stderr) + elif command in ('J'): + func = winterm.erase_data + func(params, on_stderr=self.on_stderr) + elif command == 'A': + if params == () or params == None: + num_rows = 1 + else: + num_rows = params[0] + func = winterm.cursor_up + func(num_rows, on_stderr=self.on_stderr) diff --git a/python 3/libs/colorama/initialise.py b/libs/colorama/initialise.py similarity index 54% rename from python 3/libs/colorama/initialise.py rename to libs/colorama/initialise.py index eca2bea40..cba3676dd 100644 --- a/python 3/libs/colorama/initialise.py +++ b/libs/colorama/initialise.py @@ -1,3 +1,4 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. import atexit import sys @@ -7,6 +8,9 @@ orig_stdout = sys.stdout orig_stderr = sys.stderr +wrapped_stdout = sys.stdout +wrapped_stderr = sys.stderr + atexit_done = False @@ -16,11 +20,14 @@ def reset_all(): def init(autoreset=False, convert=None, strip=None, wrap=True): - if wrap==False and (autoreset==True or convert==True or strip==True): + if not wrap and any([autoreset, convert, strip]): 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 wrapped_stdout, wrapped_stderr + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) global atexit_done if not atexit_done: @@ -28,6 +35,16 @@ def init(autoreset=False, convert=None, strip=None, wrap=True): atexit_done = True +def deinit(): + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + +def reinit(): + sys.stdout = wrapped_stdout + sys.stderr = wrapped_stdout + + def wrap_stream(stream, convert, strip, autoreset, wrap): if wrap: wrapper = AnsiToWin32(stream, @@ -36,3 +53,4 @@ def wrap_stream(stream, convert, strip, autoreset, wrap): stream = wrapper.stream return stream + diff --git a/libs/colorama/win32.py b/libs/colorama/win32.py new file mode 100644 index 000000000..f4024f95e --- /dev/null +++ b/libs/colorama/win32.py @@ -0,0 +1,134 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + from ctypes import windll + from ctypes import wintypes +except ImportError: + windll = None + SetConsoleTextAttribute = lambda *_: None +else: + from ctypes import ( + byref, Structure, c_char, c_short, c_uint32, c_ushort, POINTER + ) + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", wintypes._COORD), + ("dwCursorPosition", wintypes._COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", wintypes._COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + wintypes._COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + wintypes._COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + wintypes._COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + handles = { + STDOUT: _GetStdHandle(STDOUT), + STDERR: _GetStdHandle(STDERR), + } + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = handles[stream_id] + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = handles[stream_id] + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position): + position = wintypes._COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = wintypes._COORD(position.Y - 1, position.X - 1) + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = handles[stream_id] + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = handles[stream_id] + char = c_char(char) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = handles[stream_id] + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) diff --git a/libs/colorama/winterm.py b/libs/colorama/winterm.py new file mode 100644 index 000000000..270881154 --- /dev/null +++ b/libs/colorama/winterm.py @@ -0,0 +1,120 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +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) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + #I'm not currently tracking the position, so there is no default. + #position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_up(self, num_rows=0, on_stderr=False): + if num_rows == 0: + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y - num_rows, position.X) + self.set_cursor_position(adjusted_position, on_stderr) + + def erase_data(self, mode=0, on_stderr=False): + # 0 (or None) should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen. (And maybe move cursor to (1,1)?) + # + # At the moment, I only support mode 2. From looking at the API, it + # should be possible to calculate a different number of bytes to clear, + # and to do so relative to the cursor position. + if mode[0] not in (2,): + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + # here's where we'll home the cursor + coord_screen = win32.COORD(0,0) + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + dw_con_size = csbi.dwSize.X * csbi.dwSize.Y + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', dw_con_size, coord_screen) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen ); + # put the cursor at (0, 0) + win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y)) diff --git a/python 3/libs/mock.py b/libs/mock.py similarity index 95% rename from python 3/libs/mock.py rename to libs/mock.py index c01875bf3..38c446db2 100644 --- a/python 3/libs/mock.py +++ b/libs/mock.py @@ -26,7 +26,7 @@ class SentinelObject(object): def __init__(self, name): self.name = name - + def __repr__(self): return ''.format(self.name) @@ -34,11 +34,11 @@ def __repr__(self): class Sentinel(object): def __init__(self): self._sentinels = {} - + def __getattr__(self, name): return self._sentinels.setdefault(name, SentinelObject(name)) - - + + sentinel = Sentinel() DEFAULT = sentinel.DEFAULT @@ -58,21 +58,21 @@ def _copy(value): class Mock(object): - def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, + def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, name=None, parent=None, wraps=None): self._parent = parent self._name = name if spec is not None and not isinstance(spec, list): spec = [member for member in dir(spec) if not _is_magic(member)] - + self._methods = spec self._children = {} self._return_value = return_value self.side_effect = side_effect self._wraps = wraps - + self.reset_mock() - + def reset_mock(self): self.called = False @@ -84,16 +84,16 @@ def reset_mock(self): child.reset_mock() if isinstance(self._return_value, Mock): self._return_value.reset_mock() - - + + def __get_return_value(self): if self._return_value is DEFAULT: self._return_value = Mock() return self._return_value - + def __set_return_value(self, value): self._return_value = value - + return_value = property(__get_return_value, __set_return_value) @@ -102,7 +102,7 @@ def __call__(self, *args, **kwargs): self.call_count += 1 self.call_args = (args, kwargs) self.call_args_list.append((args, kwargs)) - + parent = self._parent name = self._name while parent is not None: @@ -111,44 +111,44 @@ def __call__(self, *args, **kwargs): break name = parent._name + '.' + name parent = parent._parent - + ret_val = DEFAULT if self.side_effect is not None: - if (isinstance(self.side_effect, Exception) or + if (isinstance(self.side_effect, Exception) or isinstance(self.side_effect, (type, ClassType)) and issubclass(self.side_effect, Exception)): raise self.side_effect - + ret_val = self.side_effect(*args, **kwargs) if ret_val is DEFAULT: ret_val = self.return_value - + if self._wraps is not None and self._return_value is DEFAULT: return self._wraps(*args, **kwargs) if ret_val is DEFAULT: ret_val = self.return_value return ret_val - - + + def __getattr__(self, name): if self._methods is not None: if name not in self._methods: raise AttributeError("Mock object has no attribute '{0!s}'".format(name)) elif _is_magic(name): raise AttributeError(name) - + if name not in self._children: wraps = None if self._wraps is not None: wraps = getattr(self._wraps, name) self._children[name] = Mock(parent=self, name=name, wraps=wraps) - + return self._children[name] - - + + def assert_called_with(self, *args, **kwargs): assert self.call_args == (args, kwargs), 'Expected: {0!s}\nCalled with: {1!s}'.format((args, kwargs), self.call_args) - + def _dot_lookup(thing, comp, import_path): try: @@ -199,8 +199,8 @@ def patched(*args, **keywargs): patching.__exit__() patched.patchings = [self] - patched.__name__ = func.__name__ - patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno", + patched.__name__ = func.__name__ + patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno", func.func_code.co_firstlineno) return patched @@ -209,7 +209,7 @@ def get_original(self): target = self.target name = self.attribute create = self.create - + original = DEFAULT if _has_local_attr(target, name): try: @@ -221,7 +221,7 @@ def get_original(self): raise AttributeError("{0!s} does not have the attribute {1!r}".format(target, name)) return original - + def __enter__(self): new, spec, = self.new, self.spec original = self.get_original() @@ -247,15 +247,15 @@ def __exit__(self, *_): else: delattr(self.target, self.attribute) del self.temp_original - - + + def patch_object(target, attribute, new=DEFAULT, spec=None, create=False): return _patch(target, attribute, new, spec, create) def patch(target, new=DEFAULT, spec=None, create=False): try: - target, attribute = target.rsplit('.', 1) + target, attribute = target.rsplit('.', 1) except (TypeError, ValueError): raise TypeError("Need a valid target to patch. You supplied: {0!r}".format(target,)) target = _importer(target) diff --git a/python 2/contemplate_koans.py b/python 2/contemplate_koans.py deleted file mode 100644 index 621119451..000000000 --- a/python 2/contemplate_koans.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Acknowledgment: -# -# Python Koans is a port of Ruby Koans originally written by Jim Weirich -# and Joe O'brien of Edgecase. There are some differences and tweaks specific -# to the Python language, but a great deal of it has been copied wholesale. -# So thank guys! -# - -import unittest -import sys - - -if __name__ == '__main__': - if sys.version_info >= (3, 0): - print("\nThis is the Python 2 version of Python Koans, but you are " + - "running it with Python 3 or newer!\n\n" - "Did you accidentally use the wrong python script? \nTry:\n\n" + - " python contemplate_koans.py\n") - else: - if sys.version_info < (2, 6): - print("\n" + - "********************************************************\n" + - "WARNING:\n" + - "This version of Python Koans was designed for " + - "Python 2.6 or greater.\n" + - "Your version of Python is older, so this is unlikely " + - "to work!\n\n" + - "But lets see how far we get...\n" + - "********************************************************\n") - - from runner.mountain import Mountain - - Mountain().walk_the_path(sys.argv) diff --git a/python 2/koans/a_normal_folder/a_module.py b/python 2/koans/a_normal_folder/a_module.py deleted file mode 100644 index 59e06f574..000000000 --- a/python 2/koans/a_normal_folder/a_module.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Duck(object): - @property - def name(self): - return "Howard" \ No newline at end of file diff --git a/python 2/koans/a_package_folder/a_module.py b/python 2/koans/a_package_folder/a_module.py deleted file mode 100644 index 899ac9702..000000000 --- a/python 2/koans/a_package_folder/a_module.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Duck(object): - @property - def name(self): - return "Donald" \ No newline at end of file diff --git a/python 2/koans/about_attribute_access.py b/python 2/koans/about_attribute_access.py deleted file mode 100644 index bcb293255..000000000 --- a/python 2/koans/about_attribute_access.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Partially based on AboutMessagePassing in the Ruby Koans -# - -from runner.koan import * - -class AboutAttributeAccess(Koan): - - class TypicalObject(object): - pass - - def test_calling_undefined_functions_normally_results_in_errors(self): - typical = self.TypicalObject() - - try: - typical.foobar() - except Exception as exception: - self.assertEqual(__, type(exception).__name__) - self.assertMatch(__, exception[0]) - - def test_calling_getattribute_causes_an_attribute_error(self): - typical = self.TypicalObject() - - try: - typical.__getattribute__('foobar') - except AttributeError as exception: - self.assertMatch(__, exception[0]) - - # THINK ABOUT IT: - # - # If the method __getattribute__() causes the AttributeError, then - # what would happen if we redefine __getattribute__()? - - # ------------------------------------------------------------------ - - class CatchAllAttributeReads(object): - def __getattribute__(self, attr_name): - return "Someone called '" + attr_name + "' and it could not be found" - - def test_all_attribute_reads_are_caught(self): - catcher = self.CatchAllAttributeReads() - - self.assertMatch(__, catcher.foobar) - - def test_intercepting_return_values_can_disrupt_the_call_chain(self): - catcher = self.CatchAllAttributeReads() - - self.assertMatch(__, catcher.foobaz) # This is fine - - try: - catcher.foobaz(1) - except TypeError as ex: - self.assertMatch(__, ex[0]) - - # foobaz returns a string. What happens to the '(1)' part? - # Try entering this into a python console to reproduce the issue: - # - # "foobaz"(1) - # - - def test_changes_to_the_getattribute_implementation_affects_getattr_function(self): - catcher = self.CatchAllAttributeReads() - - self.assertMatch(__, getattr(catcher, 'any_attribute')) - - # ------------------------------------------------------------------ - - class WellBehavedFooCatcher(object): - def __getattribute__(self, attr_name): - if attr_name[:3] == "foo": - return "Foo to you too" - else: - return super(AboutAttributeAccess.WellBehavedFooCatcher, self). \ - __getattribute__(attr_name) - - def test_foo_attributes_are_caught(self): - catcher = self.WellBehavedFooCatcher() - - self.assertEqual(__, catcher.foo_bar) - self.assertEqual(__, catcher.foo_baz) - - def test_non_foo_messages_are_treated_normally(self): - catcher = self.WellBehavedFooCatcher() - - try: - catcher.normal_undefined_attribute - except AttributeError as ex: - self.assertMatch(__, ex[0]) - - # ------------------------------------------------------------------ - - global stack_depth - stack_depth = 0 - - class RecursiveCatcher(object): - def __init__(self): - global stack_depth - stack_depth = 0 - self.no_of_getattribute_calls = 0 - - def __getattribute__(self, attr_name): - #Uncomment for debugging info: - #print 'Debug __getattribute__(' + type(self).__name__ + "." + attr_name + ") dict=" + str(self.__dict__) - - global stack_depth # We need something that is outside the scope of this class - stack_depth += 1 - - if stack_depth<=10: # to prevent a stack overflow - self.no_of_getattribute_calls += 1 - # Oops! We just accessed an attribute (no_of_getattribute_calls) - # Guess what happens when self.no_of_getattribute_calls is - # accessed? - - # Using 'object' directly because using super() here will also - # trigger a __getattribute__() call. - return object.__getattribute__(self, attr_name) - - def my_method(self): - pass - - def test_getattribute_is_a_bit_overzealous_sometimes(self): - catcher = self.RecursiveCatcher() - catcher.my_method() - global stack_depth - self.assertEqual(__, stack_depth) - - # ------------------------------------------------------------------ - - class MinimalCatcher(object): - class DuffObject(object): pass - - def __init__(self): - self.no_of_getattr_calls = 0 - - def __getattr__(self, attr_name): - self.no_of_getattr_calls += 1 - return self.DuffObject - - def my_method(self): - pass - - def test_getattr_ignores_known_attributes(self): - catcher = self.MinimalCatcher() - catcher.my_method() - - self.assertEqual(__, catcher.no_of_getattr_calls) - - def test_getattr_only_catches_unknown_attributes(self): - catcher = self.MinimalCatcher() - catcher.purple_flamingos() - catcher.free_pie() - - self.assertEqual(__, - type(catcher.give_me_duff_or_give_me_death()).__name__) - - self.assertEqual(__, catcher.no_of_getattr_calls) - - # ------------------------------------------------------------------ - - class PossessiveSetter(object): - def __setattr__(self, attr_name, value): - new_attr_name = attr_name - - if attr_name[-5:] == 'comic': - new_attr_name = "my_" + new_attr_name - elif attr_name[-3:] == 'pie': - new_attr_name = "a_" + new_attr_name - - object.__setattr__(self, new_attr_name, value) - - def test_setattr_intercepts_attribute_assignments(self): - fanboy = self.PossessiveSetter() - - fanboy.comic = 'The Laminator, issue #1' - fanboy.pie = 'blueberry' - - self.assertEqual(__, fanboy.a_pie) - - prefix = '__' - self.assertEqual("The Laminator, issue #1", getattr(fanboy, prefix + '_comic')) - - # ------------------------------------------------------------------ - - class ScarySetter(object): - def __init__(self): - self.num_of_coconuts = 9 - self._num_of_private_coconuts = 2 - - def __setattr__(self, attr_name, value): - new_attr_name = attr_name - - if attr_name[0] != '_': - new_attr_name = "altered_" + new_attr_name - - object.__setattr__(self, new_attr_name, value) - - def test_it_modifies_external_attribute_as_expected(self): - setter = self.ScarySetter() - setter.e = "mc hammer" - - self.assertEqual(__, setter.altered_e) - - def test_it_mangles_some_internal_attributes(self): - setter = self.ScarySetter() - - try: - coconuts = setter.num_of_coconuts - except AttributeError: - self.assertEqual(__, 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) diff --git a/python 2/koans/about_class_attributes.py b/python 2/koans/about_class_attributes.py deleted file mode 100644 index dac8f59f4..000000000 --- a/python 2/koans/about_class_attributes.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutClassMethods in the Ruby Koans -# - -from runner.koan import * - -class AboutClassAttributes(Koan): - class Dog(object): - pass - - def test_new_style_class_objects_are_objects(self): - # Note: Old style class instances are not objects but they are being - # phased out it Python 3. - - fido = self.Dog() - self.assertEqual(__, isinstance(fido, object)) - - def test_classes_are_types(self): - self.assertEqual(__, self.Dog.__class__ == type) - - def test_classes_are_objects_too(self): - self.assertEqual(__, issubclass(self.Dog, object)) - - def test_objects_have_methods(self): - fido = self.Dog() - self.assertEqual(__, len(dir(fido))) - - def test_classes_have_methods(self): - self.assertEqual(__, len(dir(self.Dog))) - - def test_creating_objects_without_defining_a_class(self): - singularity = object() - self.assertEqual(__, len(dir(singularity))) - - def test_defining_attributes_on_individual_objects(self): - fido = self.Dog() - fido.legs = 4 - - self.assertEqual(__, fido.legs) - - def test_defining_functions_on_individual_objects(self): - fido = self.Dog() - fido.wag = lambda : 'fidos wag' - - self.assertEqual(__, 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' - fido.wag = wag - - try: - rover.wag() - except Exception as ex: - self.assertMatch(__, ex[0]) - - # ------------------------------------------------------------------ - - class Dog2(object): - def wag(self): - return 'instance wag' - - def bark(self): - return "instance bark" - - def growl(self): - return "instance growl" - - @staticmethod - def bark(): - return "staticmethod bark, arg: None" - - @classmethod - 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()) - - def test_classmethods_are_not_independent_of_instance_methods(self): - fido = self.Dog2() - self.assertMatch(__, fido.growl()) - self.assertMatch(__, self.Dog2.growl()) - - def test_staticmethods_are_unbound_functions_housed_in_a_class(self): - self.assertMatch(__, self.Dog2.bark()) - - def test_staticmethods_also_overshadow_instance_methods(self): - fido = self.Dog2() - self.assertMatch(__, fido.bark()) - - # ------------------------------------------------------------------ - - class Dog3(object): - def __init__(self): - self._name = None - - def get_name_from_instance(self): - return self._name - - def set_name_from_instance(self, name): - self._name = name - - @classmethod - def get_name(cls): - return cls._name - - @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() - try: - fido.name = "Fido" - except Exception as ex: - self.assertMatch(__, 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()) - - 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()) - - # ------------------------------------------------------------------ - - class Dog4(object): - def a_class_method(cls): - return 'dogs class method' - - def a_static_method(): - return 'dogs 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()) - - def test_you_can_define_static_methods_without_using_a_decorator(self): - self.assertEqual(__, 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()) diff --git a/python 2/koans/about_classes.py b/python 2/koans/about_classes.py deleted file mode 100644 index d23b43f00..000000000 --- a/python 2/koans/about_classes.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutClasses(Koan): - class Dog(object): - "Dogs need regular walkies. Never, ever let them drive." - - def test_instances_of_classes_can_be_created_adding_parenthesis(self): - fido = self.Dog() - self.assertEqual(__, type(fido).__name__) - - def test_classes_have_docstrings(self): - self.assertMatch(__, self.Dog.__doc__) - - # ------------------------------------------------------------------ - - class Dog2(object): - def __init__(self): - self._name = 'Paul' - - def set_name(self, a_name): - self._name = a_name - - def test_init_method_is_the_constructor(self): - dog = self.Dog2() - self.assertEqual(__, dog._name) - - def test_private_attributes_are_not_really_private(self): - dog = self.Dog2() - dog.set_name("Fido") - self.assertEqual(__, dog._name) - # The _ prefix in _name implies private ownership, but nothing is truly - # private in Python. - - 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")) - # getattr(), setattr() and delattr() are a way of accessing attributes - # by method rather than through assignment operators - - self.assertEqual(__, 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. - - # ------------------------------------------------------------------ - - class Dog3(object): - def __init__(self): - self._name = None - - def set_name(self, a_name): - self._name = a_name - - def get_name(self): - return self._name - - name = property(get_name, set_name) - - - 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 - - # ------------------------------------------------------------------ - - class Dog4(object): - def __init__(self): - self._name = None - - @property - def name(self): - return self._name - - @name.setter - def name(self, a_name): - self._name = a_name - - def test_creating_properties_with_decorators_is_slightly_easier(self): - fido = self.Dog4() - - fido.name = "Fido" - self.assertEqual(__, fido.name) - - # ------------------------------------------------------------------ - - class Dog5(object): - def __init__(self, initial_name): - self._name = initial_name - - @property - def name(self): - return self._name - - def test_init_provides_initial_values_for_instance_variables(self): - fido = self.Dog5("Fido") - self.assertEqual(__, fido.name) - - def test_args_must_match_init(self): - self.assertRaises(___, self.Dog5) # Evaluates self.Dog5() - - # THINK ABOUT IT: - # Why is this so? - - def test_different_objects_have_difference_instance_variables(self): - fido = self.Dog5("Fido") - rover = self.Dog5("Rover") - - self.assertEqual(____, rover.name == fido.name) - - # ------------------------------------------------------------------ - - class Dog6(object): - def __init__(self, initial_name): - self._name = initial_name - - def get_self(self): - return self - - def __str__(self): - 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! - - def test_str_provides_a_string_version_of_the_object(self): - fido = self.Dog6("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)) - - def test_repr_provides_a_more_complete_string_version(self): - fido = self.Dog6("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(__, str("STRING")) - self.assertEqual(__, repr("STRING")) - diff --git a/python 2/koans/about_control_statements.py b/python 2/koans/about_control_statements.py deleted file mode 100644 index 7b3d30c9a..000000000 --- a/python 2/koans/about_control_statements.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutControlStatements(Koan): - - def test_if_then_else_statements(self): - if True: - result = 'true value' - else: - result = 'false value' - self.assertEqual(__, result) - - def test_if_then_statements(self): - result = 'default value' - if True: - result = 'true value' - self.assertEqual(__, result) - - def test_while_statement(self): - i = 1 - result = 1 - while i <= 10: - result = result * i - i += 1 - self.assertEqual(__, result) - - def test_break_statement(self): - i = 1 - result = 1 - while True: - if i > 10: break - result = result * i - i += 1 - self.assertEqual(__, result) - - def test_continue_statement(self): - i = 0 - result = [] - while i < 10: - i += 1 - if (i % 2) == 0: continue - result.append(i) - self.assertEqual(__, result) - - def test_for_statement(self): - phrase = ["fish", "and", "chips"] - result = [] - for item in phrase: - result.append(item.upper()) - self.assertEqual([__, __, __], result) - - def test_for_statement_with_tuples(self): - round_table = [ - ("Lancelot", "Blue"), - ("Galahad", "I don't know!"), - ("Robin", "Blue! I mean Green!"), - ("Arthur", "Is that an African Swallow or Amazonian Swallow?") - ] - result = [] - for knight, answer in round_table: - result.append("Contestant: '" + knight + "' Answer: '" + answer + "'") - - text = __ - - self.assertMatch(text, result[2]) - - self.assertNoMatch(text, result[0]) - self.assertNoMatch(text, result[1]) - self.assertNoMatch(text, result[3]) - \ No newline at end of file diff --git a/python 2/koans/about_decorating_with_classes.py b/python 2/koans/about_decorating_with_classes.py deleted file mode 100644 index 9617cf01e..000000000 --- a/python 2/koans/about_decorating_with_classes.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -import functools - -class AboutDecoratingWithClasses(Koan): - def maximum(self, a, b): - if a>b: - return a - else: - return b - - def test_partial_that_wrappers_no_args(self): - """ - Before we can understand this type of decorator we need to consider - the partial. - """ - max = functools.partial(self.maximum) - - self.assertEqual(__, max(7,23)) - self.assertEqual(__, 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)) - - 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()) - - # ------------------------------------------------------------------ - - class doubleit(object): - def __init__(self, fn): - self.fn = fn - - def __call__(self, *args): - return self.fn(*args) + ', ' + self.fn(*args) - - def __get__(self, obj, cls=None): - if not obj: - # Decorating an unbound function - return self - else: - # Decorating a bound method - return functools.partial(self, obj) - - @doubleit - def foo(self): - return "foo" - - @doubleit - def parrot(self, text): - return text.upper() - - 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')) - - # ------------------------------------------------------------------ - - def sound_check(self): - #Note: no decorator - return "Testing..." - - 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()) - - # ------------------------------------------------------------------ - - class documenter(object): - def __init__(self, *args): - self.fn_doc = args[0] - - def __call__(self, fn): - def decorated_function(*args): - return fn(*args) - - if fn.__doc__: - decorated_function.__doc__ = fn.__doc__ + ": " + self.fn_doc - else: - decorated_function.__doc__ = self.fn_doc - return decorated_function - - @documenter("Increments a value by one. Kind of.") - def count_badly(self, num): - num += 1 - if num==3: - return 5 - else: - num - @documenter("Does nothing") - def idler(self, num): - "Idler" - pass - - def test_decorator_with_an_argument(self): - self.assertEqual(__, self.count_badly(2)) - self.assertEqual(__, self.count_badly.__doc__) - - def test_documentor_which_already_has_a_docstring(self): - self.assertEqual(__, self.idler.__doc__) - - # ------------------------------------------------------------------ - - @documenter("DOH!") - @doubleit - @doubleit - def homer(self): - return "D'oh" - - def test_we_can_chain_decorators(self): - self.assertEqual(__, self.homer()) - self.assertEqual(__, self.homer.__doc__) - \ No newline at end of file diff --git a/python 2/koans/about_deleting_objects.py b/python 2/koans/about_deleting_objects.py deleted file mode 100644 index c47583051..000000000 --- a/python 2/koans/about_deleting_objects.py +++ /dev/null @@ -1,127 +0,0 @@ - #!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutDeletingObjects(Koan): - def test_del_can_remove_slices(self): - lottery_nums = [4, 8, 15, 16, 23, 42] - del lottery_nums[1] - del lottery_nums[2:4] - - self.assertEqual(__, lottery_nums) - - def test_del_can_remove_entire_lists(self): - lottery_nums = [4, 8, 15, 16, 23, 42] - del lottery_nums - try: - win = lottery_nums - except Exception as e: - pass - self.assertMatch(__, e[0]) - - # ==================================================================== - - class ClosingSale(object): - def __init__(self): - self.hamsters = 7 - self.zebras = 84 - - def cameras(self): - return 34 - - def toilet_brushes(self): - return 48 - - def jellies(self): - return 5 - - def test_del_can_remove_attributes(self): - crazy_discounts = self.ClosingSale() - del self.ClosingSale.toilet_brushes - del crazy_discounts.hamsters - - try: - still_available = crazy_discounts.toilet_brushes() - except AttributeError as e: - err_msg1 = e.args[0] - - try: - still_available = crazy_discounts.hamsters - except AttributeError as e: - err_msg2 = e.args[0] - - self.assertMatch(__, err_msg1) - self.assertMatch(__, err_msg2) - - # ==================================================================== - - class ClintEastwood(object): - def __init__(self): - self._name = None - - def get_name(self): - try: - return self._name - except: - return "The man with no name" - - def set_name(self, name): - self._name = name - - def del_name(self): - del self._name - - name = property(get_name, set_name, del_name, \ - "Mr Eastwood's current alias") - - def test_del_works_with_properties(self): - cowboy = self.ClintEastwood() - cowboy.name = 'Senor Ninguno' - self.assertEqual('Senor Ninguno', cowboy.name) - - del cowboy.name - self.assertEqual(__, cowboy.name) - - - # ==================================================================== - - class Prisoner(object): - def __init__(self): - self._name = None - - @property - def name(self): - return self._name - - @name.setter - def name(self, name): - self._name = name - - @name.deleter - def name(self): - self._name = 'Number Six' - - def test_another_way_to_make_a_deletable_property(self): - citizen = self.Prisoner() - citizen.name = "Patrick" - self.assertEqual('Patrick', citizen.name) - - del citizen.name - self.assertEqual(__, citizen.name) - - # ==================================================================== - - class MoreOrganisedClosingSale(ClosingSale): - def __init__(self): - self.last_deletion = None - super(AboutDeletingObjects.ClosingSale, self).__init__() - - def __delattr__(self, attr_name): - self.last_deletion = attr_name - - def tests_del_can_be_overriden(self): - sale = self.MoreOrganisedClosingSale() - self.assertEqual(5, sale.jellies()) - del sale.jellies - self.assertEqual(__, sale.last_deletion) diff --git a/python 2/koans/about_dice_project.py b/python 2/koans/about_dice_project.py deleted file mode 100644 index f16deed7b..000000000 --- a/python 2/koans/about_dice_project.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -import random - -class DiceSet(object): - def __init__(self): - self._values = None - - @property - 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 - -class AboutDiceProject(Koan): - def test_can_create_a_dice_set(self): - dice = DiceSet() - self.assertTrue(dice) - - def test_rolling_the_dice_returns_a_set_of_integers_between_1_and_6(self): - dice = DiceSet() - - dice.roll(5) - self.assertTrue(isinstance(dice.values, list), "should be a list") - self.assertEqual(5, len(dice.values)) - for value in dice.values: - self.assertTrue(value >= 1 and value <= 6, "value " + str(value) + " must be between 1 and 6") - - def test_dice_values_do_not_change_unless_explicitly_rolled(self): - dice = DiceSet() - dice.roll(5) - first_time = dice.values - second_time = dice.values - self.assertEqual(first_time, second_time) - - def test_dice_values_should_change_between_rolls(self): - dice = DiceSet() - - dice.roll(5) - first_time = dice.values - - dice.roll(5) - second_time = dice.values - - self.assertNotEqual(first_time, second_time, \ - "Two rolls should not be equal") - - # THINK ABOUT IT: - # - # 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? - - def test_you_can_roll_different_numbers_of_dice(self): - dice = DiceSet() - - dice.roll(3) - self.assertEqual(3, len(dice.values)) - - dice.roll(1) - self.assertEqual(1, len(dice.values)) diff --git a/python 2/koans/about_dictionaries.py b/python 2/koans/about_dictionaries.py deleted file mode 100644 index 37a21716f..000000000 --- a/python 2/koans/about_dictionaries.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutHashes in the Ruby Koans -# - -from runner.koan import * - -class AboutDictionaries(Koan): - def test_creating_dictionaries(self): - empty_dict = dict() - self.assertEqual(dict, type(empty_dict)) - self.assertEqual(dict(), empty_dict) - self.assertEqual(__, len(empty_dict)) - - def test_dictionary_literals(self): - babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish)) - - def test_accessing_dictionaries(self): - babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, babel_fish['one']) - self.assertEqual(__, babel_fish['two']) - - def test_changing_dictionaries(self): - babel_fish = { 'one': "uno", 'two': "dos" } - babel_fish['one'] = "eins" - - expected = { 'two': "dos", 'one': __ } - self.assertEqual(expected, babel_fish) - - def test_dictionary_is_unordered(self): - dict1 = { 'one': "uno", 'two': "dos" } - dict2 = { 'two': "dos", 'one': "uno" } - - self.assertEqual(____, 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(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(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"]) - diff --git a/python 2/koans/about_exceptions.py b/python 2/koans/about_exceptions.py deleted file mode 100644 index 22d94a7c6..000000000 --- a/python 2/koans/about_exceptions.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutExceptions(Koan): - - class MySpecialError(RuntimeError): - pass - - 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__) - - def test_try_clause(self): - result = None - try: - self.fail("Oops") - except StandardError as ex: - result = 'exception handled' - - self.assertEqual(__, result) - - self.assertEqual(____, isinstance(ex, StandardError)) - self.assertEqual(____, isinstance(ex, RuntimeError)) - - self.assertTrue(issubclass(RuntimeError, StandardError), \ - "RuntimeError is a subclass of StandardError") - - self.assertEqual(__, ex[0]) - - def test_raising_a_specific_error(self): - result = None - try: - raise self.MySpecialError, "My Message" - except self.MySpecialError as ex: - result = 'exception handled' - - self.assertEqual(__, result) - self.assertEqual(__, ex[0]) - - def test_else_clause(self): - result = None - try: - pass - except RuntimeError: - result = 'it broke' - pass - else: - result = 'no damage done' - - self.assertEqual(__, result) - - def test_finally_clause(self): - result = None - try: - self.fail("Oops") - except: - # no code here - pass - finally: - result = 'always run' - - self.assertEqual(__, result) diff --git a/python 2/koans/about_extra_credit.py b/python 2/koans/about_extra_credit.py deleted file mode 100644 index 54baef4ea..000000000 --- a/python 2/koans/about_extra_credit.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# EXTRA CREDIT: -# -# Create a program that will play the Greed Game. -# Rules for the game are in GREED_RULES.TXT. -# -# You already have a DiceSet class and score function you can use. -# Write a player class and a Game class to complete the project. This -# is a free form assignment, so approach it however you desire. - -from runner.koan import * - -class AboutExtraCredit(Koan): - # Write tests here. If you need extra test classes add them to the - # test suite in runner/path_to_enlightenment.py - def test_extra_credit_task(self): - pass - \ No newline at end of file diff --git a/python 2/koans/about_generators.py b/python 2/koans/about_generators.py deleted file mode 100644 index 9cc11212a..000000000 --- a/python 2/koans/about_generators.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Written in place of AboutBlocks in the Ruby Koans -# -# Note: Both blocks and generators use a yield keyword, but they behave -# a lot differently -# - -from runner.koan import * - -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) - - def test_generators_are_different_to_list_comprehensions(self): - num_list = [x*2 for x in range(1,3)] - num_generator = (x*2 for x in range(1,3)) - - 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 - - # 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 - # instead of stored. - # - # Generators are more memory friendly, but less versatile - - def test_generator_expressions_are_a_one_shot_deal(self): - dynamite = ('Boom!' for n in range(3)) - - attempt1 = list(dynamite) - attempt2 = list(dynamite) - - self.assertEqual(__, list(attempt1)) - self.assertEqual(__, list(attempt2)) - - # ------------------------------------------------------------------ - - def simple_generator_method(self): - yield 'peanut' - yield 'butter' - yield 'and' - yield 'jelly' - - 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) - - def test_coroutines_can_take_arguments(self): - result = self.simple_generator_method() - self.assertEqual(__, next(result)) - self.assertEqual(__, next(result)) - result.close() - - # ------------------------------------------------------------------ - - def square_me(self, seq): - for x in seq: - yield x * x - - def test_generator_method_with_parameter(self): - result = self.square_me(range(2,5)) - self.assertEqual(__, list(result)) - - # ------------------------------------------------------------------ - - def sum_it(self, seq): - value = 0 - for num in seq: - # The local state of 'value' will be retained between iterations - value += num - yield value - - def test_generator_keeps_track_of_local_variables(self): - result = self.sum_it(range(2,5)) - self.assertEqual(__, list(result)) - - # ------------------------------------------------------------------ - - def generator_with_coroutine(self): - result = yield - yield result - - def test_generators_can_take_coroutines(self): - generator = self.generator_with_coroutine() - - # THINK ABOUT IT: - # Why is this line necessary? - # - # Hint: Read the "Specification: Sending Values into Generators" - # section of http://www.python.org/dev/peps/pep-0342/ - next(generator) - - self.assertEqual(__, generator.send(1 + 2)) - - def test_before_sending_a_value_to_a_generator_next_must_be_called(self): - generator = self.generator_with_coroutine() - - try: - generator.send(1+2) - except TypeError as ex: - self.assertMatch(__, ex[0]) - - # ------------------------------------------------------------------ - - def yield_tester(self): - value = yield - if value: - yield value - else: - yield 'no value' - - 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')) - - generator2 = self.yield_tester() - next(generator2) - self.assertEqual(__, next(generator2)) - - def test_send_none_is_equivelant_to_next(self): - generator = self.yield_tester() - - next(generator) - # 'next(generator)' is exactly equivelant to 'generator.send(None)' - self.assertEqual(__, generator.send(None)) - - diff --git a/python 2/koans/about_inheritance.py b/python 2/koans/about_inheritance.py deleted file mode 100644 index bebb0f93f..000000000 --- a/python 2/koans/about_inheritance.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutInheritance(Koan): - class Dog(object): - def __init__(self, name): - self._name = name - - @property - def name(self): - return self._name - - def bark(self): - return "WOOF" - - class Chihuahua(Dog): - def wag(self): - return "happy" - - def bark(self): - return "yip" - - def test_subclasses_have_the_parent_as_an_ancestor(self): - self.assertEqual(____, issubclass(self.Chihuahua, self.Dog)) - - def test_this_subclass_ultimately_inherits_from_object_class(self): - self.assertEqual(____, issubclass(self.Chihuahua, object)) - - def test_instances_inherit_behavior_from_parent_class(self): - chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.name) - - def test_subclasses_add_new_behavior(self): - chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.wag()) - - try: - fido = self.Dog("Fido") - fido.wag() - except StandardError as ex: - self.assertMatch(__, ex[0]) - - def test_subclasses_can_modify_existing_behavior(self): - chico = self.Chihuahua("Chico") - self.assertEqual(__, chico.bark()) - - fido = self.Dog("Fido") - self.assertEqual(__, fido.bark()) - - # ------------------------------------------------------------------ - - class BullDog(Dog): - def bark(self): - return super(AboutInheritance.BullDog, self).bark() + ", GRR" - - def test_subclasses_can_invoke_parent_behavior_via_super(self): - ralph = self.BullDog("Ralph") - self.assertEqual(__, ralph.bark()) - - # ------------------------------------------------------------------ - - class GreatDane(Dog): - def growl(self): - return super(AboutInheritance.GreatDane, self).bark() + ", GROWL" - - def test_super_works_across_methods(self): - george = self.GreatDane("George") - self.assertEqual(__, george.growl()) - - # --------------------------------------------------------- - - class Pug(Dog): - def __init__(self, name): - pass - - class Greyhound(Dog): - def __init__(self, name): - super(AboutInheritance.Greyhound, self).__init__(name) - - def test_base_init_does_not_get_called_automatically(self): - snoopy = self.Pug("Snoopy") - try: - name = snoopy.name - except Exception as ex: - self.assertMatch(__, 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 diff --git a/python 2/koans/about_iteration.py b/python 2/koans/about_iteration.py deleted file mode 100644 index b5a00a836..000000000 --- a/python 2/koans/about_iteration.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutIteration(Koan): - - def test_iterators_are_a_type(self): - it = iter(range(1,6)) - - fib = 0 - - for num in it: - fib += num - - self.assertEqual(__ , fib) - - def test_iterating_with_next(self): - stages = iter(['alpha','beta','gamma']) - - try: - self.assertEqual(__, next(stages)) - next(stages) - self.assertEqual(__, next(stages)) - next(stages) - except StopIteration as ex: - err_msg = 'Ran out of iterations' - - self.assertMatch(__, err_msg) - - # ------------------------------------------------------------------ - - def add_ten(self, item): - return item + 10 - - def test_map_transforms_elements_of_a_list(self): - seq = [1, 2, 3] - - mapped_seq = map(self.add_ten, seq) - self.assertEqual(__, mapped_seq) - - def test_filter_selects_certain_items_from_a_list(self): - 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) - - def test_just_return_first_item_found(self): - def is_big_name(item): return len(item) > 4 - - names = ["Jim", "Bill", "Clarence", "Doug", "Eli"] - - # NOTE This still iterates through the whole names, so not particularly - # efficient - self.assertEqual([__], filter(is_big_name, names)[:1]) - - # Boring but effective - for item in names: - if is_big_name(item): - self.assertEqual(__, item) - break - - # ------------------------------------------------------------------ - - def add(self, accum, item): - return accum + item - - def multiply(self, accum, item): - return accum * item - - def test_reduce_will_blow_your_mind(self): - result = reduce(self.add, [2, 3, 4]) - self.assertEqual(__, result) - - result2 = reduce(self.multiply, [2, 3, 4], 1) - self.assertEqual(__, result2) - - # Extra Credit: - # Describe in your own words what reduce does. - - # ------------------------------------------------------------------ - - def test_creating_lists_with_list_comprehensions(self): - feast = ['lambs', 'sloths', 'orangutans', 'breakfast cereals', 'fruit bats'] - - comprehension = [delicacy.capitalize() for delicacy in feast] - - self.assertEqual(__, comprehension[0]) - self.assertEqual(__, comprehension[2]) - - def test_use_pass_for_iterations_with_no_body(self): - for num in range(1,5): - pass - - self.assertEqual(__, 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)) - - try: - # Files act like a collection of lines - file = open("example_file.txt") - - def make_upcase(line) : return line.strip().upper() - upcase_lines = map(make_upcase, file.readlines()) - self.assertEqual(__, list(upcase_lines)) - - # NOTE: You can create your own collections that work with each, - # map, select, etc. - finally: - # Arg, this is ugly. - # We will figure out how to fix this later. - if file: file.close() diff --git a/python 2/koans/about_list_assignments.py b/python 2/koans/about_list_assignments.py deleted file mode 100644 index d7e2e8998..000000000 --- a/python 2/koans/about_list_assignments.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutArrayAssignments in the Ruby Koans -# - -from runner.koan import * - -class AboutListAssignments(Koan): - def test_non_parallel_assignment(self): - names = ["John", "Smith"] - self.assertEqual(__, names) - - def test_parallel_assignments(self): - first_name, last_name = ["John", "Smith"] - self.assertEqual(__, first_name) - self.assertEqual(__, last_name) - - def test_parallel_assignments_with_sublists(self): - first_name, last_name = [["Willie", "Rae"], "Johnson"] - self.assertEqual(__, first_name) - self.assertEqual(__, 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) 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_method_bindings.py b/python 2/koans/about_method_bindings.py deleted file mode 100644 index 8762ff1ee..000000000 --- a/python 2/koans/about_method_bindings.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -def function(): - return "pineapple" - -def function2(): - return "tractor" - -class Class(object): - def method(self): - return "parrot" - -class AboutMethodBindings(Koan): - def test_methods_are_bound_to_an_object(self): - obj = Class() - self.assertEqual(__, 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)) - - def test_functions_have_attributes(self): - self.assertEqual(__, len(dir(function))) - self.assertEqual(__, dir(function) == dir(Class.method.im_func)) - - def test_bound_methods_have_different_attributes(self): - obj = Class() - self.assertEqual(__, len(dir(obj.method))) - - def test_setting_attributes_on_an_unbound_function(self): - function.cherries = 3 - self.assertEqual(__, 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]) - - 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) - - def test_functions_can_have_inner_functions(self): - function2.get_fruit = function - self.assertEqual(__, 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]) - - # ------------------------------------------------------------------ - - class BoundClass(object): - 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__(): - # bound_obj = self - # binding_owner = obj - # owner_type = cls - - self.assertEqual(__, type(bound_obj).__name__) - self.assertEqual(__, type(binding_owner).__name__) - self.assertEqual(AboutMethodBindings, owner_type) - - # ------------------------------------------------------------------ - - class SuperColor(object): - def __init__(self): - self.choice = None - - 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.color = 'purple' - self.assertEqual(__, self.color.choice) - diff --git a/python 2/koans/about_methods.py b/python 2/koans/about_methods.py deleted file mode 100644 index 08c133286..000000000 --- a/python 2/koans/about_methods.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Partially based on AboutMethods in the Ruby Koans -# - -from runner.koan import * - -def my_global_function(a,b): - return a + b - -class AboutMethods(Koan): - def test_calling_an_global_function(self): - self.assertEqual(__, 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): - try: - my_global_function() - except Exception as exception: - self.assertEqual(__, type(exception).__name__) - self.assertMatch( - r'my_global_function\(\) takes exactly 2 arguments \(0 given\)' - , exception[0]) - - try: - my_global_function(1, 2, 3) - except Exception as e: - - # Note, watch out for parenthesis. They need slashes in front! - self.assertMatch(__, e[0]) - - # ------------------------------------------------------------------ - - def pointless_method(self, a, b): - sum = a + b - - def test_which_does_not_return_anything(self): - self.assertEqual(__, 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)) - - # ------------------------------------------------------------------ - - 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(('one',), self.method_with_var_args('one')) - self.assertEqual(__, 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)) - - 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)) - - # ------------------------------------------------------------------ - - 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()) - - def test_that_overlapped_method_is_still_there(self): - self.assertEqual(__, self.link_to_overlapped_method()) - - # ------------------------------------------------------------------ - - def empty_method(self): - pass - - def test_methods_that_do_nothing_need_to_use_pass_as_a_filler(self): - self.assertEqual(__, self.empty_method()) - - def test_pass_does_nothing_at_all(self): - "You" - "shall" - "not" - pass - self.assertEqual(____, "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()) - - # ------------------------------------------------------------------ - - def method_with_documentation(self): - "A string placed at the beginning of a function is used for documentation" - return "ok" - - def test_the_documentation_can_be_viewed_with_the_doc_method(self): - self.assertMatch(__, self.method_with_documentation.__doc__) - - # ------------------------------------------------------------------ - - class Dog(object): - def name(self): - return "Fido" - - def _tail(self): - # Prefixing a method with an underscore implies private scope - return "wagging" - - def __password(self): - return 'password' # Genius! - - def test_calling_methods_in_other_objects(self): - rover = self.Dog() - self.assertEqual(__, 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()) - - def test_attributes_with_double_underscore_prefixes_are_subject_to_name_mangling(self): - rover = self.Dog() - try: - #This may not be possible... - password = rover.__password() - except Exception as ex: - self.assertEqual(__, type(ex).__name__) - - # But this still is! - self.assertEqual(__, 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/python 2/koans/about_modules.py deleted file mode 100644 index 37aad480a..000000000 --- a/python 2/koans/about_modules.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# This is very different to AboutModules in Ruby Koans -# Our AboutMultipleInheritance class is a little more comparable -# - -from runner.koan import * - -from another_local_module import * -from local_module_with_all_defined import * - -class AboutModules(Koan): - def test_importing_other_python_scripts_as_modules(self): - import local_module # local_module.py - - duck = local_module.Duck() - self.assertEqual(__, 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) - - 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()) - - def test_importing_all_module_attributes_at_once(self): - # NOTE Using this module level import declared at the top of this script: - # from other_local_module import * - # - # Import wildcard cannot be used from within classes or functions - - goose = Goose() - hamster = Hamster() - - self.assertEqual(__, goose.name) - self.assertEqual(__, hamster.name) - - def test_modules_hide_attributes_prefixed_by_underscores(self): - try: - private_squirrel = _SecretSquirrel() - except NameError as ex: - self.assertMatch(__, 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) - # module level attribute hiding doesn't affect class attributes - # (unless the class itself is hidden). - - def test_a_module_can_choose_which_attributes_are_available_to_wildcards(self): - # NOTE Using this module level import declared at the top of this script: - # from local_module_with_all_defined import * - - # 'Goat' is on the __ALL__ list - goat = Goat() - self.assertEqual(__, goat.name) - - # How about velociraptors? - lizard = _Velociraptor() - self.assertEqual(__, lizard.name) - - # SecretDuck? Never heard of her! - try: - duck = SecretDuck() - except NameError as ex: - self.assertMatch(__, ex[0]) diff --git a/python 2/koans/about_monkey_patching.py b/python 2/koans/about_monkey_patching.py deleted file mode 100644 index c88f85bf4..000000000 --- a/python 2/koans/about_monkey_patching.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Related to AboutOpenClasses in the Ruby Koans -# - -from runner.koan import * - -class AboutMonkeyPatching(Koan): - class Dog(object): - def bark(self): - return "WOOF" - - def test_as_defined_dogs_do_bark(self): - fido = self.Dog() - self.assertEqual(__, fido.bark()) - - # ------------------------------------------------------------------ - - # Add a new method to an existing class. - def test_after_patching_dogs_can_both_wag_and_bark(self): - def wag(self): return "HAPPY" - self.Dog.wag = wag - - fido = self.Dog() - self.assertEqual(__, fido.wag()) - self.assertEqual(__, fido.bark()) - - # ------------------------------------------------------------------ - - 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]) - - # ------------------------------------------------------------------ - - 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()) - - \ No newline at end of file diff --git a/python 2/koans/about_multiple_inheritance.py b/python 2/koans/about_multiple_inheritance.py deleted file mode 100644 index 3b99a20bd..000000000 --- a/python 2/koans/about_multiple_inheritance.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Slightly based on AboutModules in the Ruby Koans -# - -from runner.koan import * - -class AboutMultipleInheritance(Koan): - class Nameable(object): - def __init__(self): - self._name = None - - def set_name(self, new_name): - self._name = new_name - - def here(self): - return "In Nameable class" - - class Animal(object): - def legs(self): - return 4 - - def can_climb_walls(self): - return False - - def here(self): - return "In Animal class" - - class Pig(Animal): - def __init__(self): - super(AboutMultipleInheritance.Animal, self).__init__() - self._name = "Jasper" - - @property - def name(self): - return self._name - - def speak(self): - return "OINK" - - def color(self): - return 'pink' - - def here(self): - return "In Pig class" - - class Spider(Animal): - def __init__(self): - super(AboutMultipleInheritance.Animal, self).__init__() - self._name = "Boris" - - def can_climb_walls(self): - return True - - def legs(self): - return 8 - - def color(self): - return 'black' - - def here(self): - return "In Spider class" - - class Spiderpig(Pig, Spider, Nameable): - def __init__(self): - super(AboutMultipleInheritance.Pig, self).__init__() - super(AboutMultipleInheritance.Nameable, self).__init__() - self._name = "Jeff" - - def speak(self): - return "This looks like a job for Spiderpig!" - - def here(self): - return "In Spiderpig class" - - # - # Hierarchy: - # Animal - # / \ - # Pig Spider Nameable - # \ | / - # Spiderpig - # - # ------------------------------------------------------------------ - - def test_normal_methods_are_available_in_the_object(self): - jeff = self.Spiderpig() - self.assertMatch(__, jeff.speak()) - - def test_base_class_methods_are_also_available_in_the_object(self): - jeff = self.Spiderpig() - try: - jeff.set_name("Rover") - except: - self.fail("This should not happen") - self.assertEqual(____, jeff.can_climb_walls()) - - def test_base_class_methods_can_affect_instance_variables_in_the_object(self): - jeff = self.Spiderpig() - self.assertEqual(__, jeff.name) - - jeff.set_name("Rover") - self.assertEqual(__, jeff.name) - - def test_left_hand_side_inheritance_tends_to_be_higher_priority(self): - jeff = self.Spiderpig() - self.assertEqual(__, jeff.color()) - - def test_super_class_methods_are_higher_priority_than_super_super_classes(self): - jeff = self.Spiderpig() - self.assertEqual(__, 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__) - - def test_confirm_the_mro_controls_the_calling_order(self): - jeff = self.Spiderpig() - self.assertMatch('Spiderpig', jeff.here()) - - next = super(AboutMultipleInheritance.Spiderpig, jeff) - self.assertMatch('Pig', next.here()) - - next = super(AboutMultipleInheritance.Pig, jeff) - self.assertMatch(__, 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? - # - # To avoid confusion it may help to think of super() as next_mro(). - diff --git a/python 2/koans/about_new_style_classes.py b/python 2/koans/about_new_style_classes.py deleted file mode 100644 index 9ecd47b97..000000000 --- a/python 2/koans/about_new_style_classes.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutNewStyleClasses(Koan): - class OldStyleClass: - "An old style class" - # Original class style have been phased out in Python 3. - - class NewStyleClass(object): - "A new style class" - # Introduced in Python 2.2 - # - # Aside from this set of tests, Python Koans sticks exclusively to this - # kind of class - pass - - def test_new_style_classes_inherit_from_object_base_class(self): - self.assertEqual(____, issubclass(self.NewStyleClass, object)) - self.assertEqual(____, 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(__, 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__) - - try: - cls = self.OldStyleClass.__class__ - except Exception as ex: - pass - - self.assertMatch(__, 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__) - - # ------------------------------------------------------------------ - - 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__) - - 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__) diff --git a/python 2/koans/about_none.py b/python 2/koans/about_none.py deleted file mode 100644 index f8418f775..000000000 --- a/python 2/koans/about_none.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutNil in the Ruby Koans -# - -from runner.koan import * - -class AboutNone(Koan): - - def test_none_is_an_object(self): - "Unlike NULL in a lot of languages" - self.assertEqual(__, isinstance(None, object)) - - def test_none_is_universal(self): - "There is only one None" - self.assertEqual(__, None is None) - - def test_what_exception_do_you_get_when_calling_nonexistent_methods_on_None(self): - """ - What is the Exception that is thrown when you call a method that does - not exist? - - Hint: launch python command console and try the code in the block below. - - Don't worry about what 'try' and 'except' do, we'll talk about this later - """ - try: - None.some_method_none_does_not_know_about() - except Exception as ex: - # What exception has been caught? - self.assertEqual(__, ex.__class__.__name__) - - # What message was attached to the exception? - # (HINT: replace __ with part of the error message.) - self.assertMatch(__, 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) - - diff --git a/python 2/koans/about_packages.py b/python 2/koans/about_packages.py deleted file mode 100644 index 245656dfa..000000000 --- a/python 2/koans/about_packages.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# This is very different to AboutModules in Ruby Koans -# Our AboutMultipleInheritance class is a little more comparable -# - -from runner.koan import * - -# -# Package hierarchy of Python Koans project: -# -# contemplate_koans.py -# koans/ -# __init__.py -# __init__.py -# about_asserts.py -# about_attribute_access.py -# about_class_attributes.py -# about_classes.py -# ... -# a_package_folder/ -# __init__.py -# a_module.py - -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) - - 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/ - try: - import a_normal_folder - except ImportError as ex: - self.assertMatch(__, ex[0]) - - # ------------------------------------------------------------------ - - def test_use_absolute_imports_to_import_upper_level_modules(self): - # Import /contemplate_koans.py - import contemplate_koans - - self.assertEqual(__, contemplate_koans.__name__) - - # contemplate_koans.py is the root module in this package because its - # the first python module called in koans. - # - # If contemplate_koan.py was based in a_package_folder that would be - # the root folder, which would make reaching the koans folder - # almost impossible. So always leave the starting python script in - # a folder which can reach everything else. - - 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__) diff --git a/python 2/koans/about_proxy_object_project.py b/python 2/koans/about_proxy_object_project.py deleted file mode 100644 index 7a8ca80ae..000000000 --- a/python 2/koans/about_proxy_object_project.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Project: Create a Proxy Class -# -# In this assignment, create a proxy class (one is started for you -# below). You should be able to initialize the proxy object with any -# object. Any attributes called on the proxy object should be forwarded -# to the target object. As each attribute call is sent, the proxy should -# record the name of the attribute sent. -# -# The proxy class is started for you. You will need to add a method -# missing handler and any other supporting methods. The specification -# of the Proxy class is given in the AboutProxyObjectProject koan. - -# Note: This is a bit trickier that it's Ruby Koans counterpart, but you -# can do it! - -from runner.koan import * - -class Proxy(object): - 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: -# -class AboutProxyObjectProject(Koan): - def test_proxy_method_returns_wrapped_object(self): - # NOTE: The Television class is defined below - tv = Proxy(Television()) - - self.assertTrue(isinstance(tv, Proxy)) - - def test_tv_methods_still_perform_their_function(self): - tv = Proxy(Television()) - - tv.channel = 10 - tv.power() - - self.assertEqual(10, tv.channel) - self.assertTrue(tv.is_on()) - - def test_proxy_records_messages_sent_to_tv(self): - tv = Proxy(Television()) - - tv.power() - tv.channel = 10 - - self.assertEqual(['power', 'channel='], tv.messages()) - - def test_proxy_handles_invalid_messages(self): - tv = Proxy(Television()) - - ex = None - try: - tv.no_such_method() - except AttributeError as ex: - pass - - self.assertEqual(AttributeError, type(ex)) - - - def test_proxy_reports_methods_have_been_called(self): - tv = Proxy(Television()) - - tv.power() - tv.power() - - self.assertTrue(tv.was_called('power')) - self.assertFalse(tv.was_called('channel')) - - def test_proxy_counts_method_calls(self): - tv = Proxy(Television()) - - tv.power() - tv.channel = 48 - tv.power() - - self.assertEqual(2, tv.number_of_times_called('power')) - self.assertEqual(1, tv.number_of_times_called('channel=')) - 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") - - result = proxy.upper() - - self.assertEqual("PY OHIO 2010", result) - - result = proxy.split() - - self.assertEqual(["Py", "Ohio", "2010"], result) - self.assertEqual(['upper', 'split'], proxy.messages()) - -# ==================================================================== -# The following code is to support the testing of the Proxy class. No -# changes should be necessary to anything below this comment. - -# Example class using in the proxy testing above. -class Television(object): - def __init__(self): - self._channel = None - self._power = None - - @property - def channel(self): - return self._channel - - @channel.setter - def channel(self, value): - self._channel = value - - def power(self): - if self._power == 'on': - self._power = 'off' - else: - self._power = 'on' - - def is_on(self): - return self._power == 'on' - -# Tests for the Television class. All of theses tests should pass. -class TelevisionTest(Koan): - def test_it_turns_on(self): - tv = Television() - - tv.power() - self.assertTrue(tv.is_on()) - - def test_it_also_turns_off(self): - tv = Television() - - tv.power() - tv.power() - - self.assertFalse(tv.is_on()) - - def test_edge_case_on_off(self): - tv = Television() - - tv.power() - tv.power() - tv.power() - - self.assertTrue(tv.is_on()) - - tv.power() - - self.assertFalse(tv.is_on()) - - def test_can_set_the_channel(self): - tv = Television() - - tv.channel = 11 - self.assertEqual(11, tv.channel) diff --git a/python 2/koans/about_regex.py b/python 2/koans/about_regex.py deleted file mode 100755 index 13db11a82..000000000 --- a/python 2/koans/about_regex.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * -import re -class AboutRegex(Koan): - """ - These koans are based on the Ben's book: Regular Expressions in 10 minutes. - I found this books very useful so I decided to write a koans in order to practice everything I had learned from it. - http://www.forta.com/books/0672325667/ - """ - - def test_matching_literal_text(self): - """ - Lesson 1 Matching Literal String - """ - string = "Hello, my name is Felix and this koans are based on the Ben's book: Regular Expressions in 10 minutes." - m = re.search(__, string) - self.assertTrue(m and m.group(0) and m.group(0)== 'Felix', "I want my name") - - def test_matching_literal_text_how_many(self): - """ - Lesson 1 How many matches? - - The default behaviour of most regular extression engines is to return just the first match. - In python you have the next options: - - match() --> Determine if the RE matches at the beginning of the string. - search() --> Scan through a string, looking for any location where this RE matches. - findall() --> Find all substrings where the RE matches, and returns them as a list. - finditer() --> Find all substrings where the RE matches, and returns them as an iterator. - - """ - string = "Hello, my name is Felix and this koans are based on the Ben's book: Regular Expressions in 10 minutes. Repeat My name is Felix" - m = re.match('Felix', string) #TIP: Maybe match it's not the best option - - # I want to know how many times appears my name - self.assertEqual(m, __) - - def test_matching_literal_text_not_case_sensitivity(self): - """ - Lesson 1 Matching Literal String non case sensitivity. - Most regex implementations also support matches that are not case sensitive. In python you can use re.IGNORECASE, in - Javascript you can specify the optional i flag. - In Ben's book you can see more languages. - - """ - string = "Hello, my name is Felix or felix and this koans is based on the Ben's book: Regular Expressions in 10 minutes." - - self.assertEqual(re.findall("felix", string, 20), __) - self.assertEqual(re.findall("felix", string, 10), __) - - def test_matching_any_character(self): - """ - Lesson 1 Matching any character - - . matches any character, alphabetic characters, digits and . - """ - string = "pecks.xlx\n" \ - + "orders1.xls\n" \ - + "apec1.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls" - - # TIP: remember the name of this lesson - - change_this_search_string = 'a..xlx' # <-- I want to find all uses of myArray - self.assertEquals(len(re.findall(change_this_search_string, string)),3) - - def test_matching_set_character(self): - """ - Lesson 2 Matching sets of characters - - A set of characters is defined using the metacharacters [ and ]. Everything between them is part of the set and - any one of the set members must match (but not all). - """ - string = "sales.xlx\n" \ - + "sales1.xls\n" \ - + "orders3.xls\n" \ - + "apac1.xls\n" \ - + "sales2.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls\n" \ - + "ca1.xls" - # I want to find all files for North America(na) or South America(sa), but not (ca) - # TIP you can use the pattern .a. which matches in above test but in this case matches more than you want - change_this_search_string = '[nsc]a[2-9].xls' - self.assertEquals(len(re.findall(change_this_search_string, string)),3) - - def test_anything_but_matching(self): - """ - Lesson 2 Using character set ranges - Occsionally, you'll want a list of characters that you don't want to match. - Character sets can be negated using the ^ metacharacter. - - """ - string = "sales.xlx\n" \ - + "sales1.xls\n" \ - + "orders3.xls\n" \ - + "apac1.xls\n" \ - + "sales2.xls\n" \ - + "sales3.xls\n" \ - + "europe2.xls\n" \ - + "sam.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls\n" \ - + "ca1.xls" - - # I want to find the name sam - change_this_search_string = '[^nc]am' - self.assertEquals(re.findall(change_this_search_string, string), ['sam.xls']) - - diff --git a/python 2/koans/about_scope.py b/python 2/koans/about_scope.py deleted file mode 100644 index 9fd7501e3..000000000 --- a/python 2/koans/about_scope.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -import jims -import joes - -counter = 0 # Global - -class AboutScope(Koan): - # - # NOTE: - # Look in jims.py and joes.py to see definitions of Dog used - # for this set of tests - # - - def test_dog_is_not_available_in_the_current_scope(self): - try: - fido = Dog() - except Exception as ex: - self.assertMatch(__, 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(____, type(fido) == type(rover)) - self.assertEqual(____, jims.Dog == joes.Dog) - - # ------------------------------------------------------------------ - - class str(object): - pass - - def test_bare_bones_class_names_do_not_assume_the_current_scope(self): - self.assertEqual(____, AboutScope.str == str) - - def test_nested_string_is_not_the_same_as_the_system_string(self): - self.assertEqual(____, self.str == type("HI")) - - def test_str_without_self_prefix_stays_in_the_global_scope(self): - self.assertEqual(____, str == type("HI")) - - # ------------------------------------------------------------------ - - PI = 3.1416 - - def test_constants_are_defined_with_an_initial_uppercase_letter(self): - self.assertAlmostEqual(_____, 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 - # to keep to the convention and not modify them. - - # ------------------------------------------------------------------ - - def increment_using_local_counter(self, counter): - counter = counter + 1 - - def increment_using_global_counter(self): - global counter - counter = counter + 1 - - def test_incrementing_with_local_counter(self): - global counter - start = counter - self.increment_using_local_counter(start) - self.assertEqual(____, counter == start + 1) - - def test_incrementing_with_global_counter(self): - global counter - start = counter - self.increment_using_global_counter() - self.assertEqual(____, counter == start + 1) - - # ------------------------------------------------------------------ - - global deadly_bingo - 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]) - \ No newline at end of file diff --git a/python 2/koans/about_sets.py b/python 2/koans/about_sets.py deleted file mode 100644 index cf207b031..000000000 --- a/python 2/koans/about_sets.py +++ /dev/null @@ -1,45 +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_sets_are_unordered(self): - self.assertEqual(set([__, __, __, __, __]), set('12345')) - - def test_convert_the_set_into_a_list_to_sort_it(self): - self.assertEqual(__, sorted(set('13245'))) - - - # ------------------------------------------------------------------ - - 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 set([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 2/koans/about_strings.py b/python 2/koans/about_strings.py deleted file mode 100644 index 8b6ed0def..000000000 --- a/python 2/koans/about_strings.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutStrings(Koan): - - def test_double_quoted_strings_are_strings(self): - string = "Hello, world." - self.assertEqual(__, isinstance(string, basestring)) - - def test_single_quoted_strings_are_also_strings(self): - string = 'Goodbye, world.' - self.assertEqual(__, isinstance(string, basestring)) - - def test_triple_quote_strings_are_also_strings(self): - string = """Howdy, world!""" - self.assertEqual(__, isinstance(string, basestring)) - - def test_triple_single_quotes_work_too(self): - string = '''Bonjour tout le monde!''' - self.assertEqual(__, isinstance(string, basestring)) - - def test_raw_strings_are_also_strings(self): - string = r"Konnichi wa, world!" - self.assertEqual(__, isinstance(string, basestring)) - - def test_use_single_quotes_to_create_string_with_double_quotes(self): - string = 'He said, "Go Away."' - self.assertEqual(__, string) - - def test_use_double_quotes_to_create_strings_with_single_quotes(self): - string = "Don't" - self.assertEqual(__, 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)) - - 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)) - - def test_triple_quoted_strings_can_span_lines(self): - string = """ -Howdy, -world! -""" - self.assertEqual(__, len(string)) - - def test_triple_quoted_strings_need_less_escaping(self): - a = "Hello \"world\"." - b = """Hello "world".""" - self.assertEqual(__, (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) - - def test_adjacent_strings_are_concatenated_automatically(self): - string = "Hello" ", " "World" - self.assertEqual(__, string) - - def test_plus_will_not_modify_original_strings(self): - hi = "Hello, " - there = "world" - string = hi + there - self.assertEqual(__, hi) - self.assertEqual(__, there) - - def test_plus_equals_will_append_to_end_of_string(self): - hi = "Hello, " - there = "world" - hi += there - self.assertEqual(__, hi) - - def test_plus_equals_also_leaves_original_string_unmodified(self): - original = "Hello, " - hi = original - there = "world" - hi += there - self.assertEqual(__, original) - - def test_most_strings_interpret_escape_characters(self): - string = "\n" - self.assertEqual('\n', string) - self.assertEqual("""\n""", string) - self.assertEqual(__, 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) - - 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) - - def test_any_python_expression_may_be_interpolated(self): - import math # import a standard python module with math functions - - decimal_places = 4 - string = "The square root of 5 is {0:.{1}f}".format(math.sqrt(5), \ - decimal_places) - self.assertEqual(__, string) - - def test_you_can_get_a_substring_from_a_string(self): - string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[7:10]) - - def test_you_can_get_a_single_character_from_a_string(self): - string = "Bacon, lettuce and tomato" - self.assertEqual(__, string[1]) - - def test_single_characters_can_be_represented_by_integers(self): - self.assertEqual(__, ord('a')) - self.assertEqual(__, ord('b') == (ord('a') + 1)) - - def test_strings_can_be_split(self): - string = "Sausage Egg Cheese" - words = string.split() - self.assertEqual([__, __, __], words) - - def test_strings_can_be_split_with_different_patterns(self): - import re #import python regular expression library - - string = "the,rain;in,spain" - pattern = re.compile(',|;') - - words = pattern.split(string) - - self.assertEqual([__, __, __, __], words) - - # Pattern is a Python regular expression pattern which matches ',' or ';' - - def test_raw_strings_do_not_interpret_escape_characters(self): - string = r'\n' - self.assertNotEqual('\n', string) - self.assertEqual(__, string) - self.assertEqual(__, len(string)) - - # Useful in regular expressions, file paths, URLs, etc. - - def test_strings_can_be_joined(self): - words = ["Now", "is", "the", "time"] - self.assertEqual(__, ' '.join(words)) - - def test_strings_can_change_case(self): - self.assertEqual(__, 'guido'.capitalize()) - self.assertEqual(__, 'guido'.upper()) - self.assertEqual(__, 'TimBot'.lower()) - self.assertEqual(__, 'guido van rossum'.title()) - self.assertEqual(__, 'ToTaLlY aWeSoMe'.swapcase()) diff --git a/python 2/koans/about_triangle_project.py b/python 2/koans/about_triangle_project.py deleted file mode 100644 index 6c254768f..000000000 --- a/python 2/koans/about_triangle_project.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -# You need to write the triangle method in the file 'triangle.py' -from triangle import * - -class AboutTriangleProject(Koan): - def test_equilateral_triangles_have_equal_sides(self): - self.assertEqual('equilateral', triangle(2, 2, 2)) - self.assertEqual('equilateral', triangle(10, 10, 10)) - - def test_isosceles_triangles_have_exactly_two_sides_equal(self): - self.assertEqual('isosceles', triangle(3, 4, 4)) - self.assertEqual('isosceles', triangle(4, 3, 4)) - self.assertEqual('isosceles', triangle(4, 4, 3)) - self.assertEqual('isosceles', triangle(10, 10, 2)) - - def test_scalene_triangles_have_no_equal_sides(self): - self.assertEqual('scalene', triangle(3, 4, 5)) - self.assertEqual('scalene', triangle(10, 11, 12)) - self.assertEqual('scalene', triangle(5, 4, 2)) diff --git a/python 2/koans/about_triangle_project2.py b/python 2/koans/about_triangle_project2.py deleted file mode 100644 index ffff4ac00..000000000 --- a/python 2/koans/about_triangle_project2.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -# You need to finish implementing triangle() in the file 'triangle.py' -from triangle import * - -class AboutTriangleProject2(Koan): - # The first assignment did not talk about how to handle errors. - # Let's handle that part now. - def test_illegal_triangles_throw_exceptions(self): - # Calls triangle(0, 0, 0) - self.assertRaises(TriangleError, triangle, 0, 0, 0) - - self.assertRaises(TriangleError, triangle, 3, 4, -5) - self.assertRaises(TriangleError, triangle, 1, 1, 3) - self.assertRaises(TriangleError, triangle, 2, 4, 2) diff --git a/python 2/koans/about_tuples.py b/python 2/koans/about_tuples.py deleted file mode 100644 index ae5e614e2..000000000 --- a/python 2/koans/about_tuples.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutTuples(Koan): - def test_creating_a_tuple(self): - count_of_three = (1, 2, 5) - self.assertEqual(__, 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]) - - def test_tuples_are_immutable_so_appending_is_not_possible(self): - count_of_three = (1, 2, 5) - try: - count_of_three.append("boom") - except Exception as ex: - self.assertEqual(AttributeError, type(ex)) - - # Note, assertMatch() uses regular expression pattern matching, - # so you don't have to copy the whole message. - self.assertMatch(__, ex[0]) - - # Tuples are less flexible than lists, but faster. - - def test_tuples_can_only_be_changed_through_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) - - def test_tuples_of_one_look_peculiar(self): - self.assertEqual(__, (1).__class__.__name__) - self.assertEqual(__, (1,).__class__.__name__) - self.assertEqual(__, ("Hello comma!", )) - - def test_tuple_constructor_can_be_surprising(self): - self.assertEqual(__, tuple("Surprise!")) - - def test_creating_empty_tuples(self): - 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) - - def test_tuples_are_good_for_representing_records(self): - locations = [ - ("Illuminati HQ", (38, 52, 15.56, 'N'), (77, 3, 21.46, 'W')), - ("Stargate B", (41, 10, 43.92, 'N'), (1, 49, 34.29, 'W')), - ] - - locations.append( ("Cthulu", (26, 40, 1, 'N'), (70, 45, 7, 'W')) ) - - self.assertEqual(__, locations[2][0]) - self.assertEqual(__, locations[0][1][2]) - - - diff --git a/python 2/koans/another_local_module.py b/python 2/koans/another_local_module.py deleted file mode 100644 index c7358947b..000000000 --- a/python 2/koans/another_local_module.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Goose(object): - @property - def name(self): - return "Mr Stabby" - -class Hamster(object): - @property - def name(self): - return "Phil" - -class _SecretSquirrel(object): - @property - def name(self): - return "Mr Anonymous" \ No newline at end of file diff --git a/python 2/koans/jims.py b/python 2/koans/jims.py deleted file mode 100644 index 06a6f0d13..000000000 --- a/python 2/koans/jims.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Dog(object): - def identify(self): - return "jims dog" diff --git a/python 2/koans/joes.py b/python 2/koans/joes.py deleted file mode 100644 index 491de31ff..000000000 --- a/python 2/koans/joes.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Dog(object): - def identify(self): - return "joes dog" diff --git a/python 2/koans/local_module.py b/python 2/koans/local_module.py deleted file mode 100644 index 1d9195800..000000000 --- a/python 2/koans/local_module.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class Duck(object): - def __init__(self): - self._password = 'password' # Genius! - - @property - def name(self): - return "Daffy" diff --git a/python 2/koans/local_module_with_all_defined.py b/python 2/koans/local_module_with_all_defined.py deleted file mode 100644 index 6ac36b2a2..000000000 --- a/python 2/koans/local_module_with_all_defined.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -__all__ = ( - 'Goat', - '_Velociraptor' -) - -class Goat(object): - @property - def name(self): - return "George" - -class _Velociraptor(object): - @property - def name(self): - return "Cuddles" - -class SecretDuck(object): - @property - def name(self): - return "None of your business" diff --git a/python 2/koans/triangle.py b/python 2/koans/triangle.py deleted file mode 100644 index b71c2dd77..000000000 --- a/python 2/koans/triangle.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Triangle Project Code. - -# Triangle analyzes the lengths of the sides of a triangle -# (represented by a, b and c) and returns the type of triangle. -# -# It returns: -# 'equilateral' if all sides are equal -# 'isosceles' if exactly 2 sides are equal -# 'scalene' if no sides are equal -# -# The tests for this method can be found in -# about_triangle_project.py -# and -# about_triangle_project_2.py -# -def triangle(a, b, c): - # DELETE 'PASS' AND WRITE THIS CODE - pass - -# Error class used in part 2. No need to change this code. -class TriangleError(StandardError): - pass diff --git a/python 2/libs/colorama/__init__.py b/python 2/libs/colorama/__init__.py deleted file mode 100644 index 331174e57..000000000 --- a/python 2/libs/colorama/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .initialise import init -from .ansi import Fore, Back, Style -from .ansitowin32 import AnsiToWin32 - -VERSION = '0.1.18' - diff --git a/python 2/libs/colorama/ansi.py b/python 2/libs/colorama/ansi.py deleted file mode 100644 index 7b818e19e..000000000 --- a/python 2/libs/colorama/ansi.py +++ /dev/null @@ -1,49 +0,0 @@ -''' -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/python 2/libs/colorama/initialise.py b/python 2/libs/colorama/initialise.py deleted file mode 100644 index eca2bea40..000000000 --- a/python 2/libs/colorama/initialise.py +++ /dev/null @@ -1,38 +0,0 @@ -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/python 2/libs/colorama/win32.py b/python 2/libs/colorama/win32.py deleted file mode 100644 index 5f49e6cba..000000000 --- a/python 2/libs/colorama/win32.py +++ /dev/null @@ -1,95 +0,0 @@ - -# 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/python 2/libs/colorama/winterm.py b/python 2/libs/colorama/winterm.py deleted file mode 100644 index 63f4469cc..000000000 --- a/python 2/libs/colorama/winterm.py +++ /dev/null @@ -1,69 +0,0 @@ - -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/python 2/libs/mock.py deleted file mode 100644 index c01875bf3..000000000 --- a/python 2/libs/mock.py +++ /dev/null @@ -1,271 +0,0 @@ -# mock.py -# Test tools for mocking and patching. -# Copyright (C) 2007-2009 Michael Foord -# E-mail: fuzzyman AT voidspace DOT org DOT uk - -# mock 0.6.0 -# http://www.voidspace.org.uk/python/mock/ - -# Released subject to the BSD License -# Please see http://www.voidspace.org.uk/python/license.shtml - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# Comments, suggestions and bug reports welcome. - - -__all__ = ( - 'Mock', - 'patch', - 'patch_object', - 'sentinel', - 'DEFAULT' -) - -__version__ = '0.6.0 modified by Greg Malcolm' - -class SentinelObject(object): - def __init__(self, name): - self.name = name - - def __repr__(self): - return ''.format(self.name) - - -class Sentinel(object): - def __init__(self): - self._sentinels = {} - - def __getattr__(self, name): - return self._sentinels.setdefault(name, SentinelObject(name)) - - -sentinel = Sentinel() - -DEFAULT = sentinel.DEFAULT - -class OldStyleClass: - pass -ClassType = type(OldStyleClass) - -def _is_magic(name): - return '__{0!s}__'.format(name[2:-2]) == name - -def _copy(value): - if type(value) in (dict, list, tuple, set): - return type(value)(value) - return value - - -class Mock(object): - - def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, - name=None, parent=None, wraps=None): - self._parent = parent - self._name = name - if spec is not None and not isinstance(spec, list): - spec = [member for member in dir(spec) if not _is_magic(member)] - - self._methods = spec - self._children = {} - self._return_value = return_value - self.side_effect = side_effect - self._wraps = wraps - - self.reset_mock() - - - def reset_mock(self): - self.called = False - self.call_args = None - self.call_count = 0 - self.call_args_list = [] - self.method_calls = [] - for child in self._children.values(): - child.reset_mock() - if isinstance(self._return_value, Mock): - self._return_value.reset_mock() - - - def __get_return_value(self): - if self._return_value is DEFAULT: - self._return_value = Mock() - return self._return_value - - def __set_return_value(self, value): - self._return_value = value - - return_value = property(__get_return_value, __set_return_value) - - - def __call__(self, *args, **kwargs): - self.called = True - self.call_count += 1 - self.call_args = (args, kwargs) - self.call_args_list.append((args, kwargs)) - - parent = self._parent - name = self._name - while parent is not None: - parent.method_calls.append((name, args, kwargs)) - if parent._parent is None: - break - name = parent._name + '.' + name - parent = parent._parent - - ret_val = DEFAULT - if self.side_effect is not None: - if (isinstance(self.side_effect, Exception) or - isinstance(self.side_effect, (type, ClassType)) and - issubclass(self.side_effect, Exception)): - raise self.side_effect - - ret_val = self.side_effect(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - - if self._wraps is not None and self._return_value is DEFAULT: - return self._wraps(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - return ret_val - - - def __getattr__(self, name): - if self._methods is not None: - if name not in self._methods: - raise AttributeError("Mock object has no attribute '{0!s}'".format(name)) - elif _is_magic(name): - raise AttributeError(name) - - if name not in self._children: - wraps = None - if self._wraps is not None: - wraps = getattr(self._wraps, name) - self._children[name] = Mock(parent=self, name=name, wraps=wraps) - - return self._children[name] - - - def assert_called_with(self, *args, **kwargs): - assert self.call_args == (args, kwargs), 'Expected: {0!s}\nCalled with: {1!s}'.format((args, kwargs), self.call_args) - - -def _dot_lookup(thing, comp, import_path): - try: - return getattr(thing, comp) - except AttributeError: - __import__(import_path) - return getattr(thing, comp) - - -def _importer(target): - components = target.split('.') - import_path = components.pop(0) - thing = __import__(import_path) - - for comp in components: - import_path += ".{0!s}".format(comp) - thing = _dot_lookup(thing, comp, import_path) - return thing - - -class _patch(object): - def __init__(self, target, attribute, new, spec, create): - self.target = target - self.attribute = attribute - self.new = new - self.spec = spec - self.create = create - self.has_local = False - - - def __call__(self, func): - if hasattr(func, 'patchings'): - func.patchings.append(self) - return func - - def patched(*args, **keywargs): - # don't use a with here (backwards compatability with 2.5) - extra_args = [] - for patching in patched.patchings: - arg = patching.__enter__() - if patching.new is DEFAULT: - extra_args.append(arg) - args += tuple(extra_args) - try: - return func(*args, **keywargs) - finally: - for patching in getattr(patched, 'patchings', []): - patching.__exit__() - - patched.patchings = [self] - patched.__name__ = func.__name__ - patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno", - func.func_code.co_firstlineno) - return patched - - - def get_original(self): - target = self.target - name = self.attribute - create = self.create - - original = DEFAULT - if _has_local_attr(target, name): - try: - original = target.__dict__[name] - except AttributeError: - # for instances of classes with slots, they have no __dict__ - original = getattr(target, name) - elif not create and not hasattr(target, name): - raise AttributeError("{0!s} does not have the attribute {1!r}".format(target, name)) - return original - - - def __enter__(self): - new, spec, = self.new, self.spec - original = self.get_original() - if new is DEFAULT: - # XXXX what if original is DEFAULT - shouldn't use it as a spec - inherit = False - if spec == True: - # set spec to the object we are replacing - spec = original - if isinstance(spec, (type, ClassType)): - inherit = True - new = Mock(spec=spec) - if inherit: - new.return_value = Mock(spec=spec) - self.temp_original = original - setattr(self.target, self.attribute, new) - return new - - - def __exit__(self, *_): - if self.temp_original is not DEFAULT: - setattr(self.target, self.attribute, self.temp_original) - else: - delattr(self.target, self.attribute) - del self.temp_original - - -def patch_object(target, attribute, new=DEFAULT, spec=None, create=False): - return _patch(target, attribute, new, spec, create) - - -def patch(target, new=DEFAULT, spec=None, create=False): - try: - target, attribute = target.rsplit('.', 1) - except (TypeError, ValueError): - raise TypeError("Need a valid target to patch. You supplied: {0!r}".format(target,)) - target = _importer(target) - return _patch(target, attribute, new, spec, create) - - - -def _has_local_attr(obj, name): - try: - return name in vars(obj) - except TypeError: - # objects without a __dict__ - return hasattr(obj, name) diff --git a/python 2/run.bat b/python 2/run.bat deleted file mode 100644 index 7bfc5d8b0..000000000 --- a/python 2/run.bat +++ /dev/null @@ -1,37 +0,0 @@ -@echo off - -REM This is how you run it from the command line. -REM You don't actually need this script! -SET RUN_KOANS=python.exe contemplate_koans.py - -REM Set this to your python folder: -SET PYTHON_PATH=C:\Python26 - - - -set SCRIPT= -REM Hunt around for python -IF EXIST "python.exe" ( - SET SCRIPT=%RUN_KOANS% -) ELSE ( - IF EXIST "%PYTHON_PATH%" ( - SET SCRIPT=%PYTHON_PATH%\%RUN_KOANS% - ) ELSE ( - IF EXIST %PYTHON% SET SCRIPT=%PYTHON%\%RUN_KOANS% - ) -) - -IF NOT "" == "%SCRIPT%" ( - %SCRIPT% - pause -) ELSE ( - echo. - echo Python.exe is not in the path! - echo. - echo Fix the path and try again. - echo Or better yet, run this with the correct python path: - echo. - echo python.exe contemplate_koans.py - pause -) -:end \ No newline at end of file diff --git a/python 2/run.sh b/python 2/run.sh deleted file mode 100755 index d247987bc..000000000 --- a/python 2/run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -python contemplate_koans.py diff --git a/python 2/runner/koan.py b/python 2/runner/koan.py deleted file mode 100644 index e9a0080bd..000000000 --- a/python 2/runner/koan.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import re - -# Starting a classname or attribute with an underscore normally implies Private scope. -# However, we are making an exception for __ and ___. - -__all__ = [ "__", "___", "____", "_____", "Koan" ] - -__ = "-=> FILL ME IN! <=-" - -class ___(Exception): - pass - -____ = "-=> TRUE OR FALSE? <=-" - -_____ = 0 - - -class Koan(unittest.TestCase): - def assertMatch(self, pattern, string, msg=None): - """ - Throw an exception if the regular expresson pattern is matched - """ - # Not part of unittest, but convenient for some koans tests - m = re.search(pattern, string) - if not m or not m.group(0): - raise self.failureException, \ - (msg or '{0!r} does not match {1!r}'.format(pattern, string)) - - def assertNoMatch(self, pattern, string, msg=None): - """ - Throw an exception if the regular expresson pattern is not matched - """ - m = re.search(pattern, string) - if m and m.group(0): - raise self.failureException, \ - (msg or '{0!r} matches {1!r}'.format(pattern, string)) diff --git a/python 2/runner/mountain.py b/python 2/runner/mountain.py deleted file mode 100644 index 4ad479a9c..000000000 --- a/python 2/runner/mountain.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import sys - -import path_to_enlightenment -from sensei import Sensei -from writeln_decorator import WritelnDecorator - -class Mountain: - 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]) - - self.tests(self.lesson) - self.lesson.learn() - return self.lesson diff --git a/python 2/runner/path_to_enlightenment.py b/python 2/runner/path_to_enlightenment.py deleted file mode 100644 index 61e3c2e05..000000000 --- a/python 2/runner/path_to_enlightenment.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# The path to enlightenment starts with the following: - -import unittest - -from koans.about_asserts import AboutAsserts -from koans.about_none import AboutNone -from koans.about_lists import AboutLists -from koans.about_list_assignments import AboutListAssignments -from koans.about_dictionaries import AboutDictionaries -from koans.about_strings import AboutStrings -from koans.about_tuples import AboutTuples -from koans.about_methods import AboutMethods -from koans.about_control_statements import AboutControlStatements -from koans.about_true_and_false import AboutTrueAndFalse -from koans.about_sets import AboutSets -from koans.about_triangle_project import AboutTriangleProject -from koans.about_exceptions import AboutExceptions -from koans.about_triangle_project2 import AboutTriangleProject2 -from koans.about_iteration import AboutIteration -from koans.about_generators import AboutGenerators -from koans.about_lambdas import AboutLambdas -from koans.about_scoring_project import AboutScoringProject -from koans.about_classes import AboutClasses -from koans.about_new_style_classes import AboutNewStyleClasses -from koans.about_with_statements import AboutWithStatements -from koans.about_monkey_patching import AboutMonkeyPatching -from koans.about_dice_project import AboutDiceProject -from koans.about_method_bindings import AboutMethodBindings -from koans.about_decorating_with_functions import AboutDecoratingWithFunctions -from koans.about_decorating_with_classes import AboutDecoratingWithClasses -from koans.about_inheritance import AboutInheritance -from koans.about_multiple_inheritance import AboutMultipleInheritance -from koans.about_regex import AboutRegex -from koans.about_scope import AboutScope -from koans.about_modules import AboutModules -from koans.about_packages import AboutPackages -from koans.about_class_attributes import AboutClassAttributes -from koans.about_attribute_access import AboutAttributeAccess -from koans.about_deleting_objects import AboutDeletingObjects -from koans.about_proxy_object_project import * -from koans.about_extra_credit import AboutExtraCredit - -def koans(): - loader = unittest.TestLoader() - suite = unittest.TestSuite() - loader.sortTestMethodsUsing = None - suite.addTests(loader.loadTestsFromTestCase(AboutAsserts)) - suite.addTests(loader.loadTestsFromTestCase(AboutNone)) - suite.addTests(loader.loadTestsFromTestCase(AboutLists)) - suite.addTests(loader.loadTestsFromTestCase(AboutListAssignments)) - suite.addTests(loader.loadTestsFromTestCase(AboutDictionaries)) - suite.addTests(loader.loadTestsFromTestCase(AboutStrings)) - suite.addTests(loader.loadTestsFromTestCase(AboutTuples)) - suite.addTests(loader.loadTestsFromTestCase(AboutMethods)) - suite.addTests(loader.loadTestsFromTestCase(AboutControlStatements)) - suite.addTests(loader.loadTestsFromTestCase(AboutTrueAndFalse)) - suite.addTests(loader.loadTestsFromTestCase(AboutSets)) - suite.addTests(loader.loadTestsFromTestCase(AboutTriangleProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutExceptions)) - suite.addTests(loader.loadTestsFromTestCase(AboutTriangleProject2)) - suite.addTests(loader.loadTestsFromTestCase(AboutIteration)) - suite.addTests(loader.loadTestsFromTestCase(AboutGenerators)) - suite.addTests(loader.loadTestsFromTestCase(AboutLambdas)) - suite.addTests(loader.loadTestsFromTestCase(AboutScoringProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutClasses)) - suite.addTests(loader.loadTestsFromTestCase(AboutNewStyleClasses)) - suite.addTests(loader.loadTestsFromTestCase(AboutWithStatements)) - suite.addTests(loader.loadTestsFromTestCase(AboutMonkeyPatching)) - suite.addTests(loader.loadTestsFromTestCase(AboutDiceProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutMethodBindings)) - suite.addTests(loader.loadTestsFromTestCase(AboutDecoratingWithFunctions)) - suite.addTests(loader.loadTestsFromTestCase(AboutDecoratingWithClasses)) - suite.addTests(loader.loadTestsFromTestCase(AboutInheritance)) - suite.addTests(loader.loadTestsFromTestCase(AboutMultipleInheritance)) - suite.addTests(loader.loadTestsFromTestCase(AboutScope)) - suite.addTests(loader.loadTestsFromTestCase(AboutModules)) - suite.addTests(loader.loadTestsFromTestCase(AboutPackages)) - suite.addTests(loader.loadTestsFromTestCase(AboutClassAttributes)) - suite.addTests(loader.loadTestsFromTestCase(AboutAttributeAccess)) - suite.addTests(loader.loadTestsFromTestCase(AboutDeletingObjects)) - suite.addTests(loader.loadTestsFromTestCase(AboutProxyObjectProject)) - suite.addTests(loader.loadTestsFromTestCase(TelevisionTest)) - suite.addTests(loader.loadTestsFromTestCase(AboutExtraCredit)) - - return suite diff --git a/python 2/runner/runner_tests/test_helper.py b/python 2/runner/runner_tests/test_helper.py deleted file mode 100644 index ea3ee00c3..000000000 --- a/python 2/runner/runner_tests/test_helper.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest - -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)) - - def test_that_get_class_name_works_with_a_tuple(self): - self.assertEquals("tuple", helper.cls_name((3,"pie", []))) diff --git a/python 2/runner/runner_tests/test_mountain.py b/python 2/runner/runner_tests/test_mountain.py deleted file mode 100644 index 18fcc3167..000000000 --- a/python 2/runner/runner_tests/test_mountain.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -from libs.mock import * - -from runner.mountain import Mountain -from runner import path_to_enlightenment - -class TestMountain(unittest.TestCase): - - def setUp(self): - path_to_enlightenment.koans = Mock() - self.mountain = Mountain() - self.mountain.stream.writeln = Mock() - - def test_it_gets_test_results(self): - self.mountain.lesson.learn = Mock() - self.mountain.walk_the_path() - self.assertTrue(self.mountain.lesson.learn.called) - diff --git a/python 2/runner/sensei.py b/python 2/runner/sensei.py deleted file mode 100644 index 7519e17fa..000000000 --- a/python 2/runner/sensei.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import re -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()) - self.stream.writeln("") - self.stream.writeln(self.say_something_zenlike()) - - if self.failures: return - 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@@@@@' - - 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') - - def report_progress(self): - return ("You are now {0}/{1} koans and {2}/{3} lessons away from " \ - "reaching enlightenment".format(self.pass_count, - self.total_koans(), - self.lesson_pass_count, - self.total_lessons())) - - # 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 - - 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 in 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): - if not self.all_lessons: - self.all_lessons = glob.glob('koans/about*.py') - self.all_lessons = filter(lambda filename: - "about_extra_credit" not in filename, - self.all_lessons) - - return self.all_lessons diff --git a/python 3/_runner_tests.py b/python 3/_runner_tests.py deleted file mode 100644 index 55ef5a12a..000000000 --- a/python 3/_runner_tests.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -import unittest - -from runner.runner_tests.test_mountain import TestMountain -from runner.runner_tests.test_sensei import TestSensei -from runner.runner_tests.test_helper import TestHelper - -def suite(): - suite = unittest.TestSuite() - suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMountain)) - suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestSensei)) - suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestHelper)) - return suite - -if __name__ == '__main__': - unittest.TextTestRunner(verbosity=2).run(suite()) - \ No newline at end of file diff --git a/python 3/contemplate_koans.py b/python 3/contemplate_koans.py deleted file mode 100644 index 496c85313..000000000 --- a/python 3/contemplate_koans.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Acknowledgment: -# -# Python Koans is a port of Ruby Koans originally written by Jim Weirich -# and Joe O'brien of Edgecase. There are some differences and tweaks specific -# to the Python language, but a great deal of it has been copied wholesale. -# So thank guys! -# - -import unittest -import sys - -if __name__ == '__main__': - if sys.version_info < (3, 0): - print("\nThis is the Python 3 version of Python Koans, but you are " + - "running it with Python 2 or older!\n\n" - "Did you accidentally use the wrong python script? \nTry:\n\n" + - " python3 contemplate_koans.py\n") - else: - from runner.mountain import Mountain - - Mountain().walk_the_path(sys.argv) diff --git a/python 3/example_file.txt b/python 3/example_file.txt deleted file mode 100644 index ffe7cbd89..000000000 --- a/python 3/example_file.txt +++ /dev/null @@ -1,4 +0,0 @@ -this -is -a -test diff --git a/python 3/koans/GREEDS_RULES.txt b/python 3/koans/GREEDS_RULES.txt deleted file mode 100644 index 58b5a9cb6..000000000 --- a/python 3/koans/GREEDS_RULES.txt +++ /dev/null @@ -1,66 +0,0 @@ -= Playing Greed - -Greed is a dice game played among 2 or more players, using 5 -six-sided dice. - -== Playing Greed - -Each player takes a turn consisting of one or more rolls of the dice. -On the first roll of the game, a player rolls all five dice which are -scored according to the following: - - Three 1's => 1000 points - Three 6's => 600 points - Three 5's => 500 points - Three 4's => 400 points - Three 3's => 300 points - Three 2's => 200 points - One 1 => 100 points - One 5 => 50 points - -A single die can only be counted once in each roll. For example, -a "5" can only count as part of a triplet (contributing to the 500 -points) or as a single 50 points, but not both in the same roll. - -Example Scoring - - Throw Score - --------- ------------------ - 5 1 3 4 1 50 + 2 * 100 = 250 - 1 1 1 3 1 1000 + 100 = 1100 - 2 4 4 5 4 400 + 50 = 450 - -The dice not contributing to the score are called the non-scoring -dice. "3" and "4" are non-scoring dice in the first example. "3" is -a non-scoring die in the second, and "2" is a non-score die in the -final example. - -After a player rolls and the score is calculated, the scoring dice are -removed and the player has the option of rolling again using only the -non-scoring dice. If all of the thrown dice are scoring, then the -player may roll all 5 dice in the next roll. - -The player may continue to roll as long as each roll scores points. If -a roll has zero points, then the player loses not only their turn, but -also accumulated score for that turn. If a player decides to stop -rolling before rolling a zero-point roll, then the accumulated points -for the turn is added to his total score. - -== Getting "In The Game" - -Before a player is allowed to accumulate points, they must get at -least 300 points in a single turn. Once they have achieved 300 points -in a single turn, the points earned in that turn and each following -turn will be counted toward their total score. - -== End Game - -Once a player reaches 3000 (or more) points, the game enters the final -round where each of the other players gets one more turn. The winner -is the player with the highest score after the final round. - -== References - -Greed is described on Wikipedia at -http://en.wikipedia.org/wiki/Greed_(dice_game), however the rules are -a bit different from the rules given here. diff --git a/python 3/koans/__init__.py b/python 3/koans/__init__.py deleted file mode 100644 index 8d78ec0f1..000000000 --- a/python 3/koans/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# koans \ 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/a_package_folder/__init__.py b/python 3/koans/a_package_folder/__init__.py deleted file mode 100644 index 20f417e99..000000000 --- a/python 3/koans/a_package_folder/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -an_attribute = 1984 \ No newline at end of file diff --git a/python 3/koans/about_asserts.py b/python 3/koans/about_asserts.py deleted file mode 100644 index 3905498d3..000000000 --- a/python 3/koans/about_asserts.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -class AboutAsserts(Koan): - - def test_assert_truth(self): - """ - We shall contemplate truth by testing reality, via asserts. - """ - self.assertTrue(False) # 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") - - def test_fill_in_values(self): - """ - Sometimes we will ask you to fill in the values - """ - self.assertEqual(__, 1 + 1) - - def test_assert_equality(self): - """ - To understand reality, we must compare our expectations against reality. - """ - expected_value = __ - actual_value = 1 + 1 - self.assertTrue(expected_value == actual_value) - - def test_a_better_way_of_asserting_equality(self): - """ - Some ways of asserting equality are better than others. - """ - expected_value = __ - actual_value = 1 + 1 - - self.assertEqual(expected_value, actual_value) - - def test_that_unittest_asserts_work_the_same_way_as_python_asserts(self): - """ - Knowing how things really work is half the battle - """ - - # This throws an AssertionError exception - assert False - diff --git a/python 3/koans/about_decorating_with_functions.py b/python 3/koans/about_decorating_with_functions.py deleted file mode 100644 index def65098c..000000000 --- a/python 3/koans/about_decorating_with_functions.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - - -class AboutDecoratingWithFunctions(Koan): - def addcowbell(fn): - fn.wow_factor = 'COWBELL BABY!' - return fn - - @addcowbell - 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) - - # ------------------------------------------------------------------ - - def xmltag(fn): - def func(*args): - return '<' + fn(*args) + '/>' - return func - - @xmltag - def render_tag(self, name): - return name - - def test_decorators_can_change_a_function_output(self): - self.assertEqual(__, self.render_tag('llama')) - diff --git a/python 3/koans/about_dictionaries.py b/python 3/koans/about_dictionaries.py deleted file mode 100644 index 41e26c5fc..000000000 --- a/python 3/koans/about_dictionaries.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutHashes in the Ruby Koans -# - -from runner.koan import * - -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)) - - def test_dictionary_literals(self): - babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, len(babel_fish)) - - def test_accessing_dictionaries(self): - babel_fish = { 'one': "uno", 'two': "dos" } - self.assertEqual(__, babel_fish['one']) - self.assertEqual(__, babel_fish['two']) - - def test_changing_dictionaries(self): - babel_fish = { 'one': "uno", 'two': "dos" } - babel_fish['one'] = "eins" - - expected = { 'two': "dos", 'one': __ } - self.assertDictEqual(expected, babel_fish) - - def test_dictionary_is_unordered(self): - dict1 = { 'one': "uno", 'two': "dos" } - dict2 = { 'two': "dos", 'one': "uno" } - - self.assertEqual(__, 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__) - - 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__) - - 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"]) - diff --git a/python 3/koans/about_lambdas.py b/python 3/koans/about_lambdas.py deleted file mode 100644 index 2268e6c65..000000000 --- a/python 3/koans/about_lambdas.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based slightly on the lambdas section of AboutBlocks in the Ruby Koans -# - -from runner.koan import * - -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)) - - # ------------------------------------------------------------------ - - def make_order(self, order): - return lambda qty: str(qty) + " " + order + "s" - - 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)) - - def test_accessing_lambda_without_assignment(self): - self.assertEqual(__, self.make_order('spam')(39823)) diff --git a/python 3/koans/about_regex.py b/python 3/koans/about_regex.py deleted file mode 100755 index 13db11a82..000000000 --- a/python 3/koans/about_regex.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * -import re -class AboutRegex(Koan): - """ - These koans are based on the Ben's book: Regular Expressions in 10 minutes. - I found this books very useful so I decided to write a koans in order to practice everything I had learned from it. - http://www.forta.com/books/0672325667/ - """ - - def test_matching_literal_text(self): - """ - Lesson 1 Matching Literal String - """ - string = "Hello, my name is Felix and this koans are based on the Ben's book: Regular Expressions in 10 minutes." - m = re.search(__, string) - self.assertTrue(m and m.group(0) and m.group(0)== 'Felix', "I want my name") - - def test_matching_literal_text_how_many(self): - """ - Lesson 1 How many matches? - - The default behaviour of most regular extression engines is to return just the first match. - In python you have the next options: - - match() --> Determine if the RE matches at the beginning of the string. - search() --> Scan through a string, looking for any location where this RE matches. - findall() --> Find all substrings where the RE matches, and returns them as a list. - finditer() --> Find all substrings where the RE matches, and returns them as an iterator. - - """ - string = "Hello, my name is Felix and this koans are based on the Ben's book: Regular Expressions in 10 minutes. Repeat My name is Felix" - m = re.match('Felix', string) #TIP: Maybe match it's not the best option - - # I want to know how many times appears my name - self.assertEqual(m, __) - - def test_matching_literal_text_not_case_sensitivity(self): - """ - Lesson 1 Matching Literal String non case sensitivity. - Most regex implementations also support matches that are not case sensitive. In python you can use re.IGNORECASE, in - Javascript you can specify the optional i flag. - In Ben's book you can see more languages. - - """ - string = "Hello, my name is Felix or felix and this koans is based on the Ben's book: Regular Expressions in 10 minutes." - - self.assertEqual(re.findall("felix", string, 20), __) - self.assertEqual(re.findall("felix", string, 10), __) - - def test_matching_any_character(self): - """ - Lesson 1 Matching any character - - . matches any character, alphabetic characters, digits and . - """ - string = "pecks.xlx\n" \ - + "orders1.xls\n" \ - + "apec1.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls" - - # TIP: remember the name of this lesson - - change_this_search_string = 'a..xlx' # <-- I want to find all uses of myArray - self.assertEquals(len(re.findall(change_this_search_string, string)),3) - - def test_matching_set_character(self): - """ - Lesson 2 Matching sets of characters - - A set of characters is defined using the metacharacters [ and ]. Everything between them is part of the set and - any one of the set members must match (but not all). - """ - string = "sales.xlx\n" \ - + "sales1.xls\n" \ - + "orders3.xls\n" \ - + "apac1.xls\n" \ - + "sales2.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls\n" \ - + "ca1.xls" - # I want to find all files for North America(na) or South America(sa), but not (ca) - # TIP you can use the pattern .a. which matches in above test but in this case matches more than you want - change_this_search_string = '[nsc]a[2-9].xls' - self.assertEquals(len(re.findall(change_this_search_string, string)),3) - - def test_anything_but_matching(self): - """ - Lesson 2 Using character set ranges - Occsionally, you'll want a list of characters that you don't want to match. - Character sets can be negated using the ^ metacharacter. - - """ - string = "sales.xlx\n" \ - + "sales1.xls\n" \ - + "orders3.xls\n" \ - + "apac1.xls\n" \ - + "sales2.xls\n" \ - + "sales3.xls\n" \ - + "europe2.xls\n" \ - + "sam.xls\n" \ - + "na1.xls\n" \ - + "na2.xls\n" \ - + "sa1.xls\n" \ - + "ca1.xls" - - # I want to find the name sam - change_this_search_string = '[^nc]am' - self.assertEquals(re.findall(change_this_search_string, string), ['sam.xls']) - - diff --git a/python 3/koans/about_scoring_project.py b/python 3/koans/about_scoring_project.py deleted file mode 100644 index 393af55b5..000000000 --- a/python 3/koans/about_scoring_project.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from runner.koan import * - -# Greed is a dice game where you roll up to five dice to accumulate -# points. The following "score" function will be used calculate the -# score of a single roll of the dice. -# -# A greed roll is scored as follows: -# -# * A set of three ones is 1000 points -# -# * A set of three numbers (other than ones) is worth 100 times the -# number. (e.g. three fives is 500 points). -# -# * A one (that is not part of a set of three) is worth 100 points. -# -# * A five (that is not part of a set of three) is worth 50 points. -# -# * Everything else is worth 0 points. -# -# -# Examples: -# -# score([1,1,1,5,1]) => 1150 points -# score([2,3,4,6,2]) => 0 points -# score([3,4,5,3,3]) => 350 points -# score([1,5,1,2,4]) => 250 points -# -# More scoring examples are given in the tests below: -# -# Your goal is to write the score method. - -def score(dice): - # You need to write this method - pass - -class AboutScoringProject(Koan): - def test_score_of_an_empty_list_is_zero(self): - self.assertEqual(0, score([])) - - def test_score_of_a_single_roll_of_5_is_50(self): - self.assertEqual(50, score([5])) - - def test_score_of_a_single_roll_of_1_is_100(self): - self.assertEqual(100, score([1])) - - def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores(self): - self.assertEqual(300, score([1,5,5,1])) - - def test_score_of_single_2s_3s_4s_and_6s_are_zero(self): - self.assertEqual(0, score([2,3,4,6])) - - def test_score_of_a_triple_1_is_1000(self): - self.assertEqual(1000, score([1,1,1])) - - def test_score_of_other_triples_is_100x(self): - self.assertEqual(200, score([2,2,2])) - self.assertEqual(300, score([3,3,3])) - self.assertEqual(400, score([4,4,4])) - self.assertEqual(500, score([5,5,5])) - self.assertEqual(600, score([6,6,6])) - - def test_score_of_mixed_is_sum(self): - self.assertEqual(250, score([2,5,2,2,3])) - self.assertEqual(550, score([5,5,5,5])) - self.assertEqual(1150, score([1,1,1,5,1])) - - def test_ones_not_left_out(self): - self.assertEqual(300, score([1,2,2,2])) - self.assertEqual(350, score([1,5,2,2,2])) \ No newline at end of file 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 9b6639641..000000000 --- a/python 3/koans/about_true_and_false.py +++ /dev/null @@ -1,38 +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({})) - self.assertEqual(__, self.truth_value(set())) - - def test_blank_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(1,)) - self.assertEqual(__, self.truth_value("Python is named after Monty Python")) - self.assertEqual(__, self.truth_value(' ')) diff --git a/python 3/koans/about_with_statements.py b/python 3/koans/about_with_statements.py deleted file mode 100644 index 66f0a912e..000000000 --- a/python 3/koans/about_with_statements.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Based on AboutSandwichCode in the Ruby Koans -# - -from runner.koan import * - -import re # For regular expression string comparisons - -class AboutWithStatements(Koan): - def count_lines(self, file_name): - file = open(file_name) - try: - count = 0 - for line in file.readlines(): - count += 1 - return count - finally: - if file: file.close() - - def test_counting_lines(self): - self.assertEqual(__, self.count_lines("example_file.txt")) - - # ------------------------------------------------------------------ - - def find_line(self, file_name): - file = open(file_name) - try: - for line in file.readlines(): - match = re.search('e', line) - if match: return line - finally: - if file: file.close() - - def test_finding_lines(self): - self.assertEqual(__, self.find_line("example_file.txt")) - - ## ------------------------------------------------------------------ - ## THINK ABOUT IT: - ## - ## The count_lines and find_line are similar, and yet different. - ## They both follow the pattern of "sandwich code". - ## - ## Sandwich code is code that comes in three parts: (1) the top slice - ## of bread, (2) the meat, and (3) the bottom slice of bread. The - ## the bread part of the sandwich almost always goes together, but - ## the meat part changes all the time. - ## - ## Because the changing part of the sandwich code is in the middle, - ## abstracting the top and bottom bread slices to a library can be - ## difficult in many languages. - ## - ## (Aside for C++ programmers: The idiom of capturing allocated - ## pointers in a smart pointer constructor is an attempt to deal with - ## the problem of sandwich code for resource allocation.) - ## - ## Python solves the problem using Context Managers. Consider the - ## following code: - ## - - class FileContextManager(): - def __init__(self, file_name): - self._file_name = file_name - self._file = None - - def __enter__(self): - self._file = open(self._file_name) - return self._file - - def __exit__(self, cls, value, tb): - self._file.close() - - # Now we write: - - def count_lines2(self, file_name): - with self.FileContextManager(file_name) as file: - count = 0 - for line in file.readlines(): - count += 1 - return count - - def test_counting_lines2(self): - self.assertEqual(__, self.count_lines2("example_file.txt")) - - # ------------------------------------------------------------------ - - def find_line2(self, file_name): - # Rewrite find_line using the Context Manager. - pass - - def test_finding_lines2(self): - self.assertEqual(__, self.find_line2("example_file.txt")) - self.assertNotEqual(__, self.find_line2("example_file.txt")) - - # ------------------------------------------------------------------ - - def count_lines3(self, file_name): - with open(file_name) as file: - count = 0 - for line in file.readlines(): - count += 1 - return count - - def test_open_already_has_its_own_built_in_context_manager(self): - self.assertEqual(__, self.count_lines3("example_file.txt")) diff --git a/python 3/libs/__init__.py b/python 3/libs/__init__.py deleted file mode 100644 index 421eaef9f..000000000 --- a/python 3/libs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Dummy file to support python package hierarchy diff --git a/python 3/libs/colorama/LICENSE-colorama b/python 3/libs/colorama/LICENSE-colorama deleted file mode 100644 index b7464472e..000000000 --- a/python 3/libs/colorama/LICENSE-colorama +++ /dev/null @@ -1,33 +0,0 @@ -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/python 3/libs/colorama/__init__.py b/python 3/libs/colorama/__init__.py deleted file mode 100644 index 331174e57..000000000 --- a/python 3/libs/colorama/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .initialise import init -from .ansi import Fore, Back, Style -from .ansitowin32 import AnsiToWin32 - -VERSION = '0.1.18' - diff --git a/python 3/libs/colorama/ansitowin32.py b/python 3/libs/colorama/ansitowin32.py deleted file mode 100644 index 9ce77dd6f..000000000 --- a/python 3/libs/colorama/ansitowin32.py +++ /dev/null @@ -1,176 +0,0 @@ - -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/python 3/libs/colorama/win32.py b/python 3/libs/colorama/win32.py deleted file mode 100644 index 5f49e6cba..000000000 --- a/python 3/libs/colorama/win32.py +++ /dev/null @@ -1,95 +0,0 @@ - -# 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/python 3/libs/colorama/winterm.py b/python 3/libs/colorama/winterm.py deleted file mode 100644 index 63f4469cc..000000000 --- a/python 3/libs/colorama/winterm.py +++ /dev/null @@ -1,69 +0,0 @@ - -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/run.sh b/python 3/run.sh deleted file mode 100755 index 987b4cdd3..000000000 --- a/python 3/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -python3 contemplate_koans.py - diff --git a/python 3/runner/__init__.py b/python 3/runner/__init__.py deleted file mode 100644 index 655c3567d..000000000 --- a/python 3/runner/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Namespace: runner - diff --git a/python 3/runner/helper.py b/python 3/runner/helper.py deleted file mode 100644 index ccc4f8db1..000000000 --- a/python 3/runner/helper.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -def cls_name(obj): - return obj.__class__.__name__ \ No newline at end of file diff --git a/python 3/runner/koan.py b/python 3/runner/koan.py deleted file mode 100644 index 28a9d101b..000000000 --- a/python 3/runner/koan.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -import re - -# Starting a classname or attribute with an underscore normally implies Private scope. -# However, we are making an exception for __ and ___. - -__all__ = [ "__", "___", "____", "_____", "Koan" ] - -__ = "-=> FILL ME IN! <=-" - -class ___(Exception): - pass - -____ = "-=> TRUE OR FALSE? <=-" - -_____ = 0 - - -class Koan(unittest.TestCase): - def assertNoRegexpMatches(self, text, expected_regex, msg=None): - """ - Throw an exception if the regular expresson pattern is not matched - """ - if isinstance(expected_regex, (str, bytes)): - expected_regex = re.compile(expected_regex) - if expected_regex.search(text): - msg = msg or "Regexp matched" - msg = '{0}: {1!r} found in {2!r}'.format(msg, expected_regex.pattern, text) - raise self.failureException(msg) diff --git a/python 3/runner/mockable_test_result.py b/python 3/runner/mockable_test_result.py deleted file mode 100644 index be1aca300..000000000 --- a/python 3/runner/mockable_test_result.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest - -# Needed to stop unittest.TestResult itself getting Mocked out of existence, -# which is a problem when testing the helper classes! (It confuses the runner) - -class MockableTestResult(unittest.TestResult): - pass \ No newline at end of file diff --git a/python 3/runner/path_to_enlightenment.py b/python 3/runner/path_to_enlightenment.py deleted file mode 100644 index 67cd00d3e..000000000 --- a/python 3/runner/path_to_enlightenment.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# The path to enlightenment starts with the following: - -import unittest - -from koans.about_asserts import AboutAsserts -from koans.about_none import AboutNone -from koans.about_lists import AboutLists -from koans.about_list_assignments import AboutListAssignments -from koans.about_dictionaries import AboutDictionaries -from koans.about_strings import AboutStrings -from koans.about_tuples import AboutTuples -from koans.about_methods import AboutMethods -from koans.about_control_statements import AboutControlStatements -from koans.about_true_and_false import AboutTrueAndFalse -from koans.about_sets import AboutSets -from koans.about_triangle_project import AboutTriangleProject -from koans.about_exceptions import AboutExceptions -from koans.about_triangle_project2 import AboutTriangleProject2 -from koans.about_iteration import AboutIteration -from koans.about_generators import AboutGenerators -from koans.about_lambdas import AboutLambdas -from koans.about_scoring_project import AboutScoringProject -from koans.about_classes import AboutClasses -from koans.about_with_statements import AboutWithStatements -from koans.about_monkey_patching import AboutMonkeyPatching -from koans.about_dice_project import AboutDiceProject -from koans.about_method_bindings import AboutMethodBindings -from koans.about_decorating_with_functions import AboutDecoratingWithFunctions -from koans.about_decorating_with_classes import AboutDecoratingWithClasses -from koans.about_inheritance import AboutInheritance -from koans.about_multiple_inheritance import AboutMultipleInheritance -from koans.about_regex import AboutRegex -from koans.about_scope import AboutScope -from koans.about_modules import AboutModules -from koans.about_packages import AboutPackages -from koans.about_class_attributes import AboutClassAttributes -from koans.about_attribute_access import AboutAttributeAccess -from koans.about_deleting_objects import AboutDeletingObjects -from koans.about_proxy_object_project import * -from koans.about_extra_credit import AboutExtraCredit - -def koans(): - loader = unittest.TestLoader() - suite = unittest.TestSuite() - loader.sortTestMethodsUsing = None - suite.addTests(loader.loadTestsFromTestCase(AboutAsserts)) - suite.addTests(loader.loadTestsFromTestCase(AboutNone)) - suite.addTests(loader.loadTestsFromTestCase(AboutLists)) - suite.addTests(loader.loadTestsFromTestCase(AboutListAssignments)) - suite.addTests(loader.loadTestsFromTestCase(AboutDictionaries)) - suite.addTests(loader.loadTestsFromTestCase(AboutStrings)) - suite.addTests(loader.loadTestsFromTestCase(AboutTuples)) - suite.addTests(loader.loadTestsFromTestCase(AboutMethods)) - suite.addTests(loader.loadTestsFromTestCase(AboutControlStatements)) - suite.addTests(loader.loadTestsFromTestCase(AboutTrueAndFalse)) - suite.addTests(loader.loadTestsFromTestCase(AboutSets)) - suite.addTests(loader.loadTestsFromTestCase(AboutTriangleProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutExceptions)) - suite.addTests(loader.loadTestsFromTestCase(AboutTriangleProject2)) - suite.addTests(loader.loadTestsFromTestCase(AboutIteration)) - suite.addTests(loader.loadTestsFromTestCase(AboutGenerators)) - suite.addTests(loader.loadTestsFromTestCase(AboutLambdas)) - suite.addTests(loader.loadTestsFromTestCase(AboutScoringProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutClasses)) - suite.addTests(loader.loadTestsFromTestCase(AboutWithStatements)) - suite.addTests(loader.loadTestsFromTestCase(AboutMonkeyPatching)) - suite.addTests(loader.loadTestsFromTestCase(AboutDiceProject)) - suite.addTests(loader.loadTestsFromTestCase(AboutMethodBindings)) - suite.addTests(loader.loadTestsFromTestCase(AboutDecoratingWithFunctions)) - suite.addTests(loader.loadTestsFromTestCase(AboutDecoratingWithClasses)) - suite.addTests(loader.loadTestsFromTestCase(AboutInheritance)) - suite.addTests(loader.loadTestsFromTestCase(AboutMultipleInheritance)) - suite.addTests(loader.loadTestsFromTestCase(AboutScope)) - suite.addTests(loader.loadTestsFromTestCase(AboutModules)) - suite.addTests(loader.loadTestsFromTestCase(AboutPackages)) - suite.addTests(loader.loadTestsFromTestCase(AboutClassAttributes)) - suite.addTests(loader.loadTestsFromTestCase(AboutAttributeAccess)) - suite.addTests(loader.loadTestsFromTestCase(AboutDeletingObjects)) - suite.addTests(loader.loadTestsFromTestCase(AboutProxyObjectProject)) - suite.addTests(loader.loadTestsFromTestCase(TelevisionTest)) - suite.addTests(loader.loadTestsFromTestCase(AboutExtraCredit)) - - return suite diff --git a/python 3/runner/runner_tests/__init__.py b/python 3/runner/runner_tests/__init__.py deleted file mode 100644 index 16f619ae2..000000000 --- a/python 3/runner/runner_tests/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Namespace: helpers_tests diff --git a/python 3/runner/runner_tests/test_mountain.py b/python 3/runner/runner_tests/test_mountain.py deleted file mode 100644 index 18fcc3167..000000000 --- a/python 3/runner/runner_tests/test_mountain.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import unittest -from libs.mock import * - -from runner.mountain import Mountain -from runner import path_to_enlightenment - -class TestMountain(unittest.TestCase): - - def setUp(self): - path_to_enlightenment.koans = Mock() - self.mountain = Mountain() - self.mountain.stream.writeln = Mock() - - def test_it_gets_test_results(self): - self.mountain.lesson.learn = Mock() - self.mountain.walk_the_path() - self.assertTrue(self.mountain.lesson.learn.called) - diff --git a/python 3/runner/runner_tests/test_sensei.py b/python 3/runner/runner_tests/test_sensei.py deleted file mode 100644 index c6eac74bd..000000000 --- a/python 3/runner/runner_tests/test_sensei.py +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -import unittest -import re - -from libs.mock import * - -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 -class AboutLumberjacks: - pass -class AboutTennis: - pass -class AboutTheKnightsWhoSayNi: - pass -class AboutMrGumby: - pass -class AboutMessiahs: - pass -class AboutGiantFeet: - pass -class AboutTrebuchets: - pass -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 - self.assertEqual("Steel","Lard", "Another fine mess you've got me into Stanley...") -AssertionError: Another fine mess you've got me into Stanley...""" - -error_assertion_equals = """ - -Traceback (most recent call last): - File "/Users/Greg/hg/python_koans/koans/about_exploding_trousers.py", line 49, in test_math - self.assertEqual(4,99) -AssertionError: 4 != 99 -""" - -error_assertion_true = """Traceback (most recent call last): - File "/Users/Greg/hg/python_koans/koans/about_armories.py", line 25, in test_weoponary - self.assertTrue("Pen" > "Sword") -AssertionError - -""" - -error_mess = """ -Traceback (most recent call last): - File "contemplate_koans.py", line 5, in - from runner.mountain import Mountain - File "/Users/Greg/hg/python_koans/runner/mountain.py", line 7, in - import path_to_enlightenment - File "/Users/Greg/hg/python_koans/runner/path_to_enlightenment.py", line 8, in - from koans import * - File "/Users/Greg/hg/python_koans/koans/about_asserts.py", line 20 - self.assertTrue(eoe"Pen" > "Sword", "nhnth") - ^ -SyntaxError: invalid syntax""" - -error_with_list = """Traceback (most recent call last): - File "/Users/Greg/hg/python_koans/koans/about_armories.py", line 84, in test_weoponary - self.assertEqual([1, 9], [1, 2]) -AssertionError: Lists differ: [1, 9] != [1, 2] - -First differing element 1: -9 -2 - -- [1, 9] -? ^ - -+ [1, 2] -? ^ - -""" - - -class TestSensei(unittest.TestCase): - - def setUp(self): - self.sensei = Sensei(WritelnDecorator(sys.stdout)) - self.sensei.stream.writeln = Mock() - path_to_enlightenment.koans = Mock() - self.tests = Mock() - self.tests.countTestCases = 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) - - 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"), - (AboutMessiahs(),"File 'about_messiahs.py', line 43"), - (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"), - (AboutTheKnightsWhoSayNi(),"File 'about_the_knights_whn_say_ni.py', line 24"), - (AboutTennis(),"File 'about_tennis.py', line 30"), - (AboutMessiahs(),"File 'about_messiahs.py', line 43"), - (AboutTennis(),"File 'about_tennis.py', line 2"), - (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"), - (AboutTrebuchets(),"File 'about_trebuchets.py', line 9"), - (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"), - (AboutGiantFeet(),"File 'about_giant_feet.py', line 44"), - (AboutFreemasons(),"File 'about_freemasons.py', line 1"), - (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_student_progress(self): - self.sensei.errorReport = Mock() - self.sensei.total_lessons = Mock() - self.sensei.total_koans = Mock() - - self.sensei.learn() - self.assertTrue(self.sensei.total_lessons.called) - self.assertTrue(self.sensei.total_koans.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() - self.sensei.firstFailure = Mock() - self.sensei.firstFailure.return_value = (Mock(), "FAILED") - self.sensei.errorReport() - self.assertTrue(self.sensei.scrapeInterestingStackDump.called) - - 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)) - - def test_that_scraping_the_assertion_error_with_list_error(self): - self.assertEqual(""" AssertionError: Lists differ: [1, 9] != [1, 2] - - First differing element 1: - 9 - 2 - - - [1, 9] - ? ^ - - + [1, 2] - ? ^""", - self.sensei.scrapeAssertionError(error_with_list)) - - 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)) - - def test_that_if_there_is_10_successes_it_will_say_the_sixth_zen_of_python_koans(self): - 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)) - - def test_that_if_there_is_36_successes_it_will_say_the_final_zen_of_python_koans(self): - 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)) - - def test_that_if_there_is_37_successes_it_will_say_the_first_zen_of_python_koans_again(self): - 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/python 3/runner/writeln_decorator.py b/python 3/runner/writeln_decorator.py deleted file mode 100644 index ee3cd35c0..000000000 --- a/python 3/runner/writeln_decorator.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -import sys -import os - -# Taken from legacy python unittest -class WritelnDecorator: - """Used to decorate file-like objects with a handy 'writeln' method""" - def __init__(self,stream): - self.stream = stream - - def __getattr__(self, attr): - return getattr(self.stream,attr) - - def writeln(self, arg=None): - if arg: self.write(arg) - self.write('\n') # text-mode streams translate to \r\n if needed - diff --git a/python 3/run.bat b/run.bat old mode 100644 new mode 100755 similarity index 79% rename from python 3/run.bat rename to run.bat index 0496c5f2e..d965d4efe --- a/python 3/run.bat +++ b/run.bat @@ -2,14 +2,15 @@ REM This is how you run it from the command line. REM You don't actually need this script! -SET RUN_KOANS=python.exe contemplate_koans.py +SET RUN_KOANS=python.exe -B contemplate_koans.py REM Set this to your python folder: -SET PYTHON_PATH=C:\Python31 +SET PYTHON_PATH=C:\Python311 +set SCRIPT= +:loop -set SCRIPT= REM Hunt around for python IF EXIST "python.exe" ( SET SCRIPT=%RUN_KOANS% @@ -34,4 +35,10 @@ IF NOT "" == "%SCRIPT%" ( echo python.exe contemplate_koans.py pause ) + +Set /p keepgoing="Test again? y or n - " +if "%keepgoing%" == "y" ( + goto loop + ) + :end diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..eb978ce78 --- /dev/null +++ b/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +python3 -B contemplate_koans.py + diff --git a/python 2/runner/__init__.py b/runner/__init__.py similarity index 100% rename from python 2/runner/__init__.py rename to runner/__init__.py diff --git a/python 2/runner/helper.py b/runner/helper.py similarity index 100% rename from python 2/runner/helper.py rename to runner/helper.py diff --git a/runner/koan.py b/runner/koan.py new file mode 100644 index 000000000..929ccb41b --- /dev/null +++ b/runner/koan.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import re + +# Starting a classname or attribute with an underscore normally implies Private scope. +# However, we are making an exception for __ and ___. + +__all__ = [ "__", "___", "____", "_____", "Koan" ] + +__ = "-=> FILL ME IN! <=-" + +class ___(Exception): + pass + +____ = "-=> TRUE OR FALSE? <=-" + +_____ = 0 + + +class Koan(unittest.TestCase): + pass diff --git a/python 2/runner/mockable_test_result.py b/runner/mockable_test_result.py similarity index 100% rename from python 2/runner/mockable_test_result.py rename to runner/mockable_test_result.py diff --git a/python 3/runner/mountain.py b/runner/mountain.py similarity index 97% rename from python 3/runner/mountain.py rename to runner/mountain.py index eabb55862..dab3b415f 100644 --- a/python 3/runner/mountain.py +++ b/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/runner/path_to_enlightenment.py b/runner/path_to_enlightenment.py new file mode 100644 index 000000000..b6c2fa55f --- /dev/null +++ b/runner/path_to_enlightenment.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +''' +Functions to load the test cases ("koans") that make up the +Path to Enlightenment. +''' + +import io +import unittest + + +# The path to enlightenment starts with the following: +KOANS_FILENAME = 'koans.txt' + + +def filter_koan_names(lines): + ''' + Strips leading and trailing whitespace, then filters out blank + lines and comment lines. + ''' + for line in lines: + line = line.strip() + if line.startswith('#'): + continue + if line: + yield line + return + + +def names_from_file(filename): + ''' + Opens the given ``filename`` and yields the fully-qualified names + of TestCases found inside (one per line). + ''' + with io.open(filename, 'rt', encoding='utf8') as names_file: + for name in filter_koan_names(names_file): + yield name + return + + +def koans_suite(names): + ''' + Returns a ``TestSuite`` loaded with all tests found in the given + ``names``, preserving the order in which they are found. + ''' + suite = unittest.TestSuite() + loader = unittest.TestLoader() + loader.sortTestMethodsUsing = None + for name in names: + tests = loader.loadTestsFromName(name) + suite.addTests(tests) + return suite + + +def koans(filename=KOANS_FILENAME): + ''' + Returns a ``TestSuite`` loaded with all the koans (``TestCase``s) + listed in ``filename``. + ''' + names = names_from_file(filename) + return koans_suite(names) diff --git a/python 2/runner/runner_tests/__init__.py b/runner/runner_tests/__init__.py similarity index 100% rename from python 2/runner/runner_tests/__init__.py rename to runner/runner_tests/__init__.py diff --git a/python 3/runner/runner_tests/test_helper.py b/runner/runner_tests/test_helper.py similarity index 97% rename from python 3/runner/runner_tests/test_helper.py rename to runner/runner_tests/test_helper.py index ea3ee00c3..fef9b6026 100644 --- a/python 3/runner/runner_tests/test_helper.py +++ b/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/runner/runner_tests/test_mountain.py b/runner/runner_tests/test_mountain.py new file mode 100644 index 000000000..5736f49ac --- /dev/null +++ b/runner/runner_tests/test_mountain.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +from libs.mock import * + +from runner.mountain import Mountain + +class TestMountain(unittest.TestCase): + + def setUp(self): + self.mountain = Mountain() + + def test_it_gets_test_results(self): + with patch_object(self.mountain.stream, 'writeln', Mock()): + with patch_object(self.mountain.lesson, 'learn', Mock()): + self.mountain.walk_the_path() + self.assertTrue(self.mountain.lesson.learn.called) diff --git a/runner/runner_tests/test_path_to_enlightenment.py b/runner/runner_tests/test_path_to_enlightenment.py new file mode 100644 index 000000000..dd00a40ef --- /dev/null +++ b/runner/runner_tests/test_path_to_enlightenment.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import io +import unittest + +from runner import path_to_enlightenment as pte + + +class TestFilterKoanNames(unittest.TestCase): + + def test_empty_input_produces_empty_output(self): + infile = io.StringIO('') + expected = [] + received = list(pte.filter_koan_names(infile)) + self.assertListEqual(expected, received) + return + + def test_names_yielded_match_names_in_file(self): + names = [ + 'this.is.a.test', + 'this.is.only.a.test', + ] + infile = io.StringIO('\n'.join(names)) + received = list(pte.filter_koan_names(infile)) + self.assertListEqual(names, received) + return + + def test_whitespace_is_stripped(self): + names = [ + 'this.is.a.test', + ' white.space.should.be.stripped', + 'this.is.only.a.test', + 'white.space.should.be.stripped ', + ] + infile = io.StringIO('\n'.join(names)) + expected = [ + 'this.is.a.test', + 'white.space.should.be.stripped', + 'this.is.only.a.test', + 'white.space.should.be.stripped', + ] + received = list(pte.filter_koan_names(infile)) + self.assertListEqual(expected, received) + return + + def test_commented_out_names_are_excluded(self): + names = [ + 'this.is.a.test', + '#this.is.a.comment', + 'this.is.only.a.test', + ' # this.is.also a.comment ', + ] + infile = io.StringIO('\n'.join(names)) + expected = [ + 'this.is.a.test', + 'this.is.only.a.test', + ] + received = list(pte.filter_koan_names(infile)) + self.assertListEqual(expected, received) + return + + def all_blank_or_comment_lines_produce_empty_output(self): + names = [ + ' ', + '# This is a comment.', + '\t', + ' # This is also a comment.', + ] + infile = io.StringIO('\n'.join(names)) + expected = [] + received = list(pte.filter_koan_names(infile)) + self.assertListEqual(expected, received) + return + + +class TestKoansSuite(unittest.TestCase): + + def test_empty_input_produces_empty_testsuite(self): + names = [] + suite = pte.koans_suite(names) + self.assertTrue(isinstance(suite, unittest.TestSuite)) + expected = [] + received = list(suite) + self.assertListEqual(expected, received) + return + + def test_testcase_names_appear_in_testsuite(self): + names = [ + 'koans.about_asserts.AboutAsserts', + 'koans.about_none.AboutNone', + 'koans.about_strings.AboutStrings', + ] + suite = pte.koans_suite(names) + self.assertTrue(isinstance(suite, unittest.TestSuite)) + expected = [ + 'AboutAsserts', + 'AboutNone', + 'AboutStrings', + ] + received = sorted(set(test.__class__.__name__ for test in suite)) + self.assertListEqual(expected, received) + return diff --git a/python 2/runner/runner_tests/test_sensei.py b/runner/runner_tests/test_sensei.py similarity index 54% rename from python 2/runner/runner_tests/test_sensei.py rename to runner/runner_tests/test_sensei.py index 481d7e166..109133557 100644 --- a/python 2/runner/runner_tests/test_sensei.py +++ b/runner/runner_tests/test_sensei.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import sys import unittest import re @@ -10,7 +9,6 @@ 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 @@ -81,98 +79,28 @@ class AboutFreemasons: """ + class TestSensei(unittest.TestCase): def setUp(self): - self.sensei = Sensei(WritelnDecorator(sys.stdout)) - self.sensei.stream.writeln = Mock() - path_to_enlightenment.koans = Mock() - self.tests = Mock() - self.tests.countTestCases = 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) + self.sensei = Sensei(WritelnDecorator(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) - + with patch('runner.mockable_test_result.MockableTestResult.addSuccess', Mock()): + self.sensei.passesCount = Mock() + self.sensei.addSuccess(Mock()) + self.assertTrue(self.sensei.passesCount.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()) - + with patch('runner.mockable_test_result.MockableTestResult.addSuccess', Mock()): + pass_count = self.sensei.pass_count + self.sensei.addSuccess(Mock()) + self.assertEqual(pass_count + 1, self.sensei.pass_count) + 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"), @@ -180,7 +108,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"), @@ -191,26 +119,20 @@ 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"), @@ -218,7 +140,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"), @@ -227,48 +149,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_student_progress(self): - self.sensei.errorReport = Mock() - self.sensei.total_lessons = Mock() - self.sensei.total_koans = Mock() - - self.sensei.learn() - self.assertTrue(self.sensei.total_lessons.called) - self.assertTrue(self.sensei.total_koans.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() @@ -279,19 +159,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)) @@ -312,94 +192,62 @@ 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): + + def test_that_if_there_is_1_success_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)) - def test_that_if_there_is_10_successes_it_will_say_the_sixth_zen_of_python_koans(self): + def test_that_if_there_are_10_successes_it_will_say_the_sixth_zen_of_python_koans(self): 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)) - def test_that_if_there_is_36_successes_it_will_say_the_final_zen_of_python_koans(self): + def test_that_if_there_are_36_successes_it_will_say_the_final_zen_of_python_koans(self): 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)) - def test_that_if_there_is_37_successes_it_will_say_the_first_zen_of_python_koans_again(self): + def test_that_if_there_are_37_successes_it_will_say_the_first_zen_of_python_koans_again(self): 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): diff --git a/python 3/runner/sensei.py b/runner/sensei.py similarity index 77% rename from python 3/runner/sensei.py rename to runner/sensei.py index 8e8e14815..4a8d5cd96 100644 --- a/python 3/runner/sensei.py +++ b/runner/sensei.py @@ -3,6 +3,8 @@ import unittest import re +import sys +import os import glob from . import helper @@ -17,7 +19,7 @@ def __init__(self, stream): unittest.TestResult.__init__(self) self.stream = stream self.prevTestClassName = None - self.tests = path_to_enlightenment.koans() + self.tests = path_to_enlightenment.koans() self.pass_count = 0 self.lesson_pass_count = 0 self.all_lessons = None @@ -31,16 +33,16 @@ def startTest(self, test): 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 + if helper.cls_name(test) not in ['AboutAsserts', 'AboutExtraCredit']: + self.lesson_pass_count += 1 def addSuccess(self, test): - if self.passesCount(): + 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)) + Fore.RESET, Style.NORMAL)) self.pass_count += 1 def addError(self, test, err): @@ -50,7 +52,7 @@ def addError(self, 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) @@ -62,47 +64,49 @@ def sortFailures(self, testClassName): 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()) - self.stream.writeln("") + if self.failures: + self.stream.writeln(self.report_remaining()) + self.stream.writeln("") self.stream.writeln(self.say_something_zenlike()) - - if self.failures: return + + 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)) - + "\nIf you want more, take a look at about_extra_credit.py{0}{1}" \ + .format(Fore.RESET, Style.NORMAL)) + def errorReport(self): problem = self.firstFailure() - if not problem: return - test, err = problem + if not problem: return + test, err = problem self.stream.writeln(" {0}{1}{2} has damaged your " - "karma.".format(Fore.RED, Style.BRIGHT, test._testMethodName)) + "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)) @@ -123,7 +127,7 @@ def scrapeAssertionError(self, err): 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') @@ -133,47 +137,64 @@ def scrapeInterestingStackDump(self, err): return "" lines = err.splitlines() - + sep = '@@@@@SEP@@@@@' - - scrape = "" + + stack_text = "" for line in lines: m = re.search("^ File .*$",line) if m and m.group(0): - scrape += '\n' + line + stack_text += '\n' + line m = re.search("^ \w(\w)+.*$",line) if m and m.group(0): - scrape += sep + line - - lines = scrape.splitlines() - - scrape = "" + 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): - scrape += line + '\n' - return scrape.replace(sep, '\n').strip('\n') + 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 are now {0}/{1} koans and {2}/{3} lessons away from " \ - "reaching enlightenment".format(self.pass_count, - self.total_koans(), - self.lesson_pass_count, - self.total_lessons())) - - # Hat's tip to Tim Peters for the zen statements from The Zen - # of Python (http://www.python.org/dev/peps/pep-0020/) + return "You have completed {0} ({2} %) koans and " \ + "{1} (out of {3}) lessons.".format( + self.pass_count, + self.lesson_pass_count, + self.pass_count*100//self.total_koans(), + self.total_lessons()) + + def report_remaining(self): + koans_remaining = self.total_koans() - self.pass_count + lessons_remaining = self.total_lessons() - self.lesson_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 permatation in the Ruby Koans + # 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: + if turn == 0: zenness = "Beautiful is better than ugly." elif turn == 1 or turn == 2: zenness = "Explicit is better than implicit." @@ -216,16 +237,16 @@ def say_something_zenlike(self): elif turn == 33 or turn == 34: zenness = "If the implementation is easy to explain, " \ "it may be a good idea." - else: + 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); + 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 in collapsing! Run!!!" + return "The temple is collapsing! Run!!!" def total_lessons(self): all_lessons = self.filter_all_lessons() @@ -238,10 +259,11 @@ 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('koans/about*.py') + 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 + return self.all_lessons diff --git a/python 2/runner/writeln_decorator.py b/runner/writeln_decorator.py similarity index 100% rename from python 2/runner/writeln_decorator.py rename to runner/writeln_decorator.py diff --git a/scent.py b/scent.py new file mode 100644 index 000000000..818506b10 --- /dev/null +++ b/scent.py @@ -0,0 +1,12 @@ +from sniffer.api import * +import os + +watch_paths = ['.', 'koans/'] + +@file_validator +def py_files(filename): + return filename.endswith('.py') and not os.path.basename(filename).startswith('.') + +@runnable +def execute_koans(*args): + os.system('python3 -B contemplate_koans.py')