Python」タグアーカイブ

PyObjC がトンデモなく遅い

今日は久しぶりに PyObjC で小物を作ろうとした。
いやまあ macOS プログラミングは完全に放置気味でしたね。
ということでまずアップグレードを行なってと。

# pip Upgrade
pip3 install --upgrade pip

# PyObjC Install or Upgrade
pip3 install -U pyobjc

で本題、PyObjC コードの初期化がありえないほど遅い。
NSWindow を作ったら表示されるまで十秒くらい待たされる。
Python 標準モジュールのみのコードなら普通に一瞬だ。
しばらく色々試したらこういうことだった。

#!/usr/bin/env python3

'''
    app.old.py
'''

import time
start = time.time()

from AppKit import *
import objc

class AppMenu(NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # command+Q で閉じるメニュー
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

# NSApp を作る
NSApplication.sharedApplication()
# command+Q で終了するメニューを入れる
NSApp.setMainMenu_(AppMenu.new())
# コレをしないと最前面に出てこない
NSApp.activateIgnoringOtherApps_(True)
# メインループを回さない
#NSApp.run()

# 経過時間 ms
print(f'{time.time() - start} ms')

from を使う今までやっていたコード。

#!/usr/bin/env python3

'''
    aoo.new.py
'''

import time
start = time.time()

import AppKit, objc

class AppMenu(AppKit.NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = AppKit.NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = AppKit.NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # command+Q で閉じるメニュー
        item_quit = AppKit.NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

# NSApp を作る
AppKit.NSApplication.sharedApplication()
# command+Q で終了するメニューを入れる
AppKit.NSApp.setMainMenu_(AppMenu.new())
# コレをしないと最前面に出てこない
AppKit.NSApp.activateIgnoringOtherApps_(True)
# メインループを回さないさない
#AppKit.NSApp.run()

# 経過時間 ms
print(f'{time.time() - start} ms')

都度プリフィクスを書く面倒くさいコード。

PyObjC

from で * を使うとトンデモネェ遅さになってしまった。
以前は from でも同じような速度で初期化されていたんですが。
NSApp だけでコレ、NSWindow まで作るとすげぇ悲惨。

Python 3.11 からの高速化の弊害なのかな。
from は全部辿ってキャッシュとかになっていたらまあこうなるよな。
いや PyObjC 側の不具合かもしれないけどプリフィクス化で解決するし。
Cocoa のメソッド名長いんだよな、GTK+ みたくできないのかと。
アスタリスクを使っている人は調べてみたほうがいいかも。

ついでに、objc は import しなくてもよかったのに必須になった。
activateIgnoringOtherApps が動作しない、何故?
ちょっと放置しすぎたな、もう少し調べよう。
秋の渡り鳥が来る前にやらないとまた放置しそうだし。

macOS Python3 Upgrade

macOS に入れた Python3 がいまだに 3.10 のままだった。
Fedora みたいに実用にはさほど使っていないしそんなもんだ。
まあ 3.12 にアップグレードしておこう。

Python Release Python 3.12.2 | Python.org

macOS 用はユニバーサルバイナリのみになったのね。
brew だかを使えばもっと簡単アップグレードなんだろうけど。
それ以外に使い道がないアプリをわざわざ入れるのもなぁ。
てか php を macOS に入れたい人専用だよね。

とにかく pkg を展開して上書きインストール。

open /Library/Frameworks/Python.framework/Versions

dir

古いパッケージは消してくれないんかい。
手動で削除、認証が必要だった。

zprofile

$HOME/.zprofile もパス通しが残っている。
自分で消す、令和の macOS なのに何もかも手動だなぁ。

pipのアップデート方法メモ(pip3コマンドは古いので使っちゃダメ)2023/2/10 – min117の日記

しらない間に pip がこんなことになっている。
今は pip を別個でアップグレードする必要がないんだね。

python3 -m pip install pillow
python3 -m pip install pyobjc

3.12 は別ディレクトリなので新たに入れ直す。
これ以外に何か使っていたっけ?まあ必要時に。

多分これでアップグレードは完了だけど一つだけ。
Fedora は python コマンドに 3 を付ける必要がもう無いんだよ。
macOS ではいまだに python3 と打つ必要があるのが違和感。

alias python=python3

$HOME/.zshrc にコレ定義しちゃえ!
たった 1 文字だけどなんかスッキリした。

Linux LAW File

昨日はインフルに感染して一日中死んでいました。
広めちゃいけないので本日は有給休暇、体はダルいけど頭は超ヒマだ。
そんなこんなで昨日やろうと思っていた Linux ネタ。

orf

Fedora 39 にして四ヶ月、何を今頃気がついたけれど。
Nautilus 45 は OLYMPUS LAW である ORF を認識しているじゃん。

OM SYSTEM となっていないのはまあ時間が解決するだろう。
NIKON の NEF は以前からあったけど LUMIX の RW2 も対応していた。
いやぁ写真趣味を初めて良かった、こんな検証ネタが見つかるとは。

もちろんサムネイル表示することも画像を表示することも何もできない。
macOS の uti と違って意味が無いような気がしなくもない。
いや macOS はリソースフォーク時代と拡張子との整合性とかあったからだけど。
そもそも UTI は開発者側以外は気にしなくていいことは置いておいて。
ちなみに Nautilus は拡張子より前に以下を検索します。

/usr/share/mime/magic

とりあえず開いて olympus で検索、ちなみに文字列ではない定義もある。
当然 Gedit ではエラーが出るけど強制的に開くことは可能。

orf

なんだ普通に定義されていた。
IIRO(0x08)(0x00)(0x00)(0x00)
がファイルの先頭に付いていたら ORF ということです。

つまり macOS の UTI が現在は拡張子依存なのと違い拡張子は関係ない。
拡張子だけで全てが決まるポンコツ OS は置いておいて。

#!/usr/bin/env python3

import sys, textwrap
from gi.repository import Gio

attr = ','.join([
    Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
    Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ])

for filename in sys.argv[1:]:
    f = Gio.file_new_for_path(filename)
    info = f.query_info(attr, Gio.FileQueryInfoFlags.NONE)
    _name = info.get_display_name()
    _type = info.get_content_type()
    _desc = Gio.content_type_get_description(_type)
    print(textwrap.dedent(f'''
        ----------
        Name        : {_name}
        Content Type: {_type}
        Description : {_desc}
        ----------'''))

get_ctype.py

を使って拡張子不要と表示の確認。

orf

なるほど、Description がプロパティに表示されているようで。
しかし LAW の Content-Type に対応とは、将来的に表示対応するのだろうか?
macOS でないとまともな現像アプリが無いから正直いらないけど。
フリーの GUI アプリって今現在は消費者(エンドユーザー)用途でしかないよね。
関係ないけど GNOME 45 って avif に対応していたのね、知らなかった。
最後に。

[Python]ヒアドキュメントでもインデントを崩さずに書かなきゃダメだよ | DevelopersIO

textwrap.dedent だと簡潔かつ読みやすいコードになりますね。
fstring でなく以前の formar({0}) なら先頭に定義という技があったけど。
それを fstring でやると未定義扱いになり困っていたので嬉しい発見でした。

JavaScript Object for-in

つい最近知ったのですが。
JavaScript のオブジェクトで for-in は順序固定になっていた。

#!/usr/bin/gjs

let text = 'あいうえおかきくけこ';
let ls = {};

for (let s of text) ls[s] = s+s+s;

for (let key in ls) print(`Key=${key} Value=${ls[key]}`);

gjs

マジだ、ES2020 かららしい。

これができないのが主な理由で ES6(ES2015) で Map が追加された。
Map なんて誰も使わなかったということなのだろうか。

JavaScript その他 – Paepoi
書き換えしなきゃなぁ、どんどん情報が古くなる。

そういえば Python の連想配列も 3.7 から順序固定になっていたんだっけ。
collections.OrderedDict は存在すら知らない人も多いし。

#!/usr/bin/env python3

text = 'あいうえおかきくけこ'
ls = {}

for s in text: ls[s] = s+s+s

for key in ls: print(f'Key={key} Value={ls[key]}')

まったく同じですね、流行なのかな。
まてよ、もしかして GLib なんかも。

#include <glib.h>
#include <glib/gprintf.h>

void
printfunc(gpointer key, gpointer value, gpointer user_data) {
    g_printf("key=%s value=%s\n", key, value);
}

int
main (int argc, char *argv[]) {

    GHashTable *table;

    table = g_hash_table_new(g_str_hash, g_str_equal);
    g_hash_table_insert(table, "あ", "あああ");
    g_hash_table_insert(table, "い", "いいい");
    g_hash_table_insert(table, "う", "ううう");
    g_hash_table_insert(table, "え", "えええ");
    g_hash_table_insert(table, "お", "おおお");
    g_hash_table_insert(table, "こ", "こここ");
    g_hash_table_insert(table, "け", "けけけ");
    g_hash_table_insert(table, "く", "くくく");
    g_hash_table_insert(table, "き", "ききき");
    g_hash_table_insert(table, "か", "かかか");
    //
    g_hash_table_foreach(table, printfunc, NULL);
    return 0;
}

glib

やっぱり駄目でした、普通はこうなるし。
C を久々に書いてみたけどメンドクセー!

Escape

INI ファイルの読み書きページを更新しました。
INI ファイルの読み書き – Paepoi

セクションを見つける正規表現に最初戸惑った。

EXP = r'^\[\w+\]$'

でイケると思ったけど文字列に半角スペースがあると認識できない。
半角スペースを使うなで済ませようとも思ったけど。

EXP = r'^\[[^\]]+\]$'

そうだ「閉じブラケット以外の文字列なら何でもいい」にすれば!
こんなアホな思いつきに対応できる正規表現ってやはり面白い。

とほほの正規表現入門 – とほほのWWW入門
ところで、とほほさんで見た「ブラケット内は記号の意味を失う」なんですが。

#! /usr/bin/env python3

import re

# [..] にマッチさせる正規表現
EXP = r'^\[[^\]]+\]$'
# EXP = r'^\[[^]]+\]$' # Python OK

a = ['[test]', '[test2]', '[test3] ', ' [test4]', '[test 5]']

for s in a:
    if re.search(EXP, s):
        print(s)

Python

#! /usr/bin/env php

<?php

// [..] にマッチさせる正規表現
$EXP = '/^\[[^\]]+\]$/';
// $EXP = '/^\[[^]]+\]$/'; # PHP OK

$a = ['[test]', '[test2]', '[test3] ', ' [test4]', '[test 5]'];

foreach ($a as $s) {
    if (preg_match($EXP, $s))
        echo $s.PHP_EOL;
}
?>

PHP

#! /usr/bin/gjs

// [..] にマッチさせる正規表現
const EXP = /^\[[^\]]+\]$/;
//const EXP = /^\[[^]]+\]$/; // Gjs NO

let a = ['[test]', '[test2]', '[test3] ', ' [test4]', '[test 5]'];

for (let s of a) {
    if (EXP.test(s)) {
        print(s)
    }
}

Gjs ダメだった。

JavaScript エンジンは複数あるので全部かどうかは試していないけど。
エスケープすれば全部イケたのでブラケット内もエスケープしたほうがいいかと。