Python for Windows: インターネット接続を制限された環境ではpoetryがつかえない

pipはつかえる

このあいだ、インターネット接続を制限された職場のネット環境でもpipを使う方法を見つけた、という記事を書いた。少し時間が開いてしまったけど、今日試してみた。結果は「pipはつかえる」だ。

検証のために、インストールしてあった Python をいったん削除(キャッシュやらなんやらも全部削除)して、インストールしなおした。まぁ、バージョンアップもかねて、ね。

username@workpc: Documents > python -V
Python 3.9.7

この状態ではパッケージは2つしかインストールされてない。

username@workpc: Documents > pip list
Package    Version
---------- -------
pip        21.2.3
setuptools 57.4.0

で、PyPI.org にアクセスせずローカルなネットワークで完結するよう、pip の設定ファイル %APPDATA%\Roming\pip\pip.ini を書いておく。

[global]
find-links = file://workpc/py-packages

[install]
find-links = file://workpc/py-packages
no-index = yes

[list]
no-index = yes

このあいだの記事に書いたように、--find-links オプションを指定する代わりにここに書いておく。ここでは file://workpc/py-packages を指定している。no-index = yes は PyPI.org を見に行かないようにする設定。これがないと繋がらない PyPI.org にアクセスしようとして時間を無駄にすることになる。

つぎは上の find-links で指定した共有フォルダを作る。といっても自分の PC にテキトーなフォルダを作って py-packages って名前で共有するだけだ。あとはあらかじめダウンロードしておいたパッケージファイルを放り込んでおく。

さて、これで準備は完了。試しに poetry をインストールしてみた。

username@workpc: Documents > pip install poetry
Looking in links: file://workpc/py-packages
Processing \\workpc\py-packages\poetry-1.1.11-py2.py3-none-any.whl
Processing \\workpc\py-packages\html5lib-1.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\keyring-21.8.0-py3-none-any.whl
Processing \\workpc\py-packages\shellingham-1.4.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\tomlkit-0.7.2-py2.py3-none-any.whl
Processing \\workpc\py-packages\clikit-0.6.2-py2.py3-none-any.whl
Processing \\workpc\py-packages\requests_toolbelt-0.9.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\cleo-0.8.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\poetry_core-1.0.7-py2.py3-none-any.whl
Processing \\workpc\py-packages\cachy-0.3.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\requests-2.26.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\virtualenv-20.8.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\crashtest-0.3.1-py3-none-any.whl
Processing \\workpc\py-packages\pexpect-4.8.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\cachecontrol-0.12.6-py2.py3-none-any.whl
Processing \\workpc\py-packages\pkginfo-1.7.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\packaging-20.9-py2.py3-none-any.whl
Processing \\workpc\py-packages\msgpack-1.0.2-cp39-cp39-win_amd64.whl
Processing \\workpc\py-packages\lockfile-0.12.2-py2.py3-none-any.whl
Processing \\workpc\py-packages\pylev-1.4.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\pastel-0.2.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\webencodings-0.5.1-py2.py3-none-any.whl
Processing \\workpc\py-packages\six-1.16.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\pywin32_ctypes-0.2.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\pyparsing-2.4.7-py2.py3-none-any.whl
Processing \\workpc\py-packages\ptyprocess-0.7.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\urllib3-1.26.7-py2.py3-none-any.whl
Processing \\workpc\py-packages\idna-3.2-py3-none-any.whl
Processing \\workpc\py-packages\charset_normalizer-2.0.6-py3-none-any.whl
Processing \\workpc\py-packages\certifi-2021.5.30-py2.py3-none-any.whl
Processing \\workpc\py-packages\filelock-3.3.0-py3-none-any.whl
Processing \\workpc\py-packages\backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl
Processing \\workpc\py-packages\platformdirs-2.4.0-py3-none-any.whl
Processing \\workpc\py-packages\distlib-0.3.3-py2.py3-none-any.whl
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, pylev, pastel, msgpack, crashtest, webencodings, six, pywin32-ctypes, pyparsing, ptyprocess, platformdirs, lockfile, filelock, distlib, clikit, cachecontrol, backports.entry-points-selectable, virtualenv, tomlkit, shellingham, requests-toolbelt, poetry-core, pkginfo, pexpect, packaging, keyring, html5lib, cleo, cachy, poetry
Successfully installed backports.entry-points-selectable-1.1.0 cachecontrol-0.12.6 cachy-0.3.0 certifi-2021.5.30 charset-normalizer-2.0.6 cleo-0.8.1 clikit-0.6.2 crashtest-0.3.1 distlib-0.3.3 filelock-3.3.0 html5lib-1.1 idna-3.2 keyring-21.8.0 lockfile-0.12.2 msgpack-1.0.2 packaging-20.9 pastel-0.2.1 pexpect-4.8.0 pkginfo-1.7.1 platformdirs-2.4.0 poetry-1.1.11 poetry-core-1.0.7 ptyprocess-0.7.0 pylev-1.4.0 pyparsing-2.4.7 pywin32-ctypes-0.2.0 requests-2.26.0 requests-toolbelt-0.9.1 shellingham-1.4.0 six-1.16.0 tomlkit-0.7.2 urllib3-1.26.7 virtualenv-20.8.1 webencodings-0.5.1

無事完了。ちゃんとインストールできてるのも確認できる。

username@workpc: Documents > pip list
Package                           Version
--------------------------------- ---------
backports.entry-points-selectable 1.1.0
CacheControl                      0.12.6
cachy                             0.3.0
certifi                           2021.5.30
charset-normalizer                2.0.6
cleo                              0.8.1
clikit                            0.6.2
crashtest                         0.3.1
distlib                           0.3.3
filelock                          3.3.0
html5lib                          1.1
idna                              3.2
keyring                           21.8.0
lockfile                          0.12.2
msgpack                           1.0.2
packaging                         20.9
pastel                            0.2.1
pexpect                           4.8.0
pip                               21.2.3
pkginfo                           1.7.1
platformdirs                      2.4.0
poetry                            1.1.11
poetry-core                       1.0.7
ptyprocess                        0.7.0
pylev                             1.4.0
pyparsing                         2.4.7
pywin32-ctypes                    0.2.0
requests                          2.26.0
requests-toolbelt                 0.9.1
setuptools                        57.4.0
shellingham                       1.4.0
six                               1.16.0
tomlkit                           0.7.2
urllib3                           1.26.7
virtualenv                        20.8.1
webencodings                      0.5.1

というわけで、PyPI.org に繋がらない環境でも pip がつかえるのが確認できた。まぁわかってたけどね。

poetryはつかえない

つぎはインストールしたばかりの poetry を試してみた。

username@workpc: Documents > poetry config virtualenvs.in-project true
username@workpc: Documents > poetry new py-sample
Created package py_sample in py-sample

できたプロジェクトフォルダに入って、click を追加してみた。すると:

username@workpc: Documents > cd py-sample
username@workpc: py-sample > poetry add click
Creating virtualenv py-sample in C:\Users\username\Documents\py-sample\.venv

  ConnectionError

  HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /pypi/click/json (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x0000020637A223D0>: Failed to establish a new connection: [WinError 10060] 接続済みの呼び出し 先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。'))

  at ~\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\adapters.py:516 in send
      512│             if isinstance(e.reason, _SSLError):
      513│                 # This branch is for urllib3 v1.22 and later.
      514│                 raise SSLError(e, request=request)
      515│
    → 516│             raise ConnectionError(e, request=request)
      517│
      518│         except ClosedPoolError as e:
      519│             raise ConnectionError(e, request=request)
      520│

こんなエラーに。そうすんなりとは行ってくれないか。

ググってみると、Poetry の日本語ドキュメントのページを見つけた。これによると pyproject.toml ファイルを編集してプライベートなリポジトリを追加できるようなので、こんなふうに追記した。

[[tool.poetry.source]]
name = "private"
url = "file://workpc/py-packages/"

さっきの共有フォルダを見に行ってくれることを期待して書いたんだけど、結果は次の通り:

username@workpc: py-sample > poetry add click

  InvalidSchema

  No connection adapters were found for 'file://workpc/py-packages/click/'

  at ~\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py:742 in get_adapter
      738│             if url.lower().startswith(prefix.lower()):
      739│                 return adapter
      740│
      741│         # Nothing matches :-/
    → 742│         raise InvalidSchema("No connection adapters were found for {!r}".format(url))
      743│
      744│     def close(self):
      745│         """Closes all adapters and as such the session"""
      746│         for v in self.adapters.values():

違うエラーを吐いた。どうやら PyPI.org の代わりに指定したプライベートなリポジトリを探しに行ってはくれてるようだけど、file: スキーマをサポートしてないようだ。ドキュメントを見る限りでは、プライベートなリポジトリとはいっても PyPI.org 相当の機能が必要で、パッケージファイルが置いてあればいいというわけではないらしい。ということは pipiserver を立てるしかないのか。

残念な結果に終わった。

Python for Windows: poetryの挙動がおかしい

最近 Python をバージョンアップしたら、poetry の挙動がおかしい。poetry addpoetry install を実行して仮想環境にパッケージをインストールしようとしてもエラーを吐く。正確に言うとバージョンアップのせいなのかどうかはよくわからないのだけど、ほかに思い当たることもないんだ。

順を追ってみてみよう。Python と poetry のバージョンから。どちらも最新版。

takatoh@montana: w > python -V
Python 3.9.7
takatoh@montana: w > poetry -V
Poetry version 1.1.10

さて、まずは poetry new で新しいプロジェクトを作る。

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

で、できたプロジェクトのフォルダに入って poetry add する。今回はよく使う click を追加してみた。

takatoh@montana: w > cd py-sample
takatoh@montana: py-sample > poetry add click
Creating virtualenv py-sample in C:\Users\takatoh\Documents\w\py-sample\.venv
Using version ^8.0.1 for click

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 click (8.0.1)
  • Installing pytest (5.4.3)

click のほかに仮想環境で使うパッケージもあわせて正常にインストールできた。

つぎに、いったん上のフォルダに移動して、別の新しいプロジェクトを作る。

takatoh@montana: py-sample > cd ..
takatoh@montana: w > poetry new py-sample-2
Created package py_sample_2 in py-sample-2

今できたほうのフォルダに入って、さっきと同じように poetry add で click を追加してみる。すると:

takatoh@montana: w > cd py-sample-2
takatoh@montana: py-sample-2 > poetry add click
Creating virtualenv py-sample-2 in C:\Users\takatoh\Documents\w\py-sample-2\.venv
Using version ^8.0.1 for click

Updating dependencies
Resolving dependencies...

Writing lock file

Package operations: 11 installs, 0 updates, 0 removals

  • Installing pyparsing (2.4.7)

  ValueError

  File \C:\Users\takatoh\AppData\Local\pypoetry\Cache\artifacts\92\0f\cf\effdcd5d76a6186df0969f85b3b030284ff8058936d5016540b5258ea3\pyparsing-2.4.7-py2.py3-none-any.whl does not exist

  at ~\AppData\Local\Programs\Python\Python39\lib\site-packages\poetry\core\packages\file_dependency.py:40 in __init__
       36│             except FileNotFoundError:
       37│                 raise ValueError("Directory {} does not exist".format(self._path))
       38│
       39│         if not self._full_path.exists():
    →  40│             raise ValueError("File {} does not exist".format(self._path))
       41│
       42│         if self._full_path.is_dir():
       43│             raise ValueError("{} is a directory, expected a file".format(self._path))
       44│


Failed to add packages, reverting the pyproject.toml file to its original content.

こんなふうにエラーになる。追加しようとした click じゃなくて仮想環境に必要な pyparsing っていうパッケージをインストールしようとしたところでエラーになってるのがわかる。poetry は一度インストールに使ったファイルはキャッシュしてて、このエラーはキャッシュしてるはずのファイル(\C:\Users\takatoh\AppData\Local\pypoetry\Cache\artifacts\92\0f\cf\effdcd5d76a6186df0969f85b3b030284ff8058936d5016540b5258ea3\pyparsing-2.4.7-py2.py3-none-any.whl)が見つからない、と言っている(ValueError の後に続く部分)。

ところが、ファイルはちゃんと存在する。

takatoh@montana: w > tree C:\Users\takatoh\AppData\Local\pypoetry\Cache\artifacts /F
フォルダー パスの一覧
ボリューム シリアル番号は 00000062 681C:8AA1 です
C:\USERS\TAKATOH\APPDATA\LOCAL\PYPOETRY\CACHE\ARTIFACTS
├─29
│  └─58
│      └─fc
│          └─ed8b7451d3ef91a6465024f5656141da996e7aafd4d41a1659629a75e7
│                  pluggy-0.13.1-py2.py3-none-any.whl
│
├─3d
│  └─af
│      └─11
│          └─94d692f8e0bf0791d76d42d8d257be6c7d2cba22ca4f745856c83286e9
│                  more_itertools-8.10.0-py3-none-any.whl
│
├─60
│  └─79
│      └─0b
│          └─c48bd9c2a989aa8b1eb7a67cd02b053c10734f2e4e5665f7995f09999c
│                  py-1.10.0-py2.py3-none-any.whl
│
├─6f
│  └─a9
│      └─ee
│          └─569c37f69a8c365ee41d2340aeac0214ee8c0086b8d8db43a21545204b
│                  attrs-21.2.0-py2.py3-none-any.whl
│
├─7d
│  └─f4
│      └─60
│          └─0737157bb9711fec72c70dff523aa54491eef317e0d586cf5388ff0908
│                  wcwidth-0.2.5-py2.py3-none-any.whl
│
├─92
│  └─0f
│      └─cf
│          └─effdcd5d76a6186df0969f85b3b030284ff8058936d5016540b5258ea3
│                  pyparsing-2.4.7-py2.py3-none-any.whl
│
├─9e
│  └─b3
│      └─11
│          └─7d87ac44fdb2d557301f1f4086a37c080d1482a98751abe7cdbabbad26
│                  colorama-0.4.4-py2.py3-none-any.whl
│
├─ae
│  └─32
│      └─83
│          └─e159324c1bd58177322f4e45f598d500fe22544bff20f53f55cf749da8
│                  click-8.0.1-py3-none-any.whl
│
├─b7
│  └─99
│      └─9c
│          └─c8ddc18c8225c740fc2ae6d503da3f93d5dddaf04afac3da460afacbc6
│                  atomicwrites-1.4.0-py2.py3-none-any.whl
│
├─e4
│  └─88
│      └─dd
│          └─3dbff42e3c4462dc3d027f5024025571166cb35edee215ce93852a968c
│                  pytest-5.4.3-py3-none-any.whl
│
└─f9
    └─4f
        └─09
            └─c91a145b26102e014fd6e33bd8c7b87306c8e1d4a771158f34dd13210e
                    packaging-21.0-py3-none-any.whl

pyparsing はなかほどに出力されてるね。なのに見つからないってどういうことさ。

さて、poetry の挙動がおかしいのはこれだけじゃない。というかこれから本番というか。

存在するのに見つからないっていうキャッシュをフォルダごと削除してやると、今度はインストールがうまくいく。まず削除。

takatoh@montana: w > rm C:\Users\takatoh\AppData\Local\pypoetry\Cache\artifacts -Recurse -Force

これで削除できてる。

takatoh@montana: w > tree C:\Users\takatoh\AppData\Local\pypoetry\Cache\artifacts /F
フォルダー パスの一覧
ボリューム シリアル番号は 00000066 681C:8AA1 です
C:\USERS\TAKATOH\APPDATA\LOCAL\PYPOETRY\CACHE\ARTIFACTS
無効なパスです - \USERS\TAKATOH\APPDATA\LOCAL\PYPOETRY\CACHE\ARTIFACTS
サブフォルダーは存在しません

で、あらためて poetry add click

takatoh@montana: w > cd py-sample-2
takatoh@montana: py-sample-2 > poetry add click
Using version ^8.0.1 for click

Updating dependencies
Resolving dependencies...

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 click (8.0.1)
  • Installing pytest (5.4.3)

ほら、ちゃんとインストールできた。どういうことだよ!

USBメモリにUbuntuのインストールメディアを作る

CentOS 8 の開発終了のニュースを受けて、ローカルネットワークのサーバ OS を置き換える計画をしてる。Ubuntu をインストールして使ってるサーバもバージョンが 16.04 で EOL が今年の4月と迫っている。そういうわけなので、この際まとめて Ubuntu 20.04 LTS に置き換えようと考えた。

今日はその準備としてインストール用メディアを作る。これまではダウンロードした ISO イメージを DVD-R に焼いてたんだけど、サーバにしてる PC は DVD ドライブついてないし(インストール時には外付けドライブを繋いだ)、せっかくなので USB メモリのインストールメディアを作ってみることにした。

作業環境は次の通り:

  • 作業用マシン:Windows 10
  • インストール用USBメモリ作成:Rufus

Rufus (ルーファス) ってのは起動可能な USB フラッシュドライブを作るソフト。↓ここからダウンロードした。

同様のソフトはほかにもあるようだけど、下調べした感じではこれが一番使いやすそうだった。メニューも日本語だし。バージョンは 3.13。

USB メモリは余ってた 16GB のもの。インストール用メディアとしてはもっと容量が小さくてもよさそうだけど、余ってるんだからそれを使う。

あと、肝心の OS は、Ubuntu Server 20.04.1 LTS。https://jp.ubuntu.com/download からダウンロードした ISO イメージを用意した。

さあ、始めよう。Rufus はインストールの必要はなく、ダウンロードしたファイルをダブルクリックして実行すればいい。

書き込み先(デバイス)と書き込む ISO イメージ(ブートの種類)の設定をしたところがこの画面。デバイスが「回復(G:) [16 GB]」ってなってるのは Windows の回復ドライブに使ってた USB メモリだから。ISO イメージは「選択」ボタンをクリックしてファイルを選択した。他はデフォルトのまま。

これで「スタート」をクリックすると

と表示されて追加のファイルをダウンロードするらしい。「はい」をクリック。

これもこのまま「OK」。

最終確認。問題なければ「OK」をクリックして書き込み開始。2分ほどで終わって↓の画面になる。

状態が「準備完了」になってるけど、これで書き込み終了してる。

試しに作ったばかりの USB メディアから PC を起動してみたら、ちゃんと Ubuntu のインストーラが起動した。

というわけで今日の任務完了。

Poppler for WindowsでPDFをPNGに変換する

PDF をページごとの画像ファイルに変換したくて、はじめは Python でできないか調べてた。そしたら↓のページで pdf2image という(Pythonの)ライブラリを紹介しているのを見つけた。

ところが記事を読んでみるとこう書いてある:

pdf2imageは「Poppler」というフリーのPDFコマンドラインツールを背後で用います。そのため、Popplerをダウンロードしておく必要があります。

それなら Poppler をそのまま使えばいいじゃん。

というわけで、Poppler for Windows をダウンロードした。

バージョンは 0.68.0 (poppler-0.68.0_x86.7z)。7zip なので 7z コマンドをインストールしてから展開した。

展開したファイルを眺めてみると、bin フォルダの中に pdfimages.exe という実行ファイルがある。これが使えそうだ。PATH を通してとりあえずヘルプを見てみた。

takatoh@montana: tmp > pdfimages -h
pdfimages version 0.68.0
Copyright 2005-2018 The Poppler Developers - http://poppler.freedesktop.org
Copyright 1996-2011 Glyph & Cog, LLC
Usage: pdfimages [options] <PDF-file> <image-root>
  -f <int>       : first page to convert
  -l <int>       : last page to convert
  -png           : change the default output format to PNG
  -tiff          : change the default output format to TIFF
  -j             : write JPEG images as JPEG files
  -jp2           : write JPEG2000 images as JP2 files
  -jbig2         : write JBIG2 images as JBIG2 files
  -ccitt         : write CCITT images as CCITT files
  -all           : equivalent to -png -tiff -j -jp2 -jbig2 -ccitt
  -list          : print list of images instead of saving
  -opw <string>  : owner password (for encrypted files)
  -upw <string>  : user password (for encrypted files)
  -p             : include page numbers in output file names
  -q             : don't print any messages or errors
  -v             : print copyright and version info
  -h             : print usage information
  -help          : print usage information
  --help         : print usage information
  -?             : print usage information

出力は JPEG が欲しかったので -j オプションを指定。<image-root> が何を指すのかよくわからないけどテキトーに。そしたらこうなった。

takatoh@montana: tmp > pdfimages -j sample.pdf foo
takatoh@montana: tmp > ls


    Directory: C:\Users\takatoh\Documents\tmp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2020/10/24     8:53        5364971 foo-000.ppm
-a---          2020/10/24     8:53        5364971 foo-001.ppm
-a---          2020/10/24     8:53        5364971 foo-002.ppm
-a---          2020/10/24     8:53        5364971 foo-003.ppm
-a---          2020/10/24     8:53        5364971 foo-004.ppm
-a---          2020/10/24     8:53        5364971 foo-005.ppm
-a---          2020/10/24     8:53        5364971 foo-006.ppm
-a---          2020/10/24     8:53        5364971 foo-007.ppm
-a---          2020/03/28     9:12        4082441 sample.pdf

.ppm って!いまどき .ppm ファイルなんて何で見ればいいんだ。なら PNG でいいや。あと、<image-root> は出力ファイルのプレフィックスみたいだな。なので .ppm ファイルは削除してやり直した。

takatoh@montana: tmp > pdfimages -png sample.pdf sample
takatoh@montana: tmp > ls


    Directory: C:\Users\takatoh\Documents\tmp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2020/03/28     9:12        4082441 sample.pdf
-a---          2020/10/24     9:01        1269290 sample-000.png
-a---          2020/10/24     9:01         446405 sample-001.png
-a---          2020/10/24     9:01         893712 sample-002.png
-a---          2020/10/24     9:01        1258104 sample-003.png
-a---          2020/10/24     9:01        1301072 sample-004.png
-a---          2020/10/24     9:01        1344592 sample-005.png
-a---          2020/10/24     9:01        1157016 sample-006.png
-a---          2020/10/24     9:01         755768 sample-007.png

これで OK。だけど、欲を言えば出力されるファイルをひとつのフォルダに入れたい。それらしいオプションは見当たらないけど、プレフィックスにパスを含めてやればできた。フォルダは先に作っておくこと。

takatoh@montana: tmp > mkdir out


    Directory: C:\Users\takatoh\Documents\tmp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2020/10/24    11:29                out

takatoh@montana: tmp > pdfimages -png sample.pdf out/sample
takatoh@montana: tmp > ls out


    Directory: C:\Users\takatoh\Documents\tmp\out

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2020/10/24    11:29        1269290 sample-000.png
-a---          2020/10/24    11:29         446405 sample-001.png
-a---          2020/10/24    11:29         893712 sample-002.png
-a---          2020/10/24    11:29        1258104 sample-003.png
-a---          2020/10/24    11:29        1301072 sample-004.png
-a---          2020/10/24    11:29        1344592 sample-005.png
-a---          2020/10/24    11:29        1157016 sample-006.png
-a---          2020/10/24    11:29         755768 sample-007.png

できた。

補足。

-j オプションの説明は write JPEG images as JPEG files となっている。PDFの中身が JPEG なら JPEG で出力するってことのようだ。

Excel VBAの配列のインデックスは0からはじまる

他人の書いた VBA のコードを調べていて初めて知った。

今までずっと VBA の配列のインデックスは 1 始まりだと思ってたんだ。だからたとえば3個の要素を持つ配列についての繰り返しはこんなふうに書いてた。

Dim arr(3)

...

For i = 1 To 3
    MsgBox arr(i)
Next i

これで期待通り動いてたんだから何の疑いも持たずにいた。数あるプログラミング言語の中で 1始まりは少数派かもしれないけど、n 個の要素を持つ配列のインデックスが 1 ~ n というのはわかりやすいともいえる。

だけど、実はインデックス 0 も使えるという。

え?
ということは、配列を Dim arr(3) って宣言するとインデックス 0 から 3 までの4要素の配列ができるってこと?

実際にやってみよう。

Sub TEST()
    Dim arr(3) As String
    
    arr(0) = "Andy"
    arr(1) = "Bill"
    arr(2) = "Charlie"
    arr(3) = "Dave"
    
    For i = 0 To 3
        MsgBox arr(i)
    Next i
End Sub

この TEST マクロを実行すると、たしかに Andy、Bill、Charlie、Dave の4つが順にメッセージボックスに表示される。

うへぇ、なにこの変な仕様。

Windows10のiTunesからMacのミュージックアプリへ音楽の移行は出来ない

出来ない、というか少なくともウチの環境では出来なかった。

移行アシスタント

Windows10 の iTunes にある音楽を新しい iMac のミュージックアプリにどうやって移行するのか、ググってみたところで見つかったのが「移行アシスタント」だ。これは同じネットワーク内にある Windows 側と iMac 側の両方で移行アシスタントを実行すると簡単にデータ(今回の目的は音楽だけだけど、これに限らない)を移行できるというもの。少なくとも Apple のページにはそう書いてある。

ところが、実際にページに書いてある手順でやろうとしたところ、そもそも Windows 版の移行アシスタントがインストールできない。ファイルをダウンロードし直したり、PC を再起動したりと色々やってみたけど、どうにもならない。移行アシスタントアプリのバージョンのせいなのかそれとも Windows の環境のせいなのか、何度やってもダメだった。

USBメモリでファイルを移動

仕方がないので、Windows 側の iTunes のデータフォルダを丸ごと USB メモリにコピーして iMac に挿した。で、ミュージックアプリにインポート。時間はかかったけど、とりあえず曲の移行はできた。

が、だ。

このやり方はつまり、ミュージックアプリに新しい曲をファイルから追加しただけなので、再生回数みたいなメタデータやプレイリストは移行されてない。

これらをどうやったら Windows から持ってくれるか、調べてみたけどいい方法が見つからなかった。もしかしたらやり方があるのかもしれないけど。

で、いつまでもこれだけに構っていられないので、もう諦めることにした。プレイリストもいくつか程度しか作ってないし、作り直せばいいだろ。

というわけで、何の役にも立たない記事だな。

あ、あとこれだけは書いておく。USB メモリを Windows と Mac の両方で使うには exFAT でフォーマットしておく必要がある。

Python: BOMつきUTF-8のCSVファイルを読み込む

Windows の Excel で、CSV ファイルに UTF-8 で出力ができることを知った。調べてみると結構前からできるようになってたようだ。

Excel で作ったデータを CSV ファイルに出力して Python のスクリプトで処理する、っていうのを時々やるんだけど、今まではいったん Shift JIS で出力したのを秀丸エディタを使って UTF-8 に変換してから処理していた。スクリプトは Linux でも使うから入力データのエンコーディングは UTF-8 にしておきたいんだよね。

で、Excel が UTF-8 の CSV をはいてくれるなら面倒な変換の手間を省いてそのままスクリプトで処理できる……と思ってやってみたらエラーになった。BOM(バイトオーダーマーク)がついているのが原因らしい。UTF-8 に BOM がついてるのとついてないのがあるのは知ってたけど、気にしたことはなかった。今回初めて BOM つきの UTF-8 に出くわした。軽くググってみると Windows だけが BOM をつけるらしい。

さて、本題。

Python で BOM つきの UTF-8 を読み込むには、ファイルを開くときのエンコーディングに utf-8-sig を指定してやればいい。↓こんな感じ。

>>> import csv
>>> with open('data_with_bom.csv', encoding='utf-8-sig') as f:
...     for row in csv.reader(f):
...         if row[0]:
...             print(row[0])
...

utf-8-sig というエンコーディングは BOM なしの UTF-8 も扱える。なので UTF-8 であることがわかっていれば BOM を気にしなくていい。

>>> with open('data_without_bom.csv', encoding='utf-8-sig') as f:
...     for row in csv.reader(f):
...         if row[0]:
...             print(row[0])
...

というわけで、解決。

[余談]

コード中に if row[0]: とあるのは Excel のはいた CSV ファイルの後ろのほうにカンマだけの行がくっついてるからそれを避けるため。なんでそんな行がくっつくのかは謎。

っていうか Python って後置の if が使えないんだね。ちょっと使いにくいな。

UbuntuでOneDriveをつかう

下準備

必要なパッケージをインストールする。

takatoh@apostrophe $ sudo apt install libcurl4-openssl-dev libsqlite3-dev gdebi
takatoh@apostrophe $ wget http://downloads.dlang.org/releases/2020/dmd_2.090.1-0_amd64.deb
takatoh@apostrophe $ sudo gdebi dmd_2.090.1-0_amd64.deb

OneDrive Client のインストール

takatoh@apostrophe $ git clone [email protected]:skilion/onedrive.git
takatoh@apostrophe $ cd onedrive
takatoh@apostrophe $ make
takatoh@apostrophe $ sudo make install

設定ファイルのコピー

takatoh@apostrophe $ mkdir -p ~/.config/onedrive
takatoh@apostrophe $ cp ./config ~/.config/onedrive/config

デフォルトの設定ファイルはつぎのようになっている。

# Directory where the files will be synced
sync_dir = "~/OneDrive"
# Skip files and directories that match this pattern
skip_file = ".*|~*"

サービスの起動

takatoh@apostrophe $ systemctl --user enable onedrive
takatoh@apostrophe $ systemctl --user start onedrive

初回起動

サービスとして起動したら、onedrive コマンドを実行。

takatoh@apostrophe $ onedrive

すると、端末にメッセージが表示される。「Authorize this app visiting:」につづいて URL が表示されるので、これをコピーしてブラウザでアクセスし、Microsoftアカウントにログインする。アプリから OneDrive へのアクセスを許可するかと聞かれるので「はい」を選択。新しい、空白のページが表示されるので、このページの URL をコピーして端末に戻り、「Enter the response uri:」のあとに貼り付けてエンター。これでファイルの同期が始まる。

自動起動の設定

ファイルの同期を続けるためには、onedrive コマンドを実行しておく必要があるらしい。Ubuntu にログインするときに自動的に実行するように設定する。

Alt+F2 キーを押してアクティビティ画面を表示し、「gnome-session-properties」を検索。出てきたアイコンをクリックすると「自動的に起動するプログラムの追加」ウィンドウが表示される。ここに onedrive -m コマンドを追加する。

参考ページ

 cf. UbuntuでOneDriveを使う – Qiita

Visual Studio Community 2019

Visual Studio Community 2019 をインストールしてみた。

ダウンロードはここから、Community 版をダウンロードした。

 cf. https://visualstudio.microsoft.com/ja/downloads/

ダウンロードしたファイルは、必要なファイルをダウンロードしながらインストールするタイプのものだ。実行すると少したって、コンポーネント(っていうのか?)を選ぶ、ワークロードっていう画面になるので、今回は「.NETデスクトップ開発」を選んでみた。なんかダウンロードするファイルが1.2GBくらいあって、しばらく時間がかかる。

インストールが完了すると、Windows の再起動を促される。指示通り再起動。これで完了(たぶん)。

Windows の再起動後、スタートメニューから Visual Studio 2019 を選んで起動すると、最初はサインインの画面になった。[email protected] でサインインする。初期化かなんかが行われたあと、作業できるようになる。

「新しいプロジェクトの作成」を選んでみよう。とりあえず「コンソールアプリ」。プロジェクト名を入力して「作成」をクリック。すると、どうやらサンプルとして、いわゆる hello wold プログラムのコードが書かれている画面になる。これがプロジェクトの編集画面らしい。

というわけで、ひとまずは大丈夫のようだ。あと2カ月で年も変わるし、C# でもやってみるかな。