python_koans / python3 / koans / about_with_statements.py

#!/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):
        try:
            file = open(file_name)
            try:
                count = 0
                for line in file.readlines():
                    count += 1
                return count
            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):
        try:
            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:
    ##
    ## 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"))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.