はじめに
このブログは総じて自分メモなのですが、その中でも次の過去記事の応用(?)として、自分の手に馴染むルート相対/相対パス周りのオレオレミニライブラリを作成したのでそのメモです。
なぜこの記事なのか
ほぼ車輪の再発明なんですが、というか再発明でさえなく/ライブラリと呼べるものでもないというのが実体なんですが、実は再発明というよりは、この類のラッパーを作って、ミニDSLっぽくしたいことがあるんですよね。
つまり、プログラムの純粋な共通化やその他のプラクティスの方向というよりは、当時自分がやりたかった案件・ドメインを切り取ったような・それを体現したような関数やクラスを残しておきたいというところ。
なお、隠れたウリとして、次のPythonのメタな処理の関数を使って、走行確認テスト((テストコードとは呼べないな))をタイプ量少なめで実装してみています。
- inspect.getmembers によるクラスのメソッド名一覧取得
- inspect.signature による、あるメソッドのシグネチャ確認
- getattr による、メソッド名文字列を与えて関数実行
inspect.getmembers と inspect.signature docs.python.org docs.python.org
getattr ビルトイン関数( getattr(x,'foobar')は x.foobarと等価です。というのが全てかも) docs.python.org
プログラムと実行結果
from urllib.parse import urlparse from urllib.parse import urljoin import os.path import os import re import sys class ResourcePath: def __init__(self,address): _a = re.sub('/index.html$','/',address) _a = re.sub('/index.htm$','/',address) self.url = _a self.IMG = 'jpg/jpeg/JPG/JPEG/png/PNG/gif/GIF/pdf/PDF'.split('/') self.CSS = 'css'.split('/') self.JS = 'js/json'.split('/') def __str__(self): return self.url # 部分値の取得 ------------ def scheme(self): return urlparse(self.url).scheme def netloc(self): return urlparse(self.url).netloc def dirname(self): return os.path.dirname(urlparse(self.url).path) def just_dirname(self): return self.dirname().split('/')[-1] def basename(self): return os.path.basename(urlparse(self.url).path) def extension(self): work = self.basename().split('.') return work[-1] if len(work) > 1 else '' # ルート相対パス関連の個別関数 ------------ def ルート相対パス取得可能(self): if self.is相対パス(): return False return True def ルート相対パス(self): try: if self.is相対パス(): raise ValueError return urlparse(self.url).path except: print('ERROR:相対パスに対してルート相対パス情報取得',sys.stderr) sys.exit() def set_cwd(self, cwd=None): # 相対パスの場合、指定のカレントディレクトリを結合してルート相対パス情報を格納する。 try: if self.is相対パス(): if len(cwd) > 0 and cwd.startswith('/'): cwd = cwd if cwd.endswith('/') else cwd + '/' self.url = urljoin(cwd, urlparse(self.url).path) # 注:この使い方なら、os.path.joinでも同等の結果が得られる。 else: raise ValueError else: pass except: print('ERROR:不正なカレントディレクトリが指定',sys.stderr) sys.exit() # 判定関数 ------------ def is絶対パス(self): if self.url.startswith('http://') or self.url.startswith('https://') or self.url.startswith('//'): return True return False def isルート相対パス(self): if ( not self.url.startswith('//') ) and self.url.startswith('/'): return True return False def ルート相対パス_matches(self,prefixdir): #### if self.ルート相対パス().startswith(prefixdir): return True return False def is相対パス(self): if self.is絶対パス(): return False if self.isルート相対パス(): return False return True def is_cssfile(self): if self.extension() in self.CSS: return True return False def is_jsfile(self): if self.extension() in self.JS: return True return False def is_imgfile(self): if self.extension() in self.IMG: return True return False def is_htmlext(self): if self.extension() in ['html','htm','HTML','HTM']: return True return False # 変換した値を戻す関数 ------------ def change_scheme(self, to): if self.is絶対パス(): return re.sub('(https://|http://|//)',to,self.url) def change_netloc(self,to): # toはschemeから指定の文字列を指定する。また、ルート相対パス型であれば、実質「to」で指定のnetlocを追加するような動作とする。 return to + self.ルート相対パス() def 引数なしのメソッド走行確認(クラス, インスタンス): """ リフレクション系(Pythonにおいて(でなくても)適切な用語でないかも...)のメソッドを使って、一括で走行確認 """ import inspect メソッド一覧 = inspect.getmembers(クラス, inspect.isfunction) # ↑はこんな↓感じ #In [244]: inspect.getmembers(ResourcePath,inspect.isfunction) #Out[244]: [('__init__', <function __main__.x.__init__(self, path)>)] # 1要素めにメソッド名が入っている アンスコ始まりでないメソッド一覧 = [m for m in メソッド一覧 if not m[0].startswith('_')] # 2要素めに関数定義?が入っているので、parametersでパラメータ(シグネチャ)が取れる。inspect.signatureも参照) runlist = [m for m in アンスコ始まりでないメソッド一覧 if len(inspect.signature(m[1]).parameters) == 1] # selfしか引数に持たないもの for i in runlist: メソッド名 = i[0] # getattrでメソッドを実行して結果を確認。print関数で雑な出力。 print(インスタンス, '->', メソッド名, '->', getattr(インスタンス, メソッド名)()) a = [ 'https://example.com/a/b/c.html', 'http://example.com/a/b/c.html', '//example.com/a/b/c.html', '/a/b/c.html', '/a/b/', '/a/b', '/a/b/index.html' ] for i in a: x = ResourcePath(i) 引数なしのメソッド走行確認(ResourcePath,x) b = 'x/y.jpg' c = '/XXX/YYY/' x = ResourcePath(b) x.set_cwd(c) 引数なしのメソッド走行確認(ResourcePath,x) b_dash = 'x/y.jpg' x = ResourcePath(b) # こちらは途中でエラーになる(期待値どおり) 引数なしのメソッド走行確認(ResourcePath,x)
↓ 実行結果
https://example.com/a/b/c.html -> basename -> c.html https://example.com/a/b/c.html -> dirname -> /a/b https://example.com/a/b/c.html -> extension -> html https://example.com/a/b/c.html -> is_cssfile -> False https://example.com/a/b/c.html -> is_htmlext -> True https://example.com/a/b/c.html -> is_imgfile -> False https://example.com/a/b/c.html -> is_jsfile -> False https://example.com/a/b/c.html -> isルート相対パス -> False https://example.com/a/b/c.html -> is相対パス -> False https://example.com/a/b/c.html -> is絶対パス -> True https://example.com/a/b/c.html -> just_dirname -> b https://example.com/a/b/c.html -> netloc -> example.com https://example.com/a/b/c.html -> scheme -> https https://example.com/a/b/c.html -> ルート相対パス -> /a/b/c.html https://example.com/a/b/c.html -> ルート相対パス取得可能 -> True http://example.com/a/b/c.html -> basename -> c.html http://example.com/a/b/c.html -> dirname -> /a/b http://example.com/a/b/c.html -> extension -> html http://example.com/a/b/c.html -> is_cssfile -> False http://example.com/a/b/c.html -> is_htmlext -> True http://example.com/a/b/c.html -> is_imgfile -> False http://example.com/a/b/c.html -> is_jsfile -> False http://example.com/a/b/c.html -> isルート相対パス -> False http://example.com/a/b/c.html -> is相対パス -> False http://example.com/a/b/c.html -> is絶対パス -> True http://example.com/a/b/c.html -> just_dirname -> b http://example.com/a/b/c.html -> netloc -> example.com http://example.com/a/b/c.html -> scheme -> http http://example.com/a/b/c.html -> ルート相対パス -> /a/b/c.html http://example.com/a/b/c.html -> ルート相対パス取得可能 -> True //example.com/a/b/c.html -> basename -> c.html //example.com/a/b/c.html -> dirname -> /a/b //example.com/a/b/c.html -> extension -> html //example.com/a/b/c.html -> is_cssfile -> False //example.com/a/b/c.html -> is_htmlext -> True //example.com/a/b/c.html -> is_imgfile -> False //example.com/a/b/c.html -> is_jsfile -> False //example.com/a/b/c.html -> isルート相対パス -> False //example.com/a/b/c.html -> is相対パス -> False //example.com/a/b/c.html -> is絶対パス -> True //example.com/a/b/c.html -> just_dirname -> b //example.com/a/b/c.html -> netloc -> example.com //example.com/a/b/c.html -> scheme -> //example.com/a/b/c.html -> ルート相対パス -> /a/b/c.html //example.com/a/b/c.html -> ルート相対パス取得可能 -> True /a/b/c.html -> basename -> c.html /a/b/c.html -> dirname -> /a/b /a/b/c.html -> extension -> html /a/b/c.html -> is_cssfile -> False /a/b/c.html -> is_htmlext -> True /a/b/c.html -> is_imgfile -> False /a/b/c.html -> is_jsfile -> False /a/b/c.html -> isルート相対パス -> True /a/b/c.html -> is相対パス -> False /a/b/c.html -> is絶対パス -> False /a/b/c.html -> just_dirname -> b /a/b/c.html -> netloc -> /a/b/c.html -> scheme -> /a/b/c.html -> ルート相対パス -> /a/b/c.html /a/b/c.html -> ルート相対パス取得可能 -> True /a/b/ -> basename -> /a/b/ -> dirname -> /a/b /a/b/ -> extension -> /a/b/ -> is_cssfile -> False /a/b/ -> is_htmlext -> False /a/b/ -> is_imgfile -> False /a/b/ -> is_jsfile -> False /a/b/ -> isルート相対パス -> True /a/b/ -> is相対パス -> False /a/b/ -> is絶対パス -> False /a/b/ -> just_dirname -> b /a/b/ -> netloc -> /a/b/ -> scheme -> /a/b/ -> ルート相対パス -> /a/b/ /a/b/ -> ルート相対パス取得可能 -> True /a/b -> basename -> b /a/b -> dirname -> /a /a/b -> extension -> /a/b -> is_cssfile -> False /a/b -> is_htmlext -> False /a/b -> is_imgfile -> False /a/b -> is_jsfile -> False /a/b -> isルート相対パス -> True /a/b -> is相対パス -> False /a/b -> is絶対パス -> False /a/b -> just_dirname -> a /a/b -> netloc -> /a/b -> scheme -> /a/b -> ルート相対パス -> /a/b /a/b -> ルート相対パス取得可能 -> True /a/b/index.html -> basename -> index.html /a/b/index.html -> dirname -> /a/b /a/b/index.html -> extension -> html /a/b/index.html -> is_cssfile -> False /a/b/index.html -> is_htmlext -> True /a/b/index.html -> is_imgfile -> False /a/b/index.html -> is_jsfile -> False /a/b/index.html -> isルート相対パス -> True /a/b/index.html -> is相対パス -> False /a/b/index.html -> is絶対パス -> False /a/b/index.html -> just_dirname -> b /a/b/index.html -> netloc -> /a/b/index.html -> scheme -> /a/b/index.html -> ルート相対パス -> /a/b/index.html /a/b/index.html -> ルート相対パス取得可能 -> True /XXX/YYY/x/y.jpg -> basename -> y.jpg /XXX/YYY/x/y.jpg -> dirname -> /XXX/YYY/x /XXX/YYY/x/y.jpg -> extension -> jpg /XXX/YYY/x/y.jpg -> is_cssfile -> False /XXX/YYY/x/y.jpg -> is_htmlext -> False /XXX/YYY/x/y.jpg -> is_imgfile -> True /XXX/YYY/x/y.jpg -> is_jsfile -> False /XXX/YYY/x/y.jpg -> isルート相対パス -> True /XXX/YYY/x/y.jpg -> is相対パス -> False /XXX/YYY/x/y.jpg -> is絶対パス -> False /XXX/YYY/x/y.jpg -> just_dirname -> x /XXX/YYY/x/y.jpg -> netloc -> /XXX/YYY/x/y.jpg -> scheme -> /XXX/YYY/x/y.jpg -> ルート相対パス -> /XXX/YYY/x/y.jpg /XXX/YYY/x/y.jpg -> ルート相対パス取得可能 -> True x/y.jpg -> basename -> y.jpg x/y.jpg -> dirname -> x x/y.jpg -> extension -> jpg x/y.jpg -> is_cssfile -> False x/y.jpg -> is_htmlext -> False x/y.jpg -> is_imgfile -> True x/y.jpg -> is_jsfile -> False x/y.jpg -> isルート相対パス -> False x/y.jpg -> is相対パス -> True x/y.jpg -> is絶対パス -> False x/y.jpg -> just_dirname -> x x/y.jpg -> netloc -> x/y.jpg -> scheme -> ERROR:相対パスに対してルート相対パス情報取得 <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>