久しぶりにPythonで開発を進めているのですが、前回os.path.joinで苦戦して、今回はos.path.basenameでも困ったのでメモとして残しておきます。

さすがにまた似たような挙動でハマったらPython用にWikiページを作ります。

参考用に今回もNode.jsでコードを書いてみます:

const path = require('path')

console.log(path.basename('/foo/bar'))  // bar
console.log(path.basename('/foo/bar/')) // bar

/foo/barというディレクトリがあった場合はどちらもbarを返してくれます。 というよりはUNIXのbasenameコマンドと挙動は同じように作られているようです。

かたやPython (3.6以上)はというと:

from os import path

print(path.basename('/foo/bar'))   # bar
print(path.basename('/foo/bar/'))  #

なぜか/foo/bar/を渡すと空の文字列が返ってくるという仕組み。推測するに/foo/barの中のディレクトリ(.?)を指しているのかなと思えなくもないですが、なんとも紛らわしい仕様のようです。

https://docs.python.org/3/library/os.path.html#os.path.basename

Note that the result of this function is different from the Unix basename program; where basename for '/foo/bar/' returns 'bar', the basename() function returns an empty string ('').

訳: この関数の結果は Unix のbasenameプログラムとは異なることに注意してください。basename'/foo/bar/' の場合は 'bar' を返しますが、 basename() 関数は空の文字列 ('') を返します。

全く同じ例が公式ドキュメントにも記載されていました。それではどうすればよいのかというと今回もStackOverflowに回答がありました。

https://stackoverflow.com/a/7783326

from os import path

print(path.basename(path.abspath('/foo/bar')))   # bar
print(path.basename(path.abspath('/foo/bar/')))  # bar

今回も/bar//barに書き換えているイメージですね。 join()関数といい、何かと小さいミスが出そうなので普段から単体テストを書きましょうってことなのかもしれませんね。