使い捨て python コードの書き方

26
使い捨てコードの書き方 2012/08/25 @shiumachi

Upload: sho-shimauchi

Post on 18-Jun-2015

4.941 views

Category:

Technology


0 download

DESCRIPTION

pyfes LT 2012.08 でしゃべったときのスライドです

TRANSCRIPT

Page 1: 使い捨て python コードの書き方

使い捨てコードの書き方  

2012/08/25  @shiumachi  

Page 2: 使い捨て python コードの書き方

お前誰よ?  

•  Sho  Shimauchi    (  @shiumachi  )  •  Cloudera  の問い合わせ担当  •  技術質問から人生相談まで幅広く対応  

Page 3: 使い捨て python コードの書き方

サポートに必要なもの  •  Linux  コマンド  –  sed,  awk,  grep,  find,  sort  …  –  これで対応できるなら問題なし  

•  シェルスクリプト  –  for  i  in  `ls`  と  for  i  in  `seq  N`  があればなんとかなる  

•  正規表現  –  最強  –  最強と使いやすいかどうかは別問題  

•  軽量言語  –  上記3つで簡単に対応できない場合はこれを使う  –  特に集計処理とか入ると正規表現ブン回すより簡単だし

再利用しやすい  

Page 4: 使い捨て python コードの書き方

ユースケース  

•  ほとんどがパース・集計処理  – ログ内の特定の文字をカウントする  – シーケンスIDをピックアップし、ギャップを検出、カ

ウントする  – 特定のIDを時系列で追跡し、クラスタ内での移動

経路を追う  

•  その一回だけでしか使わないことが大半なので、使い捨てのコードを書くことが多い  

Page 5: 使い捨て python コードの書き方

ゴール  

とにかく楽に使い捨てたい  できれば再利用したい  

Page 6: 使い捨て python コードの書き方

使い捨て方(1)  適当なディレクトリに適当に書く  

•  論外  •  まず再利用不可能  – 場所がわからん  – 何に使ったかわからん  

Page 7: 使い捨て python コードの書き方

使い捨て方(2)  git  で綺麗に管理する  

•  理想的だが案外面倒  •  どのコードも目的が違うので、ドキュメントの

整理・構造化も必要  •  同一名で投げ込むことができず、名前空間の

管理も必要  

Page 8: 使い捨て python コードの書き方

使い捨て方(3)  gist  に突っ込んでおく  

•  多くの人が選んでいるであろう方法  •  保存・管理の手間のコストパフォーマンスを考

えるとこれがベスト  

Page 9: 使い捨て python コードの書き方

テスト  

使い捨てコードだったらテストなんていらなくね?    

違います。楽するためにテスト必要  

Page 10: 使い捨て python コードの書き方

xUnit/TDD  の(個人的な)弊害  •  xUnit  – クラス必須みたいに見えること  – 使い捨てコードでクラス必須って考えるだけでだるく

なる(個人的に)  •  TDD  – 入門書を読むと 100%  原則を守らないといけないよう

に感じる  

Page 11: 使い捨て python コードの書き方

nose  

•  色々便利な特徴はあるが、使い捨てコードにおける大きな特徴は2つ  

•  テストクラス作成が不要  –  test_*.py  としておいて、def  test_*:  というメソッド

を書いておけば nosetests  で自動実行できる  

•  nose.tools    – eq_(a,  b)  だけでほぼ全てまかなえる  

Page 12: 使い捨て python コードの書き方

テスト=ドキュメント  

•  頭の中で設計したら、ドキュメントじゃなくテストとしてdumpしておくこと  

•  どうせパースだけなので、実際の入力データから数行ひっぱってくれば十分  

•  網羅性を追求しない  •  品質を追求しない  •  実際に動かしてこけたら、その都度こけた入

力をテストに追加  

Page 13: 使い捨て python コードの書き方
Page 14: 使い捨て python コードの書き方

エキPy  11  章より  テストのメリットは4つ  

1.   ソフトウェアのリグレッションの防止

2.   コード、の品質の向上

3.   最適で低レベルなドキュメントの提供

4.   よりすばやく、信頼性の高いコードの生産

Page 15: 使い捨て python コードの書き方

エキPy  11  章より  テストのメリットは4つ  

1.   ソフトウェアのリグレッションの防止

2.   コード、の品質の向上

3.   最適で低レベルなドキュメントの提供

4.   よりすばやく、信頼性の高いコードの生産  

使い捨てコードにおいては3,4  だけで十分

Page 16: 使い捨て python コードの書き方

テストもgistにアップロードする  

•  gistは一つのコンテンツに複数ファイルアップロード可能  

•  テストも一緒に突っ込んでおく  – テストを読めば何をするコードか大体わかる  – 別の環境(Mac  -­‐>  Linux  など)でも動くかどうかテス

ト可能  

Page 17: 使い捨て python コードの書き方

サンプルコード  

•  hYps://gist.github.com/3460244  

Page 18: 使い捨て python コードの書き方

実際の入力データ  hadoop  のネームノード(マスタ)のログ  2012-­‐06-­‐04  13:30:59,197  INFO  org.apache.hadoop.hdfs.server.namenode.NameNode:  STARTUP_MSG:    /************************************************************  STARTUP_MSG:  Starcng  NameNode  STARTUP_MSG:      host  =  sho-­‐mba.local/192.168.100.130  STARTUP_MSG:      args  =  []  STARTUP_MSG:      version  =  0.20.2-­‐cdh3u4  STARTUP_MSG:      build  =  git://ubuntu-­‐slave01/var/lib/jenkins/workspace/CDH3u4-­‐Full-­‐RC/build/cdh3/hadoop20/0.20.2-­‐cdh3u4/source  -­‐r  214dd731e3bdb687cb55988d3f47dd9e248c5690;  compiled  by  'jenkins'  on  Mon  May    7  13:01:39  PDT  2012  ************************************************************/  2012-­‐06-­‐04  13:30:59,680  INFO  org.apache.hadoop.metrics.jvm.JvmMetrics:  Inicalizing  JVM  Metrics  with  processName=NameNode,  sessionId=null  2012-­‐06-­‐04  13:30:59,683  INFO  org.apache.hadoop.hdfs.server.namenode.metrics.NameNodeMetrics:  Inicalizing  NameNodeMeterics  using  context  object:org.apache.hadoop.metrics.spi.NullContext  2012-­‐06-­‐04  13:30:59,788  INFO  org.apache.hadoop.hdfs.ucl.GSet:  VM  type              =  64-­‐bit  2012-­‐06-­‐04  13:30:59,788  INFO  org.apache.hadoop.hdfs.ucl.GSet:  2%  max  memory  =  19.9175  MB  2012-­‐06-­‐04  13:30:59,788  INFO  org.apache.hadoop.hdfs.ucl.GSet:  capacity            =  2^21  =  2097152  entries  2012-­‐06-­‐04  13:30:59,789  INFO  org.apache.hadoop.hdfs.ucl.GSet:  recommended=2097152,  actual=2097152  2012-­‐06-­‐04  13:30:59,940  INFO  org.apache.hadoop.hdfs.server.namenode.FSNamesystem:  fsOwner=sho  (auth:SIMPLE)  2012-­‐06-­‐04  13:30:59,940  INFO  org.apache.hadoop.hdfs.server.namenode.FSNamesystem:  supergroup=supergroup  2012-­‐06-­‐04  13:30:59,940  INFO  org.apache.hadoop.hdfs.server.namenode.FSNamesystem:  isPermissionEnabled=true  2012-­‐06-­‐04  13:30:59,953  INFO  org.apache.hadoop.hdfs.server.namenode.FSNamesystem:  dfs.block.invalidate.limit=1000  

Page 19: 使い捨て python コードの書き方

やりたいこと  

•  ログに出力されているログレベル(INFO,  WARN,  ERROR,  …)  を集計したい  

•  「それ awk  でできんじゃね?」とは言ってはいけない  

Page 20: 使い捨て python コードの書き方

テストコード  def test_parse():!    input = "2012-06-04 13:31:07,065 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /default-rack/127.0.0.1:50010"!    input2 = "2012-06-04 13:31:05,466 WARN org.apache.hadoop.util.PluginDispatcher: Unable to load dfs.namenode.plugins plugins"!

    expected = 'INFO'!    expected2 = 'WARN’!    eq_(expected, parse(input))!    eq_(expected2, parse(input2))!

入力データをベタ貼り  

Page 21: 使い捨て python コードの書き方

サンプルコード  

def parse(line):!    arr = line.strip().split() !    log_level = arr[2]!    return log_level!  

Page 22: 使い捨て python コードの書き方

テストは通るが実際には動かない  2012-06-04 13:30:59,197 INFO org.apache.hadoop.hdfs.server.namenode.NameNode: STARTUP_MSG: "/************************************************************"Traceback (most recent call last):" File "nn_parse.py", line 23, in <module>" main(sys.stdin)" File "nn_parse.py", line 11, in main" log_level = parse(line)" File "nn_parse.py", line 5, in parse" log_level = arr[2]"IndexError: list index out of range  

Page 23: 使い捨て python コードの書き方

失敗した行をそのままテストに追加  def test_parse():!    input = "2012-06-04 13:31:07,065 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /default-rack/127.0.0.1:50010"!    input2 = "2012-06-04 13:31:05,466 WARN org.apache.hadoop.util.PluginDispatcher: Unable to load dfs.namenode.plugins plugins"!

    input3 = "/************************************************************"!    expected = 'INFO'!    expected2 = 'WARN'!    expected3 = '_NULL'!    eq_(expected, parse(input))!    eq_(expected2, parse(input2))!    eq_(expected3, parse(input3))  

Page 24: 使い捨て python コードの書き方

失敗した行をそのままテストに追加  def test_parse():!    input = "2012-06-04 13:31:07,065 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /default-rack/127.0.0.1:50010"!    input2 = "2012-06-04 13:31:05,466 WARN org.apache.hadoop.util.PluginDispatcher: Unable to load dfs.namenode.plugins plugins"!

    input3 = "/************************************************************"!    expected = 'INFO'!    expected2 = 'WARN'!    expected3 = '_NULL'!    eq_(expected, parse(input))!    eq_(expected2, parse(input2))!    eq_(expected3, parse(input3))  

Page 25: 使い捨て python コードの書き方

コードもIndexErrorに対応  

def parse(line):!    arr = line.strip().split()!    try: !        log_level = arr[2]!    except:!        log_level = '_NULL'!    return log_level!  

Page 26: 使い捨て python コードの書き方

おしまい