Pythonで、RequestのCookieを使ってみた

以前wsgi-interceptを使った時に、PythonのHTTPライブラリとして、Requestsを使いました。
Requests: HTTP for Humans — Requests 2.13.0 documentation

 
使っている中で、RequestのCookieの使い方について迷ったことがあったため、メモを残します。

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.1
  • Requests 2.13.0
  • Bottle 0.12.13
    • Requestsを試すためのアプリ
  • pytest 3.0.7
    • テストランナー

 

Requestsを試すための用意したBottleアプリ

CookieをセットするだけのBottleアプリを用意しました。

仕様は

  • /へアクセス
  • /redirectへアクセス
    • Cookieredirectをセット
    • /ヘリダイレクト

です。

from bottle import run, get, redirect, response

@get('/')
def get_root():
    # Cookieにrootをセット
    response.set_cookie('root', 'foo')
    return 'Hello world'

@get('/redirect')
def get_redirect():
    # Cookieにredirectをセットし、`/`へリダイレクト
    response.set_cookie('redirect', 'bar')
    redirect('/')

if __name__ == "__main__":
    run(host='localhost', debug=True, reloader=True)

 
このBottleアプリが想定した動作をするか、Chromeで挙動を見てみます。

 

/にアクセスした時

レスポンスヘッダを見ると、Cookierootがセットされました。

Server:WSGIServer/0.2 CPython/3.6.1
Set-Cookie:root=foo

 

/redirectへアクセスした時

リダイレクト元の/redirectでのレスポンスヘッダを見ると、Cookieredirectがセットされました。

Location:http://localhost:8080/
Server:WSGIServer/0.2 CPython/3.6.1
Set-Cookie:redirect=bar

 
リダイレクト先の/でのレスポンスヘッダでは、Cookierootがセットされました。

Server:WSGIServer/0.2 CPython/3.6.1
Set-Cookie:root=foo

 
なお、リクエストヘッダを見ると、

Cookie:root=foo; redirect=bar
Host:localhost:8080

と、2つのCookieが設定されていることも確認できました。

 

RequestsのResponseオブジェクトのCookieを使う

ドキュメントを見ると、ResponseオブジェクトにCookieがありました。
Cookies | Quickstart — Requests 2.13.0 documentation

 
そこで、reqeusts.Responseを使った

という3種類テストコードを作成します。

なお、テストを試す時は、上記のBottleアプリを起動した後、テストコードを実行します。

 

/へGET
def test_get(self):
    response = requests.get('http://localhost:8080')
    assert response.status_code == 200
    assert response.cookies.get('root') == 'foo'

テストはパスしました。

 

/redirectへGETし、/へリダイレクト
def test_allow_redirect(self):
    response = requests.get('http://localhost:8080/redirect')
    assert response.status_code == 200
    assert response.cookies.get('root') == 'foo'
    # ここで失敗する
    assert response.cookies.get('redirect') == 'bar'
    #=> AssertionError: assert None == 'bar'

テストが失敗しました。リダイレクト元のCookieは保持しないようです。

 

/redirectへGETし、リダイレクトは行わない
def test_forbid_redirect(self):
    response = requests.get('http://localhost:8080/redirect', allow_redirects=False)
    assert response.status_code == 303
    assert response.cookies.get('root') is None
    assert response.cookies.get('redirect') == 'bar'

リダイレクトしない時は、Cookieが正しくセットされています。

 

reqeusts.SessionオブジェクトのCookieを使う

ResponseオブジェクトのCookieでは、リダイレクトが発生するとCookieがなくなるため、あまり実用的ではないかもしれません。

他を探したところ、SessionオブジェクトにCookieがありました。
Using Python Requests: Sessions, Cookies, and POST - Stack Overflow

 
そこで、requests.Sessionオブジェクトを試してみます。

 

/へGET
def test_get(self):
    session = requests.Session()
    response = session.get('http://localhost:8080')
    assert response.status_code == 200
    # responseとsessionの両方にCookieがセットされる
    assert response.cookies.get('root') == 'foo'
    assert session.cookies.get('root') == 'foo'

テストがパスしました。

 

/redirectへGETし、/へリダイレクト
def test_allow_redirect(self):
    session = requests.Session()
    response = session.get('http://localhost:8080/redirect')
    assert response.status_code == 200
    # Cookie「redirect」はsessionのみセットされる
    assert response.cookies.get('root') == 'foo'
    assert response.cookies.get('redirect') is None
    assert session.cookies.get('root') == 'foo'
    assert session.cookies.get('redirect') == 'bar'

テストがパスしました。

Responseオブジェクトと異なり、Sessionオブジェクトはリダイレクト時にもCookieの値を保持するようです。

 

/redirectへGETし、リダイレクトは行わない
def test_forbid_redirect(self):
    session = requests.Session()
    response = session.get('http://localhost:8080/redirect', allow_redirects=False)
    assert response.status_code == 303
    # responseとsessionの両方にCookieがセットされる
    assert response.cookies.get('root') is None
    assert response.cookies.get('redirect') == 'bar'
    assert session.cookies.get('root') is None
    assert session.cookies.get('redirect') == 'bar'

テストがパスしました。

 

requests.SessionオブジェクトをContext Managerとして使う

Requestsのドキュメントを読むと、requests.SessionはContext Managerとしても使えるようでした。
Session Objects | Advanced Usage — Requests 2.13.0 documentation

そのため、上記のSessionオブジェクトのコードは、以下の通りにも書けます。

def test_get(self):
    with requests.Session() as session:
        response = session.get('http://localhost:8080')
        assert response.status_code == 200
        # responseとsessionの両方にCookieがセットされる
        assert response.cookies.get('root') == 'foo'
        assert session.cookies.get('root') == 'foo'

def test_allow_redirect(self):
    with requests.Session() as session:
        response = session.get('http://localhost:8080/redirect')
        assert response.status_code == 200
        # Cookie「redirect」はsessionのみセットされる
        assert response.cookies.get('root') == 'foo'
        assert response.cookies.get('redirect') is None
        assert session.cookies.get('root') == 'foo'
        assert session.cookies.get('redirect') == 'bar'

def test_forbid_redirect(self):
    with requests.Session() as session:
        response = session.get('http://localhost:8080/redirect', allow_redirects=False)
        assert response.status_code == 303
        # responseとsessionの両方にCookieがセットされる
        assert response.cookies.get('root') is None
        assert response.cookies.get('redirect') == 'bar'
        assert session.cookies.get('root') is None
        assert session.cookies.get('redirect') == 'bar'

ソースコード

GitHubにあげました。e.g._usage_cookieが今回のサンプルです。
thinkAmi-sandbox/python_requests-sample