Programming」カテゴリーアーカイブ

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 添付画像の差し替え。

CRLF

何を今頃だけど JavaScript の replace について。

String.prototype.replace() – JavaScript | MDN

第一引数を正規表現にすればすべてのマッチした箇所を置換できるのか。
最初に一致した箇所だけだとずっと勘違いしていた筆者であった。

JXA: doShellScript Line feed code | Paepoi Blog

これを試しに書き換えしてみよう。

#!/usr/bin/osascript

let app = Application.currentApplication();
app.includeStandardAdditions = true;

res = app.doShellScript('ls -l');
console.log(res.replace(/\r/g, '\n'));

いけた。

ついでに。
よく考えたらコマンドの中で tr を使って変換すりゃいいじゃん。
と思ってやってみたら上手くいかなかった。

#!/usr/bin/osascript

let app = Application.currentApplication();
app.includeStandardAdditions = true;

// no...
//res = app.doShellScript('ls -l | tr "\r" "\n"');

// test
res = app.doShellScript('ls -l | tr "\n" "|"');
console.log(res);

となる。

つまりコマンド実行の時点では改行コードは LF のまま。
doShellScript が値を戻す時に CR へ変換しているようです。
LF のまま戻すオプションって無いのかな?

lf

#!/usr/bin/osascript

let app = Application.currentApplication();
app.includeStandardAdditions = true;

res = app.doShellScript('ls -l', {alteringLineEndings:false});
console.log(res);

あった、今後はコレで。

ついでに、doShellScript は bash の POSIX 互換モードです。
zsh やフル状態の bash ではないので注意、macOS って本当に色々面倒臭い。
プログラミングするならやっぱり Linux だよ、某サル専用を除く。

関係ないけど Macbook Air 2020 が出たね、かなり良さげ。
でも筆者はこんな使い方ばかりだから 2018 モデルで何の不満も無いのよね。
ペチペチキーボードにもすっかり慣れてしまったし、うるさいけど。
さて 10.15.4 にアップデートするか。

echo hyphen

ポータブルな echo 代替、reverb コマンド – 拡張 POSIX シェルスクリプト Advent Calendar 2013 – ダメ出し Blog

そうか、echo はオプションとまったく同じ文字列を出力できなかったのか。
でもそんなの — を使えばいいんでね?

先頭にハイフンが付くファイルを削除できない – ITmedia エンタープライズ

echo

だめジャン、これ echo には通用しないのか。
先頭にハイフンがあるだけなら普通に表示できるし関係なかったか。
でも何か手段が他にあるはず、と思って探してみた。

Bashで文字列をエスケープをする – Qiita

printf %q “$value”
こんな手段があるとは。

printf

イケた、オプションと同じ文字列は case 文で振り分けすれば使えそう。
って zsh だと \n を入れなくても強制改行してしまうんだね。
そんなことより gnome-terminal で zsh を使うと Home/End キーでカーソル移動ができないことのほうが気になるぞ!
こんな所でも bash と zsh の違いがあるのがなんとも。

macOS Get UTI

Apple 関連で開発をしていると UTI を調べる必要がある場合が多々ある。

Uniform Type Identifier – Wikipedia

Uniform Type Identifier Concepts

検索をしていたら素敵なページを見つけた。

Mac や iOS でファイルの種類を表す識別子 Uniform Type Identifiers を拡張子から調べる(Swiftで1行で出来る) – niwatakoのはてなブログ

てゆーか JXA でも PyObjC でもできる。
しかし困ったことに JXA では CFString が NSString にキャストできない。

objective c – JXA: Accessing CFString constants from CoreServices – Stack Overflow

上記を見つけてようやく解決。
console.log って C 言語の char[] を出力できる、初めて知った。
UTF16LE に変換は不要、CJK 文字列でも問題ないようです。

#!/usr/bin/osascript -l JavaScript

let jp = $('スズキ GSX250R').UTF8String;
console.log(jp);
//=> スズキ GSX250R

// ex: ft=js.jxa

ということで JXA にて簡単に調べるコマンドを作ってみる。

#!/usr/bin/osascript -l JavaScript

ObjC.import('CoreServices');

function run(argv) {
    for (let ext of argv) {
        let uti = $.UTTypeCreatePreferredIdentifierForTag(
            $.kUTTagClassFilenameExtension, $(ext), null);
        let s = $.CFStringGetCStringPtr(uti, 0);
        console.log(`${ext}: ${s}`);
    }
}

// ex: ft=js.jxa

getuti.js

せっかくなので基底タイプも調べたいぞ。
JXA で得る方法が解らなかったので PyObjC で書いてみる。
PyObjC は CFDictionary や CFString も Python の型と等価なので超簡単。
他の言語を使うのが馬鹿馬鹿しくなってしまうので注意が必要。

#!/usr/bin/env python3

import sys, CoreServices

for ext in sys.argv[1:]:
    uti = CoreServices.UTTypeCreatePreferredIdentifierForTag(
        CoreServices.kUTTagClassFilenameExtension, ext, None)
    arr = CoreServices.UTTypeCopyDeclaration(uti)['UTTypeConformsTo']
    con = ','.join(arr)
    print(f'{ext}: {uti} [{con}]')

# ex: ft=py

@PyObjC

getuti という拡張子の無い名前で +x のパーミッションを付けパスの通った場所へ。

getuti

JXA でやりたかったけどまだまだ修行が足りない。

注意: macOS 13 Ventura からは上記が使えません。
macOS: Get UTI (Ventura) | Paepoi Blog

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 が凄いだけなんだろうけど。