python/hack 属性シンタックスによるによるミニDSL。
# -*- coding: utf-8 -*-
"""
近頃はスクリプト言語のシンタックスを乱用、じゃなくてクリエイティブに使って違う言語のようにすると「DSL」と呼べるらしい。 ならテングもpythonでマイクロDSL。
"""
def main():
"""
「.」によるパス表記
"""
class PathAttrChain(AttrChain):
def _end(self, path_func=lambda p: p):
import os.path
path=os.path.join(self.kw.get('base','/'), *self._tuple())
return path_func(path)
"""
ものぐさモジュールアクセス
"""
class ModuleAttrChain(AttrChain):
def _end(self):
return __import__(self._dot(), fromlist=['hoge'])os.environ['DJANGO_SETTINGS_MODULE']='settings'
sys.path.insert(0, '/home/tengu/disco/server')"""
ものぐさな人に: インポートしなくてもそのままモジュール内のものがアクセスできる表現。
"""
print ModuleAttrChain().django.contrib.auth.models().User
#"""
ウェブサイトアクセス
"""
class WwwHostAttrChain(AttrChain):
def _end(self, path='/'):
import urllib2
url='http://%s%s' % (self._dot(),path)
return urllib2.urlopen(url)"""
ホスト(パス) --> http response
"""
print WwwHostAttrChain().www.gnu.org('/robots.txt').read()
# robots.txt for http://www.gnu.org/
# User-agent: *
# ..."""
属性チェーン表記の利点は…
特にない。ただできるからやってみただけ。 多分、最初は独自のHTMLテンプレートのデータアクセスの表記を pythonのパーサにやらせようとしたのが切っ掛けだったと思う。
"""
"""
実装
"""
"""
class AttrChain(object):
""" 鎖のリンク
foo.bar.bazのような連鎖表現のそれぞれの節はこのクラスのインスタンス。
"""
def __init__(self, prev=None, *args, **kw):
self._prev=prev
self._attr=None
self._path=Noneself.args=args
self.kw=kwdef __repr__(self):
return '<%s "%s">' % (self.__class__.__name__, self._attr)def __str__(self):
""" 文字列にするとチェーンが終了する。
str(foo.bar.baz) は foo.bar.baz._end() の簡略版。
"""
return self._end()def __call__(self, *args, **kw):
""" foo.bar.baz(arg)
関数として呼出すとチェーンが終了する。
最終リンクだけ引数をうけることができる。
foo.bar().bazはできない。
"""
return self._end(*args, **kw)def _tuple(self):
""" 属性チェーンを単語のリストに変換。
foo.bar.baz --> ('foo', 'bar', 'baz')
"""
cur=self
path=[]
while cur:
path.append(cur)
cur=cur._prev
path.reverse()
self._path=path # avoid traversing again..
return tuple([ n._attr for n in path if n._attr and n._attr!='_' ])def _dot(self):
""" foo.bar.baz --> "foo.bar.baz"
「.」つなぎ表記。
"""
return '.'.join(self._tuple())def _end(self):
""" ディフォルトの最終リンク。「.」つなぎ表記("foo.bar.baz")を返す。
サブクラスはこれをオーバライドすることにより、好みのオブジェクトを返す
ことができる。
"""
return self._dot()def __getattr__(self, name):
""" 属性連鎖のミソ
"""
self._attr=name
# foo.bar.baz._ を foo.bar.baz._end() として扱う。
if name=='_':
return self._end()
# _で始まる属性は普通扱い。リンク属性は'_'で始まってはならない。
elif name.startswith('_'):
return object.__getattribute__(self, name)
# 普通の名前の属性はリンク扱い。自分と同じクラスのものを返す。
return self.__class__(prev=self, *self.args, **self.kw)
"""
if __name__=='__main__':
main()
"""
ちなみに、このポストのソースはそのまま実行可能なpythonスクリプトだ。こういうのを「literate programming」って言うのかな。ブログを実行するとこうなる:
"""
python hack-attrchain-ja.py
foo.bar.baz
/usr/local/bin/emacs
/alt/local/bin/emacs
emacs size= 10938819
# robots.txt for http://www.gnu.org/User-agent: *
Crawl-delay: 4
Disallow: /private/User-agent: *
Crawl-delay: 4
Disallow: /savannah-checkouts/