<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 [Part I] Introduction to Generators and Coroutines
generator
Step1:  generator 함수를 호출하는것은 generator 객체를 생성하는것이지 함수를 실행하는 것이 아님
generator.next() 를 호출하면 함수가 실행되고, 
yield 를 통해서 값을 생성하고, 함수의 실행을 잠시 중단하고, 
.next() 호출을 통해 실행을 재개한다. 
generator 가 리턴하면, iteration 은 멈춘다.
Step3:  tail -f (python version)
Step4:  Coroutine
yield 를 표현식으로, 
next() 또는 send(None) 메소드로 함수를 시작하고
yield 를 통해 값을 대기하고, send() 메소드로 값을 전달
Step6:  next() 는 까먹기 쉬우니까 decorator 를 이용하자.
종료시에는 close() 를 호출하자 (가비지 컬렉터가 알아서 close() 를 호출하지만)
close() 를 호출하면 GeneratorExit 예외가 발생하니까 coroutine 에서 잡아준다.
Step7:  예외 던지기
Step8:  bogus sample
Step10:  정리
generator 는 iteration 을 위해 데이터를 생성한다.   (producer)
coroutine 은 데이터의 사용자이다. (consumer)
coroutine 은 iteration 과 아무 상관없다. 
[Part II] Coroutines, Pipelines, and Dataflow
coroutine 은 파이프를 설치하는데 사용할 수 있음
send() -> [coroutine] -> send() -> [coroutine] ->
example
Step11:  Filter example
<ASSISTANT_TASK:>
Python Code:
def countdown(n):
    print '> counting down from {}'.format(n)
    while n > 0:
        yield n
        n -= 1
    print ''
    print '< countdown'
for n in countdown(10):
    print n,
Explanation: [Part I] Introduction to Generators and Coroutines
generator
End of explanation
# calling generator fucntion creates the generator object not start the function
x = countdown(3)
print x
# call `.next()` starts generator object.
print x.next()
print x.next()
print x.next()
print x.next()
Explanation: generator 함수를 호출하는것은 generator 객체를 생성하는것이지 함수를 실행하는 것이 아님
generator.next() 를 호출하면 함수가 실행되고, 
yield 를 통해서 값을 생성하고, 함수의 실행을 잠시 중단하고, 
.next() 호출을 통해 실행을 재개한다. 
generator 가 리턴하면, iteration 은 멈춘다.
End of explanation
import os
print os.getcwd()
generator 강의자료에 run/foo/xxx 등이 있는데 거기에 있는걸 복사해서 실행환경을 만들면 됨
# follow.py
#
# Follow a file like tail -f.
import time
def follow(thefile):
    thefile.seek(0,2)
    while True:
        line = thefile.readline()
        if not line:
            time.sleep(0.1)
            continue
        yield line
# Example use
# Note : This example requires the use of an apache log simulator.
# 
# Go to the directory run/foo and run the program 'logsim.py' from
# that directory.   Run this program as a background process and
# leave it running in a separate window.  We'll write program
# that read the output file being generated
# 
#logfile = open("run/foo/access-log","r")
logfile = open(r'd:\work.python\python_async_stuffs\coroutine_www.dabeaz.com\run\foo\access-log')
for line in follow(logfile):
    print line,
Explanation: tail -f (python version)
End of explanation
def grep(pattern):
    print 'looking for %s' % pattern
    while True:
        line = (yield)
        if pattern in line:
            print line,
g = grep("python")
g.next()   # prime it!
g.send("hey!!")
g.send("welcome to koread")
g.send("wow python rocks!")
g.send("really?")
Explanation: Coroutine
yield 를 표현식으로, 
next() 또는 send(None) 메소드로 함수를 시작하고
yield 를 통해 값을 대기하고, send() 메소드로 값을 전달
End of explanation
def coroutine(func):
    
    
    
    def start_coroutine(*args, **kwargs):
        crtn = func(*args, **kwargs);
        crtn.next()
        return crtn
    return start_coroutine
@coroutine
def grep(pattern):
    print '[*] looking for %s' % pattern
    try:
        while True:
            line = (yield)
            if pattern in line:
                print line,
    except GeneratorExit as e:
        print '\n[*] Going away, bye (gc or U called close())'
    
g = grep('python')
#g.next()   # no need to call next()
g.send("hey!!")
g.send("welcome to koread")
g.send("wow python rocks!")
g.close()
Explanation: next() 는 까먹기 쉬우니까 decorator 를 이용하자.
종료시에는 close() 를 호출하자 (가비지 컬렉터가 알아서 close() 를 호출하지만)
close() 를 호출하면 GeneratorExit 예외가 발생하니까 coroutine 에서 잡아준다.
End of explanation
g = grep('python')
g.send('wow! python is rock!')
g.send('wow! python is rock!')
g.send('wow! python is rock!')
g.throw(RuntimeError, "exception thrown")
Explanation: 예외 던지기
End of explanation
# bogus.py
#
# Bogus example of a generator that produces and receives values
def countdown(n):
    print "Counting down from", n
    while n >= 0:
        newvalue = (yield n)
        # If a new value got sent in, reset n with it
        if newvalue is not None:
            n = newvalue
        else:
            n -= 1
# The holy grail countdown
c = countdown(5)
for x in c:
    print x
    if x == 5:
        c.send(3)
Explanation: bogus sample
End of explanation
import time
def coroutine(func):
    A decorator function that takes care of starting a coroutine
    automatically on call.
    
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        #print 'coroutine started...'
        return cr
    
    return start
# data source
def follow(thefile, target):
    thefile.seek(0, 2)      # goto end of the file
    while True:
        line = thefile.readline()
        if not line:
            time.sleep(0.1)
            continue
        
        target.send(line)    # 최초로 호출되는 시점에 객체(target)가 생성됨 
        
# sink - a coroutine that receives data
@coroutine
def printer():
    while True:
        line = (yield)
        print line
        
# useage
f = open(r'd:\work.python\python_async_stuffs\coroutine_www.dabeaz.com\run\foo\access-log')
#follow(f, printer)   # 요거는 오류 남, 'AttributeError: 'function' object has no attribute 'send'
follow( f, printer() )  
Explanation: 정리
generator 는 iteration 을 위해 데이터를 생성한다.   (producer)
coroutine 은 데이터의 사용자이다. (consumer)
coroutine 은 iteration 과 아무 상관없다. 
[Part II] Coroutines, Pipelines, and Dataflow
coroutine 은 파이프를 설치하는데 사용할 수 있음
send() -> [coroutine] -> send() -> [coroutine] ->
example
End of explanation
# data source
def follow(thefile, target):
    thefile.seek(0, 2)
    while True:
        line = thefile.readline()
        if not line:
            time.sleep(0.1)
            continue            
        target.send(line)
        
# filter        
@coroutine
def grep(pattern, target):
    while True:
        line = (yield)              # receive a line
        if pattern in line:
            target.send(line)       # send to next stage
        
# sink - a coroutine that receives data
@coroutine
def printer():
    while True:
        line = (yield)
        print line,
# useage
f = open(r'd:\work.python\python_async_stuffs\coroutine_www.dabeaz.com\run\foo\access-log')
follow( f, grep('python', printer()) )  
Explanation: Filter example
End of explanation
<END_TASK>