自動補完ツールはひとつでいいという話

適当にいじってそのまま放っておいたEmacsの自動補完関連の環境をきれいにした.

適当に行き当たったサイトを参考にしてHelmをインストールして,ついでにそこで(たぶん)紹介されていたAuto-Completeも有効にして使っていたが,Elpyを使うときにElpyのデフォルトの補完とAuto-Completeの補完が両方出て非常に操作しにくくなるということが時々起こっていた(毎回起こるわけではないのも不可解だった).私の環境ではElpyの補完は黄色いプルダウンで,Auto-Completeのは灰色っぽい水色のプルダウンで表示されていた.

f:id:lingvisticae:20190517093645p:plain
Elpy (Company) の補完
Auto-Completeの補完は出たり出なかったりしてよくわからない.

で,試しにAuto-CompleteをElpy-modeでオフにしてみたら,ちゃんと毎回補完が出るし,二系統の補完が出て操作に支障が出るということもなくなった.ついでに,Elpyの補完で不満だった,C-nC-pでカーソルを移動できないという問題も,割と一般的なものらしく,調べたら解決策がすぐにわかった.

その過程で,Elpyの補完はCompanyを使っているということがわかったので,統一したほうが混乱がないのですべてのモードでAuto-Comleteをオフにした.だいぶ快適になった.畢竟,HelmやElpyのことはよくわからないし調べるのに時間もかかりそうだからと,不満のある(というより,いじって壊してしまったという印象だった)環境の解決を棚上げしていたのだった.

さらに,ESS-modeのときにdocumentationを勝手に検索してバッファが切り替わってしまうことがあるのが非常に問題だった(この問題はまったく同じ設定にしていても端末によって起きたり起きなかったりするのがさらに不可解だった).これもACを使わないことで解決した.ただし,Companyにはdocumentationを閲覧する機能はないらしいので,これを使いたい場合には困るかもしれない.

github.com

以下,Elpy, Auto-Complete(全コメントアウト),Companyの設定.
Companyについては以下のサイトを参照.
qiita.com

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;elpy settings
(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(package-selected-packages
   (quote
    (markdown-mode+ exec-path-from-shell helm-c-yasnippet helm markdown-mode ess elmacro pyenv-mode-auto elpygen pyenv-mode elpy))))
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 )

(package-initialize)
(elpy-enable)
(pyenv-mode)

;; ;; auto-completeをelpy-modeで無効化
;; (add-hook 'elpy-mode-hook
;;	  '(lambda ()
;;	     (auto-complete-mode -1)))

(defun ssbb-pyenv-hook ()
"Automatically activates pyenv version if .python-version file exists."
(f-traverse-upwards
(lambda (path)
  (let ((pyenv-version-path (f-expand ".python-version" path)))
    (if (f-exists? pyenv-version-path)
	(pyenv-mode-set (s-trim (f-read-text pyenv-version-path 'utf-8))))))))

(add-hook 'find-file-hook 'ssbb-pyenv-hook)

(add-hook 'before-save-hook 'whitespace-cleanup)

;; (require 'set-pyenv-version-path)
;; (add-hook 'find-file-hook 'set-pyenv-version-path)
;; (add-to-list 'exec-path "~/.pyenv/shims")

;; (setq split-width-threshold nil)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auto Completeの設定
;; -> Companyに移行
;;
;; (require 'auto-complete)
;; (require 'auto-complete-config)
;; (global-auto-complete-mode t)  ;; 個別に指定しなくてもこれでいいのか
;; (ac-config-default)
;; ;; (add-to-list 'ac-modes 'text-mode)         ;; text-modeでも自動的に有効にする
;; ;; (add-to-list 'ac-modes 'fundamental-mode)  ;; fundamental-mode
;; ;; (add-to-list 'ac-modes 'org-mode)
;; ;; (add-to-list 'ac-modes 'Emacs-Lisp-mode)
;; ;; (add-to-list 'ac-modes 'yatex-mode)
;; ;; (add-to-list 'ac-modes 'R-mode)
;; ;; (add-to-list 'ac-modes 'python-mode)
;; (ac-set-trigger-key "TAB")
;; (setq ac-use-menu-map t)       ;; 補完メニュー表示時にC-n/C-pで補完候補選択
;; (setq ac-use-fuzzy t)          ;; 曖昧マッチ

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Companyの設定
;; https://qiita.com/blue0513/items/c0dc35a880170997c3f5
;;
(require 'company)
(global-company-mode) ; 全バッファで有効にする
(setq company-transformers '(company-sort-by-backend-importance)) ;; ソート順
(setq company-idle-delay 0) ; デフォルトは0.5
(setq company-minimum-prefix-length 3) ; デフォルトは4
(setq company-selection-wrap-around t) ; 候補の一番下でさらに下に行こうとすると一番上に戻る
(setq completion-ignore-case t)
(setq company-dabbrev-downcase nil)
(global-set-key (kbd "C-M-i") 'company-complete)
(define-key company-active-map (kbd "C-n") 'company-select-next) ;; C-n, C-pで補完候補を次/前の候補を選択
(define-key company-active-map (kbd "C-p") 'company-select-previous)
(define-key company-search-map (kbd "C-n") 'company-select-next)
(define-key company-search-map (kbd "C-p") 'company-select-previous)
(define-key company-active-map (kbd "C-s") 'company-filter-candidates) ;; C-sで絞り込む
(define-key company-active-map (kbd "C-i") 'company-complete-selection) ;; TABで候補を設定
(define-key company-active-map [tab] 'company-complete-selection) ;; TABで候補を設定
(define-key company-active-map (kbd "C-f") 'company-complete-selection) ;; C-fで候補を設定
(define-key emacs-lisp-mode-map (kbd "C-M-i") 'company-complete) ;; 各種メジャーモードでも C-M-iで company-modeの補完を使う

;; yasnippetとの連携
(defvar company-mode/enable-yas t
  "Enable yasnippet for all backends.")
(defun company-mode/backend-with-yas (backend)
  (if (or (not company-mode/enable-yas) (and (listp backend) (member 'company-yasnippet backend)))
      backend
    (append (if (consp backend) backend (list backend))
	    '(:with company-yasnippet))))
(setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))

ついでに,下記のサイトを参考にCompanyの色設定を変更した.
auto-completeからの乗り換え時の違和感を減らしたかったという動機はなかったが,黄色いポップアップはいかにもださく,それに比べれば青灰基調のほうがだいぶよいように思ったから.今後色は適当に変えるかもしれない.

qiita.com

参考サイトとは色を一部変えている.

f:id:lingvisticae:20190519110636p:plain
色変更後のCompanyの補完ポップアップ

;; color settings
(set-face-attribute 'company-tooltip nil
		    :foreground "black" :background "lightgrey")
(set-face-attribute 'company-tooltip-common nil
		    :foreground "black" :background "lightgrey")
(set-face-attribute 'company-tooltip-common-selection nil
		    :foreground "white" :background "steelblue")
(set-face-attribute 'company-tooltip-selection nil
		    :foreground "black" :background "steelblue")
(set-face-attribute 'company-preview-common nil
		    :background nil :foreground "lightgrey" :underline t)
(set-face-attribute 'company-scrollbar-fg nil
		    :background "grey60")
(set-face-attribute 'company-scrollbar-bg nil
		    :background "gray40")

gitのローカルリポジトリに自動コミット・自動プッシュを設定する

以前のエントリで示したように,ローカルの作業フォルダをgitで管理してクラウドドライブにpushするようにしたわけだが,
livingdead0812.hatenablog.com
案の定,手動での同期は操作忘れを頻発することがわかったので,自動でのコミットとプッシュのためにいくつか方法を試みた.

やったこと1: .bash_profileで自動でコミットするスクリプトを実行する

簡便な方法として,一定時間おきにgitコマンドを実行するシェルスクリプトをログイン時に実行するという方法を試した.

#! /bin/bash

while true; do
    git pull
    git add .
    git commit -m "auto commit"
    git push
    sleep 300

下記のサイトを参考に上のようなスクリプトを作成し,bash /hoge/huga/auto-commit.shを.bash_profileに書き込んだ.
qiita.com

結果,.bash_profileを読み込んで起動するすべてのシェル上でスクリプトが実行され,すべてのシェルが待機状態になるという結果になった.
用は足りるのかもしれないが,そういうことがしたかったのではない.

やったこと2: launchdを設定して定期的に実行する

以下を参考にして,git操作だけをするシェルスクリプトを実行するようにした.
qiita.com

#! /bin/bash

git pull
git add .
git commit -m "auto commit"
git push

実行するためには,以下のようなplistファイルを作り,~/Library/LaunchAgent/に置く.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>auto-commit</string>
    <key>ProgramArguments</key>
    <array>
	<string>/bin/bash</string>
	<string>/Users/username/work/auto-commit.sh</string>
    </array>
    <key>StartInterval</key>
    <integer>300</integer>
    <key>StandardOutPath</key>
    <string>/Users/username/commitlog/auto-commit.out</string>
    <key>StandardErrorPath</key>
    <string>/Users/username/commitlog/auto-commit.err</string>
</dict>
</plist>

plistファイルを作ったら,以下のコマンドを実行.

$ launchctl load ~/Library/LaunchAgent/filename.plist

これでバックグラウンドでgitコマンドが実行されるようになる.

やったこと3: もう少し行き届いたコミットをさせたい

ここまでだと,コミットメッセージがずっと同じで追随性が悪いし,いろいろ不便がある.

コミットメッセージに変更をある程度反映させたい

とりあえず変更のあったファイル名を取得し,それをコミットメッセージに含めるため,commitの行を以下のように変更.

git commit -m " auto commit `git diff --name-only`"

git diff --name-onlyで変更のあったファイル名を取得し,それをバッククオートでくくることで文字列として出力する.
これをcommit -mのクオート内に書くことでコメントに流しこむ.

もう少し変更内容を反映したい

git diff --name-onlyのオプションを--statに変えると,以下のような表示が得られる.

     hoge | 2 --
     huga | 0
     2 files changed, 2 deletions(-)
新規ファイルも同様に反映したい

上記二つの方法は既存のファイルしか表示してくれない.
新規ファイルの差分も取得するためには,git add -N filenameを使う.

rainbowdevil.jp

git add -N filenameは新規ファイルをステージ対象にするだけで,実際にステージするわけではない(ステージするだけならgit add .でいい)ので,このあとでgit addが必要.

最終的にはこんな感じ.

#! /bin/bash

git pull
git add -N .
git add .
git commit -m "auto commit" -m "`git diff --stat`"
git push

2019/5/12追記

上記の設定でとりあえず起動すると,gitの出力が保存されるcommitlogというフォルダが作られる.
勝手に作られた状態だと,オーナーがrootでパーミッションが711(たぶん)になっていて,launchdが書き込めない状態になっている.
これが原因で,この状態だと動かず,ステータスコード78が返ってくる.
stackoverflow.com
この問題はパーミッションとオーナーを変えれば解決する.

cd # -> to home directory
sudo chmod 755 commtlog
sudo chown username commitlog

このログをファイルに残す必要があるかというと,ないような気がするが,StandardOutを指定しないとうまく動かなかったので,検討の余地あり.
残さないためには出力先を/dev/nullにすればいいとは思う.

リストやタプルの中身を展開して関数に引数として渡す

いくつかの関数は,可変長位置引数としてリストやタプルを渡すことができるが,できないものもある.
参考:変数の種類について
note.crohaco.net

たとえば,osモジュールのos.path.join()などがそれである.
os.path.join()は,直接複数の文字列を渡すとパス区切りで繋いでくれるが,リストを渡すとリストがそのまま返ってくる.

import os

os.path.join('hoge', 'huga', 'piyo')
# -> 'hoge/huga/piyo'

lst = ['hoge', 'huga', 'piyo']
os.path.join(lst)
# -> ['hoge', 'huga', 'piyo']

じゃあリストの形で保持している引数は渡せないのかというと,さっと検索すればこんなのがある.
torina.top

os.path.join(*lst)
# -> 'hoge/huga/piyo'

リストにアステリスクをつけると展開される.

同様のことはapply()というのでもできるらしい.
python-reference.readthedocs.io

ところが,アステリスクによって展開したリストはほかの文字列の引数と共存できない(少なくとのこの場合は).

os.path.join(*lst, 'paru')
# ->   File "<stdin>", line 1
# -> SyntaxError: only named arguments may follow *expression

まあ,この仕様のことがわかっていれば,追加したい文字列をリストにあらかじめ追加しておけばいいのだが.
ちなみにpython2を前提に考えているが,3系統だと違った挙動をするのかも.

また,join()系統はリストに空要素がある場合には無視するらしい.
つまり絶対パスを.split()なんかで分割してしまった場合,冒頭の'/'の前の部分を指していた空要素は消えてしまう.

lst2 = '/hoge/huga/piyo'.split(os.sep)
# -> lst2 = ['', 'hoge', 'huga', 'piyo']
os.path.join(*lst2)
# -> 'hoge/huga/piyo'

OS X のemacsをシェルから起動してもPATHが引き継がれなくなった問題

起こったこと

あるとき気がついたら,YaTeXC-c t jC-c t bが効かなくなっていた.
具体的には,

/bin/bash: platex: command not found

というメッセージが出て止まってしまう.

ターミナルでは,

which platex
/Library/TeX/texbin/platex

となる一方,M-! which platexとすると何も表示されないので,platexのパスが通っていないらしい.

やってみたこと

emacsが参照しているパスを確認するには,環境変数を確認するコマンドM-x getenvからPATHを選択する.

/use/bin:/bin:/usr/sbin:/sbin

なるほど全然ダメだ.
tasuwo.github.io

最初はYaTeXの問題かと思ったので,M-x describe-variableからtex-commandを見てみたものの,

tex-command is a variable defined in ‘yatex.el’.
Its value is "platex"

  This variable may be risky if used as a file-local variable.

Documentation:
∗Default command for typesetting LaTeX text.
Overridden with ‘%#! CommandLine...’ in the buffer.

となるので,.emacsyatex.elplatexに正しいパスを追記してみるも,効果なし.

また,M-! source ~/.bash_profileなどとやってみても効果なし.

解決した方法

emacsには変数PATHexec-pathがあり,M-!などでの実行時に読まれているのはM-!らしい.
おそらくもっとも簡便なのは,exec-path-from-shellを設定すること.
sakito.jp
keisanbutsuriya.hateblo.jp
github.com

このときのトラブルのためか,M-x package-installが正常に動作しなかったので,M-x package-list-packagesで一覧から探してインストールした.
ついで.emacs(など)に,

(when (memq window-system '(mac ns x))
  (exec-path-from-shell-initialize))

と書いて,再起動.

無事PATHは引き継がれるようになり,YaTeXも正常に動作するようになりました.
しかしなんでシェルから起動してるのに引き継がれないのだろう.

しばらくして

なぜか.bash_profile, .bashrc, .emacs, .Rprofileのテキストがすべて消えるという謎現象が起こった.

クラウドドライブにgitのリモートリポジトリを構築する

そもそもクラウド自体がgit(だと思う)を使って実現しているわけだから,いかにも屋上屋を架している感があるが,メリットがないわけではない.

こういう場合に

  • 複数台のコンピュータを使うが,それ以外の環境(スマートフォンタブレット)ではアクセスしない
    • gitがインストール・実行できる環境であることが必要
    • androidなら(中身はLinuxだし)可能?
  • 主に扱うのはテキストファイル(ワードやパワーポイントはあまり使わない)
    • SourcetreeなどのCUI環境も使えば対応可能
  • 細かい変更履歴がほしい
  • 任意の時点の状態に戻せる可能性がほしい
  • クラウドの容量にそれほど余裕がない(節約できると嬉しい)
    • 試してないけど,push先を分散させれば,手元のディレクトリ構造がクラウドの容量に制約されなくなるのでは
2019/4/25追記

デメリットとして,「web上のクラウドのページからファイルにブラウザでアクセスできない」ということがあるのに気づいた.

これを解決するには,たとえばAWSのようなサーバにリモートを作成して,同じサーバの上にローカルを作って,
一定時間で(あるいは他のリモートからアクションがあったら)自動でプルするようにしておけばいいのかもしれないが,
容量の節約というメリットはなくなる.

やったこと

mac osxの場合(ほかはわからない)

OpenSSHのインストール(しなくても大丈夫かも)

homebrewで,

$ brew install openssh

もしhomebrewのインストールがまだなら,適当に検索してインストール.

「設定」→「共有」→「リモートログイン」にチェック

若干はまった.
Macの「設定」から設定を変更し,アクセスを許可する.
qiita.com

gitのユーザー名とメールアドレスを設定(一度もgitを使ったことがない場合)
$ git config --global user.name "username"
$ git config --global user.email "user@email.com"
リモートリポジトリを作る

pushする先になるディレクトリをDropboxなどの適当な場所に作る.
末尾を ".git" にするのが慣習らしい.
ディレクトリを作ったら,gitをリモートとして(--bareオプション)初期化する.

$ mkdir ~/Dropbox/myrepo.git
$ git --bare init --shared
ローカルリポジトリを作る

共有したい既存のフォルダでgitを初期化し,リモートリポジトリを登録する.

$ git init
$ git remote add origin localhost:/Path/to/Dropbox/myrepo.git

www.sejuku.net

2台目以降のコンピュータにローカルリポジトリを設置する場合には,プルリクエストして取得してもいいが,

$ cd Path/to/dest/
$ git clone localhost:/Path/to/Dropbox/myrepo.git

とやることで,/Path/to/dest/myrepoが作成される.

公開鍵認証を設定する(次以降で詳しく)

公開鍵認証の設定

今回ほとんど唯一と言っていい,はまった箇所.
公開鍵暗号を設定すると,いちいちコマンドを打つたびにパスワードを聞かれるということがなくなる.

...はずなのだが,この公開鍵認証が一向に成功せず,パスワードを聞かれ続け,
やっと聞かれなくなったと思ったら認証が通らない状態になった.

こんな感じのエラーが出て,

username@localhost: Permission denied (publickey,password).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
$ ssh -v localhost

デバッグモードで接続してログを詳細表示すると,

OpenSSH_7.9p1, OpenSSL 1.0.2q  20 Nov 2018
debug1: Reading configuration data /usr/local/etc/ssh/ssh_config
debug1: Connecting to localhost [::1] port 22.
debug1: Connection established.
debug1: identity file /Users/username/.ssh/id_rsa type 0
debug1: identity file /Users/username/.ssh/id_rsa-cert type -1
debug1: identity file /Users/username/.ssh/id_dsa type -1
debug1: identity file /Users/username/.ssh/id_dsa-cert type -1
debug1: identity file /Users/username/.ssh/id_ecdsa type -1
debug1: identity file /Users/username/.ssh/id_ecdsa-cert type -1
debug1: identity file /Users/username/.ssh/id_ed25519 type -1
debug1: identity file /Users/username/.ssh/id_ed25519-cert type -1
debug1: identity file /Users/username/.ssh/id_xmss type -1
debug1: identity file /Users/username/.ssh/id_xmss-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_7.9
debug1: Remote protocol version 2.0, remote software version OpenSSH_6.9
debug1: match: OpenSSH_6.9 pat OpenSSH* compat 0x04000000
debug1: Authenticating to localhost:22 as 'username'
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC:  compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC:  compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:(なんらかの鍵)
debug1: Host 'localhost' is known and matches the ECDSA host key.
debug1: Found key in /Users/username/.ssh/known_hosts:2
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey after 134217728 blocks
debug1: Will attempt key: /Users/username/.ssh/id_rsa RSA SHA256:(公開鍵)
debug1: Will attempt key: /Users/username/.ssh/id_dsa
debug1: Will attempt key: /Users/username/.ssh/id_ecdsa
debug1: Will attempt key: /Users/username/.ssh/id_ed25519
debug1: Will attempt key: /Users/username/.ssh/id_xmss
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/username/.ssh/id_rsa RSA SHA256:(公開鍵)
debug1: Authentications that can continue: publickey
debug1: Trying private key: /Users/username/.ssh/id_dsa
debug1: Trying private key: /Users/username/.ssh/id_ecdsa
debug1: Trying private key: /Users/username/.ssh/id_ed25519
debug1: Trying private key: /Users/username/.ssh/id_xmss
debug1: No more authentication methods to try.
username@localhost: Permission denied (publickey).

実際には,ちょっとずつ設定をいじるたびにちょっとずつ内容が変わるが,
だいたい最後のところでパスワードを認証に使うかどうかというところが違うくらい.

やったこと

公開鍵と秘密鍵のペアを作る
$ ssh-keygen
# いろいろ聞かれるが,基本的にデフォルトのままenterでOK
# id_rsaとid_rsa.pubが生成される
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# 要するに名前を変えてコピーしてるだけ
# とんでもない間違いに気づいたので修正…

stackoverflow.com

/etc/ssh/sshd_configを書き換える

地味にこれがあんまり情報がなくてきつかった.
結果的に動くようになった時点までの変更を挙げておくが,不要なものが混入している可能性がある.
なお,それぞれの行は分散して書かれており,コメントアウトされている.
また,参考リンクの通りにはなっていない.

# 認証に公開鍵暗号方式を使う
RSAAuthentication yes
PubkeyAuthentication yes
# パスワードを使わない
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

qiita.com

関係するファイル・ディレクトリのパーミッションを書き換える

最終的にこれにずっとはまってた.

$ chmod 600 ~/.ssh/authorized_keys
# 必須
$ chmod 700 ~/.ssh
# たぶん必須
# id_rsaパーミッションが間違ってたので修正
$ chmod 600 ~/.ssh/id_rsa
$ chmod 644 ~/.ssh/id_rsa.pub
# よくわからん
$ chmod 755 ~/
# 必須(本来はこのパーミッションのはず)

serverfault.com
qiita.com

再帰的に関数を実行するときに返り値が取得できない

はまったこと

数値を入力させたいなど,正しい値が得られるまで同じ処理を繰り返したいことがある.
具体的には,例えばこんな感じ.

def get_number():
    try:
        num = raw_input('enter number: ')
    except ValueError:
        get_number()
    else:
        return num

get_number()
type(get_number)

try節でValueErrorが発生したらexcept節で再帰的にget_number()を呼び出す.
いつかValueErrorが発生しない値が得られたら,else節でnumをreturnして終了.
そう考えたくなる.

ところが,

None
<type 'NoneType'>

実行結果はこうなる.
値が返っていない.

正しい書き方

def get_number():
    try:
        num = raw_input('enter number: ')
    except ValueError:
        return get_number()
    else:
        return num

get_number()
type(get_number)

except節で自身を呼び出すときにもreturnしてやると正しく実行できる.
stackoverflow.com

再帰的に実行しているときにreturnで値が返っても,最上位のtry-except文においてはelse節が実行されていないからこうなるらしい.

AppleScriptで動画の長さを取得する

ファイルの情報を取得するのにはFinderにファイルを渡して問い合わせるのが一般的だと思う.
2/3 AppleScriptの構造を上手に調べる [Mac OSの使い方] All About

choose file
set theFile to result

tell application "Finder"
properties of item theFile
end tell

こんな感じにすると一通りの情報が出てくるが,動画に関しては長さやビットレート,解像度といった情報は出てこない.
Finderには表示されるわけだが,これは何らかの方法でFinder以外の部分が計算しているのだろう.

とりあえず方法としては,QuickTimeでファイルを開いてdocumentの要素を取り出すというものがある.

tell application "QuickTime Player"
	set tDoc to open theFile
	tell tDoc
		duration of tDoc
	end tell
end tell

一旦開くことになるのがいまいちかもしれない方法.

ちなみに何の値が取り出せるのかは,Script Editorで「ファイル」→「用語説明」→「QuickTime Player」と辿るとわかる.
AppleScriptでQuickTime Playerを自動化する | QuickTime