0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

先生ぇー!Pythonista3 のディレクトリ探索にwebbrowser モジュールは使えますかー? 〜 使えます 〜

Last updated at Posted at 2022-12-10

この記事は、Pythonista3 Advent Calendar 2022 の11日目の記事です。

一方的な偏った目線で、Pythonista3 を紹介していきます。

ほぼ毎日iPhone(Pythonista3)で、コーディングをしている者です。よろしくお願いします。

以下、私の2022年12月時点の環境です。

sysInfo.log
--- SYSTEM INFORMATION ---
* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.1.1, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0) 828.0 x 1792.0 @ 2.0

他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。

ちなみに、model iPhone12,1 は、iPhone11 です。

探訪に最適な探訪ツールを使う(作る)

漫画ブラック・ジャックにて、ブラック・ジャックが自分自身でエキノコックスを抽出した開腹手術のエピソードがありました。

我々もPythonista3 で、Pythonista3 の中身を覗いてみましょう!!(?)

また、ブラック・ジャックですら自身を見るための鏡やメスなどを使っていたので、我々もprint 確認だけでは、辛い部分があるので、佳きツールを呼び出します。

注意事項

免責事項として書いています。

アプリやOS 内部構造を操作することになります。

変更や削除で、アプリやOS が起動不可になることもありえます。

不具合に関しては自己責任でお願い致します。

webbrowser モジュール

21.1. webbrowser — Convenient Web-browser controller — Python 3.6.1 documentation

標準的な使用方法については割愛しますが、Pythonista3 用にwebbrowser モジュールはカスタムされています。

そのカスタムされている機能をつかいます。

pythonista-url | App Extensions and Shortcuts — Python 3.6.1 documentation

挙動確認

from pathlib import Path
import webbrowser

origin_path = Path()

url = 'pythonista3://' + str(origin_path)

webbrowser.open(url)

img221127_174434.gif

なんということでしょう!

ディレクトリがGUI で表示されています。

GUI で見るScriptLibrary のようにも見えますが、前回探訪した際と同様に、GUI のScriptLibrary では確認できなかったフォルダも確認できます。

webbrowser.openurlpythonista3:// をつけることで、GUI でよびだせるようになるのです。

img221127_175134.png

左上の< で、戻ることもできます。

が、挙動が幾分バギーなので私はアプリを一度落として再起動させています。

img221127_174633.gif

もっと探訪を

GUI で確認できることにより、ディレクトリの行き来が容易になりました。

階層を指定できるようにすれば、さらに素敵ですね:

from pathlib import Path
import webbrowser


def parent_level(level_int):
  return '../' * level_int


origin_path = Path()

level = 2
target_url = origin_path / parent_level(level)

webbrowser.open(f'pythonista3://{target_url}')

level で上の階層を指定できるようにしました。

  • 0: Documents
  • 1: Pythonista3
  • 2: IDそれぞれ-違う-英数字

と、登っていけます。

しかし、

  • 3: AppGroup

より上から、内容が見えなくなります。

img221127_232605.png

ディレクトリの内容を読み取れませんでした。
このディレクトリを表示する権限がない可能性があります(例:別のアプリに属しているため)。

iOS 側の権限でアクセスできない場所もあるのです。

とこが、

  • 8: private
  • 9: (root)

は、アクセスが可能になっています。面白いですね。

9 がroot となるので、それ以降の数値を入れても9 と同じ結果となります。

補足

webbrowser.open で使用しているorigin_path = Path() は、「現在のpath」を示しているのではありません。

PosixPath. を便宜的に呼び出ししています。

img221127_234635.png

つまり、以下コードと呼び出しは同意義となります:

import webbrowser

webbrowser.open('pythonista3://.')

pythonista3://. は、Documents 直下となります。

階層指定:

import webbrowser


def parent_level(level_int):
  return '../' * level_int


origin_path = './'

level = 9
target_url = origin_path + parent_level(level)

webbrowser.open(f'pythonista3://{target_url}')

pathlib でのメリットとしては、print 時に.resolve() すれば絶対パスが確認できるところでしょうか。

from pathlib import Path
import webbrowser


def parent_level(level_int):
  return '../' * level_int


origin_path = Path()

level = 2
target_url = origin_path / parent_level(level)
resolve_path = target_url.resolve()

#webbrowser.open(f'pythonista3://{target_url}')

img221127_235939.png

階層の整理を

一度ディレクトリ階層の整理をましょう。アクセス不可だったフォルダもあるので、網羅的に確認します。

🙆‍♂️(9: root
  └ 🙆‍♂️(8: private
   └ 🙅‍♀️(7: var
    └ 🙅‍♀️(6: mobile
     └ 🙅‍♀️(5: Containers
      └ 🙅‍♀️(4: Shared
       └ 🙅‍♀️(3: AppGroup
        └ 🙆‍♂️(2: それぞれ-違う-英数字
         └ 🙆‍♂️(1: Pythonista3
          └ 🙆‍♂️(0: Documents

アクセス可能が🙆‍♂️ で、不可を🙅‍♀️ としています。

表示用としてのコードは:

from pathlib import Path


def parent_level(level_int):
  return '../' * level_int


def is_access(index):
  _level = index + 1
  _path = cwd_path / parent_level(deep - _level)
  return True if list(_path.glob('*')) else False


cwd_path = Path().cwd()

directly_list = str(cwd_path).split('/')
deep = len(directly_list)

for n, d in enumerate(directly_list):
  access = '🙆‍♂️' if is_access(n) else '🙅‍♀️'
  if not d:
    d = 'root'
  output = f'{access}({deep - n -1}: {d}'
  if n:
    tab = ' ' * n
    print(f'{tab}{output}')
  else:
    print(f'{output}')

このような構成になっているのですね。普段意識することがなくとも内部が見れるのは面白いですね。

え、待って。え、他にもパスが見えたんだけど?

ディレクトリ構造を意識して見ていると、普段見ている情報でも、気になる情報が出てきます。

先ほどの絶対パスを確認したinspector 情報です

img221127_235939.png

絶対パスに気を取られていましたが、webbrowservar/ とかありますね😎

img221128_174536.png

詳細を見てみると、、、

img221128_174941.png

/var/containers/Bundle/Application/それぞれ-違う-英数字/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/webbrowser.py

これは、探索しに行かなければならないですねぇ😎

(ちなみに)var フォルダについて

いままでアクセスしていたvar と、webbrowser から見つけたvar はフォルダ名は同じですが、違う階層にあるものです。

/(root)
  ├ .ba
  ├ .file
  ├ .mb
  ├ Applications
  ├ Developer
  ├ Library
  ├ System
  ├ bin
  ├ cores
  ├ dev
  ├ etc
  ├ private
    ├ etc
    ├ preboot
    ├ system_data
    ├ var          ← 今までの「var」
    └ xarts
  ├ sbin
  ├ tmp
  ├ usr
  └ var            ← これから探索する「var」

「あれ?どこの何をアクセスしようとしてたんだっけ?」という時に参照ください。

Pythonista3.app ディレクトリへ行く

探索ルートとしては

  • .: Documents (便宜上のスタート位置)
    • (root)/private/var/mobile/Containers/Shared/AppGroup/それぞれ-違う-英数字1/Pythonista3/Documents
      • (root) は私が示しやすいように、追記してます
  • (root) まで、階層を登る
  • (root)/var/containers/Bundle/Application/それぞれ-違う-英数字2/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/webbrowser.py を目指し降りてゆく
    • それぞれ-違う-英数字2 は、それぞれ-違う-英数字1 とは違う値
  • Pythonista3.app で止まる
  • webbrowser.open で覗き見る

そのコードが:

from pathlib import Path
import webbrowser


def parent_level(level_int):
  return '../' * level_int


# todo: 今回行きたいディレクトリパスを取得する処理
wbb_file_path = webbrowser.__file__  # webbrowser モジュールのファイルパス
wbb_path_list = wbb_file_path.split('/')  # インデックスでアクセスできるように、ファイルパスをlist化
app_index = wbb_path_list.index(
  'Pythonista3.app')  # webbrowser 格納先の手前のPythonista3.app のインデックスを取得
app_path_list = wbb_path_list[:app_index + 1]  # Pythonista3.app 以降の情報を排除
app_file_path = '.' + '/'.join(app_path_list)  # Pythonista3.app までのディレクトリパスを作成

# todo: ここから、いつもの処理
origin_path = Path()
level = 9

hub_path = origin_path / parent_level(level)

target_path = hub_path / app_file_path

webbrowser.open(f'pythonista3:/{target_path}')

resolve_origin_path = origin_path.resolve()
resolve_target_path = target_path.resolve()


img221128_223837.gif

img221128_223922.gif

ほぼ、変数を外出ししているので、inspector より値確認ができます。

試しに、スタート位置のパスと、ゴール位置のパスを絶対パスとしているので確認してみてください。

resolve_origin_path = origin_path.resolve()
resolve_target_path = target_path.resolve()

img221128_223538.png

アプリケーションの情報が入ってる

Documents 内のsite-packages は、我々が追加できるモジュールが格納されるディレクトリでした。Pythonista3.app 内には、標準モジュールやPythonista3 アプリで事前に入っているモジュールが格納されていましたね。

Pythonista3.app は、Pythonista3/Documents/Pythonista3 ではありません。わかり辛いですが、、、

直下には、Pythonista3 のアイコン画像(それの色違い)やフォントなとなど様々なファイルがありました

他にも、画像や音のファイルが格納されているMedia:

/private/var/containers/Bundle/Application/それぞれ-違う-英数字2/Pythonista3.app/Media/

Theme のjson ファイルが格納されているフォルダまで:

/private/var/containers/Bundle/Application/それぞれ-違う-英数字2/Pythonista3.app/Media/

Pythonista3 アプリを触っていればいるだけ「こんな所に、こんなファイルがあるのか」と、探訪が止まりませんね。

冒頭にもお伝えしましたが、これらのファイルの扱いは丁重におこなってくださいね。

何をどうしたら動かなくなる。など、判断がつかないものは自己責任でお願いします。

次回は

自分(Pythonista3)で自分の中身(Pythonista3)を見るなんて、なかなかスリリングな探訪でしたね。

今回は闇雲に、手当たり次第・見つけ次第で確認行きましたが、以下のようにiOS, iPadOS のベースの部分を理解すると目的に合わせた探し方もできるかもしれません。

iOSアプリケーションのフォルダ構成|【Swift】ARシューティングアプリを作ってみよう!|Techpit

今こそ復習したい、iOSアプリのディレクトリ構成 - Qiita

私自身はiOS アプリ開発の経験もないので、Pythonista3 をいじりながら「なるほどねー」と雰囲気で把握している感じです。ですので、理解違いで説明が違っている所が多いかもしれません。

また「ディレクトリ」「フォルダ」の記載ゆらぎが多々あったかと思われます。こちらも雰囲気で書いており重ねてお詫び申し上げます。。。

次回は、iOS 自体の探訪を中心に紹介したいと思います。

ここまで、読んでいただきありがとうございました。

せんでん

Discord

Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。

書籍

iPhone/iPad でプログラミングする最強の本。

その他

  • サンプルコード

Pythonista3 Advent Calendar 2022 でのコードをまとめているリポジトリがあります。

コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー

  • Twitter

なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー

  • GitHub

基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?