Python」タグアーカイブ

M1 Mac Setting

macOS のサンドボックスってこんなにウザかったっけ?

sandbox1

端末アクセスごときにチマチマ許可を与えるのが面倒すぎる。
とっとと端末のサンドボックス設定をフルディスクアクセスに変えよう。
zsh という意味ではないです、Terminal.app のみに適用される。

full_disk

それと、こんなのを見つけたので前回の PyObjC を試す。

Macで実行しているアプリがIntel x86_64かApple Silicon arm64アーキテクチャかをメニューバーに表示してくれるオープンソースのユーティリティ「Silicon Info」がリリース。 | AAPL Ch.

端末から Python3 に渡して起動すると arm64 になる。
Atom から command+option+T で起動する端末は arm64 になる。
Atom から atom-runner でプロセス起動すると Intel になる。

Rosetta2 で起動した Python3 でも PyObjC は使えたんだね。
まあ Atom が arm64 になるまで端末利用かなって。

後は apache だな。
どうせまたアップデート毎に httpd.conf が初期化されるので。

#!/usr/bin/env python3

'''
    # Run on root
    sudo python3 httpd_rewrite.py
    #
    # start or restart
    sudo apachectl restart
    #
    # Auto start of service
    sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
'''

import shutil

CONF = '/etc/apache2/httpd.conf'
out = []
# User Rewright
ROOT = 'DocumentRoot "/Users/sasakima-nao/www"\n'
DIRC = '<Directory "/Users/sasakima-nao/www">\n'

shutil.copyfile(CONF, 'backup_httpd.conf')

with open(CONF, 'r') as f:
    for line in f:
        if 'php7_module' in line:
            out.append(line[1:])
        elif line.startswith('DocumentRoot'):
            out.append(ROOT)
        elif '<Directory "/Library/WebServer/Documents">\n' == line:
            out.append(DIRC)
        else:
            out.append(line)

with open(CONF, 'w') as f:
    for s in out:
        f.write(s)

以前 JXA で作ったけど手抜きだったので Python で描き直し。
これで同じ LAN に繋がった iPhone 等から
http://コンピュータ名.local
でアクセスできる、てか今までと変わっていなかった。

http

Mac の人は面倒なのに vim で書き換えしている人ばかりなのは何故?
スクリプトを作っておけば初期化されても一発で書き換えできるのに。
Python とかのスクリプト言語ってこういうことをする為にある。

これで 2018 Air の頃と同じように使えるようになった。
後は Lightroom か、arm64 版も出たみたいだし。
その前にサブスクについてもう少し調べなきゃだが。

macOS tree command

前回 CommandLineTools 以下に「SDK 丸ごと落とされる」と書いた。
情報として何という名のファイルが「ドコにドンダケ」ってほど入ってしまうのかを公開したい。
tree というまさしくソレ用みたいなコマンドがある。

あぁ、このコマンドって使い道があったんだなぁって思った筆者であった。
けれど macOS には tree コマンドが入っていなかった。

Fedora なら使えるのでリモート接続してリダイレクトしようかなと。
てなわけで、sftp でリモート接続した Fedora から tree コマンドを打ってみた。
けど、何時間たっても処理が戻ってこなくて諦めた。
こんなに遅いはずがない、サンドボックスかな?

普通に「macos tree コマンド」と検索するとションボリ。
何故みんなコンパイラを手に入れたのにインストールしかしないの?
brew 使う人って(以下略
てか Catarina 以降だと Gatekeeper にブロックされると思うんだけーが。

Catalina時代の「GateKeeper」と付き合う方法 – 新・OS X ハッキング!(257) | マイナビニュース

spctl は Windows でいうウイルス対策アプリを無効にする手段。
慎重に活用とかアホかと、その必要があるアプリは淘汰されるべき。

で。

実はそんなことをしなくても検疫機能を回避する方法がある。
自分でビルドすればいい、もしくは自分でスクリプトを書く。

tree コマンドが無い環境で tree コマンドを実現 – Qiita

シェルスクリプトだけでがんばった人がいた。
これでもいいけど、よし筆者は Python でがんばってみよう。
macOS で動かせるように標準モジュールだけを使う。

#!/usr/bin/env python3

'''
    tree command in Python 3.7
'''

import os, sys

all_dir_num = 0
all_file_num = 0

def flist(path):
    '''
        Exclude UNIX hidden files. and sorted.
    '''
    l = os.listdir(path)
    res = []
    for f in l:
        if f.startswith('.'): continue
        if f.endswith('~'): continue
        res.append(f)
    return sorted(res)

def tree(path, tab):
    files = flist(path)
    length = len(files)
    head = '├── '
    num = 1
    for f in files:
        if num == length:
            head = '└── '
        subdir = os.path.join(path, f)
        if os.path.isdir(subdir):
            if sys.stdout.isatty():
                # stdout
                print(f'{tab}{head}\033[34m{f}\033[0m')
            else:
                # Redirect
                print(f'{tab}{head}{f}')
            global all_dir_num
            all_dir_num += 1
            # Recursion
            tree(subdir, f'{tab}│   ')
        else:
            print(f'{tab}{head}{f}')
            global all_file_num
            all_file_num += 1
        num += 1

if __name__=='__main__':
    d = '.'
    if len(sys.argv) > 1:
        d = sys.argv[1]
        if d.startswith('~'):
            d = os.path.expanduser(d)
    print(d)
    tree(d, '')
    # footer
    print(f'\n{all_dir_num} directories, {all_file_num} file')

tree

stdout で色を付けるとリダイレクトでアララとなるから振り分けしてね。
os.listdir はカレントディレクトリならドットでいいのか、へー。

中身が一つだけの時に前の縦線を消すナイスな方法は思いつかなかった。
その中身がディレクトリで更に中身が一つだった場合等でチグハグになる。
それとディレクトリを青色にしたけど tree コマンドとなんか色が違う。
とはいえ完全に同じにする必要は無いんだしコレでいいかなと。

コレを mac で自分がパスを通した場所にコピーして。
tree という拡張子の無い名前を付け +x パーミッションを付けて。

tree /Library/Developer/CommandLineTools > cltool.txt

したものを置いておきます。
十七万五千行、17MB になってしまったので zip 圧縮した。
cltool.zip

python3 は先に自分で入れたものなのかコレに含まれているのかは解らない。
まあ微々たる差だ。
それより macOS だと場所によってはサンドボックスに引っ掛るのが困る。

mp4 mov rotate

スマホ動画の回転情報を得て我が Y901x にて正しい回転表示をさせる。
と随分前に書いてずっとサボッていたけどいいかげんに本気出そうと思う。
検索ワードを mov バイナリ 回転 とか mov に決め打ちして探したら。

バイナリエディタで .mov を回転 ? マキシマ文庫

おぉ多分コレだ!
回転情報書き換えなら Mac の QuickTime だけで簡単にできるのは置いておいて。
そこのバイナリを取得すれば回転情報は得られるってことね。

バイナリ取得や書き換えなら C や Python 等で簡単にできるのに。
てか既に沢山ありそう、参考用に探してみよう。

GitHub – danielgtaylor/qtrotate: Tools for handling rotated Quicktime/MP4 files

ズバリをあっさり見つけた。
mdat や trac アトムも取得しているし他にもやることあるみたい。
mov の構造は下記に、ソースコメントのリンク先はもう古いので。

Movie Atoms

試すならサポート終了の Python2 コードなので Python3 に書き換えてね。
print に括弧を付加と割り算を二重スラッシュにするのは毎度のこととして。
atom 比較の所の文字列には全部 b を付けてバイナリ比較に変更するのを忘れずに。
よく解らない人用に筆者が書き換えしたものを一応置いておきます。

qtrotate_py3.zip

どうやら mov と mp4 は同じ回転情報を使うようで mp4 にも適用できる。
とりあえず筆者が iPhone で撮影したり編集した動画は回転情報が得られた。

qtrotate

書き換えも問題無く可能だった。
Nautilus のサムネイルには即時繁栄されないので一旦コピペ。
ただ手持ち mp4 の中には正しくない情報が出るものもあった。
ここらをもう少し調べて Y901x に適用し。。。。。

しまった、Y901x は Gjs で作っていたんだった!
JavaScript はバイナリ編集なんてできないぞ。
GLib を使っても多分 Uint8Array に変換されて劇遅になるだけだ。

ClutterImage PyGObject/Gjs | Paepoi Blog

本気で困った。
Comipoli 同様コイツも PyGObject に書き換えることになりそう。

Python3 socket

Python からの socket アクセス。
http だけなら urllib.request で充分なんですけど。
ssh 等の通信にも使えるようなので使い方は勉強しておこう。

#!/usr/bin/env python3

'''
    http:// version
'''

import socket

HOST = 'palepoli.skr.jp'
FILE = '/suzuki/katana.html'
PORT = 80 # http

output = []

#with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
with socket.socket() as sock:
    sock.connect((HOST, PORT))
    msg = f'GET {FILE} HTTP/1.1\r\nHost: {HOST}\r\nConnection: close\r\n\r\n'
    s.sendall(msg.encode('utf-8'))
    while True:
        res = ssock.recv(4096)
        if not res: break
        output.append(res.decode())

print(''.join(output))

よく見かけるコードを fstring で今っぽく。
AF_INET 等は python3.7 ではデフォルト引数で指定されているので不要。
コピペだけで動くようにファイルは筆者のこのサーバーに置いています。

でもコレでは https でアクセスできないことに気が付いた。
https のポートを調べると 443 番だ、そりゃ 80 番では弾かれる。
でもポートを変更しただけでは駄目、ssl モジュールを使う必要あり。

#!/usr/bin/env python3

'''
    https:// version
'''

import socket, ssl

HOST = 'palepoli.skr.jp'
FILE = '/suzuki/v-strom.html'
PORT = 443 # https

output = []

with socket.socket() as sock:
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    with context.wrap_socket(sock, server_hostname=HOST) as ssock:
        ssock.connect((HOST, PORT))
        msg = f'GET {FILE} HTTP/1.1\r\nHost: {HOST}\r\nConnection: close\r\n\r\n'
        ssock.send(msg.encode('utf-8'))
        while True:
            res = ssock.recv(4096)
            if not res: break
            output.append(res.decode())

print(''.join(output))

できた。

ところで実は GNOME ならこんなことができる。

#!/usr/bin/env python3

'''
    GNOME only
'''
 
from gi.repository import Gio
 
f = Gio.file_new_for_uri("https://palepoli.skr.jp/suzuki/burgman.html")
ok, contents, etag_out = f.load_contents()
if ok:
    print(contents.decode());
else:
    print('File not Found')

Gio は Gvfs という仮想ファイルでアクセス可能な URI なら全部扱える。
GNOME 標準アプリは全部 Gio アクセスなので以下みたいなことも。

sasikae

sftp でアクセスしてリモート編集が可能だったりする。
解りやすいようにコマンドで書いたけど Nautilus から sftp でアクセスして W クリックすればコレと同じことになる。
自分でアプリを作る時も Gio を使うだけ、これが GNOME の魅力。
SNS くらいしか使っていないショボイ人だとこの魅力は解らないだろうな。

追記
2020.04.22 添付画像の差し替え。

GExiv2 and subprocess

前回の GExiv2 の件。
comipoli に実装しようとしてハマったので覚書。

GSubprocess から GUnixInputStream を得て GExiv2 に読ませる。
その GUnixInputStream からは GdkPixbuf を作ることができない。
順番を逆にしても駄目、Stream の再利用はできないみたい。

他の手段で GExiv2 を使おうと思ったけど上手くいかない。
他の手段で GdkPixbuf を得ることは無理。
exif 取得と画像取得で別にアクセスするしかないようだ。
遅くなるだろうけど気にならないレベルならいいかなって。

しかし GSubprocess をもう一つ使をうとするもエラー。
原因は解らない、GSubprocess の情報なんて皆無だ。

失敗をズラズラ書いても無意味なので結論。
GSubprocess と subprocess を両方使う強行手段でなんとかなった。

class ComipoliArchive:
    def __init__(self):
    	# etc...

    def _zip_escape(self, filename):
        ESCAPE = '[]*?!^-\\'
        res = ''
        for s in filename:
            if s in ESCAPE:
                res += '\\'
            res += s
        return res

    def _on_wait(self, proc, res):
        proc.wait_check_finish(res)

    def __getitem__(self, num):
        if num == self.max:
            raise IndexError
        try:
            args = ['unzip', '-pj', self.path, self._zip_escape(self.namelist[num])]
            # tag
            ori = 0
            pr = subprocess.Popen(args, encoding='UTF-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            fd = pr.stdout.fileno()
            with open(fd, 'rb') as f:
                metadata = GExiv2.Metadata()
                metadata.open_buf(f.read())
                ori = metadata.get_orientation()
            pr.wait()
            # pixbuf
            sp = Gio.Subprocess.new(args, Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_MERGE)
            sp.wait_check_async(None, self._on_wait)
            stream = sp.get_stdout_pipe()
            p = GdkPixbuf.Pixbuf.new_from_stream(stream)
            if ori == GExiv2.Orientation.HFLIP:
                p = p.flip(True)
            elif ori == GExiv2.Orientation.ROT_180:
                p = p.rotate_simple(GdkPixbuf.PixbufRotation.UPSIDEDOWN)
            elif ori == GExiv2.Orientation.VFLIP:
                p = p.flip(False)
            elif ori == GExiv2.Orientation.ROT_90_HFLIP:
                p = p.rotate_simple(GdkPixbuf.PixbufRotation.CLOCKWISE).flip(True)
            elif ori == GExiv2.Orientation.ROT_90:
                p = p.rotate_simple(GdkPixbuf.PixbufRotation.CLOCKWISE)
            elif ori == GExiv2.Orientation.ROT_90_VFLIP:
                p = p.rotate_simple(GdkPixbuf.PixbufRotation.CLOCKWISE).flip(False)
            elif ori == GExiv2.Orientation.ROT_270:
                p = p.rotate_simple(GdkPixbuf.PixbufRotation.COUNTERCLOCKWISE)
            stream.close()
            sp.force_exit()
            return p
        except Exception as e:
            raise e

前回の cbz を開いてみる。

orientation

よし回転されているな。
注意点は ROT_90 は 90 度傾いているから 270 度回転させるということ。
戻す度数という意味ではない、flip は手持ちサンプルが無いのでこれで正しいか未確認。
後 Popen は wait を忘れずに。

Scaling: GDK-PixBuf Reference Manual

展開速度も別に気にならない、てか何か変わったか?のレベル。
多分 GExiv2 が凄いだけなんだろうけど。