Python:FletとPyInstallerでGUIアプリを作る

Poetryでプロジェクトを作る。

takatoh@sofa: w > poetry new fletsample
Created package fletsample in fletsample
takatoh@sofa: w > cd fletsample

Fletを追加。

takatoh@sofa: fletsample > poetry add flet

flet create コマンドでプロジェクトを初期化。--templateオプションはひな形の指定で、minimumcounter がある(デフォルトは minimum)。

takatoh@sofa: fletsample > poetry run flet create --template counter .

Copying from template version 0.0.0.post9.dev0+cdc6738
 identical  .
    create  .gitattributes
    create  .gitignore
    create  assets
    create  assets\favicon.png
    create  assets\icon.png
    create  assets\manifest.json
    create  main.py
  conflict  README.md
 Overwrite README.md? [Y/n] y


Done. Now run:

flet run

見ての通りいくつかのファイルが作られる。main.py がメインのファイル。テンプレートに counter を指定したので、ボタンをクリックするとカウンターの数値が変わるアプリの実装が書かれている。

import flet as ft


def main(page: ft.Page):
    page.title = "Flet counter example"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER

    txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100)

    def minus_click(e):
        txt_number.value = str(int(txt_number.value) - 1)
        page.update()

    def plus_click(e):
        txt_number.value = str(int(txt_number.value) + 1)
        page.update()

    page.add(
        ft.Row(
            [
                ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
                txt_number,
                ft.IconButton(ft.icons.ADD, on_click=plus_click),
            ],
            alignment=ft.MainAxisAlignment.CENTER,
        )
    )


ft.app(main)

アプリを実行するには、flet run コマンド。新しいウィンドウでアプリが起動する。+ ボタンと - ボタンをクリックするたびに表示されてる数値が1ずつ変わる。そっけないアプリだけどちゃんと動く。

takatoh@sofa: fletsample > poetry run flet run

-w オプションを指定すると、webアプリとして起動し、ブラウザが開く。

takatoh@sofa: fletsample > poetry run flet run -w
http://127.0.0.1:50767

PyInstaller を追加。

takatoh@sofa: fletsample > poetry add --group dev pyinstaller
Using version ^6.1.0 for pyinstaller

Updating dependencies
Resolving dependencies...

The current project's Python requirement (>=3.10,<4.0) is not compatible with some of the required packages Python requirement:
  - pyinstaller requires Python <3.13,>=3.8, so it will not be satisfied for Python >=3.13,<4.0

Because no versions of pyinstaller match >6.1.0,<7.0.0
 and pyinstaller (6.1.0) requires Python <3.13,>=3.8, pyinstaller is forbidden.
So, because fletsample depends on pyinstaller (^6.1.0), version solving failed.

  • Check your dependencies Python requirement: The Python requirement can be specified via the `python` or `markers` properties

    For pyinstaller, a possible solution would be to set the `python` property to ">=3.10,<3.13"

    https://python-poetry.org/docs/dependency-specification/#python-restricted-dependencies,
    https://python-poetry.org/docs/dependency-specification/#using-environment-markers

……なんかエラーが出た。

調べてみると、pyproject.toml ファイルで Python のバージョンを ^3.10 と指定されてるのが原因らしい。PyPI.org の pyinstaller のページには、Python のバージョンについていこんなふうに書いてある。

3.8-3.12. Note that Python 3.10.0 contains a bug making it unsupportable by PyInstaller. PyInstaller will also not work with beta releases of Python 3.13.

pyinstaller 6.1.0 | PyPI.org

これを読むと ^3.10 でよさそうなものだけど……。ともかく、エラーメッセージにあるように、pyproject.toml の中の Python のバージョン指定を書き換えた(3.10.0も避けた)。

python = ">=3.10.1,<3.13"

もう一度、インストール実行。

takatoh@sofa: fletsample > poetry add --group dev pyinstaller

今度は無事インストールできた。ところで、--group dev はパッケージを開発用にインストールする。従来の --dev オプションは非推奨で代わりにこっちを使え、とマニュアルに書いてある。 ともあれ、この時点で pyproject.toml ファイルは次のようになった。

[tool.poetry]
name = "fletsample"
version = "0.1.0"
description = ""
authors = ["takatoh <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.10.1,<3.13"
flet = "^0.10.3"


[tool.poetry.group.dev.dependencies]
pyinstaller = "^6.1.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

GUI アプリをビルドするにはメインのソースファイルを指定して flet pack コマンドを実行する。--name オプションは出来上がるファイルの名前を指定している。省略すると mein.exe になる。

takatoh@sofa: fletsample > poetry run flet pack --name fletsample main.py

ビルドされたファイルは dist フォルダの中にできる。

takatoh@sofa: fletsample > ls dist

    Directory: C:\Users\takatoh\Documents\w\fletsample\dist

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2023/10/21    20:40       31291901 fletsample.exe

エクスプローラで探してファイルをダブルクリックすると、GUIアプリが起動した。うまくいってるようだ。試しに、ファイルをほかの場所、例えばデスクトップに移動しても、ダブルクリックすればちゃんと起動する。

Flet はまだバージョン1.0に届いてなくて開発途中のようだけど、簡単なGUIアプリを作るのにはいいんじゃないかと思えた。見た目もモダンでいい。

PyInstaller も初めて使ったけど、Python をインストールしなくてもいいので、他の人に使ってもらうのにはちょうどいいんじゃないかな。ファイルサイズが30MB以上もあるけど、Pythonの実行環境を含んでいるからこれは仕方がないかな。

[追記]

pyproject.toml での Python のバージョン指定、^3.10 だと 3.13 もOKってことになる。だからエラーが出るんだ。

Node.jsのバージョン管理にVoltaとfnmを試してみた

Node.jsのバージョン管理ツール多すぎ問題

Ruby なら rbenv、Python なら pyenv で決まり、みたいなバージョン管理ツールだけど、Node.js ではやたらと乱立していてどれを使うのがいいのかよくわからない。この問題自体はいろんなところで書かれていて、今回ググった中では↓このページがよくまとまっていた。

このページに挙げられているツールは以下の通り:

  • nvm
  • n
  • volta
  • asdf
  • nodenv
  • fnm
  • nvs
  • nodebrew

nvm、n、nodenv は前に試したことがある。ほかは聞いたことだけあったり、初めて知ったりだ。

いずれにせよ、JavaScript (Node.js)は Ruby や Python ほど使わないこともあって今までテキトーにしてたんだけど、静的サイトジェネレータの 11ty を試したこと(しかも Windows で)がきっかけで、少しまじめに考えてみようという気になった。なので Linux でも Windows でも使えるのが最低条件だ。

で、どれがいいかというと、上のページでは volta を薦めている。ほかには「Windows なら fnm がいい」というページがいくつか見つかった。そういうわけでこの2つを試してみることにする。どちらも Rust で書かれている。

Volta

公式サイトから Windows 用のインストーラをダウンロードする。

最新バージョンは1.0.8だ。ダウンロードしたら普通のアプリ同様にインストールすればいい。

takatoh@sofa: Documents > volta --version
1.0.8

ヘルプを見るとこんな感じ。

takatoh@sofa: Documents > volta --help
Volta 1.0.8
The JavaScript Launcher ⚡

    To install a tool in your toolchain, use `volta install`.
    To pin your project's runtime or package manager, use `volta pin`.

USAGE:
    volta.exe [FLAGS] [SUBCOMMAND]

FLAGS:
        --verbose
            Enables verbose diagnostics

        --quiet
            Prevents unnecessary output

    -v, --version
            Prints the current version of Volta

    -h, --help
            Prints help information


SUBCOMMANDS:
    fetch          Fetches a tool to the local machine
    install        Installs a tool in your toolchain
    uninstall      Uninstalls a tool from your toolchain
    pin            Pins your project's runtime or package manager
    list           Displays the current toolchain
    completions    Generates Volta completions
    which          Locates the actual binary that will be called by Volta
    setup          Enables Volta for the current user / shell
    run            Run a command with custom Node, npm, and/or Yarn versions
    help           Prints this message or the help of the given subcommand(s)

Node.js をインストールするには volta install

takatoh@sofa: Documents > volta install node@14
success: installed and set [email protected] (with [email protected]) as default

node@14 という書き方をしてるのは、Volta が Node.js だけでなく npm や yarn のバージョン管理にも対応してるから。ともかく Node 14 系の最新版がインストールされた。

takatoh@sofa: Documents > node -v
v14.20.0

Node 16 系もインストールしてみる。

takatoh@sofa: Documents > volta install node@16
success: installed and set [email protected] (with [email protected]) as default
takatoh@sofa: Documents > node -v
v16.17.0

volta list all でインストールされている Node.js のリストが表示される。

takatoh@sofa: Documents > volta list node
⚡️ Node runtimes in your toolchain:

    v14.20.0
    v16.17.0 (default)

プロジェクトごとにバージョンを分けることもできる。Volta では package.json に利用するバージョンが記載される。サンプルを作って試してみよう。

takatoh@sofa: Documents > cd node-sample
takatoh@sofa: node-sample > npm init

この状態で package.json はこんなふうになっている。

{
  "name": "sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

このプロジェクトに特定の Node.js のバージョンを指定するには volta pin コマンド。

takatoh@sofa: node-sample > volta pin node@14
success: pinned [email protected] (with [email protected]) in package.json

バージョンの指定は package.json に書き込まれ、次のようになる。

{
  "name": "sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "volta": {
    "node": "14.20.0"
  }
}

node -v で確認。

takatoh@sofa: node-sample > node -v
v14.20.0

プロジェクトのディレクトリを抜けると、デフォルトのバージョンに戻る。

takatoh@sofa: node-sample > cd ..
takatoh@sofa: Documents > node -v
v16.17.0

fnm

fnm は Fast Node Manager の略だそうだ。公式サイトは見当たらないんだけど、GitHub で公開されている。

Windows にインストールするには Chocolatey を使う。PowerShell を「管理者として実行」して、次のコマンドでインストール。

takatoh@sofa: Documents > choco install fnm

PowerShell で使うため、設定ファイルに次の行を書き足す。

fnm env --use-on-cd | Out-String | Invoke-Expression

PowerShell の設定ファイルは $profile と打ち込めばパスを表示してくれる。

takatoh@sofa: Documents > $profile
C:\Users\takatoh\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

これで準備は完了。

Node.js をインストールするには fnm install コマンドを使う。16系をインストールしてみよう。

takatoh@sofa: Documents > fnm install 16
Installing Node v16.17.0 (x64)

でも、これだけだとまだ使えない。

takatoh@sofa: Documents > node -v
node: The term 'node' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

PowerShell を起動しなおしたら使えるようになった。

takatoh@sofa: Documents > node -v
v16.17.0

14系もインストールしてみよう。

takatoh@sofa: Documents > fnm install 14
Installing Node v14.20.0 (x64)

fnm list コマンドでインストール済みのバージョンを確認できる。v14.20.0 とv16.17.0 がインストールされてるけど、この時点では v16.17.0 がデフォルトになってる。

takatoh@sofa: Documents > fnm list
* v14.20.0
* v16.17.0 default
* system
takatoh@sofa: Documents > node -v
v16.17.0

切り替えるには fnm use コマンド。

takatoh@sofa: Documents > fnm use 14
Using Node v14.20.0
takatoh@sofa: Documents > node -v
v14.20.0

ところで system ってのは何だろう?

takatoh@sofa: Documents > fnm use system
Bypassing fnm: using system node
takatoh@sofa: Documents > node -v
node: The term 'node' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

たぶん、fnm の管理下にない、もとからインストールされてるバージョンのことだな。今回の場合はそんなものはないのでエラーが出てる、と。fnm use コマンドでインストール済みのバージョンを指定してやれば直る。

takatoh@sofa: Documents > fnm use 16
Using Node v16.17.0
takatoh@sofa: Documents > node -v
v16.17.0

プロジェクトごとにバージョンを使い分けるには次のようにする。

takatoh@sofa: Documents > mkdir node-sample2
takatoh@sofa: Documents > cd node-sample2
takatoh@sofa: node-sample2 > npm init
takatoh@sofa: node-sample2 > fnm use 14
takatoh@sofa: node-sample2 > node -v > .node-version

fnm では Volta と違って .node-version ファイルに保存される。中身は次のようになってる。

takatoh@sofa: node-sample2 > cat .node-version
v14.20.0

.node-version ファイルがあるディレクトリに移動すると、自動的にバージョンを切り替えてくれる。いったん上のディレクトリに移動し、v16.17.0 に変更した後、もう一度プロジェクトのディレクトリに戻ると、v14.20.0 に切り替わる。

takatoh@sofa: node-sample2 > node -v
v14.20.0
takatoh@sofa: node-sample2 > cd ..
takatoh@sofa: Documents > fnm use 16.17.0
Using Node v16.17.0
takatoh@sofa: Documents > node -v
v16.17.0
takatoh@sofa: Documents > cd node-sample2
Using Node v14.20.0
takatoh@sofa: node-sample2 > node -v
v14.20.0

が、上のディレクトリに戻っても v16.17.0 には切り替わってくれない。どうも .node-version ファイルがないディレクトリに移動したときには切り替えが発生しないみたいだ。どうしようかと思ったら fnm default コマンドがあった。

takatoh@sofa: Documents > fnm default v16.17.0
takatoh@sofa: Documents > node -v
v14.20.0
takatoh@sofa: Documents > fnm use 16
Using Node v16.17.0
takatoh@sofa: Documents > node -v
v16.17.0
takatoh@sofa: Documents > cd node-sample2
Using Node v14.20.0
takatoh@sofa: node-sample2 > node -v
v14.20.0
takatoh@sofa: node-sample2 > cd ..
takatoh@sofa: Documents > node -v
v14.20.0

なのに期待通り動作しないじゃない。よくよくみると上のほうで fnm list コマンドを実行したときの出力に v16.17.0 が default ってなってる。

何か忘れてる?それともバグ?

ディレクトリ内の画像からコンタクトシートを作るツールを作った(Go 言語で)

コンタクトシートってのは、画像(写真)のサムネイルを一覧にして1枚のシート(あるいは1ページ)に並べたもののこと。説明するまでもないか。

とにかく、コンタクトシートを作る必要ができて方法を調べてみたら、Windows の標準機能でできることを知った。ところが、確かに簡単に PDF に出力できるんだけど、どういうわけかファイル名の順に並んでくれない。用が足りないわけじゃないんだけどなんとも気持ちが悪い。

なので、ツールを自作することにした。最近は Python を使うことが多いので今回も Python で、と思って PDF の作り方を調べている途中で、そういえば以前、ディレクトリ内の画像をサムネイルのリストを表示する html ファイルを作るツールを作ったな、と思い出した。↓これ。

Go 言語だった。

そういうわけで、久しぶり(最後のコミットは2019年の2月、なんと3年半前だ)に Go を書いて、自分でも忘れてたツールを機能拡張することにした。

方針はこうだ。

mkphotoindex コマンドに --contactsheet オプションを追加して、指定したディレクトリに含まれる画像のサムネイルとファイル名を並べた PDF ファイルを出力する。

PDF を出力できるパッケージを探してみると、github.com/signintech/gopdf というのが見つかった。おおまかな使い方は、ページを追加し、テキストや画像を配置、PDF に出力する、というシンプルなもの。あるいは原始的ともいう。

もう少し楽にできそうなのはないかと探して見つけたのが、github.com/mikeshimura/goreport。これは内部では github.com/signintech/gopdf を使っているけど、帳票を作るのに便利なように作られている。決まったフォーマットの PDF を作るにはちょうどよさそうだと思って、これを使って実装を始めた。参考にしたページは以下。

ところが、なかなか思うようにはいかなかった。Qiita の記事を読むと、結構複雑な帳票も簡単にできるように思えるんだけど、サムネイルを例えば 4列×5行とかに並べるのがうまくできない。多分理解が足りないだけで、いいやり方があるんだと思うんだけど。

で、結局のところ、A4 のページにサムネイル(とファイル名)を並べたいだけだと思いなおして、github.com/signintech/gopdf を直接使うことにした。サムネイルのサイズや位置の調整に時間をとられたけど、とりあえず用は足りるものはできた。

とはいえ問題が一つ残った。github.com/signintech/gopdf は ttf フォントを読み込んで使える(もちろん日本語も)ので、IPAex フォントをダウンロードして使ったんだけど、現状の実装ではカレントディレクトリに フォントファイル ipaex.ttf を置いておく必要がある。これは結構面倒だ。今回に限っては Windows で実行するのが前提なので Windows に標準でついているフォントファイルのパスをコードに埋め込んでしまえば回避できる。でも、ほかの OS でも使うことを考えるとそれは避けたい。コマンドラインオプションでフォントファイルを指定してやることも考えたけど、デフォルトのフォントがあったほうがいい。OS ごとにデフォルトのフォントを決めてやるにはどうしたらいいんだろうか。

Ubuntu Server 22.04にSambaサーバをたてる(Dockerなしで)

Docker で Samba サーバをたてるのは諦めた。Ubuntu の上で直接 Samba サーバを動かすことにする。

まずはお決まりの apt update から。

takatoh@wplj:~$ sudo apt update

Samba サーバのインストール。

takatoh@wplj:~$ sudo apt install -y samba

設定ファイルを編集(バックアップをとってから)。

takatoh@wplj:~$ sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.orig
takatoh@wplj:~$ sudo vim /etc/samba/smb.conf

ワークグループの名前を変更したほか、ユーザー認証なしでアクセスできる public と takatoh のみアクセスできる restricted の2つの共有フォルダを作った。

もとの設定ファイルとの差分:

takatoh@wplj:~$ diff /etc/samba/smb.conf.orig /etc/samba/smb.conf
29c29
<    workgroup = WORKGROUP
---
>    workgroup = PANICBLANKET
241a242,257
> 
> [public]
>     path = /mnt/data/samba
>     writable = yes
>     force create mode = 0644
>     force directory mode = 0755
>     guest ok = yes
>     guest only = yes
> 
> [restricted]
>     path = /mnt/data/samba_takatoh
>     writable = yes
>     force create mode = 0644
>     force directory mode = 0755
>     valid users = takatoh
>     force user = takatoh

コメントを除く全体はこう:

[global]
   workgroup = PANICBLANKET
   server string = %h server (Samba, Ubuntu)
   log file = /var/log/samba/log.%m
   max log size = 1000
   logging = file
   panic action = /usr/share/samba/panic-action %d
   server role = standalone server
   obey pam restrictions = yes
   unix password sync = yes
   passwd program = /usr/bin/passwd %u
   passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
   pam password change = yes
   map to guest = bad user
   usershare allow guests = yes

[printers]
   comment = All Printers
   browseable = no
   path = /var/spool/samba
   printable = yes
   guest ok = no
   read only = yes
   create mask = 0700

[print$]
   comment = Printer Drivers
   path = /var/lib/samba/printers
   browseable = yes
   read only = yes
   guest ok = no

[public]
    path = /mnt/data/samba
    writable = yes
    force create mode = 0644
    force directory mode = 0755
    guest ok = yes
    guest only = yes

[restricted]
    path = /mnt/data/samba_takatoh
    writable = yes
    force create mode = 0644
    force directory mode = 0755
    valid users = takatoh
    force user = takatoh

で、共有用のディレクトリを作る。

takatoh@wplj:~$ sudo mkdir -p /mnt/data/samba
takatoh@wplj:~$ sudo chown nobody:nogroup /mnt/data/samba
takatoh@wplj:~$ sudo mkdir -p /mnt/data/samba_takatoh
takatoh@wplj:~$ sudo chown takatoh:takatoh /mnt/data/samba_takatoh

結果としてこうなった。

takatoh@wplj:~$ ls -l /mnt/data
total 8
drwxr-xr-x 2 nobody  nogroup 4096 May  6 18:13 samba
drwxr-xr-x 3 takatoh takatoh 4096 May  6 18:13 samba_takatoh

そして、smbd をリスタート。

takatoh@wplj:~$ sudo systemctl restart smbd

Samba にユーザーを作る。pdbedit コマンド。

takatoh@wplj:~$ sudo pdbedit -a takatoh
new password:
retype new password:
Unix username:        takatoh
NT username:          
Account Flags:        [U          ]
User SID:             S-1-5-21-3828460484-3255695466-1925772709-1000
Primary Group SID:    S-1-5-21-3828460484-3255695466-1925772709-513
Full Name:            takatoh
Home Directory:       \\WPLJ\takatoh
HomeDir Drive:        
Logon Script:         
Profile Path:         \\WPLJ\takatoh\profile
Domain:               WPLJ
Account desc:         
Workstations:         
Munged dial:          
Logon time:           0
Logoff time:          Thu, 07 Feb 2036 00:06:39 JST
Kickoff time:         Thu, 07 Feb 2036 00:06:39 JST
Password last set:    Fri, 06 May 2022 18:12:32 JST
Password can change:  Fri, 06 May 2022 18:12:32 JST
Password must change: never
Last bad password   : 0
Bad password count  : 0
Logon hours         : FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

これでOK。Windows マシンからアクセスしてみると、ちゃんと \\wplj\public には認証無しで読み書きできる。\\wplj\restricted にはユーザー名とパスワードを要求され、正しく入力すれば読み書きできる。

無事完了。

[追記]

Samba のインストールと設定に際して、ファイアウォール関連は何もしていない。設定は次のようになっている。

takatoh@wplj:~$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
137                        ALLOW       Anywhere                  
138                        ALLOW       Anywhere                  
139                        ALLOW       Anywhere                  
445                        ALLOW       Anywhere                  
22/tcp (v6)                ALLOW       Anywhere (v6)             
137 (v6)                   ALLOW       Anywhere (v6)             
138 (v6)                   ALLOW       Anywhere (v6)             
139 (v6)                   ALLOW       Anywhere (v6)             
445 (v6)                   ALLOW       Anywhere (v6)

Docker を使ってやってたときには何が悪かったのか、結局よくわからない。

DockerでSambaサーバをたてたけど外部からつながらない

昨日・今日といろいろ調べながら作業をしたけど、何が悪いのかわからない。もう Docker を使うのは諦めて、Ubuntu に直接 Samba サーバをインストールすることにしようと思うが、記録だけ残しておく。あ、「外部」ってのはローカルネットワーク上の別の PC のこと。

環境

ホストはこの間インストールした Ubuntu Server 22.04 だ。マシン自体は古いけど、いま Samba サーバになってるマシン(これは CentOS 7)はもっと古いので、これを置きかえるつもり。

  • Ubuntu Server 22.04 LTS
  • Docker version 20.10.14, build a224086349
  • docker-compose version 1.29.2, build unknown
  • dperson/samba (Samba の Docker イメージ)

Docker と docker-compose は Ubuntu のインストール時に一緒にインストールしたもの。Samba には公式の Docker イメージがないらしく、ググるとこの dperson/samba がよく使われてるようなのでこれにした。

Samba サーバをたてる

あちこちのページを参考にして、docker-compose.yml ファイルはこうした。

version: "3"

services:

  samba:
    image: dperson/samba
    container_name: samba
    restart: always
    ports:
      - "137:137"
      - "138:138"
      - "139:139"
      - "445:445"
    volumes:
      - "/mnt/data/samba:/mnt/public"
    command: [
      "-r",
      "-S",
      "-w", "PANICBLANKET",
      "-s", "public;/mnt/public;yes;no;yes;all"
    ]

これで Docker コンテナを起動してやる。

takatoh@wplj:~$ docker-compose up -d
Creating network "takatoh_default" with the default driver
Creating samba ... done
takatoh@wplj:~$ docker-compose ps
Name              Command                State                  Ports           
--------------------------------------------------------------------------------
samba   /sbin/tini -- /usr/bin/sam    Up (healthy)   0.0.0.0:137->137/tcp,:::137
        ...                                          ->137/tcp, 137/udp, 0.0.0.0
                                                     :138->138/tcp,:::138->138/t
                                                     cp, 138/udp, 0.0.0.0:139->1
                                                     39/tcp,:::139->139/tcp, 0.0
                                                     .0.0:445->445/tcp,:::445->4
                                                     45/tcp

次に、外部から接続できるようにホスト側のファイアウォールを設定する。Samba に関連するポートは、137、138、139、445 だ。ufw コマンドで接続を許可する。

akatoh@wplj:~$ sudo ufw allow 137
Rule added
Rule added (v6)
takatoh@wplj:~$ sudo ufw allow 138
Rule added
Rule added (v6)
takatoh@wplj:~$ sudo ufw allow 139
Rule added
Rule added (v6)
takatoh@wplj:~$ sudo ufw allow 445
Rule added
Rule added (v6)

そして、ファイアウォールを有効化。

takatoh@wplj:~$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

次のような状態になった。

akatoh@wplj:~$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
137                        ALLOW       Anywhere                  
138                        ALLOW       Anywhere                  
139                        ALLOW       Anywhere                  
445                        ALLOW       Anywhere                  
22/tcp (v6)                ALLOW       Anywhere (v6)             
137 (v6)                   ALLOW       Anywhere (v6)             
138 (v6)                   ALLOW       Anywhere (v6)             
139 (v6)                   ALLOW       Anywhere (v6)             
445 (v6)                   ALLOW       Anywhere (v6)

これで完了(のはず)。

Windows から接続テスト

Samba サーバは出来上がったはずなので、Windows マシンから接続できるかテストしてみたところ、つながらない。共有フォルダどころか、コンピュータ(ホスト名は wplj)さえも見つからないと言われる。困った。

ネットワークの確認

Samba サーバをたてた wplj とは別の Ubuntu マシン(ホスト名は apostrophe)から、ネットワーク越しに接続できるのかを確認してみた。

まずは ping

takatoh@apostrophe:~$ ping wplj
PING wplj (192.168.2.12) 56(84) バイトのデータ
64 バイト応答 送信元 wplj (192.168.2.12): icmp_seq=1 ttl=64 時間=0.619ミリ秒
64 バイト応答 送信元 wplj (192.168.2.12): icmp_seq=2 ttl=64 時間=0.538ミリ秒
64 バイト応答 送信元 wplj (192.168.2.12): icmp_seq=3 ttl=64 時間=1.15ミリ秒
64 バイト応答 送信元 wplj (192.168.2.12): icmp_seq=4 ttl=64 時間=0.694ミリ秒
^C
--- wplj ping 統計 ---
送信パケット数 4, 受信パケット数 4, パケット損失 0%, 時間 3057ミリ秒
rtt 最小/平均/最大/mdev = 0.538/0.750/1.150/0.237ミリ秒

OK。そりゃそうだ、SSH で接続できてんだから。

なら、ポートが使えるかどうかを確認する nc コマンド。繋がるのがわかっている 22 番ポート(SSH)で試してみるとこうなる。

takatoh@apostrophe:~$ nc -vz wplj 22
Connection to wplj 22 port [tcp/ssh] succeeded!

では、Samba の使っている(はず)のポートではどうか。

akatoh@apostrophe:~$ nc -vz -u wplj 137
takatoh@apostrophe:~$ nc -vz -u wplj 138
takatoh@apostrophe:~$ nc -vz wplj 139
nc: connect to wplj port 139 (tcp) failed: Connection refused
takatoh@apostrophe:~$ nc -vz wplj 445
nc: connect to wplj port 445 (tcp) failed: Connection refused

137 番と 138 番で -u オプションを指定してるのは udp だから。何も出力されてないのは、接続できないからだ。tcp をつかう 139 番と 445 番は「失敗した」と明快に出力されてる。

一方のサーバ(wplj)側ではどうだろう。ss コマンドを使う。

takatoh@wplj:~$ ss -natu
Netid State  Recv-Q Send-Q                      Local Address:Port Peer Address:Port                                   Process                                  
udp   UNCONN 0      0                           127.0.0.53%lo:53        0.0.0.0:*                                                                               
udp   UNCONN 0      0                     192.168.2.12%enp1s0:68        0.0.0.0:*                                                                               
udp   UNCONN 0      0      [fe80::cad3:ffff:fe9d:2f5f]%enp1s0:546          [::]:*                                                                               
tcp   LISTEN 0      4096                        127.0.0.53%lo:53        0.0.0.0:*                                                                               
tcp   LISTEN 0      128                               0.0.0.0:22        0.0.0.0:*                                                                               
tcp   ESTAB  0      0                            192.168.2.12:22   192.168.2.14:58570                                                                           
tcp   LISTEN 0      128                                  [::]:22           [::]:*

Status の UNCONNLISTEN はそれぞれ udp、tcp の待受状態を表す。一つだけある ESTAB はつながっていることを表していて、これは SSH(22番ポート)だ。見事に Samba 関連のポートがない。

[追記]

いまためしたら、Samba のコンテナが起動してなかった。起動した上で ss コマンドを試すとこうなった。

takatoh@wplj:~$ ss -natu
Netid State  Recv-Q Send-Q                      Local Address:Port Peer Address:Port                                   Process                                  
udp   UNCONN 0      0                           127.0.0.53%lo:53        0.0.0.0:*                                                                               
udp   UNCONN 0      0                     192.168.2.12%enp1s0:68        0.0.0.0:*                                                                               
udp   UNCONN 0      0      [fe80::cad3:ffff:fe9d:2f5f]%enp1s0:546          [::]:*                                                                               
tcp   LISTEN 0      4096                              0.0.0.0:137       0.0.0.0:*                                                                               
tcp   LISTEN 0      4096                              0.0.0.0:138       0.0.0.0:*                                                                               
tcp   LISTEN 0      4096                              0.0.0.0:139       0.0.0.0:*                                                                               
tcp   LISTEN 0      4096                        127.0.0.53%lo:53        0.0.0.0:*                                                                               
tcp   LISTEN 0      128                               0.0.0.0:22        0.0.0.0:*                                                                               
tcp   LISTEN 0      4096                              0.0.0.0:445       0.0.0.0:*                                                                               
tcp   ESTAB  0      0                            192.168.2.12:22   192.168.2.14:58570                                                                           
tcp   LISTEN 0      4096                                 [::]:137          [::]:*                                                                               
tcp   LISTEN 0      4096                                 [::]:138          [::]:*                                                                               
tcp   LISTEN 0      4096                                 [::]:139          [::]:*                                                                               
tcp   LISTEN 0      128                                  [::]:22           [::]:*                                                                               
tcp   LISTEN 0      4096                                 [::]:445          [::]:*

Samba 関連のポートを待受けるようになった。けど、やっぱり他のコンピュータからはつながらない。

takatoh@apostrophe:~$ nc -u -vz wplj 137
takatoh@apostrophe:~$ nc -u -vz wplj 138
takatoh@apostrophe:~$ nc -vz wplj 139
nc: connect to wplj port 139 (tcp) failed: Connection timed out
takatoh@apostrophe:~$ nc -vz wplj 445
nc: connect to wplj port 445 (tcp) failed: Connection timed out

[追記ここまで]

まとめ

というわけで、どうやら Docker でたてた Samba のせいじゃなく、コンピュータ間で通信できてないように見える。SSH は問題なくつながってるのに何が違うんだ?

新しいWindows11マシン

メインじゃないほうの Windows マシンを新しくした。

本当は今年の後半まで待ちたかったんだけど(主に経済的な理由で)、Bluetooth のキーボードとマウスを認識してくれなくなってしまったので、前倒しして買い替えた。

届いたのは火曜日。今日やっと最低限のセットアップだけして、この記事を書いてる。アプリも Office と Dropbox しかインストールしてない。

ほかにやってるのは Ubuntu 22.04 LTS のダウンロード(まさに今ダウンロード中)。

なんとも内容のない記事だけど、いいんだ。書かないと今月ひとつも記事がないことにもなりかねないから。あ、でも Ubuntu のインストールをすればネタができるか。

Windows11へのGNU Fortranのインストールと算術IFについて

古いFortran(FORTRAN77だ!)のプログラムをコンパイルする必要があって、GNU Fortran をインストールした。

GNU Fortran の公式サイトは↓ここにあるんだけど、どこからダウンロードすればいいんだか、ひどくわかりにくい。

さいわい、丁寧に説明してくれているページを見つけた。日付が古いが内容は古くなってない。

公式サイトのここをクリックして、次の画面のここをクリックして……みたいなことをグダグダと書かれているが――という言い方はよくないな。丁寧に説明してくれてるんだからありがたい。悪いのはわかりにくい公式サイトだ――要するに TDM GCC をインストールすれば GNU Fortran もインストールできる。TDM GCC のサイトは↓ここ。

バージョン10.3.0が最新だった。ダウンロードしたのは64ビット版のtdm64-gcc-10.3.0-2.exe。

このファイルは Windows のインストーラになってるので、普通にインストール作業すれば何も困ることはない。ただ、デフォルトでは Fortran はインストールされないので、途中で Fortran をインストールするようにチェックを入れる必要がある。

さて、インストールできたので早速試してみた。コマンドは gfortran

takatoh@sofa: sample > gfortran --version
GNU Fortran (tdm64-1) 10.3.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

コンパイルするには引数にソースファイルの名前を指定すればいい。-o オプションはコンパイルされた実行ファイルの名前の指定。省略すると a.exe になる。

takatoh@sofa: sample > gfortran -o auto auto.for

ソースファイルの拡張子が .for だと FORTRAN77 だと見做すらしい。.f90 なら Fortran90。

こんな調子で20個ほどコンパイルしたんだけど、そのひとつで次のような warning がでた。

takatoh@sofa: sample > gfortran -o ohsp ohsp.for
ohsp.for:70:72:

   70 |       IF(RLOG-R1) 170,180,180
      |                                                                        1
Warning: Fortran 2018 deleted feature: Arithmetic IF statement at (1)

Arithmetic IF statement ってなんだと思って調べたら、算術IFっていうんだと。

↓ここに説明がある。

条件式の値が負かゼロか正かで分岐して指定された番号の文へジャンプする。条件式は論理式じゃなくて算術式、つまり評価すると数値になる式。こんな感じ:

      IF(X-10) 100,200,300
  100 ...
      GO TO 500
  200 ...
      GO TO 500
  300 ...
      GO TO 500
  500 ...

X-10 が負なら100へ、ゼロなら200へ、正なら300へジャンプする。

もう少し調べてみたら、論理IF よりも 算術IF のほうが先にあったらしい。

FORTRAN77 って学生の時にやったんだけど、こんなの覚えてないよ。

それはさておき、こういう古いソースコードでもコンパイルできるってのはすごいことだよな。

Windows11にJDK17をインストールした

Java についてはもう何年も前から、ライセンスで有料化だとか、開発主体が Oracle から離れたとかいう話を聞いていて、部外者から見ると何がどうなってんだかわからない状態なんだけど、Java で書かなきゃいけなさそうな事案に遭遇したので調べてみた。

といっても、ググっても情報が新旧入り混じっててむしろ混乱した。

なんとなくわかったことは:

  • Java SE 17 が最新
  • コミュニティによって OpenJDK として開発されてて、Oracle はじめいろんなところからバイナリ配布されてるらしい
  • 以前はランタイムだけの JRE ってのもあったけど今はない。開発ツールを含んだ JDK だけ

くらいなとこか。

結局 Oracle の JDK に msi インストーラがあったのでこれをインストールした。

Windows 用のインストーラなので指示通りにクリックしていけばOK。試しにコンソールから実行してみるとこんな感じ。

takatoh@sofa: Documents > javac -version
javac 17.0.1
takatoh@sofa: Documents > java -version
java version "17.0.1" 2021-10-19 LTS
Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode, sharing)

まぁ、何とかなるだろ。

Python: ローカルネットワー上のプライベートなPyPIリポジトリからpoetry addする on Windows

一昨日、Windows 10 上に pypiserver で立てたプライベートリポジトリから pip コマンドでパッケージをインストールできたので、今日は Poetry が使えるか試してみた。

まずは pypi-server コマンドでサーバを起動してプライベートリポジトリを作っておく。

takatoh@montana: Documents > pypi-server -p 8080 ./py-packages

別のターミナルを開いて、Poetry で新しいプロジェクトを作って中に移動する。

takatoh@montana: Documents > poetry new py-sample
Created package py_sample in py-sample
takatoh@montana: Documents > cd py-sample

Poetry にプライベートリポジトリを登録する。

takatoh@montana: py-sample > poetry config repositories.local http://localhost:8080/simple/

リポジトリの名前が local で、その URL が http://localhost:8080/simple/ だ。

つづいて pyproject.toml ファイルにプライベートリポジトリを参照するように次を追記する。

[[tool.poetry.source]]
name = "local"
url = "http://localhost:8080/simple/"

これで準備は完了。poetry install や poetry add でパッケージをインストールできるはず。あと、キャッシュを削除しておくのを忘れずに(まだ直ってない)。今回は PyPI.org には公開してない、自作の outputdatareader っていうパッケージを追加してみた。

takatoh@montana: py-sample > poetry add outputdatareader
Using version ^0.1.0 for outputdatareader

Updating dependencies
Resolving dependencies...

Writing lock file

Package operations: 11 installs, 0 updates, 0 removals

  • Installing pyparsing (2.4.7)
  • Installing atomicwrites (1.4.0)
  • Installing attrs (21.2.0)
  • Installing colorama (0.4.4)
  • Installing more-itertools (8.10.0)
  • Installing packaging (21.0)
  • Installing pluggy (0.13.1)
  • Installing py (1.10.0)
  • Installing wcwidth (0.2.5)
  • Installing outputdatareader (0.1.0)
  • Installing pytest (5.4.3)

無事インストールできた。

ちなみに、プライベートリポジトリには outputdatareader パッケージしか置いてないから、ほかのパッケージは PyPI.org からインストールしてる。

サーバ側のターミナルにはこんなふうに出力されている。

127.0.0.1 - - [22/Oct/2021 22:41:31] "GET /simple/outputdatareader/ HTTP/1.1" 200 353
127.0.0.1 - - [22/Oct/2021 22:41:33] "GET /simple/outputdatareader/ HTTP/1.1" 200 353
127.0.0.1 - - [22/Oct/2021 22:41:36] "GET /simple/pyparsing/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/atomicwrites/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/packaging/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/more-itertools/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/py/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/attrs/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/pluggy/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/colorama/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:42] "GET /simple/wcwidth/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:48] "GET /simple/outputdatareader/ HTTP/1.1" 200 353
127.0.0.1 - - [22/Oct/2021 22:41:48] "GET /simple/pytest/ HTTP/1.1" 303 0
127.0.0.1 - - [22/Oct/2021 22:41:50] "GET /packages/outputdatareader-0.1.0-py3-none-any.whl HTTP/1.1" 200 2016

outputdatareader 以外のパッケージではステータスコードが 303 (See Other)になってる。つまりプライベートリポジトリには無いってことで、そうすると Poetry は PyPI.org に探しに行く。最後の行から、プライベートリポジトリからは outputdatareader-0.1.0-py3-none-any.whl ファイルだけがダウンロードされてるのがわかる。

というわけで、今日はここまで。

Python: ローカルネットワーク上のプライベートなPyPIリポジトリからインストールする on Windows

先月同じようなタイトルの記事を書いたけど……

pip コマンドの --find-links オプションを使えば、パッケージファイルを放り込んだディレクトリを指定するだけでインストールすることができた。のだけれど、poetry を使うときにはこの方法ではダメだった。結局のところ PyPI.org 相当のプレイべートなリポジトリサーバを立ててやる必要がある。

選択肢としては pypiserver っていうパッケージがあって、それは知ってたんだけど、設定とか面倒そうだと勝手に思ったので今までやってみなかった。ところが今日試してみたら存外に簡単にできたのでメモしておくことにする。記事のタイトルにもあるように環境は Windows。

pypiserver 自体は pip コマンドでインストールできる。

takatoh@montana: Documents > pip install pypiserver
Looking in links: http://nightschool/py-packages/
Collecting pypiserver
  Using cached pypiserver-1.4.2-py2.py3-none-any.whl (77 kB)
Installing collected packages: pypiserver
Successfully installed pypiserver-1.4.2

これで完了。以下のようにインストールされてるのが確認できる。

takatoh@montana: Documents > pip list | grep pypiserver
pypiserver                        1.4.2

つぎに、パッケージファイルを置いておくフォルダを作る。

takatoh@montana: Documents > mkdir py-packages

    Directory: C:\Users\takatoh\Documents

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2021/10/19    20:32                py-packages

で、ここに自作のパッケージファイルを放り込んだ。

takatoh@montana: Documents > ls py-packages

    Directory: C:\Users\takatoh\Documents\py-packages

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2021/10/17    18:51           4035 brs-0.8.1-py3-none-any.whl

あとは pypi-server コマンドで起動すれば準備は完了。

takatoh@montana: Documents > pypi-server -p 8080 ./py-packages

-p 8080 オプションでポートを、引数でパッケージファイルの入っているフォルダを指定している。

さて、別のコンソールを立ち上げて pip install してみる。--extra-index-url http://localhost:8080/simple/ オプションでローカルホストに立てた pypiserver の URL を指定している。

takatoh@montana: takatoh > pip install --extra-index-url http://localhost:8080/simple/ brs
Looking in indexes: https://pypi.org/simple, http://localhost:8080/simple/
Looking in links: http://nightschool/py-packages/
Collecting brs
  Downloading http://localhost:8080/packages/brs-0.8.1-py3-none-any.whl (4.0 kB)
Requirement already satisfied: click<9.0.0,>=8.0.1 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from brs) (8.0.1)
Requirement already satisfied: PyYAML<6.0.0,>=5.4.1 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from brs) (5.4.1)
Requirement already satisfied: requests<3.0.0,>=2.26.0 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from brs) (2.26.0)
Requirement already satisfied: colorama in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from click<9.0.0,>=8.0.1->brs) (0.4.4)
Requirement already satisfied: idna<4,>=2.5 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from requests<3.0.0,>=2.26.0->brs) (3.2)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from requests<3.0.0,>=2.26.0->brs) (2021.5.30)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from requests<3.0.0,>=2.26.0->brs) (1.26.7)
Requirement already satisfied: charset-normalizer~=2.0.0 in c:\users\takatoh\appdata\local\programs\python\python39\lib\site-packages (from requests<3.0.0,>=2.26.0->brs) (2.0.6)
Installing collected packages: brs
Successfully installed brs-0.8.1

Looking in indexes: で始まる行で、PyPI.org とローカルに立てた pypiserver を見に行ってるのがわかる。つづく Looking in links: で始まる行では、先日の記事で設定したパッケージファイルの置いてある場所を探しに行っている。結果として、ローカルの pypiserver からダウンロードしてインストールしてるのがわかる。

サーバ側にはこんなふうに出力されていた。

127.0.0.1 - - [19/Oct/2021 20:35:27] "GET /simple/ HTTP/1.1" 200 207
127.0.0.1 - - [19/Oct/2021 20:35:27] "GET /favicon.ico HTTP/1.1" 404 711
127.0.0.1 - - [19/Oct/2021 20:36:42] "GET /simple/brs/ HTTP/1.1" 200 301
127.0.0.1 - - [19/Oct/2021 20:36:45] "GET /packages/brs-0.8.1-py3-none-any.whl HTTP/1.1" 200 4035

というわけで、Windows でもローカルに pypiserver を立てるのは簡単だということが分かった。

あとはこれをサービスとしてバックグラウンドで動かせれば、poetry からも使えるようになる(はず)。それはまた今度。