asciitree の AttributeTraversal を使う

pypiasciitree というパッケージがある。 ターミナルにツリー表示を描くために使用することができるのだけど、README をパッと見る限りではツリー構造を dict で用意しなければならないようで、なんだか不便そう。ただ、ドキュメントを見ると children という属性を見てツリー構造を解釈してくれそうな AttributeTraversal というクラスがあるが、使用例がない。

というわけで、その使用例を書いてみる。書き出すツリーは、README と同じ以下のもの。

asciitree
 +-- sometimes
 |   +-- you
 +-- just
 |   +-- want
 |       +-- to
 |       +-- draw
 +-- trees
 +-- in
     +-- your
         +-- terminal

まずは、import。

from asciitree import LeftAligned
from asciitree.traversal import AttributeTraversal

そして、children を属性に持つ Node クラスを用意してみる。(とりあえず tuple にしているけど、list でも set でも良い。) この Node クラスのオブジェクトに対して str() が行われた結果が、ツリーの各ノードに表示される文字列になるので、__repr()__ でカスタマイズできる。

class Node:
    def __init__(self, data):
        self.__data = data
        self.children = tuple()

    def __repr__(self):
        return str(self.__data)

ノードを作っていく。

ast = Node('asciitree')
sts = Node('sometimes')
you = Node('you')
jst = Node('just')
wnt = Node('want')
to  = Node('to')
drw = Node('draw')
trs = Node('trees')
inn = Node('in')
yr  = Node('your')
tnl = Node('terminal')

各ノードが持つ子どもを tuple にまとめて、children に入れていく。(list とか set にする場合には、それに応じて適当に。)

ast.children = (sts, jst, trs, inn)
sts.children = (you,)
jst.children = (wnt,)
wnt.children = (to, drw)
inn.children = (yr,)
yr.children  = (tnl,)

traverse 引数に AttributeTraversal() を指定して LeftAligned のオブジェクトを作り、call する。

tr = LeftAligned(traverse=AttributeTraversal())
print(tr(ast))

これで、ツリー構造をターミナルに書くことができる。 dict で表現するよりはかなり楽。

ちなみに、asciitree はデフォルトで children という名前の属性を見てくれるのだけど、これを変更することも可能。AttributeTraversal オブジェクトを作る際に、attribute 引数で属性名を文字列で指定すれば良い。例えば、子どもがひとつしかない構造で属性名が child があるというような場合には、AttributeTraversal(attribute='child') と書けば良い。

tar の -a オプションを知らなかった話

macOS (BSD) の tar には -a (--auto-compress) というオプションがあるのを知った。

c (create) のときに指定できるオプションで -f に指定したアーカイブファイル名の拡張子部分によって、どの圧縮形式を使うのかを判断してくれる。

macOS の man tar にはこんな風に書いてある。

 -a, --auto-compress
         (c mode only) Use the archive suffix to decide a set of the for-
         mat and the compressions.  As a simple example,
               tar -a -cf archive.tgz source.c source.h
         creates a new archive with restricted pax format and gzip com-
         pression,
               tar -a -cf archive.tar.bz2.uu source.c source.h
         creates a new archive with restricted pax format and bzip2 com-
         pression and uuencode compression,
               tar -a -cf archive.zip source.c source.h
         creates a new archive with zip format,
               tar -a -jcf archive.tgz source.c source.h
         ignores the ``-j'' option, and creates a new archive with
         restricted pax format and gzip compression,
               tar -a -jcf archive.xxx source.c source.h
         if it is unknown suffix or no suffix, creates a new archive with
         restricted pax format and bzip2 compression.

.tar.bz2 にするために、 -j オプションを知らずに、

tar cf - aaa bbb ccc | bzip2 -c > xxx.tar.gz2

とか書いていた私。

それが、

tar acf xxx.tar.bz2 aaa bbb ccc

で済むというのは、とても楽だと思う。 というか、.zip も作れちゃうのね。tar さん。ステキ。

でも、parallel bzip2 (pbzip2) を使いたいときには、" --use-compress-program pbzip2" かな。

tar cf xxx.tar.bz2 --use-compress-program pbzip2 aaa bbb ccc

"pyenv install 3.9.4" が失敗した話

ふと思い立って "brew update" -> "brew upgrade" をしたあと、"pyenv install 3.9.4" をしたら、Python のビルドに失敗した。 "pyenv install 3.9.4" を実行したときの画面出力はもう失ってしまったが、"/var/folders/<省略>/python-build.<日時>.<番号>/Python-3.9.4/config.log" を見ろと書いてあった。 "config.log" にはこんなエラーが出ていた。

configure:3926: checking whether the C compiler works
configure:3948: clang -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include   -I/usr/local/opt/readline/include -I/usr/local/opt/readline/include -I/Users/failfast/.pyenv/versions/3.9.4/include  -L/usr/local/opt/readline/lib -L/usr/local/opt/readline/lib -L/Users/failfast/.pyenv/versions/3.9.4/lib  -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib conftest.c  >&5
clang: error: invalid version number in 'MACOSX_DEPLOYMENT_TARGET=11.2'
configure:3952: $? = 1
configure:3990: result: no
configure: failed program was:
| /* confdefs.h */
| #define _GNU_SOURCE 1
| #define _NETBSD_SOURCE 1
| #define __BSD_VISIBLE 1
| #define _DARWIN_C_SOURCE 1
| #define _PYTHONFRAMEWORK ""
| /* end confdefs.h.  */
| 
| int
| main ()
| {
| 
|   ;
|   return 0;
| }
configure:3995: error: in `/var/folders/yn/rrz35k051713kwrv5j2mw8d40000gn/T/python-build.20210410220429.44736/Python-3.9.4':
configure:3997: error: C compiler cannot create executables
See `config.log' for more details

clang で単純な C プログラムをビルドできるかどうか試したがエラーで終了したようだ。そして、そのエラーは、"invalid version number in 'MACOSX_DEPLOYMENT_TARGET=11.2'"。 この macOS のバージョンは Big Sur 11.2.3 だけど、clang さんはこの 11.2 というバージョンをご存知ではないようだ。

failfast@iMac ~ % /bin/ls -1 /Library/Developer/CommandLineTools/SDKs
MacOSX.sdk
MacOSX10.14.sdk
MacOSX10.15.sdk
failfast@iMac ~ % 

このエラーの原因は、Command Line Tools for Xcode が古かったこと。新しいバージョンに含まれている clang を使わなければならない。 https://developer.apple.com/download/more/ から "Command Line Tools for Xcode 12.4" をダウンロードしてインストールした。("softwraeupdate --all --install --force" と実行することでもアップグレードできるらしい。)

failfast@iMac ~ % /bin/ls -l /Library/Developer/CommandLineTools/SDKs
total 0
lrwxr-xr-x  1 root  wheel   14  4 10 22:31 MacOSX.sdk -> MacOSX11.1.sdk
drwxr-xr-x  7 root  wheel  224  1 16  2020 MacOSX10.14.sdk
drwxr-xr-x  8 root  wheel  256  4 10 22:32 MacOSX10.15.sdk
drwxr-xr-x  7 root  wheel  224 11 30 21:33 MacOSX11.1.sdk
failfast@iMac ~ % clang --version
Apple clang version 12.0.0 (clang-1200.0.32.29)
Target: x86_64-apple-darwin20.3.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
failfast@iMac ~ % 

その後の "pyenv install 3.9.4" はエラーもなく正常に終了した。

SDKs に入っている古いバージョン(MacOSX10.1[45].sdk)はどうしたらいいんだろう。rm しちゃっていいのかな。それぞれ 250MiB も使っていないので、とりあえず放置でいっか。。。